写在整个系列前的废话
这个系列主要是记录最近刷力扣时遇到的一些有趣的算法,笔者本人现大三准备保研,一方面是帮助自己记忆,另一方面也分享给基础较弱准备校招社招的小伙伴,助力大家一同成长。本系列博客将以例题为切入点,就解题思路分析算法的适用环境,并给出样例代码(通常是C,因为笔者主修硬件,编程这方面实际能力较弱,只会C、C++、java和python),如有问题欢迎在评论区进行讨论。
例题题目
8年纪3班今天组织了第13轮团员选拔,由于班主任阎老师把关严格,这次还是只入选一个人。小明主动报名了检票员,投票完成后,负责传投票箱的小芳说有一个人得到的票已经超过了一半,但是她记不得是谁了。小明于是决定找一个方法,以最快的速度完成点票。请你帮小明设计一个算法,快速的找出入选者。已知选拔采取一人一票的形式,得票最高且数量过半者入选。
解题思路
记入选者为 A A A,其选票数量为 N A N_A NA;其余人为 A ˉ \bar{A} Aˉ,数量为 N A ˉ N_{\bar{A}} NAˉ。假设发生随机抵消,任意两张不相同的的选票相互抵消,则会出现如下两种情况:
- A A A与 A ˉ \bar{A} Aˉ相互抵消,记数量为 n 1 n_1 n1
- A ˉ \bar{A} Aˉ之间相互抵消,记数量为 n 2 n_2 n2
则必然有:
n
1
≤
N
A
ˉ
n_1\leq N_{\bar{A}}
n1≤NAˉ
由题已知:
N
A
ˉ
<
N
A
N_{\bar{A}}<N_{A}
NAˉ<NA
则随机抵消发生后,
A
A
A持有的选票数为:
N
A
−
n
1
≥
N
A
−
N
A
ˉ
>
0
N_{A}-n_1\geq N_{A}-N_{\bar{A}}>0
NA−n1≥NA−NAˉ>0
即随机抵消后,
A
A
A手中仍会持有选票,而
A
ˉ
\bar{A}
Aˉ则不会持有选票。
在此基础上,我们写一个随机抵消过程:两个变量,分别用于记录待抵消的人员的编号与其现持有的选票数。遍历数组,投票对象与待抵消对象相同,则又发现一张选票,现持有选票书+1;投票对象与抵消对象不同,若待抵消选票数大于0,则发生抵消,带抵消选票-1,若带抵消选票数等于0,则更改抵消对象,抵消对象=投票对象,带抵消选票数=1。遍历一遍后,抵消对象即为入选者。
使用条件
- 每人投一票
- 入选条件是获得超半数的选票
- 入选者存在
资源占用
内存占用:
O
(
1
)
O(1)
O(1)
时间占用:
O
(
n
)
O(n)
O(n)
代码实现
以上题目使用C语言函数实现,其中 ∗ L i s t *List ∗List为投票人数组, i i i表示 i i i号投票人, L i s t [ i ] List[i] List[i]为i号投票人的投票对象, L i s t L e n ListLen ListLen为输入数组的长度,返回 t a r g e t target target即为最终入选人员编号。
int MooreVoting(int *List,int ListLen)
{
int target = List[0];
int targetNum = 1;
//初始票数为0,则假设第一个投票对象为抵消对象,录入第一张选票后,其持有选票数为1
for(int i=1;i<ListLen;i++)
//从第二张选票开始遍历
{
if(List[i]==target)
{
targetNum++;
}
//如果对象相同,累加待抵消票数
else
{
if(targetNum!=0)
{
targetNum--;
}
//剩余选票数大于0,则扣除待抵消票数
else
{
target = List[i];
targetNum = 1;
}
//剩余选票数为0,更换抵消对象
}
}
return target;
}
本期的废话
有没有消光大佬啊,最近换了个游戏本,刚入手,根本不会玩。