摩尔投票法
引入:主要元素
数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。 输入:[1,2,5,9,5,9,5,5,5] 输出:5 输入:[3,2] 输出:-1
解:众所周知,主要元素最多只有 1 个。
摩尔投票法的核心就是比拼消耗。主要分为比拼消耗 paring 阶段和统计 counting 阶段。它基于这样一个事实:任意删除数组中两个不同的数,直至数组中不存在相同的数,那么剩下的数就是主要元素。
形象化理解:诸侯争霸游戏,假设你的人口占比超过总人口的一半,那么只要不内斗,每次和其他国家打仗都能一换一,最终赢家肯定是你。即使其他国家联合起来也打不过你,更不用说其他国家之间也会相互攻击。
算法过程:
- 遍历数组,从第一个元素开始计数,计数器初始为 1。
- 遇到与当前元素相同的元素时,计数器加 1,否则减 1(比拼消耗)。
- 若计数器为 0,则重新从当前元素开始计数,重复步骤 2 直至到达数组末尾(计数器为 0 表示前面所有的数都可以两两不同地比拼掉)。
```go
func majorityElement(nums []int) int {
majority := pairing(nums)
votes := counting(nums, majority)
if votes <= len(nums) / 2 {
} return majority }majority = -1
func pairing(nums []int) int { majority, votes := -1, 0 for _, num := range nums { if votes == 0 { majority = num votes = 1 } else { if num == majority { votes++ } else { votes— } } } return majority }
func counting(nums []int, majority int) int { votes := 0 for _, num := range nums { if (num == majority) { votes++; } } return votes }
paring 阶段必然返回数组中的一个众数,但是由于众数不止一个,所以还需要经过 counting 阶段确认是否存在主要元素。如果存在,那么 paring 阶段找到的众数必然是主要元素。<a name="97755670"></a>### 算法分析:1. 遍历两次 `nums` 数组,时间复杂度为 #card=math&code=O%28n%29)。1. 只定义了几个临时变量,时间复杂度为 #card=math&code=O%281%29)。<a name="abtDN"></a>## 推广:多个众数> 给定一个大小为 _n _的整数数组,找出其中所有出现超过 `⌊ n/3 ⌋` 次的元素。> 输入:[1,1,1,3,3,2,2,2]> 输出:[1,2]> 输入:nums = [1]> 输出:[1]解:众所周知,这样的数最多有 2 个。<br />形象化理解:诸侯争霸游戏,当人口数占比超过总人口的  的国家不互相打仗而只与其他国家打仗,且其他国家之间也会相互打仗,每次打仗人口都是一换一,那么人口数占比超过总人口的  的国家必然是最终的赢家。```gofunc majorityElement(nums []int) []int {// 空数组if len(nums) == 0 {return []int{}}cand1, cand2 := pairing(nums)vote1, vote2 := counting(nums, cand1, cand2)var cands []intif vote1 > len(nums) / 3 {cands = append(cands, cand1)}if vote2 > len(nums) / 3 {cands = append(cands, cand2)}return cands}func pairing(nums []int) (int, int) {cand1, vote1 := nums[0], 0cand2, vote2 := nums[0], 0for _, num := range nums {// 投给了第一位候选人// 计票后直接结束,不抵消第二位候选人的票if num == cand1 {vote1++continue}// 投给了第二位候选人// 计票后直接结束,不抵消第一位候选人的票if num == cand2 {vote2++continue}// 这一票没有投给当前的候选人// 先看看当前是否有两位候选人// 没有则找到匹配一个if vote1 == 0 {cand1 = numvote1 = 1continue}if vote2 == 0 {cand2 = numvote2 = 1continue}// num != cand1 && num != cand2 && vote1 != 0 && vote2 != 0// 说明当前已有两位候选人,但这一票没有投给他们两个中的一个// 因此比拼消耗vote1--vote2--}return cand1, cand2}func counting(nums []int, cand1 int, cand2 int) (int, int) {vote1, vote2 := 0, 0for _, num := range nums {if num == cand1 {vote1++} else if num == cand2 {vote2++}}return vote1, vote2}
