1. 程式人生 > >LeetCode--Majority Element--Boyer-Moore演算法總結

LeetCode--Majority Element--Boyer-Moore演算法總結

       找陣列中的Majority Element,Majority Element的定義見下,對應著LeetCode上的兩道題,直接看題:

LeetCode--169. Majority Element

給定一個長度為n的陣列,找出其中的Majority Element。其中Majority Element的定義為陣列中出現次數大於 n / 2次的數字。

解決這個問題有以下幾種思路:

1、暴力法

遍歷陣列中每一個元素,統計其出現的係數是否大於 n/2次,如果是就直接返回。時間複雜度o(n^{2})。空間複雜度o(1);

參考程式碼:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int i;
        for(i = 0;i < (int)nums.size();++i) {
            if(count(nums.begin(), nums.end(), nums[i]) > nums.size() / 2) { //這裡使用了STL函式,count函式也會遍歷整個陣列,所以時間複雜度為o(n2)
                break;
            }
        }
        return nums[i];
    }
};

注:題目中說道了,保證給定的陣列中一定存在著Majority Element,所以上面那樣寫是沒有問題的,外層的for迴圈不會遍歷完,也就是變數i不會增加到nums.size()再退出迴圈,如果這樣的話nums[i]的下標就越界了,當然題目保證了這種情況不會發生。但是上面的程式碼是不能AC的,當測試集很大時,該演算法超時!!

2、排序。

對輸入陣列先排序,那麼 索引 n/2 處對應的元素一定是Majority Element,直接返回即可。時間複雜度o(nlgn),空間複雜度o(1)。

AC程式碼:

class Solution {
public:
   int majorityElement(vector<int>& nums) {
       sort(nums.begin(), nums.end());
       return nums[nums.size() / 2];
   }
};

3、隨機法。

由於Majority Element出現的次數大於 n/2那麼每次隨機從nums中選取一個數有大於1/2的概率選中Majority Element,所以我們可以隨機選取一個索引,然後統計其出現的次數是否大於n/2,如果是就直接返回索引處對應的值,否則繼續迭代。數學期望是最多迴圈兩次就可以得到結果了,所以時間複雜度為o(n),空間複雜度為o(1)。

AC程式碼:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int res = 0;
        srand(time(NULL));
        while(true)
        {
            int n = rand() % nums.size();
            res = nums[n];
            if(count(nums.begin(),nums.end(),res) > (nums.size() >> 1) )
                break;
        }
        return res;
    }
};

4、分治。

每次將陣列一分為二,分別統計左右兩邊的Majority Element,基準條件是被分成的陣列只有一個元素了,那麼該元素就是Majority Element,直接返回就可。如果左右兩邊的Majority Element相同,返回它即可,如果不相同則返回出現次數更多的那一個。

AC程式碼:原始碼出處

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        return majority(nums, 0, nums.size() - 1);
    }
private:
    int majority(vector<int>& nums, int left, int right) {
        if (left == right) 
            return nums[left];
        int mid = left + ((right - left) >> 1);
        int lm = majority(nums, left, mid);
        int rm = majority(nums, mid + 1, right);
        if (lm == rm) 
            return lm;
        return count(nums.begin() + left, nums.begin() + right + 1, lm) > count(nums.begin() + left, nums.begin() + right + 1, rm) ? lm : rm;
    }
};

時間複雜度分析:類似於歸併排序,如果複雜度為T(n),顯然有 T(n) = 2T(n/2) + o(n)。所以最終的時間複雜度為o(nlgn)。由於遞迴函式的呼叫,消耗的空間複雜度為o(log_{2}n)。n為陣列長度。

5、Boyer-Moore演算法。

程式碼分為兩步:1、找出一個Majority Element候選值。2、檢查該候選值是否是Majority Element。由於在本題中題目已經明確存在Majority Element了,所以第二步可以省略。

找出候選者的方法:

初始化候選值為陣列中的任一元素(為了方便直接初始化為首元素),初始化變數count為1。遍歷陣列,如果等於候選值,count++,或者count--。當count == 0時,給候選值賦值為當前遍歷值。

In python:

candidate = 0
count = 0
for value in input:
  if count == 0:
    candidate = value
  if candidate == value:
    count += 1
  else:
    count -= 1

In C++ AC程式碼:

class Solution {
public:
    int majorityElement(vector<int>& nums) {
        int res = 0;
        int count = 0;
        for(int i = 0;i < nums.size();++i)
        {
            if(count == 0) {
                res = nums[i];
            }
            if(nums[i] == res) {
                count++;
            } else {
                count--;
            }
        }
        return res;
    }
};

時間複雜度o(n),空間複雜度O(1)。

上面就是該題的一些解法思路,在這裡還提高了一種使用位運算計算的程式碼,該思想與博主曾寫過的一篇位運算總結裡面的--給你一個非空的整數陣列,裡面只有一個數只出現了一次,其餘的數都出現了兩次,輸出這個這出現一次的數字,的通用解法類似,感興趣的可以去看看。

LeetCode---229. Majority Element II

與上一題不同的是,本題是找出出現次數大於 n/3 次的數。易知,一個數組中出現n/3次的數可能有一個也可能有兩個。本題使用的演算法是基於Boyer-Moore演算法的擴充套件。

AC程式碼:

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

可以發現使用這用思路可以線性時間內查找出現 n/k 次的元素,只需要再增加幾個候選項即可。

參考:

JAVA-------------------Easy Version To Understand!!!!!!!!!!!!

Majority Voting Algorithm Find the majority element in a list of values

不是吹,這年頭很少有我這麼寫註釋的了

6 Suggested Solutions in C++ with Explanations

Boyer-Moore Majority Vote algorithm and my elaboration