本文涉及的堆,下标都从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();