本文將首先介紹什麼是堆,而後介紹了堆的插入和刪除操做,最後給出了堆的代碼實現,並進行了測試。java
堆是一顆徹底二叉樹,堆中某個節點的值老是不大於或不小於其父節點的值。根節點最大的堆叫作大根堆,根節點最小的堆叫作小根堆。
首先解釋下什麼是徹底二叉樹,設一顆二叉樹的深度爲h,除第 h 層外,其它各層 (1~h-1) 的結點數都達到最大個數,第 h 層全部的結點都連續集中在最左邊,這就是徹底二叉樹。以下圖所示,左側的二叉樹知足徹底二叉樹的定義,而右側的不知足。
上圖左側即是一個小根堆,知足任意一個節點的值老是不小於其父節點的值。算法
通常的二叉樹表示時須要首先定義節點結構,節點中包含指向父節點的指針,以下所示:編程
class Node<E>{ E e;//節點儲存的值 Node left,right;//左右子節點 public Node(E e){ this.e = e; this.left = this.right = null; } }
可是堆並非像樹同樣存儲,其中沒有使用父指針或者子指針,而是用數組來實現。怎麼用數組來實現呢?先看一張圖,以下:
咱們從0開始對節點進行編號,尋找其中父子節點之間索引的對應關係。
首先,經過子節點的索引來找父節點的索引,設子節點的索引爲i,則其父節點的索引爲數組
int parentIndex = (i - 1) / 2;
而後,經過父節點的索引來找子節點的索引,設父節點的索引爲p,則其孩子節點的索引爲安全
int leftChildIndex = 2 * p + 1;//左子節點 int rightChildIndex = 2 * p + 2;//右子節點
這樣,經過子節點與父節點之間的索引關係,便至關於創建了父節點和子節點之間的指針,實現了用數組來存儲堆這種數據結構。數據結構
對於堆來講,只有插入和刪除兩種操做,先談一下堆的插入操做,此處以小根堆爲例。
如上圖1所示,在小根堆中插入元素0,首先將元素放置在二叉樹最後一行的末尾,此時依然是徹底二叉樹;而後將該元素與父節點的值比較,若改節點的值小於父節點,則進行交換,如圖3所示;以後再次與父節點進行對比交換,直至該節點的值大於等於父節點的值或者已是根節點爲止,如圖4 所示。此時堆依然知足定義。多線程
對於堆來講,刪除元素是指移除根節點。以小根堆爲例,是指移除根中最小值的節點,也就是根節點。移除很簡單,以後咱們要經過操做來使得堆依然知足定義。
首先刪除堆中索引爲0,也就是根節點,對於小根堆來講也就是最小值。而後將堆中最後一個元素填充至根節點的位置,如圖3所示;以後比較該節點與左右子節點,若該節點大於左右子節點中較小的節點的值,則與該節點進行交換(小根堆中父節點永遠與左右子節點中較小的那個子節點交換),如圖四、5所示;直至知足該節點的值小於其左右子節點的值或者該節點左右子節點均爲空。此時堆依然知足定義。測試
下面給出堆的代碼實現,以下所示,實現了堆的插入和刪除操做,並進行了測試。this
package datastructures; public class Heap { private int[] data;//存儲堆的數組 private int size;//堆中元素的數量 public Heap(int capacity){ data = new int[capacity];//初始化數組 size = 0;//初始化數量 } /** * 插入元素 */ public void insert(int value) throws Exception{ if(size == data.length) throw new Exception("堆已滿"); else{ data[size] = value;//將新插入的元素放在堆的末尾 int i = size; size ++; while(i > 0){//對堆進行調整,直至知足條件 int p = (i - 1) / 2; if(data[i] < data[p]){ int temp = data[i]; data[i] = data[p]; data[p] = temp; i = p; } else break; } } } /** * 刪除堆中的元素 * @return * @throws Exception */ public int delMin() throws Exception{ int res; if(size == 0) throw new Exception("爲空"); else{ res = data[0];//返回索引爲0的元素 size -- ; data[0] = data[size];//將堆中最後一個元素填充至索引爲0的位置 int i = 0; while(2 * i + 1 < size){//對堆進行調整 int left = 2 * i + 1; int right = 2 * i + 2; if(right < size && data[right] < data[left] && data[right] < data[i]){ int temp = data[i]; data[i] = data[right]; data[right] = temp; i = right; } else if(data[left] < data[i] && (right >= size || data[right] >= data[left])){ int temp = data[i]; data[i] = data[left]; data[left] = temp; i = left; } else break; } } return res; } //測試 public static void main(String[] args) throws Exception { Heap heap = new Heap(10); heap.insert(1); heap.insert(5); heap.insert(4); heap.insert(3); heap.insert(6); heap.insert(2); System.out.println(heap.delMin()); System.out.println(heap.delMin()); System.out.println(heap.delMin()); System.out.println(heap.delMin()); System.out.println(heap.delMin()); System.out.println(heap.delMin()); } }
推薦閱讀
爲何有紅黑樹?什麼是紅黑樹?看完這篇你就明白了
《深刻淺出話數據結構》系列之什麼是B樹、B+樹?爲何二叉查找樹不行?
都2020年了,據說你還不會歸併排序?手把手教你手寫歸併排序算法
爲何會有多線程?什麼是線程安全?如何保證線程安全?spa
以爲文章有用的話, 點贊+ 關注唄,好讓更多的人看到這篇文章,也激勵博主寫出更多的好文章。
更多關於 算法、數據結構和計算機基礎知識的內容,歡迎掃碼關注個人原創公衆號「<font color=#005AB5> 超悅編程</font>」。