寻找数组中出现次数超过三分之一的元素:五种解法详解
leetcode Leetcode solutions 项目地址: https://gitcode.com/gh_mirrors/leetcode1/leetcode
问题描述
给定一个整数数组,找出所有出现次数超过数组长度三分之一的元素。这类问题在数据处理和统计分析中非常常见,特别是在需要识别主要影响因素或异常值时。
解法一:暴力解法
思路分析
暴力解法是最直观的方法:对于数组中的每一个元素,遍历整个数组统计其出现次数,如果超过n/3就加入结果集。
代码实现
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
res = set()
for num in nums:
count = sum(1 for i in nums if i == num)
if count > len(nums) // 3:
res.add(num)
return list(res)
复杂度分析
- 时间复杂度:O(n²),因为对每个元素都要遍历整个数组
- 空间复杂度:O(1),结果集最多包含2个元素
适用场景
适用于小规模数据集,代码简单易懂,但性能较差。
解法二:排序法
思路分析
先对数组排序,这样相同的元素会聚集在一起。然后我们可以通过一次遍历统计连续相同元素的个数。
代码实现
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
nums.sort()
res, n = [], len(nums)
i = 0
while i < n:
j = i + 1
while j < n and nums[i] == nums[j]:
j += 1
if (j - i) > n // 3:
res.append(nums[i])
i = j
return res
复杂度分析
- 时间复杂度:O(n log n),主要由排序决定
- 空间复杂度:取决于排序算法,O(1)或O(n)
适用场景
当数据已经部分有序时效率较高,但排序过程可能改变原始数据顺序。
解法三:哈希表计数
思路分析
使用哈希表记录每个元素的出现次数,然后筛选出出现次数超过n/3的元素。
代码实现
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
count = Counter(nums)
res = []
for key in count:
if count[key] > len(nums) // 3:
res.append(key)
return res
复杂度分析
- 时间复杂度:O(n),需要两次遍历
- 空间复杂度:O(n),需要存储所有元素的计数
适用场景
适用于大多数情况,代码简洁,但需要额外空间。
解法四:Boyer-Moore投票算法
思路分析
这是最优解法,基于摩尔投票算法改进而来。因为最多有两个元素可能超过n/3,所以维护两个候选元素和它们的计数。
代码实现
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
n = len(nums)
num1 = num2 = -1
cnt1 = cnt2 = 0
# 第一轮投票找出两个候选元素
for num in nums:
if num == num1:
cnt1 += 1
elif num == num2:
cnt2 += 1
elif cnt1 == 0:
cnt1 = 1
num1 = num
elif cnt2 == 0:
cnt2 = 1
num2 = num
else:
cnt1 -= 1
cnt2 -= 1
# 第二轮验证候选元素是否真的超过n/3
cnt1 = cnt2 = 0
for num in nums:
if num == num1:
cnt1 += 1
elif num == num2:
cnt2 += 1
res = []
if cnt1 > n // 3:
res.append(num1)
if cnt2 > n // 3:
res.append(num2)
return res
复杂度分析
- 时间复杂度:O(n),两次线性遍历
- 空间复杂度:O(1),只使用了常数空间
适用场景
这是最优解法,适用于大规模数据集,不需要额外空间。
解法五:哈希表优化的投票算法
思路分析
这是投票算法的变种,使用哈希表来维护候选元素,当候选元素超过2个时,对所有候选元素的计数减1。
代码实现
class Solution:
def majorityElement(self, nums: List[int]) -> List[int]:
count = defaultdict(int)
for num in nums:
count[num] += 1
if len(count) <= 2:
continue
new_count = defaultdict(int)
for num, c in count.items():
if c > 1:
new_count[num] = c - 1
count = new_count
res = []
for num in count:
if nums.count(num) > len(nums) // 3:
res.append(num)
return res
复杂度分析
- 时间复杂度:O(n),虽然内部有循环,但总体是线性的
- 空间复杂度:O(1),哈希表最多存储2个元素
适用场景
当需要更灵活的候选元素管理时可以使用,但实现稍复杂。
总结比较
| 解法 | 时间复杂度 | 空间复杂度 | 特点 | |------|------------|------------|------| | 暴力 | O(n²) | O(1) | 简单但效率低 | | 排序 | O(n log n) | O(1)或O(n) | 改变原始数据 | | 哈希表 | O(n) | O(n) | 简单通用 | | 投票算法 | O(n) | O(1) | 最优解 | | 哈希投票 | O(n) | O(1) | 投票算法变种 |
在实际应用中,Boyer-Moore投票算法(解法四)是最推荐的解决方案,它在线性时间内解决问题且不需要额外空间。理解这个算法对于解决类似的多数元素问题非常有帮助。
leetcode Leetcode solutions 项目地址: https://gitcode.com/gh_mirrors/leetcode1/leetcode
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考