主元素问题:找出在一个长度为的数组中存在出现次数大于
的元素,已知这样的元素一定存在
常见解法:
- 排序法:首先能确定的是,这样的元素如果有则仅有一个,那么排序后这样的元素一定是数组的中间元素
- 字典计数法:暴力解法,用字典记录每个元素出现的次数即可找出所求元素。
摩尔投票法
-
优势:执行一遍该算法所需的时间复杂度为
,优于排序法,空间复杂度为
,优于字典计数法
- 执行过程:重复从数组中找出一对不相同的元素,将其从数组中删除,直到最后不剩下任何元素或者剩下的元素全部相同
-
算法分析:
-
不剩下任何元素:说明
为偶数,由于找出元素并删除的操作进行了
次,而每次的两个元素不相同,则每个元素最多只出现了
次,此时不存在主元素。
-
剩下的元素全部相同:可能有两种情况:
为偶数或者
为奇数。而无论哪种情况,都能说明剩下的元素是唯一可能的主元素。假设找出元素并删除的操作进行了
次,分析如下:
-
为偶数时,一定有
,而被删除的每个元素最多出现次数即为
,因此它们都不可能是主元素。
-
为奇数时,一定有
,因此同理,被删除的元素都不可能是主元素
-
-
不剩下任何元素:说明
- 求解主元素问题:根据分析可知,执行摩尔投票算法到最后如果剩下元素,则这个元素是唯一可能的主元素,因此只需要再遍历一遍数组,判断该元素出现的次数是否符合主元素的规定即可,在主元素问题中由于一定存在主元素,因此摩尔投票算法也一定会剩下元素,而且无需判断即可断定其为所求主元素
- 代码实现:
int majorityElement(int* nums, int numsSize){
int x;
int count;
int i;
/*
* 利用count模拟摩尔投票算法的执行过程:
* 1. count第p次为0时令x为nums[i],count第p+1次为0时令x为nums[j],
令k属于[i, j - 1],每次nums[k] = x时count++,否则count--
* 2. 显然区间[i, j - 1]的长度为偶数,假设为len,
那么一定有数量为len/2的k属于[i, j - 1]使得nums[k] = x
* 3. 因此可以认为在两次count为0的过程中,选出了len/2对元素,
每对元素包含一个值为nums[i]的元素和一个值不等于nums[i]的元素
* 4. 当数组遍历完成时,此时x的值就是上一轮选择结束后数组里剩余元素的值,也就是所求的众数
*/
count = 0;
for(i = 0; i < numsSize; i++)
{
if(count == 0) x = nums[i];
if(x == nums[i])
count++;
else
count--;
}
// 当数组遍历完成时,此时x的值就是上一轮选择结束后数组里剩余元素的值,也就是所求的众数
return x;
}