【摩爾投票】算法
問題: (majority element)如有一個數組L,長度爲n,找出是否有一個數N,N的出現次數大於等於n/2。數組
問題不算太難,通常能夠經過遍歷計數,或者排序找中位數的辦法來解決。可是若是要求時間複雜度是O(n),空間複雜度是O(1),那麼恐怕就沒那麼簡單了。摩爾投票算法正好是這麼一個O(n)和O(1)的算法。spa
● 描述code
聲明m=0和cnt=0兩個變量後面用。blog
遍歷數組,當cnt爲0時將當前遍歷到的元素賦值給m,而後cnt+=1。若cnt不爲0,且遍歷到的元素等於當前的m,那麼cnt += 1,;若cnt不爲0,可是遍歷到的元素也不等於當前的m,那麼cnt -= 1。最終遍歷完成以後,變量m的值就是咱們要找的那個majority element。排序
爲何這個算法奏效?咱們姑且從感性的角度來理解一下。既然是投票,那麼就把這個數組YY成一個投票人的集合。這些投票人心中都有本身想要投的人,而後咱們要找出的就是哪一個候選人得票能過半的。由於咱們沒法作到同時聽取全部人的想法,因此咱們採起一種「淘汰制」選擇。首先第一我的上臺說明他想要推舉的候選人好比說a。若是第二我的也是推舉a的,那麼能夠認爲a的人氣是兩人份的。第三人若是不是推舉a的,那麼他將減小a的一份人氣。由於選舉是要求最終得票過半,因此對於候選人a來講,每個人不投票給他,就至關於他損失了一票。若第四人也不投a,則a的人氣歸零,和其餘候選人重回起跑線,但此時站在候選臺上的仍然是a。此時第五人若想就能夠推舉他想選的人b,把a給擠下來,b的人氣爲1。以此類推… 從大局上講,若是a的支持者勢力足夠強大,那麼不管a被打倒多少次,最終仍是可以回到候選臺上。這邊勢力強大的具體量化就是a的支持者至少達到n/2人。這樣即便前一半人都支持a,後一半人都不支持a,最終a的人氣歸零,可是仍是保證站在候選臺上的是a。element
上面就是對投票算法的一個粗淺且感性的理解。it
● 代碼:class
def vote(a): m,cnt = 0,0 for n in a: if cnt == 0: m = n cnt = 1 elif n == m: cnt += 1 else: cnt -= 1 return m
上述過程當中並無對投票者支持的人很是分散的狀況做出判斷。即沒法完成選舉的時候不會給出沒法完成的錯誤,而是返回了接近數組末尾的某個「勢力相對較強」的元素。若是須要對是否超過n/2作判斷那麼能夠再去遍歷依次數組,看到底m元素出現了幾回,是否達到標準便可。變量
● 更復雜一點
若是將問題換成,找出全部出現次數大於n/3次的元素呢。顯然,這種元素最多隻能有兩個。因此咱們可使用投票算法,將有多是符合要求的兩個元素找出來,而後再看他們是否都超過了n/3來判斷是否選擇它們做爲須要選出來的元素。
具象到選舉中來,那麼能夠認爲如今要選的人是兩個。並且這兩我的競選的位置是平級,不分前後的,因此熱門候選人a和熱門候選人b的支持者之間不構成直接競爭。所以,算法就變成了,第一人推薦a,a走上甲候選臺。第二人推薦b,b走上乙候選臺,而此時對a不構成影響因此a的人氣不減。若是第三人推薦的是c,那麼a和b的人氣都要減一份,都歸零了(顯然不能只減一我的的人氣,不然另外一我的就可能會出現明明支持者不多,可是因爲推舉的順序比較靠前因此當選的bug)。若是此時第四人支持的不是a或者b而是d,那麼就能夠從甲乙任意一個候選臺中擠走一個。好比擠走a,以後d的人氣是1,而另外一個候選臺上的b仍然是0人氣。這麼循環下去,因爲a和b不互相競爭,因此經過這個算法獲得的a和b是全部候選人中相對強勢的兩個。
代碼的實現也不復雜:
def vote(a): m,n = 0,0 cm,cn = 0,0 for i in a: if cm == 0: m = i; cm += 1 elif cn == 0: n = i; cn += 1 elif i == m: cm += 1 elif i == n: cn += 1 else: cm -= 1; cn -= 1 return m,n
* 其實摩爾投票,主要是爲了可以在O(n)的時間和O(1)的空間解決問題。若是沒有這些限制,那麼使用HashMap或者其餘的一些什麼方法則要比這種方法好理解得多多。