天天看點

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

update:21/07/24

前言

絕對衆數。在數列 \(p\) 中出現次數嚴格大于 \(\frac{\vert p \vert}{2}\) 的數叫做絕對衆數。

快速排序

一般來說我們可以直接排序解決問題,如果存在絕對衆數的話,最中間的數一定是絕對衆數。

時間複雜度為 \(\mathcal{O}(n\ log n)\)

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

摩爾投票算法

摩爾投票法的基本思想很容易了解,在每一輪投票過程中,從數組中找出一對不同的元素,将其從數組中删除。循環執行這一操作,直到無法再進行投票,如果數組為空,則沒有任何元素出現的次數超過該數組長度的一半(無絕對衆數)。如果隻存在一種元素,那麼這個元素則可能為絕對衆數。

在編寫算法的過程中,我們可以直接按照數組原來的順序進行投票,删除。

具體實作:設 \(num\) 為目前出現階段超過半數的元素(候選數),\(cnt\) 為此元素出現次數。由于有了階段的概念,這其實這也是一種動态規劃思想。

一開始 \(num\) 直接為數組第一個元素,\(cnt = 1\)。(原因是隻有一個元素的數組,唯一的那個元素一定是絕對衆數)

接着周遊數列 \(p\),設目前數為 \(k\) .

  • 若 \(k = num\),則 \(cnt + 1\)。
  • 若 \(k \not = num\),則我們可以把目前候選數和目前數同時删除,具體操作就是讓 \(cnt - 1\),這樣就相當于忽略了數 \(k\),删去一個 \(num\)。
  • 若 \(cnt = 0\),表明前一階段并沒有出現次數超過半數的元素。假設絕對衆數存在,那麼絕對衆數一定在剩餘的數組中是絕對衆數,這樣我們隻需要求解原始問題的子問題即可,即在後一階段的絕對衆數是多少。回到開始, \(num\) 為目前元素,\(cnt = 1\)。

最終,若 \(cnt > 0\),則 \(num\) 可能為候選元素。掃一遍數組确認一下即可。

複雜度為線性的,\(O(n)\)。

void majorityElement(vector<int>& p) {  
    int num = -1, cnt = 0;
    for(int i = 0; i < p.size(); ++i) {
        if(cnt == 0) num = p[i], cnt++;
        else if(p[i] == num) cnt++;
        else cnt--;        
    }
    cnt = 0;
    for(int i = 0; i < p.size(); ++i)
        if(p[i] == num) cnt++;
    if(cnt > p.size() / 2) printf("Found: %d\n", num);
    else printf("Not Found\n");
}
           

摩爾投票算法的改進:

1,題目: LeetCode 229 [Majority Element II]

給定一個整型數組,找到所有主元素,它在數組中的出現次數嚴格大于數組元素個數的三分之一。

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

class Solution {
public:
    vector<int> majorityElement(vector<int>& nums) {
        vector<int> re;
        if (nums.size()==0) return re;
        int candidate1 = 0;
        int count1 = 0;
        int candidate2 = 0;
        int count2 = 0;
        for (int i=0; i<nums.size(); i++) {
            if (nums[i] == nums[candidate1]) count1++;
            else if (nums[i] == nums[candidate2]) count2++;
            else if (count1==0) {
                candidate1 = i;
                count1 = 1;
            }
            else if (count2==0) {
                candidate2 = i;
                count2 = 1;
            }
            else {
                count1--;
                count2--;
            }
        }
        count1 = 0;
        count2 = 0;
        for (int i=0; i<nums.size(); i++) {
            if (nums[i] == nums[candidate1]) count1++;
            else if (nums[i] == nums[candidate2]) count2++;
        }
        if (count1 > nums.size()/3) re.push_back(nums[candidate1]);
        if (count2 > nums.size()/3) re.push_back(nums[candidate2]);
        return re;
    }
};