169. 多数元素
普通方法-借助map计数
class Solution {
public:int majorityElement(vector<int>& nums) {map<int,int> mp;for(int num :nums){mp[num]++;}for(auto &a :mp){if(a.second>nums.size()/2){return a.first;}}return 0;}
};
进阶:尝试设计时间复杂度为 O(n)、空间复杂度为 O(1) 的算法解决此问题。
-----摩尔投票:Boyer-Moore 投票算法只能在数组中存在一个多数元素且该元素的出现次数严格超过数组长度的一半时有效。
class Solution {
public:int majorityElement(vector<int>& nums) {int ans=nums[0],count=0;for(int i=0;i<nums.size();i++){if(count==0) ans=nums[i];if(nums[i]==ans)count++;else count--;}return ans;}
};
代码逻辑解释:
-
初始化:
ans = nums[0]:ans是当前的候选多数元素,初始化为数组的第一个元素。count = 0:count是计数器,用来记录当前候选元素的优势(即它的相对出现次数)。
-
遍历数组:
for(int i = 0; i < nums.size(); i++):遍历数组中的每个元素。
-
判断当前元素是否为候选多数元素:
if(count == 0) ans = nums[i];:当计数器为 0 时,表示当前没有有效的候选多数元素,因此将当前元素nums[i]设为新的候选元素ans。
-
更新计数器:
if(nums[i] == ans) count++;:如果当前元素等于候选元素ans,增加计数count。else count--:如果当前元素不等于候选元素ans,减少计数count。
-
返回结果:
- 最终返回
ans,即数组中的多数元素。
- 最终返回
为什么算法有效?
该算法利用了一个事实:多数元素的出现次数超过了一半,即出现次数比其他所有元素的总和都多。在遍历数组时,每当遇到多数元素时,计数器增加;当遇到其他元素时,计数器减少。当计数器为 0 时,意味着之前的候选元素不再具备多数的可能性,此时更新候选元素为当前元素。
因为多数元素的出现次数超过了数组长度的一半,最终剩下的候选元素一定是多数元素。
举个例子:
假设 nums = [2, 2, 1, 1, 1, 2, 2],我们一步步执行代码:
- 初始:
ans = 2,count = 0 i = 0:当前元素2,count == 0,设置ans = 2,count++ -> count = 1i = 1:当前元素2,等于ans,count++ -> count = 2i = 2:当前元素1,不等于ans,count-- -> count = 1i = 3:当前元素1,不等于ans,count-- -> count = 0i = 4:当前元素1,count == 0,设置ans = 1,count++ -> count = 1i = 5:当前元素2,不等于ans,count-- -> count = 0i = 6:当前元素2,count == 0,设置ans = 2,count++ -> count = 1
最后 ans = 2,返回的结果即为多数元素 2。
总结:
- 时间复杂度:O(n),只需遍历数组一次。
- 空间复杂度:O(1),只用到了固定数量的变量。