最小堆又叫小頂堆,小頂堆是一棵完全二叉樹,滿足小頂堆的條件是每個孩子節點的值都大於父節點。大頂堆則相反。
/** * 最小堆 * @author dwl * */public class MinHeap {//使用數組儲存堆中的資料private int[] data;public MinHeap(int[] data ){this.data = data;bulidHeap();}/** * 建立最小堆 */private void bulidHeap(){for(int i = (data.length)/2-1;i>=0;i--){//下標小於等於i的節點擁有子節點change(i);}}/** * 根據父節點判斷是否 * 與左右孩子交換 * @param i */private void change(int i){int temp = 0;int left = left(i);int right = right(i);//存在右節點則存在左節點if(right<data.length){//拿到左右孩子中小的下標temp = min(left, right);if(data[i]>data[temp]){swap(i, temp);//如果和子節點發生交換,則要對子節點的左右孩子進行調整change(temp);}}else if(right<data.length){//不存在右節點但存在左節點,則左節點無孩子節點if(data[i]>data[left])swap(i, left);//孩子節點大於父節點,直接交換位置}}/** * 擷取兩個節點中較小的節點的下標 * @param i * @param j * @return */private int min(int i,int j){if(data[i]>=data[j])return j;return i;}/** * 根據父節點下標擷取 * 左孩子節點下表 * @param i * @return */private int left(int i){return ((i+1)<<1)-1;}/** * 根據父節點下表擷取 * 右孩子節點下標 * @param i * @return */private int right(int i){return (i+1)<<1;//左移1位相當於乘2}/** * 根據下標交換數組中的位置 * @param i * @param j */private void swap(int i,int j){int temp = data[i];data[i] = data[j];data[j] = temp;}/** * 重設堆頂 * @param root */public void newRoot(int root){data[0] = root;change(0);}/** * 擷取堆頂 * @return */public int getRoot(){return data[0];}/** * 擷取堆資料 * @return */public int[] getData(){return data;}}
小頂堆大頂堆對於解決TOP-K問題擁有較高的效率,在N個數中找到K(K<N)個最大或最小的數可以分別使用小頂堆演算法和大頂堆演算法。下面我用上面的小頂堆演算法找出一個數組中最大的7個數。
public class TopK {public static void main(String[] args) {// TODO Auto-generated method stubint[] num = {1,4,6,7,342,354,11,345,64,25,45,32,343,754};int[] data = {1,4,6,7,342,354,11};//取前7個資料建立最小堆MinHeap heap = new MinHeap(data);for(int i=7;i<num.length;i++){//迴圈與堆頂比較,大于于堆頂則重設堆頂if(num[i]>heap.getRoot()){heap.newRoot(num[i]);}}//迴圈輸出前7個最大的數for(int n:heap.getData()){System.out.println(n);}}}
測試結果如下圖: