leetcode-多数元素

78 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第4天,点击查看活动详情

题目描述

给定一个大小为 n 的数组 nums ,返回其中的多数元素。多数元素是指在数组中出现次数 大于 ⌊ n/2 ⌋ 的元素。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例 1:

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

示例 2:

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

提示:

  • n == nums.length
  • 1 <= n <= 5 * 104
  • -109 <= nums[i] <= 109

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

思路

如果没有空间复杂度为 O(1) 的限制,那么我们显然可以使用哈希表来解决。key就是数值,value为当前数值出现的次数,最多完整遍历1遍(可能遍历到中间就出现了超过1/2的数值),就可以获取到所求的值。这种方法非常直观,但是时间复杂度和控件复杂度都是 O(n)。
要实现空间复杂度 O(1),我们这里考虑使用摩尔投票法。摩尔投票法的核心就是,把每个数字看作选票,如果遇到不同的选票,就相互抵消,直到剩下k张相同的票。正确性的证明也比较容易:因为我们所求的值超过1/2,即使其他所有票跟它进行抵消,最后也会剩下出现次数最多的票。当然,如果其他票之间存在相互抵消的情况,最后剩下 最多的票就会更多,也是符合唯一一种剩下的票就是出现最多的结论。
我们可以看如下这个例子,原始数组为: 原始数组.png

我们将不通的投票进行两两抵消,第1个1跟第1个2抵消,第2个1跟第2个2抵消,第3个1跟第1个3抵消,最后剩下的1,所以1就是当前数组的多数元素

投票1.png

当然,如果抵消的过程中,2和3这2种不是多数元素的票也进行了抵消,那么剩下1的数量会更多,也是符合结论的

投票2.png

所以,本题的可以定义一个备选多数元素major和它出现的次数cnt,然后遍历数组,如果当前cnt为0,那么证明major已经被抵消完,需要重新备选major;否则cnt>0的情况,如果nums[i] == major,那就是major的选票,cnt++,如果nums[i] != major,证明是不同的选票,可以对major进行一次抵消,做cnt--。这样,最后剩下的major就是我们要求解的多数元素。
注意,major并不一定一开始就选对,如果没选对,过程中肯定会出现cnt==0而重新选出major的情况。这其实就是对应上面非major的投票之间相互抵消的情况,不会改变最后只能留下major的结论。

Java版本代码

class Solution {
    public int majorityElement(int[] nums) {
        int major = nums[0];
        int cnt = 1;
        for (int i = 1; i < nums.length; i++) {
            if (cnt == 0) {
                major = nums[i];
                cnt = 1;
            } else {
                if (nums[i] == major) {
                    cnt++;
                } else {
                    cnt--;
                }
            }
        }
        return major;
    }
}