zoukankan      html  css  js  c++  java
  • 【剑指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)。

  • 相关阅读:
    Splay板子
    HZOI帝国2019欢乐时刻
    数据分析体系 — 用户粘性的两个计算指标(DAU/MAU和月人均活跃天数)
    mysql—MySQL数据库中10位时间戳转换为标准时间后,如何对标准时间进行加减X天处理
    每日一题力扣575 分糖果
    每日一题力扣455 小饼干和孩子
    每日一题力扣561
    每日一题力扣121 最佳买股票
    每日一题力扣605 种花问题
    每日一题力扣605 种花问题
  • 原文地址:https://www.cnblogs.com/flix/p/12501153.html
Copyright © 2011-2022 走看看