什么是堆
简单的解释是,就是一个全二叉树或者贴近全二叉树,它的节点的子节点都比自己小或者都比自己大。也就是说,堆有个特点:最上边的节点(堆顶)不是最大的,就是最小的,看自己添加元素的时候采用的什么形式。
原来堆就是这么整的。十几年前学的,我居然一点儿印象也没有了。。。
简单实现
接口主要是:
- constructor() // 用数组初始化,默认排序是数字升序
- add() // 添加一个并排好序
- peek() // 查询顶部元素
- poll() // 删除顶部元素并返回其值
class Heap {
constructor(data, comparator = (a, b) => a - b) {
this.data = []
this.comparator = comparator
for (let item of data) this.add(item)
}
size() { return this.data.length }
swap(index1, index2) {
[this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]]
}
peek() {
return this.data[0]
}
add(item) {
this.data.push(item)
this.heapifyUp()
}
poll() {
this.swap(0, this.size()-1)
const top = this.data.pop()
this.heapifyDown()
return top
}
heapifyUp() {
let index = this.size() - 1
while (this.hasParent(index) && this.comparator(this.data[index], this.getParent(index)) < 0) {
this.swap(index, this.getParentIndex(index))
index = this.getParentIndex(index)
}
}
heapifyDown() {
let index = 0
while (this.hasLeftChild(index)) {
let smallerChildIndex = this.getLeftChildIndex(index)
if (this.hasRightChild(index) && this.comparator(this.getRightChild(index), this.getLeftChild(index)) < 0) {
smallerChildIndex = this.getRightChildIndex(index)
}
if (this.comparator(this.data[index], this.data[smallerChildIndex]) < 0)
break
else {
this.swap(index, smallerChildIndex)
index = smallerChildIndex
}
}
}
getParent(index) { return this.data[this.getParentIndex(index)] }
getLeftChild(index) { return this.data[this.getLeftChildIndex(index)] }
getRightChild(index) { return this.data[this.getRightChildIndex(index)] }
hasParent(index) { return this.getParentIndex(index) >= 0 }
hasLeftChild(index) { return this.getLeftChildIndex(index) < this.size() }
hasRightChild(index) { return this.getRightChildIndex(index) < this.size() }
getParentIndex(index) { return Math.floor((index - 1) / 2) }
getLeftChildIndex(index) { return index * 2 + 1 }
getRightChildIndex(index) { return index * 2 + 2 }
}
完整代码
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Heap II</title>
<link href="https://code.jquery.com/qunit/qunit-git.css" rel="stylesheet" type="text/css"/>
<script src="https://code.jquery.com/qunit/qunit-git.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script>
/**
* https://www.youtube.com/watch?v=t0Cq6tVNRBA
*/
class Heap {
constructor(data, comparator = (a, b) => a - b) {
this.data = []
this.comparator = comparator
for (let item of data) this.add(item)
}
size() { return this.data.length }
swap(index1, index2) {
[this.data[index1], this.data[index2]] = [this.data[index2], this.data[index1]]
}
peek() {
return this.data[0]
}
add(item) {
this.data.push(item)
this.heapifyUp()
}
poll() {
this.swap(0, this.size()-1)
const top = this.data.pop()
this.heapifyDown()
return top
}
heapifyUp() {
let index = this.size() - 1
while (this.hasParent(index) && this.comparator(this.data[index], this.getParent(index)) < 0) {
this.swap(index, this.getParentIndex(index))
index = this.getParentIndex(index)
}
}
heapifyDown() {
let index = 0
while (this.hasLeftChild(index)) {
let smallerChildIndex = this.getLeftChildIndex(index)
if (this.hasRightChild(index) && this.comparator(this.getRightChild(index), this.getLeftChild(index)) < 0) {
smallerChildIndex = this.getRightChildIndex(index)
}
if (this.comparator(this.data[index], this.data[smallerChildIndex]) < 0)
break
else {
this.swap(index, smallerChildIndex)
index = smallerChildIndex
}
}
}
getParent(index) { return this.data[this.getParentIndex(index)] }
getLeftChild(index) { return this.data[this.getLeftChildIndex(index)] }
getRightChild(index) { return this.data[this.getRightChildIndex(index)] }
hasParent(index) { return this.getParentIndex(index) >= 0 }
hasLeftChild(index) { return this.getLeftChildIndex(index) < this.size() }
hasRightChild(index) { return this.getRightChildIndex(index) < this.size() }
getParentIndex(index) { return Math.floor((index - 1) / 2) }
getLeftChildIndex(index) { return index * 2 + 1 }
getRightChildIndex(index) { return index * 2 + 2 }
}
QUnit.module('Heap II', function () {
QUnit.test('ESC / TopMin', function (assert) {
const heap = new Heap([5, 4, 6])
console.log(JSON.stringify(heap.data))
assert.deepEqual(heap.data, [4, 5, 6])
assert.equal(heap.peek(), 4)
heap.add(1)
heap.add(3)
assert.equal(heap.peek(), 1)
assert.deepEqual(heap.data, [1, 3, 6, 5, 4])
assert.equal(heap.poll(), 1)
assert.deepEqual(heap.data, [3, 4, 6, 5])
});
QUnit.test('DESC / TopMax', function (assert) {
const heap = new Heap([5, 4, 6], (a, b) => b - a)
assert.deepEqual(heap.data, [6, 4, 5])
assert.equal(heap.peek(), 6)
heap.add(1)
assert.deepEqual(heap.data, [6, 4, 5, 1])
heap.add(7)
assert.equal(heap.peek(), 7)
assert.deepEqual(heap.data, [7, 6, 5, 1, 4])
assert.equal(heap.poll(), 7)
assert.deepEqual(heap.data, [6, 4, 5, 1])
});
});
</script>
</body>
</html>