169. Majority Element

本文介绍五种有效算法来解决数组中寻找出现次数超过一半的多数元素问题。包括排序法、哈希表法、随机法、位运算法及推荐的投票计数法,并详细解释每种方法的实现原理和复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times.

You may assume that the array is non-empty and the majority element always exist in the array.

题解

题目要求就是找到出现次数超过一半的数,即大于n/2。

解法一:排序 —— 时间复杂度O(nlogn) 空间复杂度O(1)
一个数字在数组中的出现次数超过了一半,那么在已排好序的数组索引的N/2处(从零开始编号),就一定是这个数字。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        return nums[nums.size() / 2];
    } 
};

解法二:Hash表 —— 时间复杂度O(n) 空间复杂度O(n)
既要缩小总的时间复杂度,那么可以用查找时间复杂度为O(1)的hash表,即以空间换时间。哈希表的键值(Key)为数组中的数字,值(Value)为该数字对应的次数。
遍历时当一个数字出现的次数大于n/2,输出即可。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        unordered_map<int, int> counts; 
        int n = nums.size();
        for (int i = 0; i < n; i++)
            if (++counts[nums[i]] > n / 2)
                return nums[i];
    }
};

解法三:随机 —— 时间复杂度O(n*x) 空间复杂度O(1)
这里x指的是取随机数的次数,你可能会觉得很扯,但是这个算法的效率绝对不差。
我们可以想一想,每次直接找到结果的概率是 (出现次数最多的数的次数)/ n > 1/2。
所以如果我们认为(1/2)^k为0,当k为任意大于常数x的整数,则最多x次得到结果,时间复杂度就相当于O(n)。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int n = nums.size();
        srand(unsigned(time(NULL)));
        while (true) {
            int idx = rand() % n;
            int candidate = nums[idx];
            int counts = 0; 
            for (int i = 0; i < n; i++)
                if (nums[i] == candidate)
                    counts++; 
            if (counts > n / 2) return candidate;
        }
    }
};

解法四:位运算 —— 时间复杂度O(n) 空间复杂度O(1)
找出每一位上1出现次数超过一半的,合在一起就是结果。因为如果major该位为0,其他所有的数该位都为1,也不会超过一半。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int major = 0, n = nums.size();
        for (int i = 0, mask = 1; i < 32; i++, mask <<= 1) {
            int bitCounts = 0;
            for (int j = 0; j < n; j++) {
                if (nums[j] & mask) bitCounts++;
                if (bitCounts > n / 2) {
                    major |= mask;
                    break;
                }
            }
        } 
        return major;
    } 
};

解法五:个人推荐 —— 时间复杂度O(n) 空间复杂度O(1)
利用性质——出现超过一半次数,若每次碰见他加1,不是他-1,最后结果大于0。
一次遍历数组,保存两个值,一个major,用来保存数组中遍历到的某个数字,一个count,表示当前数字的出现次数。当count为0时,将major更新为当前数,count初始化为1,否则判断当前数是否与major相等,相等count+1,不等-1。

可能有人会有疑问,可以确保最后major就是主要数吗,答案是肯定的。
首先易知major肯定会被赋为主要数,假设我们在位置k时major被赋为主要数,则在k之前major最多出现了k/2次(否则在k之前就被赋为major了),则在剩下的n-k中major的次数必然大于(n-k)/2。
所以可以确保结果正确。

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int major, count = 0;
        for(int i = 0; i < nums.size(); i++){
            if(count == 0){
                major = nums[i];
                count = 1;
            }
            else count += (nums[i] == major) ? 1 : -1;
        }
        return major;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值