介绍一种算法,它可以在线性时间和常数空间内,在一个数组内找出出现次数超过一半的某个数字。
要解决这个问题并不难,可以使用排序或哈希,但是这两种算法都不能同时满足时间或空间的要求。
然而,该算法(A Linear Time Majority Vote Algorithm )却可以在同时满足这两个条件的情况下完美地解决问题。
现在将该算法简单描述如下:
对于数组中出现的某个数字设为待定数字,如果它出现则将其出现次数加一,如果没有出现则减一,如果减至零则将当前数字更换为新的待定数字。这样线性遍历之后可以剩下的待定数字,再用一遍遍历验证它是否满足条件。
举例:
{1,2,2,2,3}最后得到2,经验证满足条件
{1,2,3,4}最后得到4,经验证不满足条件
class Solution { public: int majorityElement(vector<int>& nums) { ,m=; ;i<nums.size();++i) { if(nums[i]==a) ++m; ) a=nums[i],m=; else --m; } return a; } };
这个算法并不太容易理解。
我通俗的解释一下,假设某个位置的数字都代表它要投的一个候选人,如果该位置的投的候选人与当前的候选人不同则意味着对当前候选人投反对票。由于最终满足条件的候选人次数大于n/2,所以其余所有人的反对票也不及它的票数,所以这样做可以得到正确结果。当然如果候选人次数小于等于n/2,就有可能被反对掉。
这个问题可以进行拓展。
如何在线性时间和常数空间内,找到出现次数超过数组大小的三分之一的两个数字。
我们同样可以使用这个算法解决,由于该两个数的出现次数超过三分之一,余下的部分不足以将它们反对掉。
class Solution { public: vector<int> majorityElement(vector<int>& nums) { ,b=,n=,m=; ;i<nums.size();++i) { if(nums[i]==a) ++m; else if(nums[i]==b) ++n; ) a=nums[i],m=; ) b=nums[i],n=; else --n,--m; } m=n=; ;i<nums.size();++i) { if(nums[i]==a) ++m; else if(nums[i]==b) ++n; } vector<int> result; ) result.push_back(a); ) result.push_back(b); return result; } };
再进一步扩展,只要是在n个数字中寻找出现次数超过n/(m+1)的m个数字都可以这样解决。
参考 http://blog.csdn.net/chfe007/article/details/42919017