完全二叉树和满二叉树
具体概念可以看数和二叉树
堆的简介
堆也叫二叉堆,实际上是一个二叉树。
- 堆是一个完全二叉树。
- 堆不是最大堆就是最小堆,最小堆是任一结点是它子树中所有结点的最小值,最大堆是任一结点是它子树中所有结点的最大值。
- 最小堆允许快速导出最小值,最大堆允许快速导出最大值。(堆排序的实现原理)
完全二叉树父子结点下标关系证明
fatherIndex = Math.floor((childIndex - 1) / 2)
首先根据等比数列求和公式a1(1 - q^n)/(1-q),我们可以知道完全二叉树的每一层(层数从0开始计数)的第一个结点的下标是 2^n -1。
假设父结点是完全二叉树第n-1层的第M个结点,那么它的下标可以表示为A = 2^(n-1) - 1 + M - 1。
因为父结点是n-1层第M个结点,父结点前面有M - 1个结点,因此父结点的左子结点前有2 * (M - 1)个结点,因此左子结点下标是B= 2 ^ n - 1 + 2M - 2。
我们可以发现 A = (B - 1)/2
最小堆的JavaScript实现(利用数组)
实现代码中把每一个堆实例都会有的方法放到原型对象的好处是不会打乱全局作用域,也不会对每一个实例都实例化一个Function对象的实例。原型对象中的方法将被所有实例共享。
- 数据入堆
function Heap() //堆的构造函数
{
this.data = [];
}
Heap.prototype.print = function()
{
console.log(this.data);
}
Heap.prototype.insert = function(value)
{
this.data.push(value);
let index = this.data.length - 1;
let fatherNodeIndex = Math.floor((index - 1)/2);
while(fatherNodeIndex >= 0)
{
if(this.data[index] < this.data[fatherNodeIndex])
{
let temp = this.data[index];
this.data[index] = this.data[fatherNodeIndex];
this.data[fatherNodeIndex] = temp;
}
index = fatherNodeIndex;
fatherNodeIndex = Math.floor((index - 1)/2);
}
}
- 我们利用数组实现一个最小堆(所有根结点都小于左右孩子)。
- 插入元素的原理在于,首先将元素放在数组的最后,然后再根据定义调整(也就是任一结点大于它的子树中的所有结点)
- 找到最小堆中的最小值
最小堆的最小值一定是数组的第一个元素。
Heap.prototype.size = function() //返回堆的大小
{
return this.data.length;
}
Heap.prototype.findMin = function() //返回最小堆的最小值
{
if(this.data.length === 1)
{
return null;
}else{
return this.data[0];
}
}
- 删除最小堆的最小值
Heap.prototype.delete = function()
{
let value;
if(this.size() === 1)
{
value = this.data.shift();
}
else if(this.size() === 0)
{
value = null;
}
else{
value = this.data[0];
this.data[0] = this.data[this.data.length - 1];
this.data.pop();
this.siftDown(0);
}
return value;
}
Heap.prototype.siftDown = function(index)
{
let element = index;
let left = 2*index + 1;
let right = 2*index + 2;
if(left < this.data.length && this.data[left] < this.data[element])
{
element = left;
}
if(right < this.data.length && this.data[right] < this.data[element])
{
element = right;
}
if(index !== element)
{
let temp = this.data[index];
this.data[index] = this.data[element];
this.data[element] = temp;
this.siftDown(element);
}
}
堆的时间复杂度
- 最小堆可以在O(logn)时间内找到最小值。
- 最大堆可以在O(logn)时间找到最大值。
堆和优先队列
- 我们常常一起听到堆和优先队列,但是堆和优先队列有什么联系呢
- 优先队列实质上是一个队列,只不过它出队是按照优先级来的。
- 堆是指二叉堆,可以在log(n)时间复杂度中找到最小值(最小堆)或最大值(最大堆)。
- 优先队列的一种实现方式是利用二叉堆。