剑指offer:数组中出现次数超过一半的数字

来源:互联网 发布:vc贪吃蛇c语言代码 编辑:程序博客网 时间:2024/06/08 11:58

题目描述:

  数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

测试样例:

  例如输入一个长度为9的数组{1,2,3,2,2,2,5,4,2}。由于数字2在数组中出现了5次,超过数组长度的一半,因此输出2。如果不存在则输出0。

思路一:

  基于Partition函数的O(n)算法:

  数组中有一个数字出现的次数超过了数组长度的一半。如果我把这个数组排序,那么排序之后位于数组中间的数字一定就是那个出现次数超过数组一半的数字。也就是说,这个数字就是统计学上的中位数,即长度为n的数组中第n/2的数字。我们有成熟的O(n)的算法得到数组中任意第K大的数字。

  这种算法是受快速排序算法的启发。在随机快速排序算法中,我们现在数组中随机选择一个数字,然后调整数组中数字的顺序,使得比选中的数字小的数字都排在它的左边,比选中的数字大的数字都排在它的右边。如果这个选中的数字的下标刚好是n/2,那么这个数字就是数组的中位数。如果它的下标大于n/2,那么中位数应该位于它的左边,我们可以接着在它的左边部分的数组中查找。如果它的下标小于n/2,那么中位数应该位于它的右边,我们可以接着在它的右边部分的数组中查找。这是一个典型的递归过程。

代码:

  时间复杂度O(n),空间复杂度O(1)

public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        int len = array.length;        if(len <= 0)            return 0;        int middle = len / 2;        int start = 0;        int end = len - 1;        int index = partition(array,start,end);        while(index != middle){            //要在左边继续找            if(index > middle){                end = index-1;                index = partition(array, start, end);             }else{                //要在右边继续找                start = index+1;                index = partition(array, start, end);            }        }        int times = 0;        for(int i = 0;i < len;i++){            if(array[i] == array[index])                 times++;        }        return times > len / 2 ? array[index] : 0;    }    public static int partition(int[] array,int low,int high){        int v = array[low];        int i = low;        int j = high + 1;        while(true){            while(i <= j && array[--j] > v);            if(i >= j)break;            while(i <= j && array[++i] < v);            if(i >= j)break;            swap(array,i,j);        }        swap(array,low,j);        return j;    }    public static void swap(int[] array,int x,int y){        int tmp = array[x];        array[x] = array[y];        array[y] = tmp;    }}

Result:

  Runtime:16ms

思路二:

  利用HashMap存储数组元素及其个数,将数字当作key,出现的次数当作value,首先取个数,如果为空,则置1,如果不为空,说明此前遇到过此数字,执行++操作。

  (补充)Map.Entry接口:Map的entrySet()方法返回一个实现Map.Entry接口的对象集合。集合中每个对象都是底层Map中一个特定的键/值对。通过这个集合的迭代器,您可以获得每一个条目(唯一获取方式)的键或值并对值进行更改。当条目通过迭代器返回后,除非是迭代器自身的remove()方法或者迭代器返回的条目的setValue()方法,其余对源Map外部的修改都会导致此条目集变得无效,同时产生条目行为未定义。

代码:

  时间复杂度O(n),空间复杂度O(n)

import java.util.*;public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {            if (array.length <= 0)            return 0;        HashMap<Integer, Integer> map = new HashMap<>();        for (int num : array) {            Integer count = map.get(num);            if (count != null) {                map.put(num, ++count);            } else {                map.put(num, 1);                count = 1;        }        if (count > array.length / 2)            return num;        }      /*    Iterator iter = map.entrySet().iterator();    while(iter.hasNext()){        Map.Entry entry = (Map.Entry)iter.next();        Integer key =(Integer)entry.getKey();        Integer val = (Integer)entry.getValue();        if(val>array.length/2){            return key;        }    }*/    return 0;    }}

Result:

  Runtime:14ms

思路三:

  首先将容器中的数字排序,因为要求输出重复次数大于数组大小一般的书,排序结束后,相同的数字都在相邻位置,直接判断数组当前位置的数字与数组位置+数组一半长度位置的数字是否相等,相等则输出该数字。

