又是感冒的一天,完全看不下去任何东西,就来刷算法题了…
首先来看看题目:
数组中占比超过一半的元素称之为主要元素。给定一个整数数组,找到它的主要元素。若没有,返回-1。
示例 1:
输入:[1,2,5,9,5,9,5,5,5] 输出:5
示例 2:
输入:[3,2] 输出:-1
示例 3:
输入:[2,2,1,1,1,2,2] 输出:2
看到这道题我的第一想法还是做Hash计数,但是数组最大元素不确定,这时候很难开辟一个合适大小的数组。
写这篇文章还是为了记录一种之前没学过的算法(菜哭了orz):Boyer-Moore Majority Vote Algorithm,又叫多数投票算法。
首先给出最终代码:
class Solution {
public:
int majorityElement(vector<int>& nums) {
int Counter = 0;
int MajorElement;
int AppearTimes = 0;
//查找此数组中出现最多次的元素
for(auto PresentElement : nums)
if(Counter == 0)
{
MajorElement = PresentElement;
Counter++;
}
else
if(MajorElement == PresentElement)
Counter++;
else
Counter--;
//确定此元素出现的次数大于nums.size() / 2, 那么它也就是主要元素
if(Counter > 0)
for(auto PresentElement : nums)
if(PresentElement == MajorElement)
AppearTimes ++;
if(AppearTimes > nums.size() / 2)
return MajorElement;
else
return -1;
}
};
多数投票算法可以在O(n)的时间复杂度,O(1)的空间复杂度中找出数组中的多数元素。
事实上,上面的代码中只有这一部分是多数投票算法:
for(auto PresentElement : nums)
if(Counter == 0)
{
MajorElement = PresentElement;
Counter++;
}
else
if(MajorElement == PresentElement)
Counter++;
else
Counter--;
它的本质其实就是元素之间的相互抵消,下面我想谈一谈我对这个算法的理解,为以后复习做一个助记。
- 这种抵消包含了假设,MajorElement = PresentElement是一种假设,此后所有的判断都基于MajorElement是多数元素的前提来进行。
- 我们的目的是找多数元素,所以不要囿于元素的具体数值。在1的假设下,此后所有元素的判断都被抽象成是或不是多数元素。
- 如果和当前假定的元素相同,那么假设得到了增强,计数值+1
- 如果和当前假定的元素不同,那么假设得到了削弱,计数值-1,注意这种削弱是相互的。
- 对4中相互削弱做一个解释,假设当前MajorElement = 5且Counter = 2, 如果下一个遍历到的数组元素是6,那么是应该将Counter - 1 = 1表示5作为多数元素的假设得到了削弱,反过来,循环会直接读取下一个元素,表示6我们也不再考虑了,它也被假设的多数元素5削弱了。正是这种削弱的相互性,使得MajorElement的寻找可以通过一次遍历完成。
End Of Blog