剑指offer第二版-39.数组中出现次数超过一半的数字

本系列导航:剑指offer(第二版)java实现导航帖

面试题39:数组中出现次数超过一半的数字

题目要求:
找出数组中出现次数超过数组长度一半的数字。如输入{1,2,3,2,2,2,5,4,2},则输出2。

解题思路:
因为该数字的出现次数超过了数组长度的一半,因此可以将问题转化为求数组的中位数。如果按照这个思路,有以下两种方式解决:排序后求中位数、用快排的分区函数求中位数(topK问题),这两种思路都比较简单,此处不再赘述。
书中还提到一种思路,相当巧妙,可以看作一种特殊的缓存机制。该思路需要一个整型的value变量和一个整型的count变量,记录缓存值与该缓存值被命中的次数。缓存规则以及执行步骤如下:

步骤1: 缓存值value,命中次数count均初始化为0。
步骤2: 从头到尾依次读取数组中的元素,判断该元素是否等于缓存值:
     步骤2.1:如果该元素等于缓存值,则命中次数加一。
     步骤2.2:如果该元素不等于缓存值,判断命中次数是否大于1:
              步骤2.2.1:如果命中次数大于1,将命中次数减去1。
              步骤2.2.2:如果命中次数小于等于1,则令缓存值等于元素值,命中次数设为1
步骤3: 最终的缓存值value即为数组中出现次数超过一半的数字。

此方法时间复杂度o(n),空间复杂度o(1),实现简单。

package chapter5;

/**
 * Created with IntelliJ IDEA.
 * Author: ryder
 * Date  : 2017/7/28
 * Time  : 17:51
 * Description: 数组中出现次数超过一半的数字
 **/
public class P205_MoreThanHalfNumber {
    //转化为topK问题(此处求第k小的值),使用快排的分区函数解决,求第targetIndex+1小的数字(下标为targetIndex)
    //书中说这种方法的时间复杂度为o(n),但没懂为什么。网上也有人说为o(nlogk)
    public static int moreThanHalfNum1(int[] data){
        if(data==null || data.length==0)
            return 0;
        int left = 0,right=data.length-1;
        //获取执行分区后下标为k的数据值(即第k+1小的数字)
        int k = data.length>>>1;
        int index = partition(data,left,right);
        while(index!=k){
            if(index>k)
                index = partition(data,left,index-1);
            else
                index = partition(data,index+1,right);
        }
        return data[k];
    }
    //分区,[小,povot,大]
    public static int partition(int[] data,int left,int right){
        int pivot = data[left];
        while(left<right){
            while (left<right && data[right]>=pivot)
                right--;
            if(left<right)
                data[left] = data[right];
            while (left<right && data[left]<pivot)
                left++;
            if(left<right)
                data[right] = data[left];
        }
        data[left] = pivot;
        return left;
    }

    //根据数组特点进行记录、查找,时间复杂度o(n)
    public static int moreThanHalfNum2(int[] data){
        if(data==null || data.length==0)
            return 0;
        int count = 1;
        int value = data[0];
        for(int i=1;i<data.length;i++){
            if(data[i]==value)
                count++;
            else if(data[i]!=value && count==1)
                value = data[i];
            else
                count--;
        }
        return value;
    }
    public static void main(String[] args){
        int[] data = {1,2,3,2,2,2,5,4,2};
        System.out.println(moreThanHalfNum2(data));
        System.out.println(moreThanHalfNum1(data));
    }
}

运行结果

2
2

推荐阅读更多精彩内容