(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. 清晰解释算法原理和正确性证明
相关推荐
维天说3 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json
Zane19944 小时前
并发 vs 并行:别再傻傻分不清了,一文讲透 Java 并发编程的第一课
java·后端
噢,我明白了5 小时前
java中Excel的导入和导出(EasyExcel)
java·开发语言·excel
吠品5 小时前
Zabbix Web界面误报Server未运行的排查与解决
java·服务器·数据库
啊湘5 小时前
天气查询API接口 按月Token鉴权 实时天气 物联网可用 文档齐全
java·后端·struts
Java内核笔记5 小时前
告别十亿美元的错误 : Spring Boot 4 空安全 (JSpecify) 实战
java·spring boot·后端
烬羽6 小时前
递归老写崩?一个"退回"公式,把回溯题变成填空题
javascript·深度学习·算法
糖果店的幽灵6 小时前
langgraph的 MessagesState 解读
java·开发语言·人工智能·windows·langgraph
闪电悠米6 小时前
力扣hot100-41.缺失的第一个正数-原地哈希详解
数据结构·算法·哈希算法
星空露珠6 小时前
28种颜色对应名称,
开发语言·数据库·算法·游戏·lua