寻找数组中出现次数超过三分之一的元素:五种解法详解

寻找数组中出现次数超过三分之一的元素:五种解法详解

leetcode Leetcode solutions leetcode 项目地址: 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 leetcode 项目地址: https://gitcode.com/gh_mirrors/leetcode1/leetcode

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

芮伦硕

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值