1. 程式人生 > >摩爾投票演算法( Boyer-Moore Voting Algorithm)

摩爾投票演算法( Boyer-Moore Voting Algorithm)

摩爾投票演算法也可以叫做多數投票演算法,是我在看到 leetcode 169(Majority Element)題目時看到的演算法。這篇文章從 leetcode 169(Majority Element)出發講解摩爾投票演算法的原理和優勢,同時從 leetcode 229(Majority Element2)出發講解摩爾投票演算法的改進和推廣。(本文所有程式碼都是python程式碼)

一、Majority Element題目介紹:給定一個長度為n的陣列的時候,找出其中的主元素,即該元素在陣列中出現的次數大於n/2的取整。題目中已經假定所給的陣列一定含有元素,且主元素一定存在。一下是一些常用方法:

1,用字典遍歷每個元素,並計數:

dic = {}
for x in nums:
    if x in dic:
        dic[x] += 1
    else:
        dic[x] = 1
for key,value in dic.items():
    if value > len(nums)/2:
        return key

2,排序法:排序後,出現次數大於一半的肯定在中間

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

二、摩爾投票演算法:摩爾投票演算法的時間和空間都很低,時間複雜度為O(n),空間複雜度為O(1),這也是選擇遮蓋演算法的原因。

1,演算法原理:每次從陣列中找出一對不同的元素,將它們從陣列中刪除,直到遍歷完整個陣列。由於這道題已經說明一定存在一個出現次數超過一半的元素,所以遍歷完陣列後陣列中一定會存在至少一個元素。

  • 演算法在區域性變數中定義一個序列元素(m)和一個計數器(i),初始化的情況下計數器為0;
  • 演算法依次掃描序列中的元素,當處理元素x的時候,如果計數器為0,那麼將x賦值給m,然後將計數器(i)設定為1;
  • 如果計數器不為0,那麼將序列元素m和x比較,如果相等,那麼計數器加1,如果不等,那麼計數器減1。
  • 處理之後,最後儲存的序列元素(m),就是這個序列中最多的元素。
    (如果不確定是否儲存的元素m是最多的元素,還可以進行第二遍掃描判斷是否為最多的元素)

2,演算法虛擬碼:

初始化元素m=0,計數器count=0;
遍歷陣列中的每個數x:
    if i = 0:
        m = x and count = 1
    else if m = x:
        count = count + 1
    else:
        count = count1
Return m

3,Majority Element的摩爾投票演算法求解:

num,count = nums[0],0
for x in nums:
    if count == 0:
        num,count = x,1
    elif x == num:
        count += 1
    else:
        count -= 1
return num

三、摩爾投票演算法的改進:

1,題目: LeetCode 229 [Majority Element II]
給定一個整型陣列,找到所有主元素,它在陣列中的出現次數嚴格大於陣列元素個數的三分之一。

演算法:每次刪除三個不相同的數,最後留下的一定是出現次數超過1/3的數,這個思想可以推廣到出現次數超過1/k次的元素有哪些。

  • 因為出現次數大於n/3的元素最多隻有兩個,所以最開始可以維護兩個數字(num1,num2)和兩個計數器(counter1,counter2);
  • 遍歷陣列,當陣列中元素和num1或者num2相同,對應的counter1或者counter2加1;
  • 如果counter1或counter2為0,將遍歷到的該元素賦給num1或者nums2;
  • 否則counter1和counter2都減1。

2,python程式碼:

num1,count1 = None,0
        num2,count2 = None,0
        for x in nums:# 演算法核心,找出主要元素的候選值
            if x == num1:
                count1 += 1
            elif x == num2:
                count2 += 1
            elif count1 == 0:
                num1,count1 = x,1
            elif count2 == 0:
                num2,count2 = x,1
            else:
                count1 -= 1
                count2 -= 1

        count1,count2 = 0,0
        for x in nums:# 統計確定候選值是真的主要元素
            if x == num1:
                count1 += 1
            if x == num2:
                count2 += 1
        res = []
        if count1 > len(nums)//3:
            res.append(num1)
        if count2 > len(nums)//3:
            res.append(num2)
        return res