本文涉及的堆,下標都從0
開始,本文算法部分嚴格按照《算法導論》並參照了維基百科算法
深度爲k
的二叉樹爲滿二叉樹的充要條件是節點數爲 $$2^{k}-1$$api
圖 1.1
數組
滿二叉樹也是一種徹底二叉樹dom
圖 2.1
測試
圖 3.1
最大堆ui
圖 3.2
最小堆this
二叉堆是一顆徹底二叉樹,二叉堆分爲最大堆和最小堆,最大堆的任何一個節點的關鍵字都大於或等於其子節點的關鍵字,最小堆的任何一個節點的關鍵字都小於或等於其子節點的關鍵字spa
3.2. 性質prototype
⌊(i - 1) / 2⌋
2 * i + 1
2 * i + 2
咱們使用一個一維數組來存儲二叉堆的元素,在數組的基礎上維持並安裝 3.1.
和 3.2.
中描述的關係去訪問及存取元素,那麼,這個數組加上咱們維持的關係共同組成了一個二叉堆3d
將 圖 1.1
(最大堆) 的第 0
個節點的值改變爲 1
,這種改變可能會致使二叉堆失衡,咱們須要對改變了的元素即第 0
個元素進行向下調整
調整策略
1 讓第0
個節點做爲當前節點,選取當前節點的兩個子節點,選擇子節點中關鍵字較大的節點,該節點的下標爲i
,若是這個節點的關鍵字大於當前節點的關鍵自則交換它們的位置,負責算法結束
2 若是沒有結束則對下標爲i的節點繼續進行調整
元素上浮和元素下沉相似,只是將當前節點和其父節點比較並交換
在 3.7.
節中實現的是基於最大堆的排序,排序後元素按照從小至大有序
排序原理
始終經過將堆頂的元素與堆的最後一個元素交換(最後一個元素是指下標爲 heap.size - 1
的元素),每交換一次都對堆的大小進行調整 heap.size = heap.size - 1
;當heap.size - 1
爲0
時排序結束
function MaxBinaryHeap(key) { if (!(this instanceof MaxBinaryHeap)) return new MaxBinaryHeap(key); this.key = key; //key表示用來排序的關鍵字 this.size = 0; //堆大小 這裏堆大小和數組大小一致 this.list = []; //用於存放堆元素 存放的是對象 } MaxBinaryHeap.prototype = { constructor: MaxBinaryHeap, //獲取某個節點的父節點 parent: function(i) { let p = Math.floor((i - 1) / 2); if (i > this.size - 1 || p < 0) return null; return p; //這裏返回的 p 是在數組中的下標,數組是從0開始的 }, //獲取某個節點的左孩子 left: function(i) { let l = 2 * i + 1; if (l > this.size - 1) return null; return l; }, //獲取某個節點的右孩子 right: function(i) { let r = 2 * i + 2; if (r > this.size - 1) return null; return r; }, //元素下沉 對下標爲i的元素向下進行調整,使堆保持其性質 maxHeapify: function(i) { let list = this.list; let key = this.key; let l = this.left(i); let r = this.right(i); let larget = null; if (l != null) { //左孩子爲空則右孩子必定爲空 if (r == null) larget = l; else larget = list[l][key] > list[r][key] ? l : r; if (list[i][key] >= list[larget][key]) return; else { let t = list[i]; list[i] = list[larget]; list[larget] = t; this.maxHeapify(larget); } } }, //元素上浮 對下標爲i的元素進行向上調整,使堆保持其性質 increase: function(i) { let list = this.list; let p = this.parent(i); while (i > 0 && list[p][this.key] < list[i][this.key]) { //i > 0 必定能保證 p != null let t = list[i]; list[i] = list[p]; list[p] = t; i = this.parent(i); p = this.parent(i); } }, //構建堆 buildHeap: function(a) { this.list = a; this.size = a.length; for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) { this.maxHeapify(i); } }, //堆排序 由小到大 heapSort: function(a) { if(a !=null) this.buildHeap(a); for (let i = this.size - 1; i > 0; i--) { let t = this.list[0]; this.list[0] = this.list[i]; this.list[i] = t; this.size--; this.maxHeapify(0); } return this.list; } } //測試用例 var a = [{key:1},{key:7},{key:2},{key:5},{key:3},{key:2},{key:6},{key:10}]; var heap = MaxBinaryHeap('key'); heap.buildHeap(a); console.log(heap.heapSort());
每一個元素都有與之相關的「優先級」,在優先級隊列中,具備高優先級的元素在一個低優先級的元素以前被服務,若是兩個元素具備相同的優先級,那麼它們將按照隊列中的順序進行服務,優先隊列的實現方式不少種,二項堆,斐波那契堆均可以實現,這裏採用二叉堆實現
這一節實現的優先隊列是基於最大堆實現的,因此關鍵字越大優先級越高支持的操做有insert//插入
,remove//刪除
, max//獲取最大
, update//更新
//優先隊列 function MaxPriorityQueue(key, a) { if (!(this instanceof MaxPriorityQueue)) return new MaxPriorityQueue(key, a); this.maxBinaryHeap = MaxBinaryHeap(key); if(a != null) this.maxBinaryHeap.buildHeap(a); this.key = key; } MaxPriorityQueue.prototype = { constructor: MaxPriorityQueue, insert: function(x) { //加入一個元素 this.maxBinaryHeap.size++; this.maxBinaryHeap.list[this.maxBinaryHeap.size - 1] = x; //上浮操做 this.maxBinaryHeap.increase(this.maxBinaryHeap.size - 1); }, max: function() { //獲取最大元素 let max = this.maxBinaryHeap.list[0]; this.removeMax(); return max; }, removeMax: function() { //移除最大元素 let list = this.maxBinaryHeap.list; let size = this.maxBinaryHeap.size; let max = list[0]; list[0] = list[size - 1]; list.shift(size - 1); //刪除 this.maxBinaryHeap.size--; this.maxBinaryHeap.maxHeapify(0); //元素下沉操做 return max; }, update: function(i, x) { //更新元素 this.maxBinaryHeap.list[i] = x; this.maxBinaryHeap.maxHeapify(i); //元素下沉操做 this.maxBinaryHeap.increase(i); //元素上浮操做 } }
function MaxBinaryHeap(key) { if (!(this instanceof MaxBinaryHeap)) return new MaxBinaryHeap(key); this.key = key; //key表示用來排序的字段 this.size = 0; //堆大小 這裏堆大小和數組大小一致 this.list = []; //用於存放堆元素 存放的是對象 } MaxBinaryHeap.prototype = { constructor: MaxBinaryHeap, //獲取某個節點的父節點 parent: function(i) { let p = Math.floor((i - 1) / 2); if (i > this.size - 1 || p < 0) return null; return p; //這裏返回的 p 是在數組中的下標,數組是從0開始的 }, //獲取某個節點的左孩子 left: function(i) { let l = 2 * i + 1; if (l > this.size - 1) return null; return l; }, //獲取某個節點的右孩子 right: function(i) { let r = 2 * i + 2; if (r > this.size - 1) return null; return r; }, //元素下沉 對下標爲i的元素向下進行調整,使堆保持其性質 maxHeapify: function(i) { let list = this.list; let key = this.key; let l = this.left(i); let r = this.right(i); let larget = null; if (l != null) { //左孩子爲空則右孩子必定爲空 if (r == null) larget = l; else larget = list[l][key] > list[r][key] ? l : r; if (list[i][key] >= list[larget][key]) return; else { let t = list[i]; list[i] = list[larget]; list[larget] = t; this.maxHeapify(larget); } } }, //元素上浮 對下標爲i的元素進行向上調整,使堆保持其性質 increase: function(i) { let list = this.list; let p = this.parent(i); while (i > 0 && list[p][this.key] < list[i][this.key]) { //i > 0 必定能保證 p != null let t = list[i]; list[i] = list[p]; list[p] = t; i = this.parent(i); p = this.parent(i); } }, //構建堆 buildHeap: function(a) { this.list = a; this.size = a.length; for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) { this.maxHeapify(i); } }, //堆排序 由小到大 heapSort: function(a) { this.buildHeap(a); for (let i = this.size - 1; i > 0; i--) { let t = this.list[0]; this.list[0] = this.list[i]; this.list[i] = t; this.size--; this.maxHeapify(0); } return this.list; } } //優先隊列 function MaxPriorityQueue(key, a) { if (!(this instanceof MaxPriorityQueue)) return new MaxPriorityQueue(key, a); this.maxBinaryHeap = MaxBinaryHeap(key); if(a != null) this.maxBinaryHeap.buildHeap(a); this.key = key; } MaxPriorityQueue.prototype = { constructor: MaxPriorityQueue, insert: function(x) { //加入一個元素 this.maxBinaryHeap.size++; this.maxBinaryHeap.list[this.maxBinaryHeap.size - 1] = x; //向上調整 this.maxBinaryHeap.increase(this.maxBinaryHeap.size - 1); }, max: function() { //獲取最大元素 let max = this.maxBinaryHeap.list[0]; this.removeMax(); return max; }, removeMax: function() { //移除最大元素 let list = this.maxBinaryHeap.list; let size = this.maxBinaryHeap.size; let max = list[0]; list[0] = list[size - 1]; list.shift(size - 1); //刪除 this.maxBinaryHeap.size--; this.maxBinaryHeap.maxHeapify(0); //元素下沉操做 return max; }, update: function(i, x) { //更新元素 this.maxBinaryHeap.list[i] = x; this.maxBinaryHeap.maxHeapify(i); //元素下沉操做 this.maxBinaryHeap.increase(i); //元素上浮操做 } } //測試用例 var a = [{key:1},{key:7},{key:2},{key:5},{key:3},{key:2},{key:6},{key:10}]; var priorityQueue = MaxPriorityQueue('key', a); priorityQueue.insert({key:11}); //插入一個元素 priorityQueue.max(); //獲取最大元素並刪除 priorityQueue.removeMax(); //刪除最大元素 priorityQueue.update(3,{key:100}); //更新下標爲3的元素 console.log(a);
最小堆和最大對的原理相同,代碼也大部分相同
function MinBinaryHeap(key) { if (!(this instanceof MinBinaryHeap)) return new MinBinaryHeap(key); this.key = key; //key表示用來排序的字段 this.size = 0; //堆大小 這裏堆大小和數組大小一致 this.list = []; //用於存放堆元素 存放的是對象 } MinBinaryHeap.prototype = { constructor: MinBinaryHeap, //獲取某個節點的父節點 parent: function(i) { let p = Math.floor((i - 1) / 2); if (i > this.size - 1 || p < 0) return null; return p; //這裏返回的 p 是在數組中的下標,數組是從0開始的 }, //獲取某個節點的左孩子 left: function(i) { let l = 2 * i + 1; if (l > this.size - 1) return null; return l; }, //獲取某個節點的右孩子 right: function(i) { let r = 2 * i + 2; if (r > this.size - 1) return null; return r; }, minHeapify: function(i) { let list = this.list; let key = this.key; let l = this.left(i); let r = this.right(i); let smallest = null; if (l != null) { //左孩子爲空則右孩子必定爲空 if (r == null) smallest = l; else smallest = list[l][key] < list[r][key] ? l : r; if (list[i][key] <= list[smallest][key]) return; else { let t = list[i]; list[i] = list[smallest]; list[smallest] = t; this.minHeapify(smallest); } } }, //元素上浮 對下標爲i的元素進行向上調整,使堆保持其性質 increase: function(i) { let list = this.list; let p = this.parent(i); while (i > 0 && list[p][this.key] > list[i][this.key]) { //i > 0 必定能保證 p != null let t = list[i]; list[i] = list[p]; list[p] = t; i = this.parent(i); p = this.parent(i); } }, //構建堆 buildHeap: function(a) { this.list = a; this.size = a.length; for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) { this.minHeapify(i); } }, //堆排序 由大到小 heapSort: function(a) { this.buildHeap(a); for (let i = this.size - 1; i > 0; i--) { let t = this.list[0]; this.list[0] = this.list[i]; this.list[i] = t; this.size--; this.minHeapify(0); } return this.list; } } //最小優先隊列 function MinPriorityQueue(key, a) { if (!(this instanceof MinPriorityQueue)) return new MinPriorityQueue(key, a); this.minBinaryHeap = MinBinaryHeap(key); this.minBinaryHeap.buildHeap(a); this.key = key; } MinPriorityQueue.prototype = { constructor: MinPriorityQueue, insert: function(x) { //加入一個元素 this.minBinaryHeap.size++; this.minBinaryHeap.list[this.minBinaryHeap.size - 1] = x; //向上調整 this.minBinaryHeap.increase(this.minBinaryHeap.size - 1); }, //remove 表示獲取後是否刪除 true 刪除 false 不刪除 min: function(remove) { //獲取最小元素 let min = this.minBinaryHeap.list[0]; if(remove) this.removeMin(); return min; }, removeMin: function() { //移除最小元素 let list = this.minBinaryHeap.list; let size = this.minBinaryHeap.size; let min = list[0]; list[0] = list[size - 1]; list.shift(size - 1); //刪除 this.minBinaryHeap.size--; this.minBinaryHeap.minHeapify(0); return min; }, update: function(i, x) { //更新元素 this.minBinaryHeap.list[i] = x; this.minBinaryHeap.minHeapify(i); this.minBinaryHeap.increase(i); } } var a = [{key:1},{key:7},{key:2},{key:5},{key:3},{key:2},{key:6},{key:10}]; var priorityQueue = MinPriorityQueue('key', a); priorityQueue.insert({key:11}); console.log(a);
topK
問題如今有 1W
個浮點數,選取其中最大的 100
個,要求,在算法實現中只能用長度爲100
的數組
使用基於最小堆的優先隊列,將 浮點數的前一百個元素一個個讀取,並存入數組,以後進行堆排序,將剩餘的元素一個個拿來和堆頂元素進行比較,若是頂元素較小,則對堆頂元素進行更新,直到全部元素被訪問完此時堆中的即是 topK
function MinBinaryHeap(key) { if (!(this instanceof MinBinaryHeap)) return new MinBinaryHeap(key); this.key = key; //key表示用來排序的字段 this.size = 0; //堆大小 這裏堆大小和數組大小一致 this.list = []; //用於存放堆元素 存放的是對象 } MinBinaryHeap.prototype = { constructor: MinBinaryHeap, //獲取某個節點的父節點 parent: function(i) { let p = Math.floor((i - 1) / 2); if (i > this.size - 1 || p < 0) return null; return p; //這裏返回的 p 是在數組中的下標,數組是從0開始的 }, //獲取某個節點的左孩子 left: function(i) { let l = 2 * i + 1; if (l > this.size - 1) return null; return l; }, //獲取某個節點的右孩子 right: function(i) { let r = 2 * i + 2; if (r > this.size - 1) return null; return r; }, minHeapify: function(i) { let list = this.list; let key = this.key; let l = this.left(i); let r = this.right(i); let smallest = null; if (l != null) { //左孩子爲空則右孩子必定爲空 if (r == null) smallest = l; else smallest = list[l][key] < list[r][key] ? l : r; if (list[i][key] <= list[smallest][key]) return; else { let t = list[i]; list[i] = list[smallest]; list[smallest] = t; this.minHeapify(smallest); } } }, //元素上浮 對下標爲i的元素進行向上調整,使堆保持其性質 increase: function(i) { let list = this.list; let p = this.parent(i); while (i > 0 && list[p][this.key] > list[i][this.key]) { //i > 0 必定能保證 p != null let t = list[i]; list[i] = list[p]; list[p] = t; i = this.parent(i); p = this.parent(i); } }, //構建堆 buildHeap: function(a) { this.list = a; this.size = a.length; for (let i = Math.floor(a.length / 2) - 1; i > -1; i--) { this.minHeapify(i); } }, //堆排序 由大到小 heapSort: function(a) { if (a != null) this.buildHeap(a); for (let i = this.size - 1; i > 0; i--) { let t = this.list[0]; this.list[0] = this.list[i]; this.list[i] = t; this.size--; this.minHeapify(0); } return this.list; } } //最小優先隊列 function MinPriorityQueue(key, a) { if (!(this instanceof MinPriorityQueue)) return new MinPriorityQueue(key, a); this.minBinaryHeap = MinBinaryHeap(key); this.minBinaryHeap.buildHeap(a); this.key = key; } MinPriorityQueue.prototype = { constructor: MinPriorityQueue, insert: function(x) { //加入一個元素 this.minBinaryHeap.size++; this.minBinaryHeap.list[this.minBinaryHeap.size - 1] = x; //向上調整 this.minBinaryHeap.increase(this.minBinaryHeap.size - 1); }, min: function(remove) { //獲取最小元素 let min = this.minBinaryHeap.list[0]; if (remove) this.removeMin(); return min; }, removeMin: function() { //移除最小元素 let list = this.minBinaryHeap.list; let size = this.minBinaryHeap.size; let min = list[0]; list[0] = list[size - 1]; list.shift(size - 1); //刪除 this.minBinaryHeap.size--; this.minBinaryHeap.minHeapify(0); return min; }, update: function(i, x) { //更新元素 this.minBinaryHeap.list[i] = x; this.minBinaryHeap.minHeapify(i); this.minBinaryHeap.increase(i); } } //生成1w個浮點數 function getDataSource() { let list = []; for (let i = 0; i < 10000; i++) { list.push(Math.random() * 1000); } return list; } function top100() { var dataSource = getDataSource(); //獲取前100個元素 let top = []; for (let i = 0; i < 100; i++) { top.push({ key: dataSource[i] }); } //構建最小優先隊列 var priorityQueue = MinPriorityQueue('key', top); let key = priorityQueue.key; //處理其它元素 for (let i = 100; i < 10000; i++) { let min = priorityQueue.min(false); if (min[key] < dataSource[i]) { priorityQueue.update(0, { key: dataSource[i] }); } } //對結果排序 priorityQueue.minBinaryHeap.heapSort(); return top; } top100();