leetcode阶段总结——求众数

本文深入探讨了摩尔投票法,一种高效查找数组中多数元素的算法。通过具体实例,详细解释了如何利用该方法解决LeetCode上的169题(多数元素)和229题(求众数II)。文章不仅提供了清晰的思路解析,还附带了Python实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

相关题目

169.多数元素
229.求众数 II

摩尔投票法

摩尔投票法基于这样一个事实,当一个数的重复次数超过数组长度的一半,每次将两个不相同的数删除,最终剩下的就是要找的数。
为了解释清楚这个问题,首先来看leetcode的第169题。

给定一个大小为 n 的数组,找到其中的多数元素。多数元素是指在数组中出现次数大于 ⌊ n/2 ⌋ 的元素。
你可以假设数组是非空的,并且给定的数组总是存在多数元素。
示例 1:
输入: [3,2,3]
输出: 3
示例 2:
输入: [2,2,1,1,1,2,2]
输出: 2

解决这个题的思路是这样的:

我们维护一个计数器,如果遇到一个我们目前的候选众数,就将计数器加一,否则减一。只要计数器等于 0 ,我们就将 nums 中之前访问的数字全部 忘记 ,并把下一个数字当做候选的众数。这样到最后,总能找到符合要求的数。

为什么呢?考虑这个问题:当我们“减到0重新换个数开始计数”,实际上我们从原数组中忽略了2n个数,其中有n个是减到零之前那个候选的数,另外n个数不知道是什么数,反正不是那个候选的数。也就是说,可以看作删掉了n组元素,每组元素包括两个元素,这两个元素是不同的。
这一过程可以看作“抵消”。什么意思呢,最差情况下,每一对中都有一个众数,那么一个众数抵消一个非众数,由于本题中的众数是大于出现次数一半的,所以抵消之后,剩下的数组中,众数的值不受影响。因此,到最后还剩一个或几个数,一定是众数。

class Solution:
    def majorityElement(self, nums: List[int]) -int:
        count = 0
        for num in nums:
            if count == 0:
                candidate = num
            count = count + 1 if candidate == num else count - 1
        return candidate

到这里还比较好理解。下面是一个扩展:leetcode第229题。

给定一个大小为 n 的数组,找出其中所有出现超过 ⌊ n/3 ⌋ 次的元素。 说明: 要求算法的时间复杂度为 O(n),空间复杂度为 O(1)。
示例 1:
输入: [3,2,3]
输出: [3]
示例 2:
输入: [1,1,1,3,3,2,2,2]
输出: [1,2]

这个题的解决思路是这样的:
超过n/3的数最多只能有两个。先选出两个候选人A,B。 遍历数组,分三种情况:
1.如果投A(当前元素等于A),则A的票数++;
2.如果投B(当前元素等于B),B的票数++;
3.如果A,B都不投(即当前与A,B都不相等),那么检查此时A或B的票数是否减为0。
评论区里说这样可以选出来第一多和第二多的元素,但是根据我的实验并非如此,第二多的元素不一定会被选出来,思考了很久之后,借鉴上面的想法,我觉得应该是这样的:
我们有两个计数器,count1和count2,当遍历到的元素等于num1时,count1自增1,而当遍历到的元素不等于num1也不等于num2时,count1才自减1。这说明,当count1减到零的时候,我们选择遗忘num1,这实际上也是抵消了n组元素,每组元素包括3个元素,这三个元素也是不同的(num1,num2,另一个不同的数)。
那么,和之前的思路相同,假设有一个数出现的次数大于n/3,它一定会留到最后。那么留到最后的数是否一定出现的次数大于n/3呢?或者是否一定是第一第二大的数呢?非也。如果并不存在这样符合要求的数,那么最后会留下什么数就是天知道。实际上上一道题也是一样,不过上一题的题干里保证了有这样的数,否则一样要验证。

class Solution:
    def majorityElement(self, nums: List[int]) -> List[int]:
        count1,count2 = 0,0
        num1,num2 = None,None

        for num in nums:
            if num == num1:
                count1 += 1
            elif num == num2:
                count2 += 1
            elif count1 == 0:
                num1 = num
                count1 += 1
            elif count2 == 0:
                num2 = num
                count2 += 1
            else:
                count1 -= 1
                count2 -= 1

        count1,count2 = 0,0
        for num in nums:
            if num == num1: count1 += 1
            if num == num2: count2 += 1

        return [i[0] for i in list(zip([num1,num2],[count1,count2])) if i[1] > len(nums) // 3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值