Given an array nums
of size n
, return the majority element.
The majority element is the element that appears more than ⌊n / 2⌋
times. You may assume that the majority element always exists in the array.
Example 1:
Input: nums = [3,2,3]
Output: 3
Example 2:
Input: nums = [2,2,1,1,1,2,2]
Output: 2
Constraints:
n == nums.length
1 <= n <= 5 * 104
-109 <= nums[i] <= 109
Follow-up: Could you solve the problem in linear time and in O(1)
space?
给定一个大小为 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 * 104
-109 <= nums[i] <= 109
进阶: 尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
第一反应是分别计数,这种方法显然有点笨。。。
因为题目想找出现次数最多的数字(这种题目要求大于一半数量,说明肯定有大于一半数量存在的),其实可以这样,不用分别计数,而是按照顺序依次“增减”计数,比如:
1 1 2 2 1 1
最开始遇到1,遇到1就计数+1,遇到不是1就计数-1,如果这个数字大于一半数量,那么增增减减之后,一定也还是大于零
🥬上菜
在这个问题中,需要找到一个出现次数超过 ⌊n/2⌋
次的多数元素。对于这种问题,有一种非常高效的解决方法,称为 Boyer-Moore 投票算法。这种算法可以在 O(n) 时间复杂度内找到多数元素,并且其空间复杂度为 O(1)。
Boyer-Moore 投票算法
其核心思想是通过一种巧妙的方式取消掉非多数元素的计数,来找到多数元素。具体步骤如下:
- 初始化:设置两个变量,
candidate
(候选人)初始化为任意值,count
(计数)初始化为 0。 - 第一遍遍历:遍历数组
nums
。- 如果
count
为 0,设当前元素为candidate
。 - 如果当前元素等于
candidate
,增加count
。 - 否则减少
count
。
- 如果
- 第二遍遍历:验证
candidate
是否为多数元素(这一步在题目中可以省略,因为题目保证了总是存在多数元素)。- 计算
candidate
的出现次数。 - 如果出现次数大于
⌊n/2⌋
,返回candidate
。
- 计算
直觉的理解:
每次在找到两个不同的元素时就将它们“抵消”。如果一个元素的数量超过总数的一半,那么即使它与其他所有不同的元素进行抵消,最后仍然会剩余该元素,因为它的数量多于其他所有元素的总和。
但是要注意这个算法成立的关键前提:数组中存在一个多数元素,其出现次数严格大于数组长度的一半。
实现代码(C++)
// 169. Majority Element
#include <iostream>
#include <vector>
using namespace std;
class Solution {
public:
int majorityElement(vector<int>& nums) {
int candidate;
int count = 0;
for (int num : nums) {
// 说明增增减减之后又回到零
if (count == 0) {
candidate = num;
}
count += (candidate == num) ? 1 : -1;
}
// 由于题目限制,下面的判断可以省略(但其实很必要)
// count = 0;
// for (int num : nums) {
// if (num == candidate) {
// count++;
// }
// }
// if (count > nums.size() / 2) return candidate;
return candidate;
}
};