0
点赞
收藏
分享

微信扫一扫

微信小程序通过createSelectorQuery获取元素 高度,宽度与界面距离

老牛走世界 2023-10-09 阅读 11

给定一个大小为 n 的整数数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。

示例 1:

输入:nums = [3,2,3]
输出:[3]

示例 2:

输入:nums = [1]
输出:[1]

示例 3:

输入:nums = [1,2]
输出:[1,2]

提示:

  • 1 <= nums.length <= 5 * 10^4
  • -10^9 <= nums[i] <= 10^9

进阶: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1)的算法解决此问题。


题目集合:

  • 169. 多数元素
  • 面试题 17.10. Find Majority Element LCCI
  • 229. 多数元素 II
    1. Check If a Number Is Majority Element in a Sorted Array
  • 1157. 子数组中占绝大多数的元素:困难

解法 摩尔投票法

在之前的169和面试题17.10中,我们使用「摩尔投票」在 O ( 1 ) O(1) O(1) 空间复杂度内找到了出现次数超过一半的元素,即出现次数大于 n / 2 n / 2 n/2 的数。并使用了例子进行辅助证明。有兴趣的可以参考论文的证明过程,论文地址:MJRTYA Fast Majority Vote Algorithm

对于本题,我们需要统计出现次数超过 n / 3 n / 3 n/3 的数。不失一般性的将其拓展为「统计出现次数超过 n / k n / k n/k 的数」

可以证明,出现次数超过 n / k n / k n/k 的数最多只有 k − 1 k - 1 k1。否则必然违背「数总共只有 n n n 个」或者「当前统计的是出现次数超过 n / k n / k n/k 的数」的前提条件。当明确了符合要求的数的数量之后,我们可以使用有限变量来代表这 k − 1 k - 1 k1 个候选数及其出现次数。

然后使用「摩尔投票」的标准做法,在遍历数组时同时 c h e c k check check k − 1 k - 1 k1 个数,假设当前遍历到的元素为 x x x

  • 如果 x x x 本身是候选者的话,则对其出现次数加一;
  • 如果 x x x 本身不是候选者,检查是否有候选者的出现次数为 0 0 0
    • 若有,则让 x x x 代替其成为候选者,并记录出现次数为 1 1 1
    • 若无,则让所有候选者的出现次数减一
  • 当处理完整个数组后, k − 1 k - 1 k1 个数可能会被填满,但不一定都是符合出现次数超过 n / k n / k n/k 要求的。需要进行二次遍历,来确定候选者是否符合要求,将符合要求的数加到答案。

上述做法正确性的关键是:若存在出现次数超过 n / k n / k n/k 的数,最后必然会成为这 k − 1 k - 1 k1 个候选者之一。我们可以通过「反证法」来进行证明:若出现次数超过 n / k n / k n/k 的数 x x x 最终没有成为候选者。有两种可能会导致这个结果:

  • 数值 x x x 从来没成为过候选者:如果 x x x 从来没成为过候选者,那么在遍历 x x x 的过程中,必然有 k − 1 k-1 k1 个候选者被减了超过 n / k n/k n/k 次,假设当前 x x x 出现次数为 C C C ,已知 C > n / k C > n / k C>n/k ,此时总个数为
    ( k − 1 ) ∗ C + C = C ∗ k (k−1)∗C+C=C∗k (k1)C+C=Ck
    再根据 C > n / k C > n / k C>n/k ,可知 C ∗ k > n C * k > n Ck>n ,而我们总共就只有 n n n 个数,因此该情况恒不成立。
  • 数值 x x x 成为过候选者,但被逐出替换了:同理,被逐出替换,说明发生了对 x x x 出现次数减一的动作(减到 0 0 0 ),每次减一操作,意味着有其余的 k − 2 k - 2 k2 个候选者的出现次数也发生了减一动作,加上本身被遍历到的当前数 n u m [ i ] num[i] num[i] ,共有 k − 1 k-1 k1 个数字和 x x x 被一同统计。 因此,根据我们摩尔投票的处理过程,如果 x x x 成为过候选者,并被逐出替换,那么同样能够推导出我们存在超过 n n n 个数。
  • 综上,如果存在出现次数超过 n / k n/k n/k 的数,其必然会成为 k − 1 k-1 k1 个候选者之一

一个对摩尔投票法非常形象的比喻是:多方混战。我们把这道题比作一场三方混战,战斗结果一定只有最多两个阵营幸存,其他阵营被歼灭。数组中的数字即代表某士兵所在的阵营。

我们维护两个潜在幸存阵营 A A A B B B 。遍历数组:

  • 如果遇到了属于 A A A 或者属于 B B B 的士兵,则把士兵加入 A A A B B B 队伍中,该队伍人数加一。继续遍历。
  • 如果遇到了一个士兵既不属于 A A A 阵营,也不属于 B B B 阵营,这时有两种情况:
    1. A A A 阵营和 B B B 阵营都还有活着的士兵,那么进行一次厮杀,参与厮杀的三个士兵全部阵亡: A A A 阵营的一个士兵阵亡, B B B 阵营的一个士兵阵亡,这个不知道从哪个阵营来的士兵也阵亡。继续遍历。
    2. A A A 阵营或 B B B 阵营已经没有士兵了。这个阵营暂时消失了。那么把当前遍历到的新士兵算作新的潜在幸存阵营,这个新阵营只有他一个人。继续遍历。
  • 大战结束,最后 A A A B B B 阵营就是初始人数最多的阵营。判断一下 A , B A,B A,B 的人数是否超过所有人数的三分之一即可。
class Solution {
public:
    vector<int> majorityElement(vector<int> & nums, int k){
        vector<int> cand(k-1, INT_MIN);
        vector<int> cnt(k-1, 0);
        for(int i = 0; i < nums.size(); i++){
            bool oldcand = false;
            for(int j = 0; j < k-1; j++){
                if(cand[j] == nums[i]){
                    cnt[j]++;
                    oldcand = true;
                    break;
                }
            }
            if(!oldcand){
                bool newcand = false;
                for(int j = 0; j < k-1; j++){
                    if(cnt[j] == 0){
                        cand[j] = nums[i];
                        cnt[j] = 1;
                        newcand = true;
                        break;
                    }
                }
                if(!newcand){
                    for(int j = 0; j < k-1; j++){
                        cnt[j]--;
                    }
                }
            }
        }
        vector<int> res; 
        int target = nums.size() / k;
        for(int i = 0; i < k-1; i++){
            int count = 0;
            for(int j = 0; j < nums.size(); j++){
                if(cand[i] == nums[j]){
                    count++;
                }
            }
            if(count > target){
                res.push_back(cand[i]); 
            }
        }
        return res;
    }
    vector<int> majorityElement(vector<int>& nums) {
        return majorityElement(nums, 3);    
    }
};

复杂度分析:

  • 时间复杂度: O ( n ) O(n) O(n)
  • 空间复杂度: O ( 1 ) O(1) O(1)
举报

相关推荐

0 条评论