優化的求眾數方法 - 摩爾投票演算法(演算法思想+求眾數的三種方法+摩爾投票演算法改進版求眾數 II)
摩爾投票演算法是一種線上性時間O(n)和空間複雜度O(1)的情況下,在一個元素序列中查詢包含最多的元素的典型的流演算法。
下面用此演算法來解LeetCode的169. 求眾數、229. 求眾數 II。
一、求眾數:
給定一個大小為 n 的陣列,找到其中的眾數。眾數是指在陣列中出現次數大於 ⌊ n/2 ⌋ 的元素。
你可以假設陣列是非空的,並且給定的陣列總是存在眾數。
示例 1:
輸入: [3,2,3]
輸出: 3示例 2:
輸入: [2,2,1,1,1,2,2]
輸出: 2
1.普通方法(二重迴圈):(此方法在資料量大時,必然消耗時間很大)
class Solution {
public int majorityElement(int[] nums) {
int n = nums.length/2;
for(int i=0; i<nums.length; i++){
int count = 0;
for(int j=0; j<nums.length; j++){
if(nums[j] == nums[i]){
count++;
}
}
if(count > n){
return nums[i];
}else{
count = 0;
}
}
return -1;
}
}
2.排序法:(排序後眾數一定出現在索引為nums.length/2的位置上)
import java.util.Arrays;
class Solution {
public int majorityElement(int[] nums) {
Arrays.sort(nums);
return nums[nums.length/2];
}
}
3.摩爾投票演算法:(時間複雜度為O(n),空間複雜度為O(1))
首先,陣列中出現次數大於 [n/2 ]的元素只可能有一個(假設這個數字在陣列中一定存在)。摩爾投票演算法是掃描整個陣列,每次從其中選擇兩個不相同的數字刪除掉,直到最後剩下一個數字或幾個相同的數字,就是出現次數大於總數一半的那個。
(1)先定義一個基數(陣列中可以直接使用索引,下面程式中時i,)和一個計數器(count)。
(2)計數器基數為1,代表已經掃描過nums[0],從索引1的位置開始掃描整個陣列(也可以直接從頭開始),遇到與基數相同的數加1,遇到不同的數減1。
(3)當計數器為0時,資料被分為兩段,前一段資料中被計數的元素數和基數數量是相等的,而後面的資料又滿足詞頻最高的數大於總數一半的情形,因此後面的資料就成為了我們的下一個目標,前面的資料就被捨棄了。
(4)完畢之後索引i記錄的那個數就是出現次數最多的數。
class Solution {
public int majorityElement(int[] nums) {
int i = 0;
int count = 1;
for(int j=1; j<nums.length-1; j++){
if(nums[i] == nums[j]){
count++;
}
else{
count--;
if(count == 0){
i = j+1;
}
}
}
return nums[i];
}
}
以上程式的測試類:
public class LeetcodeTest {
public static void main(String[] args) {
Solution So = new Solution();
int[] nums = {2,2,1,1,1,2,2};
System.out.println(So.majorityElement(nums));
}
}
二、求眾數 II
給定一個大小為 n 的陣列,找出其中所有出現超過 ⌊ n/3 ⌋
次的元素。
說明: 要求演算法的時間複雜度為 O(n),空間複雜度為 O(1)。
示例 1:
輸入: [3,2,3]
輸出: [3]示例 2:
輸入: [1,1,1,3,3,2,2,2]
輸出: [1,2]
要求演算法的時間複雜度為 O(n),空間複雜度為 O(1),那麼這個題自然就是摩爾投票演算法的升級版。
掃描整個陣列,每次從其中選擇三個不相同的數字刪除掉,最後留下的就是出現次數超過1/3的數。
(1)同樣先定義基數nums[i]、nums[j]和計數器count1,count2,這裡我們同樣可以推斷出在整組資料中出現次數超過
[ n/3 ]
的數不多於兩個。(2)掃描整個陣列,遇到與基數相同的數為對應計數器加1,遇到不同的數減1。
(3)當計數器count1或count2為0時,對應前面的資料段捨棄,以下一個數為基數繼續遍歷。
(4)完畢之後索引i、j記錄的數就是出現次數超過
[ n/3 ]
的數。
import java.util.List;
import java.util.ArrayList;
class Solution {
public List<Integer> majorityElement(int[] nums) {
List<Integer> res = new ArrayList<Integer>();
int i = 0;
int j = 1;
int count1 = 0;
int count2 = 0;
for(int k=0; k<nums.length;k++){
if(nums[k] == nums[i]){
count1++;
}else if(nums[k] == nums[j]){
count2 ++;
}else if(count1 == 0){
i = k;
count1 = 1;
}else if(count2 == 0){
j = k;
count2 = 1;
}else{
count1--;
count2--;
}
}
//檢驗
count1 = 0;
count2 = 0;
for(int k=0; k<nums.length; k++){
if(nums[k] == nums[i]){
count1++;
}else if(nums[k] == nums[j]){
count2++;
}
}
if(count1 > nums.length/3){
res.add(nums[i]);
}
if(count2 > nums.length/3){
res.add(nums[j]);
}
return res;
}
}
測試類:
public class LeetcodeTest {
public static void main(String[] args) {
Solution So = new Solution();
int[] nums = {1,1,1,3,3,2,2,2};
List<Integer> res = So.majorityElement(nums);
System.out.println(res);
}
}
相關文章
- 【演算法】已知必存在眾數,求該眾數 -- Boyer-Moore 投票演算法演算法
- 演算法題:求眾數演算法
- 【演算法】求眾數-js解法演算法JS
- 演算法:Majority Element(求眾數)演算法
- 【LeetCode】求眾數(四種方法)LeetCode
- 【演算法解題報告】求眾數演算法
- 演算法面試題彙總_2求眾數演算法面試題
- 求眾數、排序演算法、二分法排序演算法
- 【leetcode】求眾數LeetCode
- 摩爾投票
- 找眾數演算法演算法
- c++實現求眾數及其重數C++
- Leetcode刷題——求眾數LeetCode
- 每日一道演算法題--leetcode 169--求眾數--python--兩種方法演算法LeetCodePython
- 求素數(質數)演算法演算法
- 【20190326】【每天一道演算法題】求眾數(分治演算法)演算法
- 分治演算法-眾數問題演算法
- 【演算法練習】(8)眾數演算法
- 演算法之絕對眾數演算法
- LeetCode169求眾數——分治LeetCode
- 淺談摩爾投票法
- 求取眾數及重數的演算法實現(Java)演算法Java
- 求自然數的組合數的回溯演算法演算法
- Leetcode 169:求眾數(最詳細的解法!!!)LeetCode
- LeetCode每日一題:求眾數(No.169)LeetCode每日一題
- 分治法求眾數和重數(含檔案輸入輸出)
- javascript求餘數的方法JavaScript
- 微信公眾號投票活動製作教程 微信公眾號投票怎麼弄?
- java演算法 求最大公約數Java演算法
- 演算法設計--眾數和重數問題(分治法)演算法
- 求多個數最大公約數、最小公倍數的一種變換演算法演算法
- (演算法)求1到1億間的質數或素數演算法
- [資料結構與演算法]15 兩行程式碼就可以搞定求眾數,但還有更巧的資料結構演算法行程
- (七)一個尋找陣列中眾數的演算法陣列演算法
- 【總結】求組合數的方法
- 求最大公公約數(最大公因數)—— 歐幾里得演算法演算法
- 一個小小的演算法題:求兩數之和演算法
- 【演算法拾遺】三種方法求連續子陣列的最大和演算法陣列