【剑指Offer-时间效率】面试题39:数组中出现超过一半的数字

题目描述

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

思路1

假如数组是排好序的并且存在超过长度(假设为n)一半的数字,那么这个数字肯定是第n/2个数字,也就是说这个数字是排序后数组的中位数。我们可以借助快速排序的partition函数来实现这一算法。partition函数的步骤为:选择一个基准数字(通常选数组的第一个数字),将数组分为两部分,左边都小于基准,右边都大于基准,然后返回分割点的下标。如果分割点的下标小于n/2,那么说明中位数在分割点的右半部分;如果分割点的下标大于n/2,则说明中位数在分割点的左半部分;如果分割点的下标等于n/2,则循环结束。代码如下:

class Solution {
public:
    
    void swap(vector<int>& v, int i, int j){
        int t = v[i];
        v[i] = v[j];
        v[j] = t;
    }
    
    int partition(vector<int> v, int start, int end){
        int i=start;
        int j=end;
        int baseline = v[start];
        while(i<j){
            while(v[i]<=baseline && i<j)
                i++;
            while(v[j]>=baseline && i<j)
                j--;
            swap(v, i, j);
        }
        swap(v, start, i);
        return i;
    }
    
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty())
            return 0;
        
        int left = 0;
        int length = numbers.size()-1;
        int right = numbers.size();
        int baseline = numbers[0];
        int idx = partition(numbers, left, right);
        while(idx!=length/2){
            if(idx<length/2){
                left = idx+1;
                idx = partition(numbers, left, right);
            }
            else if(idx>length/2){
                right = idx-1;
                idx = partition(numbers, left, right);
            }
        }
        
        int ans = numbers[idx];    //判断答案的出现次数是否大于数组长度的一半
        int times = 0;
        for(int i=0; i<numbers.size(); i++){
            if(numbers[i]==ans)
                times++;
        }
        if(times>length/2)
            return ans;
        else return 0;
    }
};

该算法的时间复杂度为O(n).

思路2

假如数组中存在出现次数超过数组长度一半的数字,则该数字出现的次数比其他数字出现次数的和还要大。因此,在遍历数组的时候保留两个值:一个是数组中的一个数字,另一个是次数(初始化为1)。当下一个数字和保留的数字相同时,次数加1,;当不同时,次数减1,当次数为0时(说明保留的数字不满足出现次数比其余数字出现的次数和还要大),将保留的数字变为下一个数字,并把次数设为1。这样,由于满足条件的数字出现的次数比其余数字出现次数的和还要大,所以当遍历结束时,最后一次保留的数字就是要求的答案。代码如下:

class Solution {
public:
    
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        if(numbers.empty())
            return 0;
        
        int ans = numbers[0];
        int cnt = 1;
        for(int i=1; i<numbers.size(); i++){
            if(numbers[i]==ans)
                cnt++;
            else{
                cnt--;
                if(cnt==0){
                    ans = numbers[i];
                    cnt = 1;
                }
            }
        }
       
        int times = 0;
        for(int i=0; i<numbers.size(); i++){
            if(numbers[i]==ans)
                times++;
        }
        if(times>numbers.size()/2)
            return ans;
        else return 0;
    }
};

这个算法只需要把数组遍历一遍,所以时间复杂度为O(n)。

posted @ 2020-03-16 10:56  Flix  阅读(137)  评论(0编辑  收藏  举报