剑指 Offer 39. 数组中出现次数超过一半的数字

163 阅读2分钟

前言:剑指offer刷题系列

问题:

数组中有一个数字出现的次数超过数组长度的一半,请找出这个数字。

你可以假设数组是非空的,并且给定的数组总是存在多数元素。

示例:

输入: [1, 2, 3, 2, 2, 2, 5, 4, 2]
输出: 2

思路1:

因为最近学的python嘛,记忆还很深刻,一开始看到这个题,脑子里就有的是python中列表的count()函数,只要数的个数大于数组总数的一半就可以了。

for循环数组中的每个元素,如果数组中某个元素的个数大于数组总数的二分之一,就可以直接返回这个数,即“众数”

时间复杂度O(n*n)

空间复杂度O(1)

基于上述思考,代码如下:

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        for i in range(len(nums)):
            if nums.count(nums[i]) >= len(nums)/2:
                return nums[i]

执行结果如下图:

image-20230803221541382.png

方法是对的,但是最后一个用例卡住了。

思路2:

然后换了思路,这是一个数学题,超过数组总数一半的数,肯定是数组的中位数,直接将数组排序,然后输出数组的中位数就是“众数”啦

基于上述思考,代码如下:

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        nums.sort()
        return nums[int(len(nums)/2)]

执行结果如下图:

image-20230803221846335.png

思路3:

是看了评论区的一种神奇算法,叫摩尔投票法,就好比打擂台,假设每个人都是一对一,不相上下、同归于尽:一个人守擂台,每上来一个数,如果和擂台上的数相同就留下,要是不同就一起打一架,然后一起同归于尽,最后剩下的数就一定是众数,可以用数学归纳法证明。

基于上述思考,代码如下:

class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        votes = 0
        for i in range(len(nums)):
            if votes == 0: x = nums[i]
            votes += 1 if nums[i] == x else -1
        return x

执行结果如下图:

image-20230803222634819.png

学到的知识点:

摩尔投票法:

基于当一个数的重复次数超过数组长度的一半,每次将两个不相同的数删掉,最终剩下的就是要找的数。

优点:在一次扫描数组的过程中,找出出现次数超过数组长度一半的数。

使用场景就是:当我们需要在一个无序且候选人不定的数组中,寻找出现次数超过总数一半的元素,这种问题在数据流处理(数据不是一次性给的)或众数求解中经常出现。