摩爾多數表決算法(Moore Majority Vote Algorithm)

摩爾多數表決算法

也叫摩爾投票法。

摩爾投票算法是一種使用線性時間和常數空間查找大部分元素序列的算法。它以1981年出版的Robert S. Boyer和J Strother Moore的名字命名,並且是流式算法的典型例子。
在這裏插入圖片描述
首先請考慮最基本的摩爾投票問題:

找出一組數字序列中出現次數大於總數1/2的數字(並且假設這個數字一定存在)

顯然這個數字只可能有一個。摩爾投票算法是基於這個事實:每次從序列裏選擇兩個不相同的數字刪除掉(或稱爲“抵消”),最後剩下一個數字或幾個相同的數字,就是出現次數大於總數一半的那個。

這個算法有兩個先決條件:

  1. 出現超過一半以上(n/2)的元素有且只有一個
  2. 這個元素一定存在

該算法維護兩個變量,major和count,其實這兩個變量想表達的是兩個隱形的數組a和r,a數組存儲的是“當前暫時無法刪除的數字”,r裏面存儲的是每次刪除一對元素之後的當前結果。

舉個栗子:

有數組{1,2,1,3,1,1,2,1,5},要找出該數組出現次數大於數組長度一半的數,很明顯是1。

  1. 從第一個數字1開始,我們想要把它和一個不是1的數字一起從數組裏抵消掉,但是目前我們只掃描了一個1,所以暫時無法抵消它,把它加入到array,array變成了{1},result由於沒有抵消任何元素所以還是原數組{1,2,1,3,1,1,2,1,5}。
  2. 然後繼續掃描第二個數,是2,我們可以把它和一個不是2的數字抵消掉了,因爲我們之前掃描到一個1,所以array變成了{},result變成了{1,3,1,1,2,1,5}
  3. 繼續掃描第三個數1,無法抵消,於是array變成了{1},result還是{1,3,1,1,2,1,5};
  4. 接下來掃描到3,可以將3和array數組裏面的1抵消,於是array變成了{},result變成了{1,1,2,1,5}
  5. 接下來掃描到1,此時array爲空,所以無法抵消這個1,array變成了{1},result還是{1,1,2,1,5}
  6. 接下來掃描到1,此時雖然array不爲空,但是array裏也是1,所以還是無法抵消,把它也加入這個array,於是array變成了{1,1}(其實到這我們可以發現,array裏面只可能同時存在一種數,因爲只有array爲空或當前掃描到的數和array裏的數字相同時才把這個數字放入array),result還是{1,1,2,1,5}
  7. 接下來掃描到2,把它和一個1抵消掉,至於抵消哪一個1,無所謂,array變成了{1},result是{1,1,5}
  8. 接下來掃描到1,不能抵消,array變成了{1,1},result{1,1,5}
  9. 接下來掃描到5,可以將5和一個1抵消,array變成了{1},result變成了{1}

至此掃描完成了數組裏的所有數,result裏剩了1,所以1就是大於一半的數組。


我們再根據只有兩個變量的實際代碼理一遍:

major 初始化隨便一個數,

count 初始化爲0

數組:{1,2,1,3,1,1,2,1,5}

  1. 掃描到1,count是0(沒有元素可以和當前的1抵消),於是major = 1,count = 1(此時有1個1無法被抵消)
  2. 掃描到2,它不等於major,於是可以抵消掉一個major => count -= 1,此時count =
    0,其實可以理解爲掃到的元素都抵消完了,這裏可以暫時不改變major的值
  3. 掃描到1,它等於major,於是count += 1 => count = 1
  4. 掃描到3,它不等於major,可以抵消一個major => count -= 1 => count =
    0,此時又抵消完了(實際的直覺告訴我們,掃描完前四個數,1和2抵消了,1和3抵消了)
  5. 掃描到1,它等於major,於是count += 1 => count = 1
  6. 掃描到1,他等於major,無法抵消 => count += 1 => count = 2 (掃描完前六個數,剩兩個1無法抵消)
  7. 掃描到2,它不等於major,可以抵消一個major => count -= 1 => count = 1,此時還剩1個1沒有被抵消
  8. 掃描到1,它等於major,無法抵消 => count += 1 => count = 2
  9. 掃描到5,它不等於major,可以抵消一個major => count -= 1 => count =
    1至此掃描完成,還剩1個1沒有被抵消掉,它就是我們要找的數。

一個有趣的舉例

其實這個算法的核心就是對拼消耗。

玩一個諸侯爭霸的遊戲,假設你方人口超過總人口一半以上,並且能保證每個人口出去幹仗都能保證一換一。如果最後還有人活下來的國家就是勝利。那直接就大混戰唄,最差所有人都聯合起來對付你(對應你每次選擇作爲計數器的數都是衆數),或者其他國家也會相互攻擊(會選擇其他數作爲計數器的數),但是隻要你們不要內鬥,最後肯定你贏。最後能剩下的必定是自己人。

int majorityElement(vector<int>& nums)
{
	int major = 0, count = major;
	for (int i = 0; i < nums.size(); i++)
	{
		if (count == 0)
		{
			major = nums[i];
			count++;
		}
		else major == nums[i] ? count++ : count--;
	}
	return major;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章