1) 问题描述
基本问题:对于一个给定数组A[0:n-1]
,找出出现次数大于
⌊n/2⌋
的元素,称为Majority element.
一般问题:对于一个给定数组A[0:n-1]
,找出出现次数大于
⌊n/k⌋(k≥2)
的所有元素.
LeetCode问题链接:
Majority Element: https://leetcode.com/problems/majority-element/
Majority Element II: https://leetcode.com/problems/majority-element-ii/
2) k = 2问题
k=2时,满足要求的元素最多只有一个,我们将该唯一可能满足的元素称为Candidate,记作
v
,注意是可能满足的元素。其余元素的重复次数均不超过
将问题分解成递归问题。算法做两件事情:
- 找到暂时唯一满足条件的Candidate v
- 继续扫描数组A,计算
v 出现的次数,判断 v 的出现次数是否大于⌊n/2⌋ 。
如何找到Candidate v 的算法如下:
i,c:=0,0
do i != n
if v = A[i] then c:=c+2
elif c = i then c,v:=c+2,A[i]
fi
i:=i+1
od
{R:only v may occur more than n/2 times in A[0:n-1]}
下面要说明程序结束时R:
判断是正确的。我们引入与R:
等价的表示:
因此我们要说明
P:
在程序循环过程中始终为
True
。考虑循环部分,
- 程序第3行,
if v = A[i]
条件满足,那么v
在A[0:i]
中出现次数比在A[0:i-1]
中出现次数+1
,相应c:=c+2
,在本次循环中v
依然是唯一可能满足条件的数,(在本次循环之前c>2i
,本次循环c:=c+2>2(i+1)
),同时其他数的出现次数不变。因此P:
为True
。 - 程序第4行,
elif c = i
,则i-c/2=i/2
,此时在A[0:i-1]中没有元素出现次数超过i/2
次,唯一可能在A[0:i]中初始次数多于i/2
次的只可能是A[i]
,因此A[i]
成为新的Candidate。P:
依然保持True
。 - 循环内的其他情况时,即
c>i && v!=A[i]
时,P:
依然保持True
。
综上,我们知道在程序结束时,R:=True
,即v
是唯一可能的出现次数大于n/2
的数。
3) k >= 2的一般性问题
对于更一般的 2≤k≤n ,算法同样做两件事情:
- 第一步,找出满足条件的Candidate集合T,其中的元素出现次数可能大于
n/k
次。 - 继续扫描数组A,计算集合T中各个元素出现的次数。
对于k
,最多有k-1
个不同元素出现多于n/k
次,即T的尺寸为k-1
。我们将R:
进行扩展:
相应的
P:
伪代码如下:
分析和k=2情况类似。
算法复杂度:
空间复杂度O(k)
;
时间复杂度:如果集合T使用树形结构,查找O(lg(k))
和删除多个数操作复杂度为O(klg(k))
,所以时间复杂度可能为O(nklg(k))
。优化删除操作(实际上只删除一个元素即可),可以使复杂度降低为O(nlg(k))
.
第二种算法
参考文章:
[1]Finding Repeated Elements:
http://www.cs.utexas.edu/users/misra/scannedPdf.dir/FindRepeatedElements.pdf