169. Majority Element

题目描述

给定一个数组,找这个数组中的主元素,主元素是元素出现次数大于⌊ n/2 ⌋的元素。假设给定的数组非空,主元素都存在,假定只有一个正确答案。

我的解法1——暴力遍历+hash

  • 用hash存已经遍历过(即已排除的)的数,稍微降低算法复杂度
  • 两层循环,n2的复杂度。
  • 结果:beat 49.69%
class Solution(object):
    def majorityElement(self, nums):

        length = len(nums)/2
        dic = {}
        for i in nums:
            now = 0
            if(i in dic):
                continue
            else:
                dic[i] = 1
            for j in nums:
                if(i == j):
                    now += 1
                    if(now > length):
                        return i

我的解法2——排序取中位数

  • 排序后取中位数即可
  • 复杂度nlogn
  • 结果:beat 94.35%
class Solution(object):
    def majorityElement(self, nums):

        nums.sort()
        return nums[len(nums)/2]
        

简便解法1——hash数数后遍历

  • 利用hashtabel,最后遍历一下hashtable即可。
  • 一层循环,n的复杂度
class Solution:
    def majorityElement(self, nums):
        counts = collections.Counter(nums)
        return max(counts.keys(), key=counts.get)

简便解法2——分治

  • 在数组的左右两半上递归,直到区间长度为1。如果当前区间长度大于1,则必须将区间的左右两半的答案组合起来。
    • 如果要合并的两个区间的多数元素一致,那么整个切片的多数元素显然是相同的。
    • 否则,只有其中一个可以是“正确的”,所以我们需要计算左右多数元素的出现,以确定哪个子片的答案是全局正确的。
  • 时间复杂度T(n) = 2T(n/2) + 2n = nlogn
class Solution:
    def majorityElement(self, nums, lo=0, hi=None):
        def majority_element_rec(lo, hi):
            # base case; the only element in an array of size 1 is the majority
            # element.
            if lo == hi:
                return nums[lo]

            # recurse on left and right halves of this slice.
            mid = (hi-lo)//2 + lo
            left = majority_element_rec(lo, mid)
            right = majority_element_rec(mid+1, hi)

            # if the two halves agree on the majority element, return it.
            if left == right:
                return left

            # otherwise, count each element and return the "winner".
            left_count = sum(1 for i in range(lo, hi+1) if nums[i] == left)
            right_count = sum(1 for i in range(lo, hi+1) if nums[i] == right)

            return left if left_count > right_count else right
        
        return majority_element_rec(0, len(nums)-1)
            

简便方法3——摩尔投票算法

  • 好方法!!!
  • 本质上也是一种分治法
  • 每次从数组中找出一对不同的元素,将它们从数组中删除,直到遍历完整个数组。由于这道题已经说明一定存在一个出现次数超过一半的元素,所以遍历完数组后数组中一定会存在至少一个元素。 上面就是这种算法的思想,删除操作可以在常数时间内完成,但是查找不同的元素无法在常数时间内完成,需要用到以下的Trick。
  • Trick:在算法执行过程中,使用常量空间来记录一个候选元素c以及它的出现次数f(c),c即为当前阶段出现次数超过一半的元素。在遍历开始之前,该元素c为空,f(c)=0。然后开始遍历数组A时:
    • 如果f(c)为0,表示当前并没有候选元素,也就是说之前的遍历过程中没有找到超过一半的元素。那么,如果超过一半的元素c存在,那么c在剩下的子数组中,出现的次数也一定超过一半。因次我们可以将原始问题转化成它的子问题。此时c赋值为当前元素,同时f(c)=1。
    • 如果当前A[i]==c,那么f(c)+=1。(没有找到相同的元素,只需要把相同的元素累加起来)
    • 如果当前元素A[i]!=c,那么f(c)-=1(相当于删除一个c),不对A[i]做任何处理(相当于删除A[i])
  • 如果遍历结束之后,f(c)不为0,那么再次遍历一遍数组,如果c真正出现的频率。
  • 时间复杂度是O(n),空间复杂度为O(1)。
class Solution:
    def majorityElement(self, nums):
        count = 0
        candidate = None

        for num in nums:
            if count == 0:
                candidate = num
            count += (1 if num == candidate else -1)
            
        return candidate

参考答案

https://leetcode.com/problems/majority-element/solution/