Source: https://leetcode.com/problems/majority-element/
169.Majority Element
Given an array (nums) of size n,find the majority element. The majority element is the element that appears morethan ⌊
n/2
⌋
times.
Youmay assume that the array is non-empty and the majority element always exist inthe array.
Solution:
依题意,给的样例中,这样的Majority Element总是存在的,所以不需要对其他情况进行考虑。那么,不怎么需要思考可以想到一个时间复杂度为O(n2)的做法,即用一个ele数组记录其中出现过的元素,另用一个count数组记录ele数组中对应元素出现的次数。之后,从头遍历整个nums数组,对于每一个nums数组中的元素,和ele数组中出现过的元素作比较。如果ele数组中有一致的记录,则将count数组中对应的记录数值加1;否则在ele数组与count数组中增加对这一元素的记录。最后,用O(n)的时间找出count数组的最大值,这个最大值对应的ele数组中的元素即所求的Majority Element。提交代码如下,可以通过:
class Solution {
public:
int majorityElement(vector<int>& nums) {
vector<int> ele;
vector<int> count;
int i,j;
bool rpt;
ele.push_back(nums[0]);
count.push_back(1);
for (i=1;i<nums.size();i++)
{
rpt=0;
for (j=0;j<ele.size();j++)
{
if (ele[j]==nums[i])
{
rpt=1;
count[j]++;
break;
}
}
if (rpt==0)
{
ele.push_back(nums[i]);
count.push_back(1);
}
}
int ansId=0;
for (i=1;i<count.size();i++)
{
if (count[i]>count[ansId])
ansId=i;
}
return ele[ansId];
}
};
此外,鉴于这道题是出现在分治策略中的题目,也可以用分治的思路解决这个问题。这一基本思路为,将整个nums数组等分为两个数组,然后比较两个数组的Majority Element。如果两个数组的Majority Element一致,无疑它们就是所求的Majority Element,否则比较nums数组中这两个元素出现过的次数即可,这一过程的时间复杂度是O(n)。运行时间的递归公式为T(n)=2T([n/2])+O(n),由Master theorem可知,T(n)=O(nlogn)。提交代码如下,可以通过:
(不过个人对分之策略的运用还不够熟练,代码比较混乱,通过时间也不如上一种做法)
class Solution {
public:
int majorityElement(vector<int>& nums) {
if (nums.size()>3)
{
int i,temp=nums.size()/2+1;
vector<int> formerHalf,
latterHalf;
formerHalf.insert(formerHalf.begin(),nums.begin(),nums.begin()+temp);
latterHalf.insert(latterHalf.begin(),nums.begin()+temp,nums.end());
int a=majorityElement(formerHalf),
b=majorityElement(latterHalf);
if (a==b)
return a;
else
{
int countA=0,countB=0;
for (i=0;i<nums.size();i++)
{
if (nums[i]==a)
countA++;
else if (nums[i]==b)
countB++;
}
if (countA>countB)
return a;
else if (countA<countB)
return b;
else
return 0;
}
}
else if (nums.size()==1)
return nums[0];
else if (nums.size()==2)
{
if (nums[0]==nums[1])
return nums[0];
else
return 0;
}
else
{
if (nums[0]==nums[1])
return nums[0];
else if (nums[2]==nums[0])
return nums[0];
else if (nums[2]==nums[1])
return nums[1];
else
return 0;
}
}
};
最后是一种时间复杂度为O(n)的做法,代码比较简洁,空间复杂度为O(1)。这种做法是我从网上学到的Boyer–Moore majority vote algorithm,有一定通用性。具体而言,这种算法需要从头遍历整个数组,并记录遍历到当前位置出现的Majority Element。假定nums数组中第一个元素为a,用一个count变量追踪这一MajorityElement,ans为返回的结果。将ans和count分别初始化为a与1。之后,当count不为0时,对于nums中的每一个元素,如果其与当前的ans一致,那么count值加1,否则count值减1。如果当前count为0,那么nums中的下一个元素即为下一个有效的ans。最后,当nums遍历结束后,得到的ans即为nums的MajorityElement。这种做法的正确性的简单证明如下(不严谨但希望有助于理解):
假定a为当前的ans值,count值大于0。如果nums中下一个元素也为a,那么count值加1,到目前为止支持a为Majority Element的选票多了1票;否则,count值减1,相当于目前为止支持a为Majority Element的选票少了1票。最后,Majority Element的count值必然是大于0的,因为题目保证了Majority Element的存在,而Majority Element是出现次数多于⌊ n/2 ⌋的元素。这种做法看似抹杀了a的下一个元素(如果它不是a的话)被选举的权利,但是,如果a的下一个元素不是a的话(假定为b)且恰好令count变为0,那么到此为止,a、b肯定不是MajorityElement,因为有和a的个数一样多的别的元素在,所以a不可能是,而极端情况是到此为止所以和a不一样的别的元素都是b,那么b也不可能是。那么。b要做的便是,待a的选票降为0时而此时正轮到它的时候,再上台参加选举,接受他人的投票。
那么对于aabcb的组合,结果将返回b,答案不是错了吗。的确错了,因为这种做法只能保证最后的ans值为有可能成为MajorityElement的元素,但这一数组并没有MajorityElement存在。在题目没有进一步说明的情况下(详见Leetcode#229, Majority Element II),应该再计算ans值在题目中出现的次数,看其是否确实大于⌊ n/2 ⌋,进一步确定这个唯一可能是MajorityElement的元素是否确实是。但题目明确保证了MajorityElement的存在,所以省去了这一步骤。代码如下,运行时间较第一、二种做法都要快。
class Solution {
public:
int majorityElement(vector<int>& nums) {
int ans=nums[0];
int count=1;
int len=nums.size();
for (int i=1;i<len;i++)
{
if (count==0)
{
ans=nums[i];
count=1;
}
else
{
if (ans==nums[i])
count++;
else
count--;
}
}
return ans;
}
};