在问 · 算法题解

169. 多数元素

简单技巧Boyer-Moore 投票力扣原题 ↗

题目描述

输入一个长度为 n 的数组 nums。若某个元素出现的次数严格大于 ⌊n/2⌋,就称它为多数元素。

返回这个多数元素。数组非空,且一定存在多数元素。

示例 1
输入:nums = [3,2,3]
输出:3

解释:3 出现 2 次,超过 ⌊3/2⌋ = 1。

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

解释:2 出现 4 次,超过 ⌊7/2⌋ = 3。

数据范围

  • n == nums.length
  • 1 <= n <= 5 * 10^4
  • -10^9 <= nums[i] <= 10^9
  • 数组中必有多数元素。

进阶:能否用 O(n) 时间、O(1) 额外空间解决?

解题思路:Boyer-Moore 投票

Boyer-Moore 投票:候选与不同元素抵消,多数元素最终不会被抵消。

为什么正确

扫描前缀中,count 表示候选相对其他元素的未抵消票数。

复杂度分析

时间 O(n),空间 O(1)。

易错点

算法依赖题目保证多数元素存在;若无此保证,需要第二次计数验证候选。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        candidate = None
        count = 0
        for num in nums:
            if count == 0:
                candidate = num
            count += 1 if num == candidate else -1
        return candidate

载入编辑器,自己跑一遍 →

其他解法

哈希表计数 O(n) 时间但占 O(n) 空间;排序和分治都是 O(n log n);投票法同时做到 O(n) 时间和 O(1) 空间。

解法 2:哈希表计数 时间 O(n),空间 O(n)

用哈希表统计每个元素的出现次数。计数时一旦某个元素的次数超过 n/2,就返回它。

最直观,也能推广到“出现次数最多的元素”。需要 O(n) 额外空间。

from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        cnt = {}
        for x in nums:
            cnt[x] = cnt.get(x, 0) + 1
            if cnt[x] * 2 > len(nums):
                return x
        return -1

解法 3:排序取中位 时间 O(n log n),空间 O(log n)

把数组排序。多数元素出现超过 n/2 次,排序后它占据的连续区间一定覆盖下标 n/2。直接返回 nums[n/2]。

只有两行代码,关键是理解“中位数必是多数元素”。时间多一个 log n,并且会打乱输入顺序。

from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        nums.sort()
        return nums[len(nums) // 2]

解法 4:分治 时间 O(n log n),空间 O(log n)

把区间对半分开,递归求左右两半各自的多数元素。若两边结果相同就直接返回。否则在整个区间内分别统计两个候选的次数,返回次数更多的那个。

依据是全局多数元素至少是某一半的多数元素,是分治思想的好例子。时间和空间都不如投票法。

from typing import List

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        def count(x, lo, hi):
            return sum(1 for i in range(lo, hi + 1) if nums[i] == x)

        def solve(lo, hi):
            if lo == hi:
                return nums[lo]
            mid = (lo + hi) // 2
            left = solve(lo, mid)
            right = solve(mid + 1, hi)
            if left == right:
                return left
            return left if count(left, lo, hi) > count(right, lo, hi) else right

        return solve(0, len(nums) - 1)

技巧 专题的其他题目

查看 技巧 全部题目 →