王道/天勤 课后习题 寻找数组的主元素 详解,绝对能看懂!

主元素就是指数组中,出现次数大于数组长度的一半的数。
在习题中,给出的解答如下:

int majority(int A[],int n)
{
	int i, c, count = 1;
	c = A[0];
	for(i=1;i<n;i++)
	{
		if (A[i] == c)
			count++;
		else
		{
			if (count > 0)
				count--;
			else
			{
				c = A[i];
				count = 1;
			}
		}
	}
	if(count>0)
	{
		for(i=count=0;i<n;i++)
		{
			if (A[i] == c)
				count++;	
		}
	}
	if (count > n / 2) return c;
	return -1;
}

具体思路是:
1.筛选可能的主元素,记录为a(只筛选一次)
2.判断该原元素是否为主元素。(遍历一次,记录次数,看是否大于长度的一半)

这里最大的问题在于:
为什么只需要筛选一次,就可以得到候选主元素?

下面将从数学的角度去说明这一点。

前提:
1.假设主元素出现了x次,那这个数组的长度最长是2*x-1.也就是说,主元素出现次数至少大于数组长度的一半。
推论:
一、记录第一个元素,标记次数count=1.
下一次遇到该元素则+1,没有遇到则-1.
如果这个元素是主元素,则最终count必然大于1.

下面我们从最简单的例子来入手:

1.假设这个数组为A={1,1}则显然按照上面的计算,count=2>0,为主元素。
2.如果A={1,2}呢?count=1-1=0,不大于0,所以不是主元素

那我们递推一下。

1.如果A={1,1,2},则count=1+1-1=1>0,成立
2.如果A={1,2,1},则对最初的1来讲,刚开始就遇到了2,于是count(1)=1-1=0。

接下来就是最重要的部分了!!!
标准答案在处理的时候,跳过了数组中count=0的第一位(1),继续处理之后的第二位(2),并且把count(2)=1

继续按照之前的算法,数组的第二位(2)和第三位(1)进行比较,发现不同,因此count(2)=1-1=0
接下来继续循环,来判断第三位(1),并将count(3)置为1.

此时循环已经结束了。
最后留下的count=1的数据,正是1.
因此只需要判断1是不是主元素即可。

最后检验1的确是主元素。

那么这是不是巧合呢?为什么可以不管前面的那些count=0的数据呢?

结论1
凡是出现了count=0,则表示该元素从计数起到结束,所经历的数组元素中,有一半是该元素,有一半不是。

例如11122211222
对于开始的1,结束的时候是在111222的位置。这期间1的出现次数,和“非1”的出现次数一样。

结论2
如果经历了一段数组,其中有一半是元素a,另一半不是a,则可以去掉这经历的一半,因为这里面的即使有主元素,根据假设1的对称性,也可以去掉这一段数组,从剩余数组中寻找候选主元素

这样看来,这个算法是正确的。

有一种极端情况,例如121212
最终前五个数的count均为0,只有最后一个2,因为没有后续,所以count=1,此时会只需要判断一下,此时2就是候选主元素。只需要遍历一次数组,看看2是否为主元素即可。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值