左神算法课-找出数组中出现次数超过一半的数及其进阶

来源:互联网 发布:炉石毕游侠知乎 编辑:程序博客网 时间:2024/05/02 23:55

原问题描述

给定一个整形数组arr,打印其中出现次数超过一半的数,如果没有这样的数,打印“no such number”;

解法1:

思路:遍历数组中所有的数,用map记录各数出现的次数,key表示原数组中的某个数,value代表出现的次数;找出map中value最大的那个key,再去遍历原数组,看该数是不是满足出现次数大于一半这个条件;
代码:这种情况比较简单就不贴代码了

解法2:

思路:如果存在这样的数,那么按照大小排序过后的数组中间那个数必然是所求的数,按照这个思路,将原数组排序,找出中间那个数,再遍历原数组看是否满足条件(无论如何這一步是省不掉的);
代码:比较简单就不贴了

解法3(左神思路)

思路:如果存在这样的数,那么我每次删掉两个不相同的数,最后剩下的那个就是所求的数,思路比较好理解,但是编码就比较巧妙了,按照这个思路可以有以下编码技巧:所谓删除并不是真正的删除而是记录之后就过去了;temp记录当前等待配对的数,lives表示等待配对的数有几个; 遍历到某个数a[i]时,如果等于等待配对的数,生命值加一,如果不等于,且现在生命值>0,就生命值减1(就相当于同时删除这两个数);如果现在生命值为0,变换等待配对的数为a[i]生命值为1;最终等待配对的数就是剩下的数,拿这个数去验证出现次数是否满足要求;算法复杂度是O(n);
代码:

public static void func2(int[] a) {            int temp = a[0];            int lives = 1;            for (int i = 1; i < a.length; i++) {                if (a[i] == temp) {                    lives++;                } else if (lives > 0) {                    lives--;                } else{                    temp = a[i];                    lives = 1;                }               }            int times = 0;            for (int i = 0; i < a.length; i++) {                if (temp == a[i])                    times++;            }            if (times > a.length / 2)                System.out.println(temp);            else                System.out.println("no such number");        }

巧妙处在于如果当前的待配对数的生命值大于零,且来了一个跟配对数不一样的数,就将生命值减一,好像是删除了这两个数;最后那个待配对数就是待验证的数;

进阶题目描述

给定一个整形数组arr长度是N,给定整数k,打印数组中出现次数大于N/k的数,如果没有这样的数,打印“no such number”
思路:首先要知道,出现次数大于k的数最多最多是k-1个,仿照上面的解法3;可以得到:设立一个大小最大为k-1的map,map的key指数组中的某个数,value指该数的生命值。遍历到某数a[i]时,如果map中有a[i]对应的key就将它的value加一,如果map中没有,就看下现在map满没有,如果没有满,就将这个a[i]放进去;如果现在map已经满了,就将所有的map中的value减一,之后将所有的value为0的键值对删除(这样就是变相的删除了不同的k个数);最终将map中所有的值拿去验证出现次数,满足条件的输出;
代码

public static void func3(int[] a, int k) {        HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(k - 1);        for (int i = 0; i < a.length; i++) {            if (!map.containsKey(a[i]))                if (map.size() < k - 1)                    map.put(a[i], 1);                else {                    Iterator<Integer> iterator = map.keySet().iterator();                    while (iterator.hasNext()) {                        int key = iterator.next();                        int value = map.get(key);                        if (--value == 0)                            iterator.remove();                        else                            map.put(key, value);                    }                }            else                 map.put(a[i], map.get(a[i]) + 1);        }        boolean flag = false;        for (Integer key : map.keySet()) {            for (int i : a)                System.out.print(i);            int times = 0;            for (int i = 0; i < a.length; i++) {                if (a[i] == key)                    times++;            }            if (times > a.length / k) {                System.out.println(key);                flag = true;            }        }        if (!flag)            System.out.println("no such number");    }

思路倒是不难想,主要是这种好像删除的操作编码方式,值得借鉴!