剑指 offer

java实现:剑指 offer--面试题39(数组中出现次数过半的数字)

第五章 优化时间和空间效率

面试题39

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

方法一 切分法

由于数字超过数组长度的一半,故数组排序后,数组中间的数据必然是这个数字。
这样问题就转化为求数组的中位数,快速排序使用的切分算法可以方便的找出中位数,且时间负责度为O(n),找出中位数后,还需要再遍历一次数组,检查该数据是否出现次数超过数组长度的一半。
总结基于切分算法有如下两步骤:

  • 切分找出中位数
  • 检查中位数出现的次数
public class offer039 {

    public int moreThanHalfNum(int[] array){
        if(null == array || array.length == 0){
            return 0;
        }

        int mid = find(array,array.length/2);

        return checkMoreThanHalf(array,mid);
    }

    private int checkMoreThanHalf(int[] array, int number) {
        int count = 0;

        for(int a :array){
            if(number == a){
                count++;
            }
        }
        return count>array.length/2?number:0;
    }


    /**
     * 选择排名为k的元素,只是部分排序,时间复杂度为O(N)
     * @param array
     * @param k
     * @return
     */
    private int find(int[] array, int k) {
        int low = 0;
        int high= array.length -1;

        while(high>low){
            int j = partition(array,low,high);
            if(j == k) {
                return array[k];
            }else if(j>k){
                high = j-1;
            }else {
                low= j+1;
            }

        }

        return array[k];
    }

    /**
     * 快速排序的切分方法
     * @param array
     * @param start
     * @param end
     * @return
     */
    private int partition(int[] array, int start, int end) {
        int low = start;
        int high = end;
        //这里选择开始节点作为哨兵,也可以随机一个点
        int soldier = array[start];

        while (low<high){
           while(low <high && soldier<= array[high]){
               high--;
           }
           array[low] = array[high];
           while (low<high && array[low]<=soldier){
               low++;
           }
           array[high] = array[low];
        }
        array[low] = soldier;
        return low;
    }
}


方法二 基于数组特性

数组中有个数字出现次数超过一半,故这个数字出现的次数比其他所有数字次数的出现次数之和还要多。
于是可以考虑下面的方法:
使用两个变量:currentValue—表示当前的数字,count—表示当前数字的计数值;
情况1:count=0时,currentVaule = 新遍历的数字,count = 1;
情况2:count>0,新遍历的值和currentVaule 相同,count+1;不同,count-1
遍历完成后,最后currentValue可能就是我们要找的数字,
注意是可能,因为输入数组可能本身就不满足里面有某个数字出现次数超过一半。因此和上面的找中位数方法一样,得到的currentValue还需要进一步检验。


    public int moreThanHalfNum(int[] array){
        int currentValue =0;
        int count=0;
        
        for(int a : array){
            if(count == 0){
                currentValue = a;
                count++;
            }else {
                if(a == currentValue){
                    count ++;
                }else {
                    count --;
                }
            }
        }

        return checkMoreThanHalf(array,currentValue);
    }

    private int checkMoreThanHalf(int[] array, int number) {
        int count = 0;

        for(int a :array){
            if(number == a){
                count++;
            }
        }
        return count>array.length/2?number:0;
    }
   
    
 
    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值