Loading

Leetcode - 面试经典 150 题 - 多数元素

题目:多数元素

题目描述

给定一个大小为 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
输入保证数组中一定有一个多数元素。

题解

字典/哈希表

思路

遍历所有元素,将元素值作为键,元素出现次数作为值,只需要最后找到该记录中值最大的键值对中的键即可。下面的实现利用字典,也可以直接用内置的哈希表。

实现

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        records = {}
        for n in nums:
            if str(n) not in records:
                records[str(n)] = 1
            else:
                records[str(n)] += 1
            
        max_k, max_v = 0, 0
        for k, v in records.items():
            if v > max_v:
                max_k = k
                max_v = v
        
        return int(max_k)

复杂度

  • 时间复杂度:遍历一次数组,遍历一次字典,复杂度为 \(O(n)\)

  • 空间复杂度:分配了一个新的字典空间,在一定存在多数元素的情况下,必定有至少 ⌊ n/2 ⌋ + 1 个元素相同,则最多分配 n - ⌊ n/2 ⌋ 个键值对空间,因此复杂度为 \(O(n)\)

排序(The best)

思路

由于多数元素的定义,可以推断出如果数组是有序的,则在 ⌊ n/2 ⌋ 处的元素一定是多数元素,因为有序数组中的前或者后 ⌊ n/2 ⌋ + 1 个元素一定是多数元素,因此不管哪种情况,nums[len(nums) // 2] 一定是多数元素。综上,只需要排序然后返回 nums[len(nums) // 2] 即可。

实现

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

复杂度

  • 时间复杂度:取决于排序算法,由于使用了内置排序,此处复杂度为 \(O(nlogn)\)

  • 空间复杂度:同上,复杂度为 \(O(logn)\)

分治

思路

首先明确,数组的多数元素,一定是分解后所有子集中某个子集的多数元素:若数组被分解成 n 个子集,且其多数元素 \(a\) 不是这 n 个子集中的任意一个子集的多数元素,则一定有任意的第 i 个数组 \(a_i\) 中,\(a\) 的个数都少于 \(⌊ \frac{n_i}{2} ⌋\) ,加和后得出 \(a\) 的个数一定少于 ⌊ n/2 ⌋,与假设冲突,结论得证。

有了上述结论,我们可以将传入的数组分成两个部分,分别找出两边的多数元素,然后通过对比得到的两个元素哪个多即可。

实现

# 官方题解
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        def majority_element_rec(lo, hi) -> int:
            if lo == hi:
                return nums[lo]

            mid = (hi - lo) // 2 + lo
            left = majority_element_rec(lo, mid)
            right = majority_element_rec(mid + 1, hi)

            if left == right:
                return left

            left_count = sum(1 for i in range(lo, hi + 1) if nums[i] == left)
            right_count = sum(1 for i in range(lo, hi + 1) if nums[i] == right)

            return left if left_count > right_count else right

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

复杂度

  • 时间复杂度:\(O(nlogn)\),推导过程:

    image

  • 空间复杂度:开辟额外栈空间,复杂度为 \(O(logn)\)

Boyer-Moore 投票算法

思路

利用多数元素性质,维护一个变量 count,遍历数组,若当前元素是多数元素则加1,否则减1,最终结果一定是大于0的。

算法步骤如下:

  1. 维护一个候选众数 candidate 和它出现的次数 count。初始时 candidate 可以为任意值,count\(0\)

  2. 遍历数组的所有元素,对于每个元素 x,在判断 x 之前,如果 count 的值为 \(0\),先将 x 的值赋予 candidate,随后判断 x

    • x == candidatecount += 1

    • x != candidatecount -= 1

  3. 遍历结束后,candidate 为众数(多数元素)。

count == 0 为界可以将一个数组分为多个子集,在每一段中,如果该段众数与整体众数相同,那么 count 的变化一定与真正的 count 相同,若不同则一定与之互为相反数,即记录的 count 与真正的 count 的关系只会使相等或者相反数,同时由于记录的 count 一定是非负的,则最后一段中记录的 count 与真正的 count 一定是相等的,反过来就可以推出此时 candidate 所维护的就是数组众数(多数元素)。

实现

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        count = 0
        candidate = None

        for num in nums:
            if count == 0:
                candidate = num
            count += (1 if num == candidate else -1)

        return candidate

复杂度

  • 时间复杂度:只需要一次遍历,复杂度为 \(O(n)\)

  • 空间复杂度:只需要维护两个变量,复杂度为 \(O(1)\)

posted @ 2025-11-19 20:36  SmoothWater  阅读(6)  评论(0)    收藏  举报