给定一个大小为 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) 的算法解决此问题。
为了解决这个问题,我们可以使用一种称为“摩尔投票算法”(Boyer-Moore Voting Algorithm)的高效算法。这个算法在时间复杂度为O(n)且空间复杂度为O(1)的条件下解决了这个问题。
摩尔投票算法的基本思想
- 维护一个候选多数元素
candidate
和一个计数器count
。初始时,candidate
可以为任意值,count
为0。 - 遍历数组
nums
中的每个元素x
:- 如果
count
为0,我们将candidate
设置为x
,即认为当前元素可能是多数元素。 - 如果
x
等于candidate
,则将count
加1。 - 如果
x
不等于candidate
,则将count
减1。
- 如果
- 遍历完成后,
candidate
即为整个数组的多数元素。
算法的正确性
由于多数元素在数组中出现的次数大于n/2
,那么在遍历过程中,candidate
最终一定会被设置为多数元素。因为每当count
减到0时,意味着到目前为止,没有任何元素可以成为多数元素。此时,可以将candidate
设置为当前元素,并开始重新计数。由于多数元素的数量超过了数组长度的一半,所以candidate
最终一定会是多数元素。
Java实现
public class Solution {
public int majorityElement(int[] nums) {
int count = 0;
Integer candidate = null;
for (int num : nums) {
if (count == 0) {
candidate = num;
}
count += (num == candidate) ? 1 : -1;
}
return candidate;
}
}
复杂度分析
- 时间复杂度:O(n),其中n是数组
nums
的长度。我们只需要遍历数组nums
一次。 - 空间复杂度:O(1),我们只需要常数空间存放
candidate
和count
。
为什么这个算法有效?
每当count变为0时,就更换candidate。这意味着到目前为止,之前的所有元素中,没有任何元素的出现次数能够超过数组长度的一半。因此,可以放弃这些元素,从当前元素重新开始计数。
由于多数元素的出现次数大于n/2,即使它在数组的前半部分被抵消,剩余的部分中它仍然是出现次数最多的元素。因此,最后candidate变量中存储的元素,就是整个数组的多数元素。
这个算法的巧妙之处在于,它利用了多数元素出现次数超过总数一半的特性,通过一次遍历就能找到这个元素,而且只用了O(1)的额外空间。