题目信息
设T[0:n-1]是n个元素的数组。对任一元素x, 设S(x)={ i | T[ i ] = x }
。当S(x)|>n/2
时, 称x为主元素。 设计一个线性时间算法, 确定T[0:n-1]是否有一个主元素,如果有,则返回。
想法
1.如果x是数组a的主元素,那么x必也是数组a的中位数、众数
2.如果存在主元素,那么主元素与非主元素的个数差count一定大于0
(设总体元素个数为 n,主元素个数为 k,则非主元素个数为 n-k。count = 2k - n > 0)
3.如果序列存在主元素,删除序列两个不同的元素,主元素不变。
有两种情况:
①删除的两个元素都不是主元素,那肯定影响主元素改变
②删除的一个是主元素一个不是,count =2(k - 1) - (n -2) = 2k - n
不变
4.序列至多存在一个主元素
解答
方法一
使用快排的方法先进行一次排序,然后直接找到中位数即可
方法二
假设一定存在的情况下那么
可以用candidate来储存临时认为的主元素,count来记录在已遍历序列内主元素与非主元素的个数之差。最初:count=0.。对序列a[0 : n-1]遍历:
如果count = 0:说明已遍历序列中是偶数个数,并且两两不同元素相消后不会剩余。可以用后面未遍历序列的中位数取代完整序列的主元素。即重新从当前元素开始,count=1,candidate为当前遍历的元素a[ i ]。
否则,如果candidate == a[ i ]: 已遍历序列内主元素与非主元素的个数之差 + 1(count++)
否则:已遍历序列内主元素与非主元素的个数之差 - 1(count–)
int FindPopularElement(int *a, int n)
{
int candidate, count = 0;
for (int i = 0; i < n; i++)
{
if (count > n / 2) //提前找到主元素
return a[i];
if (count == 0)
{ //重新开始
candidate = a[i];
count++;
}
else
{
if (a[i] == candidate)
count++;
else
count--;
}
}
return candidate;
}
最后也可以验证一遍:
bool isPopularElement(int *a, int n, int candidate)
{
int num = 0;
for (int i = 0; i < n; i++)
{
if (a[i] == candidate)
num++;
if (num > n / 2)
return true;
}
return false;
}