数组中出现次数超过一半的数字 多种方法

传送门

题目描述

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

分析:

1.我们首先根据数组特性,出现次数超过一半,意味着我们将数组进行排序后,中间位置的数必然是我们要寻找的数,基于这样的想法首先得到这样的解法,时间复杂度O(nlogn)

//数组中有一个数字出现次数超过数组长度的一半,那么从统计意义上说,这个数必然是数组排序后的中位数
//由于先进行了排序  时间复杂度为O(nlogn)
class Solution {
public:
    Solution()
    {
         
    }
    ~Solution()
    {
         
    }
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if(len<=0) return 0;
        sort(numbers.begin(),numbers.end());
        int solve = numbers[len/2];
        //从左右开始统计
        int ans = 0;
        for(int i = len/2,j = 0;;j++)
        {
            if(j==0) {
                ans++;
                continue;
            }
            if(i-j>=0&&numbers[i-j]==solve) ans++;
            if(i+j<len&&numbers[i+j]==solve) ans++;
            if(i-j<0&&i+j>=len) break;
        }
        if(ans>len/2) return solve;
        return 0;
    }
};

2.上述解法虽然能够AC,但是时间复杂度为O(nlogn),不够优秀,这时我们能够想到其实可以使用哈希表来统计每个数出现的次数,只需要遍历一遍数组即可,时间复杂度为O(n)

//利用哈希表思路,时间复杂度为O(n)
class Solution {
private:
    map<int,int>num;
public:
    Solution()
    {
        num.clear();
    }
    ~Solution()
    {
         
    }
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if(len<=0) return 0;
        int k = len>>1;
        for(int i=0;i<len;i++)
        {
            num[numbers[i]]++;
            if(num[numbers[i]]>k) {
                return numbers[i];
            }
        }
        return 0;
    }
};

3.接下来我们思考可不可以不开辟额外空间,我们了解到一次快排可以得知当前主元在原数组中所处的位置,同时我们要得到下标为数组长度一半的元素,这样我们使用快排+二分

注意:这里的快排使用了随机化主元的方式,在进行randindex()函数的时候,出现过除0错误,这是由于rand的上界和下界相等造成的,加一个相等的特判即可。

//基于快排+二分的思想
//这种写法是单路快排,更加好的方法三路快排
class Solution {
public:
    Solution()
    {
        
    }
    ~Solution()
    {
        
    }
    int randindex(int l ,int r)
    {
        if(l==r) return l;
        srand(time(NULL));
        return (int)rand()%(r-l)+l;
    }
    int quick_sort(int l, int r,vector<int> &num)
    {
        //找到主元所在位置   随机化主元
        int i = l ;
        int j = r;
        int index = randindex(l,r);
        int temp = num[index];
        swap(num[index],num[l]);
        while(i!=j)
        {
            while(i<j&&num[j]>=temp) j--;
            while(i<j&&num[i]<=temp) i++;
            swap(num[i],num[j]);
        }
        swap(num[l],num[i]);
        return i;
    }
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if(len<=0) return 0;
        //进行多次快排寻找位于len/2的元素
        int l = 0 , r = len-1,solve;
        while(l<=r)
        {
            int index = quick_sort(l,r,numbers);
            if(index==len/2) {
                solve = numbers[index];
                break;
            }
            else if(index>len/2) r = index-1;
                 else if(index<len/2) l = index+1;
        }
        int ans = 0;
        for(int i=0;i<len;i++)
            if(numbers[i]==solve) ans++;
        if(ans>len/2) return solve;
        return 0;
    }
};

4.最后是三路快排实现的方法,三路快排返回一个区间范围,表示当前主元及与当前主元相等的元素都聚集的一个范围,当中间元素在这个范围内时,则说明我们找到解,之后在进行判断即可,反之,我们对区间再进行二分

//二分+三路快排
class Solution {
public:
    Solution()
    {
        
    }
    ~Solution()
    {
        
    }
    int randindex(int l,int r)
    {
        srand(time(NULL));
        if(l==r) return l;
        return rand()%(r-l)+l;
    }
    //进行三路快排,每次都将重复的主元都放到一起,返回这个区间
    pair<int,int> ThreeWaysQuick_sort(int l,int r,vector<int>& vec)
    {
        //随机化选取主元
        int index = randindex(l,r);
        int temp = vec[index];
        //利用三个指针实现三路快排
        //p1指向第一个是temp的数,方便与后面小于temp的数进行交换
        //p2指向最后一个是temp的数,一旦发现一个小于temp的数,马上与p1指针指向的数进行交换
        //p3指向大于temp的第一个数
        int p1 = l , p2 = l ,p3 = r;
        while(p2<=p3)
        {
            if(vec[p2]==temp) {
                p2++;
            }
            else if(vec[p2]<temp) {
                swap(vec[p1],vec[p2]);
                p1++;
                p2++;
            }
            else if(vec[p2]>temp) {
                swap(vec[p3],vec[p2]);
                p3--;
            }
        }
        if(vec[p3]==temp) p3++;
        return make_pair(p1,p2);
    }
    int MoreThanHalfNum_Solution(vector<int> numbers) {
        int len = numbers.size();
        if(len<=0) return 0;
        int l = 0 , r = len - 1 , solve;
        while(l<=r)
        {
            //三路快排,每次都将相同的数放到一起,所以得到的是一个当前主元的区间范围,如果返回的区间范围包括了中间值,那么结束二分
            pair<int,int> index;
            index = ThreeWaysQuick_sort(l,r,numbers);
            if(len/2>=index.first && len/2<=index.second) {
                solve = numbers[len/2];
                break;
            }
            else if(len/2<index.first) {
                r = index.first-1;
            }
            else if(len/2>index.second) {
                l = index.second+1;
            }
        }
        int ans = 0;
        for(int i = len/2 , j = 0; ; j++ )
        {
            if(j==0) {
                ans++;
                continue;
            }
            if(i-j>=0) {
                if(solve==numbers[i-j]) ans++;
            }
            if(i+j<len) {
                if(solve==numbers[i+j]) ans++;
            }
            if(i-j<0&&i+j>=len) break;
        }
        if(ans>len/2) return solve;
        return 0;
    }
};

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值