• 【剑指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)。

  • 相关阅读:
    增加一个基类没有的方法
    修改或加强基类的属性
    linux rm命令详解
    Apache的配置httpd.conf杂谈
    解决 You don't have permission to access / on this server. 错误的另一方法
    ubuntu下成功配置LAMP 并安装PHPMyadmin
    C#连接SQLite的方法
    内存使用大比拼 之 String – StringBuffer
    非常喜欢Gedit,绝不逊色EditPlus!
    关于内存
  • 原文地址:https://www.cnblogs.com/flix/p/12501153.html
Copyright © 2020-2023  润新知