(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. 清晰解释算法原理和正确性证明
相关推荐
Full Stack Developme几秒前
Java Simple Serial Connector 教程
java·stm32·单片机
xcs194053 分钟前
Java 上位机防空警报系统开发
java·开发语言
sR916Mecz5 分钟前
Linux 服务器磁盘扩容与目录迁移:rsync + bind mount 实现服务无感迁移(无需修改配置)
java·linux·服务器
AI成长日志5 分钟前
【GitHub开源项目专栏】黑客松项目架构模式解析:微服务、事件驱动与Serverless实战
算法
人道领域6 分钟前
【LeetCode刷题日记:24】两两交换链表
算法·leetcode·链表
北顾笙9809 分钟前
day16-数据结构力扣
数据结构·算法·leetcode
AI成长日志21 分钟前
【算法学习专栏】动态规划基础·简单三题精讲(70.爬楼梯、118.杨辉三角、121.买卖股票的最佳时机)
学习·算法·动态规划
wsoz23 分钟前
Leetcode子串-day4
c++·算法·leetcode
UAq6wn76j32 分钟前
.NET源码生成器使用SyntaxTree生成代码及简化语法
java·开发语言·.net
火飞鹰32 分钟前
封装MinIO为starter
java·数据库·spring boot