(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. 清晰解释算法原理和正确性证明
相关推荐
s砚山s2 小时前
代码随想录刷题——二叉树篇(二十)
算法
蜜獾云2 小时前
JAVA面试题速记-第1期-java基础
java·开发语言
百锦再2 小时前
Java中的反射机制详解:从原理到实践的全面剖析
java·开发语言·jvm·spring boot·struts·spring cloud·kafka
没有bug.的程序员2 小时前
Gradle 构建优化深度探秘:从 Java 核心到底层 Android 物理性能压榨实战指南
android·java·开发语言·分布式·缓存·gradle
-Rane3 小时前
【C++】vector
开发语言·c++·算法
tianyagukechat3 小时前
rockylinux9.5 配置IP
java·网络·tcp/ip
代码栈上的思考3 小时前
滑动窗口算法实战
算法
程序员Sonder3 小时前
黑马java----正则表达式(一文弄懂)
java·正则表达式·新人首发
doris82043 小时前
Python 正则表达式 re.findall()
java·python·正则表达式