代码:

  时间复杂度O(nlongn),空间复杂度O(1)

import java.util.*;public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        if (array.length <= 0)        return 0;    int len = array.length / 2;        Arrays.sort(array);        for(int i = 0 ; i + len < array.length; i++){            if(array[i+len] == array[i])                return array[i];        }        return 0;    }}

Result:

  Runtime:13ms

思路四:

  依旧先将数组进行快排,如果数组中有一个数字出现的次数超过数组长度的一半,那么快排后数组中间的数就一定是这个数字。遍历快排后的数组,如果最中间的数的个数大于数组长度的一半,则输出这个数,否则输出0。

代码:

  时间复杂度O(nlongn),空间复杂度O(1)

import java.util.*;public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        if (array.length <= 0)        return 0;    int len = array.length / 2;        Arrays.sort(array);        int mid = array[len],j = 0;        for(int i = 0 ; i < array.length; i++){            if(mid == array[i])               j++;            if(j > len)                return mid;        }        return 0;    }}

Result:

  Runtime:12ms

思路五:

  如果有符合条件的数字,则它出现的次数比其他所有数字出现的次数和还要多。

  在遍历数组时保存两个值:一是数组中一个数字,一是次数。遍历下一个数字时,若它与之前保存的数字相同,则次数加1,否则次数减1;若次数为0,则保存下一个数字,并将次数置为1。遍历结束后,所保存的数字即为所求。然后再判断它是否符合条件即可。

代码:

  时间复杂度O(n),空间复杂度O(1)

public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        if(array.length <= 0)            return 0;        // 遍历每个元素,并记录次数;若与前一个元素相同,则次数加1,否则次数减1        int res = array[0];        int times = 1;// 次数        for(int i = 1; i < array.length; i++){            if(times == 0){                // 更新result的值为当前元素,并置次数为1                res = array[i];                times = 1;            } else if(array[i] == res)                ++times;// 相同则加1            else                --times;        }        // 判断result是否符合条件,即出现次数大于数组长度的一半        times = 0;        for(int n : array){            if(n == res)                times++;        }        return times > array.length/2 ? res : 0;    }}

Result:

  Runtime:17ms

思路六:

  注意到目标数超过数组长度的一半,对数组同时去掉两个不同的数字(可以用0模拟去掉过程),到最后剩下的一个数就是该数字。如果剩下两个,那么这两个也是一样的,就是结果),在其基础上把最后剩下的一个数字或者两个回到原来数组中,将数组遍历一遍统计一下数字出现次数进行最终判断。

代码:

public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        int len = array.length;        if(len <= 0)            return 0;        int [] temp = new int [len];        for(int i = 0; i < len;i++)            temp[i] = array[i];        for(int i = 0; i < len;i++){            //后面需要用零来代表抹除数字,所以对0时做特殊处理            if(temp[i] == 0)                continue;            for(int j = i + 1;j < len;j++){                if(temp[j] != temp[i] && temp[j] != 0){                    temp[i] = 0;                    temp[j] = 0;//此处用0代表抹去该数字                    break;                }            }        }        //找出未被抹去的数字        int res = 0;        for(int i : temp)            if(i != 0){                res = i;                break;            }        int times = 0;        for(int n : array)            if(n == res)                times++;        return times > len / 2 ? res : 0;    }}

Result:

  Runtime:16ms

思路七:

  首先是输入是数字,介于0~9之间(晕我居然才知道)。所以我们可以首先建立一个含有10个元素的数组,然后用输入的数字作为下标以统计每个数字出现的次数。再对这个数组与长度的一半进行比较,输出结果。

代码:

public class Solution {    public int MoreThanHalfNum_Solution(int [] array) {        int len = array.length;        if(len <= 0)            return 0;        int [] temp = new int [10];        for(int i : array)            temp[i]++;        for(int j = 0; j < 10;j++)            if(temp[j] > len / 2)                return j;        return 0;    }}

Result:

  Runtime:15ms

Any comments greatly appreciated.

—–乐于分享,共同进步
—–更多文章请看:http://blog.csdn.net/u011489043

原创粉丝点击