Majority Element問題---Moore's voting算法


Leetcode上面有這么一道難度為easy的算法題:找出一個長度為n的數組中,重復次數超過一半的數,假設這樣的數一定存在。O(n2)和O(nlog(n))(二叉樹插入)的算法比較直觀。Boyer–Moore majority vote algorithm在1980年提出,用O(1)空間和O(n)時間解決了這個問題。這個算法的思路:由於重復頻率超過 floor(n/2)的數字只有一個,等價於與其余數字出現頻率的差大於零。當遍歷整個數組時,使用變量candidate記錄當前重復次數最多的數,count計算candidate重復多余的次數。以下為具體實現:

int count = 0;
int candidate;
for(int i = 0; i < n; ++i)
{
  if(count == 0)
  {
     candidate = a[i];  
  }         
  if(candidate == a[i])
   ++count;
 else
   --count;
}

在遍歷過程中,當前元素與candidate相同則投支持票,否則投反對票。當count狀態為0時,說明之前的子數組中不存在重復次數超過一半的數,遍歷余下的數組成為原問題的子問題。若該數不一定存在,那么需要再一次遍歷數組,鑒證找到的元素是否符合條件。

 

進一步思考,若要返回出現次數大於k次的所有元素,即為iceburg query問題。iceburg query的想法其實可以向其名字一樣形象。假設將數組中所有元素轉化為histogram,高度為出現的頻率,那么每個筒子有高有低,就像冰山一樣。之后不斷的下降冰山,下降k次。那么剩下還留在水面上的就是滿足要求的元素。直接這樣求解問題需要多次遍歷數組內的元素O(log(n!) + log(nk))。

當然也可以遍歷兩次。由於滿足條件的元素出現次數大於k,那么整個數組中至多存在n/k個。因此在第一次遍歷的時候,維護一個數組a,若當前元素不存在數組中,則插入該元素和出現次數1。然后判斷數組大小是否超過n/k。如果超過則所有元素下降一個,並且除去出現次數為0的元素。第二次遍歷,查看是否a中的元素出現次數都大於k(因為滿足條件的元素個數可以小於n/k)。

unordered_map m;
// first pass
for(i = 0; i < n; ++i)
{
  if(m.find(nums[i]) == m.end())
  {
    m.insert(pair<int, int>(nums[i], 1));
  }
  else
  {
    ++m[ nums[i] ];
  }

  if(m.size() > n / k)
  {
    for(auto it = m.begin(); it != m.end();++it)
    {
      --(it -> second);
      if(!(it -> second))
        m.erase(it++);
    }
  }
}

// second pass
for(auto &x: m)
  m -> second = 0;

for(i = 0; i < n; ++i)
{
  ++m[ nums[i] ];
  if(m[nums[i]] > k)
  {
    v.push_back(nums[i]);
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM