(LeetCode-Hot100)169. 多数元素

多数元素

问题简介

LeetCode 169. 多数元素

复制代码
题解github地址: https://github.com/swf2020/LeetCode-Hot100-Solutions

题目描述

给定一个大小为 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 * 10⁴
  • -10⁹ <= nums[i] <= 10⁹

解题思路

方法一:哈希表计数(直观解法)

💡 思路:

  1. 遍历数组,用哈希表记录每个元素出现的次数
  2. 当某个元素的计数超过 n/2 时,直接返回该元素

优点: 思路简单直观

缺点: 需要额外的 O(n) 空间


方法二:排序法

💡 思路:

  1. 对数组进行排序
  2. 由于多数元素出现次数超过一半,排序后中间位置的元素必定是多数元素

优点: 实现简单,空间复杂度较低

缺点: 时间复杂度较高 O(n log n)


方法三:Boyer-Moore 投票算法(最优解)

💡 核心思想:

  • 多数元素的数量比其他所有元素的总和还要多
  • 维护一个候选元素和计数器
  • 遇到相同元素计数器+1,不同元素计数器-1
  • 当计数器为0时,更换候选元素

优点: 时间复杂度 O(n),空间复杂度 O(1)

这是面试官最希望看到的解法


代码实现

java:Java 复制代码
// 方法一:哈希表计数
class Solution {
    public int majorityElement(int[] nums) {
        Map<Integer, Integer> countMap = new HashMap<>();
        int n = nums.length;
        
        for (int num : nums) {
            countMap.put(num, countMap.getOrDefault(num, 0) + 1);
            if (countMap.get(num) > n / 2) {
                return num;
            }
        }
        return -1; // 不会执行到这里
    }
}

// 方法二:排序法
class Solution {
    public int majorityElement(int[] nums) {
        Arrays.sort(nums);
        return nums[nums.length / 2];
    }
}

// 方法三:Boyer-Moore 投票算法
class Solution {
    public int majorityElement(int[] nums) {
        int candidate = 0;
        int count = 0;
        
        for (int num : nums) {
            if (count == 0) {
                candidate = num;
            }
            count += (num == candidate) ? 1 : -1;
        }
        
        return candidate;
    }
}
go:Go 复制代码
// 方法一:哈希表计数
func majorityElement(nums []int) int {
    countMap := make(map[int]int)
    n := len(nums)
    
    for _, num := range nums {
        countMap[num]++
        if countMap[num] > n/2 {
            return num
        }
    }
    return -1 // 不会执行到这里
}

// 方法二:排序法
import "sort"

func majorityElement(nums []int) int {
    sort.Ints(nums)
    return nums[len(nums)/2]
}

// 方法三:Boyer-Moore 投票算法
func majorityElement(nums []int) int {
    candidate := 0
    count := 0
    
    for _, num := range nums {
        if count == 0 {
            candidate = num
        }
        if num == candidate {
            count++
        } else {
            count--
        }
    }
    
    return candidate
}

示例演示

让我们用 Boyer-Moore 投票算法 演示示例 [2,2,1,1,1,2,2]

步骤 当前元素 候选元素 计数器 说明
1 2 2 1 初始化候选为2
2 2 2 2 相同元素,计数+1
3 1 2 1 不同元素,计数-1
4 1 2 0 不同元素,计数-1
5 1 1 1 计数为0,更换候选为1
6 2 1 0 不同元素,计数-1
7 2 2 1 计数为0,更换候选为2

最终候选元素为 2,正确!


答案有效性证明

Boyer-Moore 投票算法正确性证明:

关键观察: 多数元素的数量 > n/2,其他所有元素的总和 < n/2

证明过程:

  1. 配对消除: 将多数元素与其他元素一一配对消除
  2. 剩余元素: 由于多数元素数量更多,消除后必然有剩余
  3. 最终候选: 算法保证最后剩下的就是多数元素

数学表达:

  • 设多数元素出现次数为 m,其他元素总次数为 k
  • m > n/2k = n - m < n/2
  • 因此 m > k,配对消除后多数元素仍有剩余

复杂度分析

方法 时间复杂度 空间复杂度 是否最优
哈希表计数 O(n) O(n)
排序法 O(n log n) O(1) 或 O(n)*
Boyer-Moore O(n) O(1)

*注:某些排序算法可能需要 O(n) 额外空间


问题总结

📌 核心要点:

  • 多数元素的定义是出现次数 严格大于 n/2
  • 题目保证一定存在多数元素,无需验证结果
  • Boyer-Moore 投票算法是解决此类问题的经典方法

💡 扩展思考:

  • 如果题目要求找出所有出现次数超过 n/k 的元素,该如何处理?
  • Boyer-Moore 算法可以推广到寻找多个候选元素的情况

面试建议:

  1. 先给出哈希表的直观解法
  2. 优化到排序法
  3. 最终展示 Boyer-Moore 投票算法
  4. 清晰解释算法原理和正确性证明
相关推荐
地平线开发者4 小时前
J6B vio scenario sample
算法
用户67570498850212 小时前
Go 语言里判断字符串为空,90% 的人都写错了!
后端·go
Flittly12 小时前
【AgentScope Java新手村系列】(16)从RAG到多路检索
java·spring boot·spring
用户67570498850212 小时前
Go 进阶必修:90% 的人都没用对的“表驱动法”
后端·go
小兔崽子去哪了12 小时前
Java 生成二维码解决方案
java·后端
BothSavage16 小时前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn16 小时前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法
人活一口气17 小时前
从JVM调优到MCP协议:Java全栈技术体系深度总结与企业级架构实践
java·spring boot
烬羽18 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试