MinHeap基本性質
- 最小堆中的最小元素值出現在根結點(堆頂);
- 堆中每個父節點的元素值都小於等於其孩子結點(如果存在)
MinHeap用途
1.求一個數列中的第K大的數,建立一個大小為K的最小堆,堆頂就是第K大的數
2.遞歸去除最頂元素,用於取top K等。
MinHeap可設置容量上限N帶來兩個好處:
- 內存占用可控
- 因為上限N的存在,可提高添加和刪除元素的速度
以上述思想,接下來開始java編碼:
我們要用inputs=[1, 2, 5, 12, 7, 17, 25, 19, 36, 99, 22, 28, 46, 92]中的14個元素來建立最小堆;輸出top 為data
1.建堆
對於inputs中的每個元素調用add方法,建堆后是完全二叉樹,並且所有父節點都比子節點小
public void add(T ele) { int size = data.size();if (size < maxSize) { data.add(ele);// 堆底添加新元素 heapUp(size);// 然后從末位置開始向上調整 } else { if (ele.compareTo(data.get(0)) > 0) {// 如果大於堆頂元素則更新 data.set(0, ele); // 替換頂元素 heapDown(0);// 從上到下調整堆排序 } } }
2.向下調整代碼
從堆頂某一元素開始向下調整排序
private void heapDown(int i) { int l = 2 * (i + 1) - 1; int r = 2 * (i + 1); int smallest = i;// 左、右、父節點的最小值的索引 // 如果左孩子存在,且左孩子小於smallest
if (l < data.size() && data.get(l).compareTo(data.get(smallest)) < 0) { smallest = l; } // 如果右孩子存在,且右孩子小於smallest if (r < data.size() && data.get(r).compareTo(data.get(smallest)) < 0) { smallest = r; } // 如果父節點本來就比左右孩子小,則直接返回,不用繼續向下調整 if (smallest == i) { return; } // 父節點跟較小的那個子節點交換 swap(i, smallest); // 交換后影響到了子樹,所以對子樹遞歸進行heapify heapDown(smallest); }
3.向上調整代碼
從位置i開始開始向上調整排序
private void heapUp(int i) { if (i == 0)//如果是堆頂,就返回,不需要再調整了 return; while (i > 0) {//不在堆頂,並且當前結點i的值比父節點小的時候就繼續向上調整 T ele = data.get(i); int p = (i - 1) / 2;// 父節點的index if (ele.compareTo(data.get(p)) < 0) { swap(i, p); i = p; } else {// 否則停止 break; } } }
4.堆排序:刪除堆頂元素
時間復雜度是O(NlogN),假如要從小到大排序,那么只需要建立最小堆,然后每次刪除頂部元素並將頂部元素輸出或者放入到一個新的數組中,直到堆為空為止。
public void delRoot() { int size = data.size(); if (size > 0) { T lastEle = data.get(size - 1); if (size > 1) { data.set(0, lastEle);// 最后元素設置為堆頂 data.remove(size - 1); heapDown(0);// 從根節點開始向下重整堆 } else { data.remove(size - 1); } } }