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()。空間複雜度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()。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