leetcode 169: 多数元素

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

  1. 暴力求解:双层循环,枚举每一个元素,遍历得到每一个元素的个数进而找到元素个数最多的。 时间复杂度为O(n^2)。
  2. 利用哈希表map,键值对为(key,count),统计每一个元素出现的次数,然后遍历map得到出现次数最多的元素。时间复杂度为O(n),空间复杂度为O(n)。
class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        dict = {}
        max = 0
        for i in range(len(nums)):
            if nums[i] in dict.keys():
                dict[nums[i]] += 1
            else:
                dict[nums[i]] = 1
        for key, value in dict.items():
            if value > max:
                val = key
                max = value
        return val
  1. sort整个数组,因为多数元素出现次数大于n/2,所以下标为|n/2|的元素即为所求。时间复杂度为O(n logn),自己写堆排空间复杂度可以为O(1),调库的空间复杂度为O(logn)。
  2. 分治法:
    如果数 a 是数组 nums 的众数,如果我们将 nums 分成两部分,那么 a 必定是至少一部分的众数。将数组分成左右两部分,分别求出左半部分的众数 a1 以及右半部分的众数 a2,随后在 a1 和 a2 中选出正确的众数。
    使用分治算法递归求解,直到所有的子问题都是长度为 1 的数组。长度为 1 的子数组中唯一的数显然是众数,直接返回即可。如果回溯后某区间的长度大于 1,我们必须将左右子区间的值合并。如果它们的众数相同,那么显然这一段区间的众数是它们相同的值。否则,我们需要比较两个众数在整个区间内出现的次数来决定该区间的众数,即遍历一遍。
class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        return self.majorty(nums, 0, len(nums)-1)

    def majorty(self, nums, start, end):
        if start == end:
            return nums[start]
        else:
            mid = int((start + end) / 2)
            left = self.majorty(nums, start, mid)
            right = self.majorty(nums, mid+1, end)
            if left == right:
                return left
            else:
                count_left = 0
                count_right = 0
                for i in range(end-start+1):
                    if nums[start+i] == left:
                        count_left += 1
                    elif nums[start+i] == right:
                        count_right += 1
                if count_left >= count_right:
                    return left
                else:
                    return right

复杂度分析

时间复杂度:O(nlogn)。函数 majority() 会求解 2 个长度为n/2 的子问题,并做两遍长度为 n 的线性扫描(最后的一整个遍历)。因此,分治算法的时间复杂度可以表示为:
T(n) = 2T(n/2) + 2n
根据 主定理,本题满足第二种情况,所以时间复杂度可以表示为:
在这里插入图片描述
主定理:
在这里插入图片描述

空间复杂度:O(logn)。尽管分治算法没有直接分配额外的数组空间,但在递归的过程中使用了额外的栈空间。算法每次将数组从中间分成两部分,所以数组长度变为 1 之前需要进行 O(logn) 次递归,即空间复杂度为 O(logn)。

  1. 摩尔投票法: Boyer-Moore 投票算法
    因为所求的数出现的次数大于n/2,所以采用两两对抗抵消的思想,遍历数组完成。具体地,设置一个winner,对其进行计数count,令数组的第一个元素为winner,没遇到一个不一样的值相互抵消,count-=1;如果抵消没了,数组的下一个值作为winner;遇到和winner相同的数,就count+=1。最后winner就是所求,因为他的数量大于n/2,永远不会被抵消没。
class Solution(object):
    def majorityElement(self, nums):
        """
        :type nums: List[int]
        :rtype: int
        """
        winner = None
        count = 0
        for i in range(len(nums)):
            if count == 0:
                winner = nums[i]
            if nums[i] != winner:
                count -= 1
            elif nums[i] == winner:
                count += 1
        return winner

时间复杂度O(n),空间复杂度为O(1)。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值