摩尔投票算法能高效找出数组中出现次数超过一半的数字,其核心是通过抵消机制在O(n)时间与O(1)空间内锁定候选者,最终遍历验证其合法性。

要找出数组中出现次数超过一半的数字,最优雅且高效的方法无疑是摩尔投票算法(Moore's Voting Algorithm)。它以一种巧妙的“抵消”机制,能够在只遍历一次数组的情况下,用极小的额外空间找到这个特殊的数字。
解决这类问题,我个人最偏爱摩尔投票算法。它的核心思想其实非常直观:如果你有一个元素,它的出现次数超过了数组总长度的一半,那么即使你让它和所有其他不同的元素“同归于尽”,它也一定能笑到最后。
具体操作起来,我们维护两个变量:一个
candidate
count
candidate
count
count
candidate
candidate
count
candidate
count
candidate
count
candidate
为什么这个方法管用? 因为它利用了多数元素的特性。假设多数元素是M,少数元素是X。无论M和X如何交错出现,M的数量总是多于X的总和。当M与X相互抵消时,最终M一定会剩下。这个过程,就像一场多数派的“胜利”。
例如,数组
[2, 1, 2, 3, 2, 2]
candidate = 2
count = 1
1
1 != 2
count = 0
candidate
candidate = 1
count = 1
2
2 != 1
count = 0
candidate
candidate = 2
count = 1
3
3 != 2
count = 0
candidate
candidate = 3
count = 1
2
2 != 3
count = 0
candidate
candidate = 2
count = 1
2
2 == 2
count = 2
candidate
def find_majority_element(nums):
candidate = None
count = 0
for num in nums:
if count == 0:
candidate = num
count = 1
elif num == candidate:
count += 1
else:
count -= 1
# 如果题目保证存在,这一步就足够了。
# 如果不保证,还需要第二遍遍历验证candidate的实际出现次数。
return candidate
# 示例
# arr = [1, 2, 3, 2, 2, 2, 5, 2]
# print(find_majority_element(arr)) # 输出 2每次我思考这类“多数元素”问题时,摩尔投票算法总是第一个跳入脑海。它的核心魅力在于其无与伦比的效率。我们来看它的时间复杂度和空间复杂度:
candidate
count
这种O(n)时间、O(1)空间的能力,在算法世界里简直是“圣杯”级别的表现。相比之下,其他方法,比如先排序再取中间元素(O(n log n)时间),或者用哈希表计数(O(n)时间,O(n)空间),都无法同时达到这样的极致效率。摩尔投票算法之所以高效,正是因为它巧妙地利用了多数元素的“数量优势”,避免了复杂的存储和比较。它不是在“找”这个数,而是在“淘汰”掉所有非多数元素的过程中,让多数元素自然浮现。
这是一个很关键的问题,也是摩尔投票算法的一个“小陷阱”或者说需要注意的细节。如果题目明确保证数组中一定存在出现次数超过一半的数字,那么摩尔投票算法的单次遍历结果就是正确的。但如果这个前提不成立,也就是说,数组中可能不存在这样的多数元素,那么摩尔投票算法在第一遍遍历结束后,
candidate
举个例子:数组
[1, 2, 3, 4, 5]
candidate = 1
count = 1
2
count = 0
candidate = 2
count = 1
3
count = 0
candidate = 3
count = 1
4
count = 0
candidate = 4
count = 1
5
count = 0
candidate = 5
count = 1
5
5
再比如:数组
[1, 1, 2, 2, 3]
candidate = 1
count = 1
1
candidate = 1
count = 2
2
candidate = 1
count = 1
2
candidate = 1
count = 0
3
candidate = 3
count = 1
3
所以,当不确定多数元素是否存在时,我们需要在摩尔投票算法的第一遍遍历结束后,再进行第二次遍历。这次遍历的目的是统计
candidate
def find_majority_element_robust(nums):
if not nums:
return None # 或者抛出异常,根据具体需求
candidate = None
count = 0
# 第一遍:找出候选者
for num in nums:
if count == 0:
candidate = num
count = 1
elif num == candidate:
count += 1
else:
count -= 1
# 第二遍:验证候选者是否真的是多数元素
actual_count = 0
for num in nums:
if num == candidate:
actual_count += 1
if actual_count > len(nums) // 2:
return candidate
else:
return None # 或者其他指示,表示不存在这种两遍遍历的方法,虽然多了一次遍历,但总时间复杂度依然是O(n),空间复杂度保持O(1),提供了更强的鲁棒性。
在解决“找出数组中出现次数超过一半的数字”这个问题时,除了摩尔投票算法,我们当然还有其他方法。每种方法都有其适用场景和优缺点,理解这些能帮助我们根据具体需求做出最佳选择。
哈希表(Hash Map / Dictionary)计数法:
len(nums) // 2
排序法:
nums[len(nums) // 2]
分治法(Divide and Conquer):
总结一下,摩尔投票算法以其O(n)时间复杂度和O(1)空间复杂度,在这个特定问题上表现出了压倒性的优势。但如果问题稍作变动,比如需要找出出现次数超过
k
以上就是如何找出数组中出现次数超过一半的数字?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号