二叉樹(Binary Tree)是一種樹形結構,它的特色是每一個節點最多隻有兩個分支節點,一棵二叉樹一般由根節點,分支節點,葉子節點組成。而每一個分支節點也經常被稱做爲一棵子樹。前端
經常使用術語
在二叉樹中,咱們經常還會用父節點和子節點來描述,好比圖中2爲6和3的父節點,反之6和3是2子節點api
在二叉樹的第i層上,至多有2^i-1個節點數組
深度爲k的二叉樹至多有2^k-1個節點函數
二叉樹分爲徹底二叉樹(complete binary tree)和滿二叉樹(full binary tree)ui
用一個數組來表示二叉樹的結構,將一組數組從根節點開始從上到下,從左到右依次填入到一棵徹底二叉樹中,以下圖所示this
經過上圖咱們能夠分析獲得數組表示的徹底二叉樹擁有如下幾個性質:spa
二叉堆由一棵徹底二叉樹來表示其結構,用一個數組來表示,但一個二叉堆須要知足以下性質:code
從上圖能夠看出:視頻
從上面簡單的介紹,咱們能夠知道,一個二叉堆的初始化很是的簡單,它就是一個數組blog
class Heap{ constructor(arr){ this.data = [...arr]; this.size = this.data.length; } }
max-heapify是把每個不知足最大堆性質的分支節點進行調整的一個操做。
如上圖:
調整分支節點2(分支節點2不知足最大堆的性質)
將2與左右分支比較,從2,12,5中找出最大值,而後和2交換位置
重複step2的操做,從2,4,7中找出最大值與2作交換
maxHeapify(i) { let max = i; if(i >= this.size){ return; } // 當前序號的左節點 const l = i * 2 + 1; // 當前須要的右節點 const r = i * 2 + 2; // 求當前節點與其左右節點三者中的最大值 if(l < this.size && this.data[l] > this.data[max]){ max = l; } if(r < this.size && this.data[r] > this.data[max]){ max = r; } // 最終max節點是其自己,則已經知足最大堆性質,中止操做 if(max === i) { return; } // 父節點與最大值節點作交換 const t = this.data[i]; this.data[i] = this.data[max]; this.data[max] = t; // 遞歸向下繼續執行 return this.maxHeapify(max); }
咱們能夠看到,剛初始化的堆由數組表示,這個時候它可能並不知足一個最大堆或最小堆的性質,這個時候咱們可能須要去將整個堆構建成咱們想要的。
上面咱們作了max-heapify操做,而max-heapify只是將某一個分支節點進行調整,而要將整個堆構建成最大堆,則須要將全部的分支節點都進行一次max-heapify操做,以下圖,咱們須要依次對12,3,2,15這4個分支節點進行max-hepify操做
具體步驟:
找到全部分支節點:上面堆的性質提到過葉子節點的序號>=Math.floor(n/2),所以小於Math.floor(n/2)序號的都是咱們須要調整的節點。
rebuildHeap(){ // 葉子節點 const L = Math.floor(this.size / 2); for(let i = L - 1; i>=0; i--){ this,maxHeapify(i); } }
最大堆的排序,如上圖所示:
sort() { for(let i = this.size - 1; i > 0; i--){ swap(this.data, 0, i); this.size--; this.maxHeapify(0); } }
這個的插入和刪除就相對比較簡單了,就是對一個數組進行插入和刪除的操做
insert(key) { this.data[this.size] = key; this.size++ if (this.isHeap()) { return; } this.rebuildHeap(); }
delete(index) { if (index >= this.size) { return; } this.data.splice(index, 1); this.size--; if (this.isHeap()) { return; } this.rebuildHeap(); }
/** * 最大堆 */ function left(i) { return i * 2 + 1; } function right(i) { return i * 2 + 2; } function swap(A, i, j) { const t = A[i]; A[i] = A[j]; A[j] = t; } class Heap { constructor(arr) { this.data = [...arr]; this.size = this.data.length; } /** * 重構堆 */ rebuildHeap() { const L = Math.floor(this.size / 2); for (let i = L - 1; i >= 0; i--) { this.maxHeapify(i); } } isHeap() { const L = Math.floor(this.size / 2); for (let i = L - 1; i >= 0; i++) { const l = this.data[left(i)] || Number.MIN_SAFE_INTEGER; const r = this.data[right(i)] || Number.MIN_SAFE_INTEGER; const max = Math.max(this.data[i], l, r); if (max !== this.data[i]) { return false; } return true; } } sort() { for (let i = this.size - 1; i > 0; i--) { swap(this.data, 0, i); this.size--; this.maxHeapify(0); } } insert(key) { this.data[this.size++] = key; if (this.isHeap()) { return; } this.rebuildHeap(); } delete(index) { if (index >= this.size) { return; } this.data.splice(index, 1); this.size--; if (this.isHeap()) { return; } this.rebuildHeap(); } /** * 堆的其餘地方都知足性質 * 惟獨跟節點,重構堆性質 * @param {*} i */ maxHeapify(i) { let max = i; if (i >= this.size) { return; } // 求左右節點中較大的序號 const l = left(i); const r = right(i); if (l < this.size && this.data[l] > this.data[max]) { max = l; } if (r < this.size && this.data[r] > this.data[max]) { max = r; } // 若是當前節點最大,已是最大堆 if (max === i) { return; } swap(this.data, i, max); // 遞歸向下繼續執行 return this.maxHeapify(max); } } module.exports = Heap;
堆講到這裏就結束了,堆在二叉樹裏相對會比較簡單,經常被用來作排序和優先級隊列等。堆中比較核心的仍是max-heapify這個操做,以及堆的三個性質。
下一篇應該會介紹二叉搜索樹。歡迎你們指出文章的錯誤,若是有什麼寫做建議也能夠提出。我會持續的去寫關於前端的一些技術文章,若是你們喜歡的話能夠關注一和點個贊,你的贊是我寫做的動力。
順便再提一下,我在等第一個粉絲哈哈
如下我的公衆號,歡迎你們關注,用戶量達到必定的量,我會推出一些前端教學視頻