1. 程式人生 > >scala寫算法-用小根堆解決topK

scala寫算法-用小根堆解決topK

app unit roo ast atm mark 構建 操作 mnt

topK問題是指從大量數據中獲取最大(或最小)的k個數,比如從全校學生中尋找成績最高的500名學生等等.
本問題可采用小根堆解決.思路是先把源數據中的前k個數放入堆中,然後構建堆,使其保持堆序(可以簡單的看成k次insert操作).然後從源數據中的第k個數據之後的每個元素與堆的根節點(小根堆得root是最小的)比較,如果小於root,那麽直接pass;如果大於,則執行headp.deleteMin,然後把該元素插入堆中並再次保持堆序.保持堆序需要涉及上濾與下濾的過程.
樣例為:

object Main extends App{
  val array=Array(0,14,16,19,3,3768,345
,3,343,545,455,7567,657,67,65756,756,756,756,7657,657,657,4,534,535,345343,423,4,46546,546,544,546,546,345,345,435,34534,53,5345,45,45,435,43,54,35,435,435,34,5,435,45,65,65,6576,7,65,56,7,45,43,543,53,453,45,345,34) //數組從1開始 val k=3 val heap=new Heap 1 to k foreach(i=>heap.insert(array(i))) k+1
to array.length-1 foreach(i=>{ while (heap.getSize>10){ heap.deleteMin } if(array(i)>heap.getMin){ heap.deleteMin heap.insert(array(i)) } }) heap.print }

再來說說,Heap是如何實現的
運用scala常常遇到這樣的選擇:變量的變與不變,這是一個問題.
還有for推導式,實際上是map flatMap等的語法糖.

代碼:

class Heap {
  type
T=Int private var size=0 val elemnts=new Array[T](200) // 把index=0處的位置空出 2n 2n+1 n/2 def getSize:Int=return size def insert(x:T):Unit={ def loop(i:Int):Int={ if(elemnts(i/2)<=x) return i elemnts(i)=elemnts(i/2) loop(i/2) } val i=loop(size+1) elemnts(i)=x size+=1 } def deleteMin:T={ def loop(i:Int):Int={ if(i>size) return i/2 val left=elemnts(2*i) val right=elemnts(2*i+1) if(left<right){ elemnts(i)=left loop(i*2) }else{ elemnts(i)=right loop(2*i+1) } } val result=elemnts(1) val last=elemnts(size) val i=loop(1) elemnts(i)=last size-=1 return result } def print:Unit= 1 to size foreach(i=>println(elemnts(i))) def getMin:T=return elemnts(1) //代碼親測無誤 }

在實現insertdeleteMin時,由於scala並沒有break關鍵字(雖然你可以使用Breakable這個類實現,實際上通過拋出異常模擬break,不靈活),為實現上慮(insert),考慮用遞歸來模擬for循環.
代碼:

  def insert(x:T):Unit={
    def loop(i:Int):Int={
      if(elemnts(i/2)<=x)
        return i //如果父親節點比待插入值x小,則本節點應該插入x
      elemnts(i)=elemnts(i/2) //上慮
      loop(i/2)
    }
    val i=loop(size+1) //返回待x插入的位置
    elemnts(i)=x
    size+=1
  }

相比從c語言版,基於scala的代碼還是容易記憶與相當穩健:

void insert(Element x,Heap* h){
    int i=0;
    if(isFull(h))
        error("full");
    for(i=++h->size;h->elements[i/2]>x;i/=2)
        h->elements[i]=h->elements[i/2];
    h->elements[i]=x;
}

scala寫算法-用小根堆解決topK