剑指Offer——数组中出现次数超过一半的数字——一题多解

看题目:算法

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

 

个人直接思路:函数

用map计数,简单直接,遍历一次数组,用hashmap记录,key为int值,value为出现次数;spa

第二次再用map.entrySet找出有没value大于数组长度通常的entry,有的话返回它的key。code

时间复杂度也是2n而已,这个方法时间复杂度是O(n)空间复杂度也是O(n)blog

 

代码实现:排序

/*方法1
      蛮力,遍历一次,用一个map来记录
      第二次遍历把出现次数大于length/2的那个值找出来
      */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        int targetCount = array.length / 2;
        
        //key为数组中的值,value为出现的次数
        HashMap<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int temp : array) {
            if(map.get(temp) == null)map.put(temp, 1);
            else map.put(temp, map.get(temp) + 1);
        }
        Set<Entry<Integer,Integer>> entrySet = map.entrySet();
        for(Entry<Integer,Integer> entry : entrySet) {
            if(entry.getValue() > targetCount)return entry.getKey();
        }
        return 0;
    }
    

 

方法2:中位数get

若是数组是排好序的,就好解决了:
  若是排好序,而后又存在这样的数字的话,那么它的值确定和数组中间那个值是同样的!!(好比:1,2,2,2,3;或2,2,2,3,4;或2,3,4,4,4等等)
 
因此咱们只须要排序后,一次遍历,访问每一个元素都和中间值比较,只要相等计数器就加一,遍历完后若是计数器大于数组长度一半就返回那个中间元素就好。
 
排序最快是O(Nlogn),而后加上遍历是O(n),空间复杂度是O(1)
 
代码里直接用的是Arrays的sort方法,它的实现没记错是O(Nlogn)的快排。
看代码:
/*方法2
      排序后中位数法
      若是有个数字出现的次数大于数组长度的一半,那么这个数组排序后,它的中位数必定是这个数字
    */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        Arrays.sort(array);
        int count = 0, middleNum = array[array.length / 2];
        for(int temp : array) {
            if(temp == middleNum)count++;
        }
        if(count > array.length / 2)return middleNum;
        else return 0;
    }
    

 

 

方法3:——快排思路hash

方法2中,咱们排序是为了找出中位数,那么若是能够更快地找出中位数就不用排序了。it

借鉴快速排序算法,其中的Partition()方法是一个最重要的方法,该方法返回一个index,可以保证index位置的数是已排序完成的,在index左边的数都比index所在的数小,在index右边的数都比index所在的数大。那么本题就能够利用这样的思路来解。

经过Partition()返回index,若是index==mid,那么 就代表找到了数组的中位数若是index<mid,代表中位数在[index+1,end]之间;若是index>mid,代表中位数在[start,index-1]之间。直到最后求得index==mid循环结束。
 
说白了就是找中位数这一步,咱们不选择直接排序数组,而是用快排的Partition方法
 
该算法的时间复杂度为O(n),空间复杂度为O(1)
 
看代码:
/*方法3
    利用快排的思想
    */
    
    public int MoreThanHalfNum_Solution(int [] array) {
        if(array.length <= 0)return 0;
        
        int begin = 0, end = array.length - 1, middle = array.length / 2;
        int partition = partition(array, begin, end);
        
        while(partition != middle) {
            if(partition > middle) {//中位数在partition的左边
                partition = partition(array, begin, partition - 1);
            } else {//中位数在partition右边
                partition = partition(array, partition + 1, end);
            }
        }
        
        //找出中位数了,看这个中位数出现的次数是否符合要求
        int count = 0, middleKey = array[middle];
        for(int temp : array) {
            if(temp == middleKey)count++;
        }
        
        if(count > array.length / 2)return array[middle];
        else return 0;
    }
    
    //这个方法是以第一个元素为基准,而后进行划分,划分后比基准元素小的数字都在它左边,比它大的数字都在它右边
    返回划分后,这个元素的新index//
    private int partition(int[] a, int begin, int end) {
        int key = a[begin];
        
        int i = begin, j = end;
        while(i < j) {
            while(i < j && a[j] >= key)j--;
            while(i < j && a[i] <= key)i++;
            swap(a, i, j);
        }
        swap(a, begin, i);
        return i;
    }
    
    //交换数字函数,传入数组还有要交换的两个数字的index//
    private void swap(int[] a, int first, int second) {
        int temp = a[first];
        a[first] = a[second];
        a[second] = temp;
    }
    

 

方法4——阵地攻守思想

第一个数字做为第一个士兵,守阵地;count = 1;
遇到相同元素,count++;
遇到不相同元素,即为敌人,玉石俱焚,count--;当遇到count为0的状况,又以新的i值做为守阵地的士兵,继续下去,同时count更新为1.
到最后还留在阵地上的士兵,有多是主元素。

再加一次循环,记录这个士兵的个数看是否大于数组通常便可。
 
这个方法也是主要由于考虑到:题目中要找的数字出现的次数超过数组长度的一半,也就是说它出现的次数比其余全部数字出现的次数的和还要多。,那么确定最后留下来的是那个出现超过一半的。
 
该方法时间复杂度O(N),空间复杂度O(1)
 
 
实现的时候就维护两个变量:一个是数组中的一个数字,一个是次数。

当咱们遍历到下一个数字的时候,

若是下一个数字和当前咱们保存的数字相同,则次数加 1;

若是和当前咱们保存的数字不一样,则次数减 1;

当次数减到 0 的时候,咱们将保存的数字改成当前遍历所处的位置,并将次数更改成 1。

 

看代码:

/*方法4
        阵地攻守思想,其实和牛客上面那个什么“用户分形叶”的思路同样的,不一样实现而已
    */
    public int MoreThanHalfNum_Solution(int [] array) {
        int count = 1, key = array[0];
        for(int temp : array) {
            if(temp == key)count++;
            else if(count > 0)count--;
            else {
                key = temp;
                count = 0;
            }
        }
        
        //判断这个获得的key是否是符合要求
        int count2 = 0;
        for(int temp : array) {
            if(temp == key)count2++;
        }
        if(count2 > array.length / 2)return key;
        else return 0;
    }
相关文章
相关标签/搜索