剑指offer之面试题29:数组中出现次数超过一半的数字

来源:互联网 发布:出入无时 莫知其乡解释 编辑:程序博客网 时间:2024/05/16 16:04

题目描述

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

思路:
一般方法:先排序,然后统计每个数字出现的次数,O(nlogn)
其他方法:
数组中有一个数字出现的次数超过数组长度的一半,如果把这个数组排序,那么排序之后位于数字中间的数字一定就是要找的数,如果有的话,转化成中位数问题,即长度为n的数组中第n/2大的数字,有期望时间为线性的随机选择算法或者最坏时间为线性的k选择算法。

代码如下:

public class Solution {    public static boolean g_InputInvalid=false;    public static int MoreThanHalfNum_Solution(int [] array) {        //数组长度超过一半,换种角度考虑就是,当数组有序时,该数应位于数组的中间        //后者其实就是中位数的定义,那原来的题目就变成找出数组的中位数(前提:并对该中位数计数,若超出数组长度一半时,才等价)        //中位数的程序,算法导论中有讲到,利用partition函数随机化版本,可以在期望时间O(n)内找到,这种算法是原址的,不使用额外空间        if(array==null||array.length<=0){            g_InputInvalid=true;            return 0;        }        int middle=array.length>>1;//位操作比除操作快,等价于array.length/2        int start=0;        int end=array.length-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 result=array[middle];        if(!CheckMoreThanHalf(array,result)){            result=0;        }        return result;    }    public static boolean CheckMoreThanHalf(int[] array, int result) {         int times = 0;            for (int i = 0; i < array.length; i++) {                if(array[i] == result){                    times++;                }            }            boolean isMoreThanHalf = true;            if(times * 2 <= array.length){                g_InputInvalid=true;                isMoreThanHalf = false;            }            return isMoreThanHalf;    }    //O(n)    public static int Partition(int[] array, int start, int end) {        int k=(int)(Math.random()*(end - start + 1)) + start;        swap(array,k,end);        int x=array[end];        int i=start-1;        for(int j=start;j<end;j++){            if(array[j]<=x){                i++;                swap(array,i,j);            }        }        swap(array,i+1,end);        return i+1;    }    public static void swap(int[] array, int k, int end) {        int temp=array[k];        array[k]=array[end];        array[end]=temp;           }    public static void main(String[] args){        int[] array = new int[]{1,2,3,2,2,2,5,4,2};        int a = MoreThanHalfNum_Solution(array);        System.out.println(a);    }}

另外一种解法,时间O(n)
数组中一个数字出现的次数超过数组长度的一半,说明出现的次数比其他数出现的次数之和还多。可以保存两个值,一个是数字,一个是次数。当遍历到一个数时,如果与前一个数相同,则times+1,如果不同times-1,当times=0时,将当前值保存,并将times设为1,当然这样只能排除不超过一半的,如果存在超过一半的只能是最后设为1的数,所以还要加个判断。
代码如下:

public class Solution {   public static boolean g_InputInvalid=false;    // not change array's element,cost is O(n)    public static int MoreThanHalfNum_Solution(int[] array) {        if(array==null||array.length<=0){            g_InputInvalid=true;            return 0;        }        int result=array[0];        int times=1;        for(int i=0;i<array.length;i++){            if(array[i]==result)                times++;            else                times--;            if(times==0){                result=array[i];                times=1;            }        }        if(!CheckMoreThanHalf(array,result)){            result=0;        }        return result;    }    public static boolean CheckMoreThanHalf(int[] array, int result) {        int times=0;        for(int i=0;i<array.length;i++){            if(array[i]==result)                times++;        }        boolean isMoreThanHalf=true;        if(times*2<=array.length){            isMoreThanHalf=false;            g_InputInvalid=true;        }        return isMoreThanHalf;    }    public static void main(String[] args){        int[] array=new int[]{1,2,3,2,2,2,5,4,2};        int result=MoreThanHalfNum_Solution(array);        System.out.println(result);    }}
0 0