问题
题目列表第八道:找出整型数组中占比超过一半的数,难度为易
问题描述
小R从班级中抽取了一些同学,每位同学都会给出一个数字。已知在这些数字中,某个数字的出现次数超过了数字总数的一半。现在需要你帮助小R找到这个数字。
测试样例
样例1:
输入:
array = [1, 3, 8, 2, 3, 1, 3, 3, 3]
输出:3
样例2:
输入:
array = [5, 5, 5, 1, 2, 5, 5]
输出:5
样例3:
输入:
array = [9, 9, 9, 9, 8, 9, 8, 8]
输出:9
解题思路
这道题似乎挺容易的,可以先对数字排个序,找中间位置的数,然后再用一趟遍历确认它是不是要找的占比超过一半的数。排序需要的时间复杂度是O(nlogn),遍历需要的时间复杂度是O(n),加起来是O(nlogn)。
不过,我在写的过程中还发现了另外一种写法——Boyer-Moore 投票算法(摩尔投票法,又叫多数投票法),能够在O(n)的时间复杂度和O(1)的空间复杂度下解决问题。 摩尔投票算法最早由Robert S. Boyer和J Strother Moore在1981年的论文《MJRTY—A Fast Majority Vote Algorithm》中提出。这篇论文介绍了摩尔投票算法的原理和证明,并展示了它在实际应用中的高效性。
摩尔投票算法的基本思想非常简单,就是通过不同元素的两两消除来找到可能存在的多数元素。即,如果一个数组中存在超过半数的众数,那么对数组的不同元素进行两两消除,最后必然能剩下这个众数。算法要两次遍历数组,分别用于寻找可能的候选元素和遍历确认。在寻找候选元素的遍历中会维护两个变量:候选元素candidate和其对应的票数count。在开始时,候选元素为空,票数为0。然后对于数组中的每个元素,如果票数为0,将当前元素设为候选元素,并将票数设置为1;如果当前元素等于候选元素,则票数加1;如果当前元素不等于候选元素,则票数减1。这样,相同元素的票数会累计,不同元素相消会导致票数减少。由于多数元素的出现次数为一半以上,所以最终留下的候选元素就很有可能是多数元素。然后我们需要进一步验证候选元素是否真的是多数元素(因为可能存在没有多数元素的情况)。我们再次遍历数组,统计候选元素的出现次数,如果它的出现占比超过了一半,则确认它为多数元素;否则,表示没有多数元素。
具体实现
思路阐明后,代码的实现还是比较简单的,如下。
candidate = None
count = 0
# 第一遍遍历:找到候选元素
for num in array:
if count == 0:
candidate = num
count = 1
elif candidate == num:
count += 1
else:
count -= 1
# 第二遍遍历:验证候选元素是否为多数元素
if candidate is not None:
if array.count(candidate) > len(array) // 2:
return candidate
else:
raise ValueError("No majority element exists.")
return candidate
结语
通过这道题,我们对摩尔投票法有了更深入的理解,希望在刷题的过程中能积累更多高效巧妙的算法。