Boyer-Moore 投票算法 (Boyer-Moore Voting Algorithm) 是解决“众数问题”的最优解。
它的核心思想非常简单粗暴,可以类比为: “极限一换一” 。
假设有一个数组,定义超过一半的为众数,如何求这个众数?对应:169. 多数元素
核心逻辑:诸侯争霸
想象一下,这个数组是一个战场,里面有很多不同的国家(数字),每个数字代表一个士兵。
-
众数(Majority Element):在这个战场上,某个国家的士兵人数 超过了总人数的一半(> n/2)。
-
规则:
- 士兵们在战场上通过,如果你遇到一个友军(相同的数字),你们的战斗力就增强(
count + 1)。 - 如果你遇到一个敌人(不同的数字),不管他是哪个国家的,你们两个就同归于尽(
count - 1)。
- 士兵们在战场上通过,如果你遇到一个友军(相同的数字),你们的战斗力就增强(
推论: 因为众数的人数超过了一半,所以哪怕所有非众数的士兵联合起来,哪怕他们采用“自杀式袭击”和众数士兵一换一,最后活在战场上的那个(candidate) ,一定还是众数。
算法执行流程
我们需要维护两个变量:
candidate(候选人) :当前擂台上的霸主。count(票数/血量) :霸主当前的势力值。
步骤:
遍历数组:
-
如果
count为 0:当前没有霸主(或者之前的霸主被耗死了),那么当前的数字自动登基成为新的candidate,并将count设为 1。 -
如果
count不为 0:- 遇到友军 (
num === candidate):count ++。 - 遇到敌军 (
num !== candidate):count --(一换一消耗掉)。
- 遇到友军 (
图解演示
假设数组是:[2, 2, 1, 1, 1, 2, 2] (众数显然是 2)
| 步骤 | 当前数字 | 动作/逻辑 | Candidate (候选人) | Count (血量) |
|---|---|---|---|---|
| Start | - | 初始化 | null | 0 |
| 1 | 2 | count是0,2登基 | 2 | 1 |
| 2 | 2 | 是友军(2),血量+1 | 2 | 2 |
| 3 | 1 | 是敌军(1),一换一,血量-1 | 2 | 1 |
| 4 | 1 | 是敌军(1),一换一,血量-1 | 2 | 0 (2下台了!) |
| 5 | 1 | count是0,1登基 | 1 | 1 |
| 6 | 2 | 是敌军(2),一换一,血量-1 | 1 | 0 (1下台了!) |
| 7 | 2 | count是0,2登基 | 2 | 1 |
结束:最终剩下的 candidate 是 2。
代码实现 (JavaScript)
/**
* @param {number[]} nums
* @return {number}
*/
var majorityElement = function(nums) {
let candidate = null;
let count = 0;
for (let num of nums) {
// 1. 如果当前没有霸主(count为0),当前数字登基
if (count === 0) {
candidate = num;
}
// 2. 这里的逻辑简写了:
// 如果是友军,count加1;如果是敌军,count减1
count += (num === candidate) ? 1 : -1;
}
return candidate;
};
为什么它比 Map 好?
-
HashMap 方法:需要建立一个对象来存每个数字出现的次数。如果数组有 100 万个不同的数字,你就需要存 100 万个键值对。
- 空间复杂度:
-
Boyer-Moore 投票法:只需要
candidate和count两个变量,不管数组多大,占用的内存都是固定的。- 空间复杂度:
- 时间复杂度: