数据结构——二叉堆和优先队列

一、概念

队列和堆都是一种数据结构,优先队列是用二叉堆实现的。优先队列和堆常用语辅助实现其它算法,例如数据压缩赫夫曼编码算法、Dijkstra最短路径算法、Prim最短生成树算法,优先队列还可以实现事件模拟、选择问题,操作系统的线程调度也使用优先队列。

优先队列

特点:元素正常入队,按照优先级大小出队。
实现方式:有序表或无序表(即数组,或顺序表和链表)、二叉树、AVL平衡二叉树、堆和二叉堆。使用普通的表实现优先队列的性能比较低,有序表插入为O(1),无序表取出为O(n),另外使用普通二叉树实现优先队列只能保证平均时间为O(log n),但最差情况是O(n),AVL树则需要复杂的无关操作,而堆可以实现性能较好的优先队列,取出效率可达到O(1),一般的操作都能达到O(log n)。

二叉堆

特点:二叉堆是完全二叉树,分为最大堆和最小堆两种类型,最小堆中根结点的关键字是其它所有结点关键字的最小值,最大堆则是最大值。
表示方式:完全二叉树是二叉堆的逻辑表示方式,数组是二叉堆的物理表示,也是计算机中的实际形式。
应用:堆排序复杂度O(nlogn)、优先队列

// 二叉堆构造函数,capacity: 容量(决定二叉堆大小),compare: 比较函数(决定是最大堆还是最小堆),array: 数组
function BinaryHeap(capacity, compare, array){
    if(array){
        this.array = array.concat();
        this.size = array.length;
        
        if(capacity < 3)
            this.capacity = this.size * 2;
        else
            this.capacity = capacity;
        this.compare = compare;
    }
    else{
        if(capacity < 3)
            return null;
        this.array = new Array(capacity);
        this.size = 0;
        this.capacity = capacity;
        this.compare = compare;
    }
}
// 检查二叉堆是否已空
BinaryHeap.prototype.isEmpty = function(){
    return this.size == 0;
}
// 获取结点i的父结点
BinaryHeap.prototype.parent = function(i){
    return Math.floor((i - 1) / 2);
}

// 获取结点i的左儿子
BinaryHeap.prototype.left = function(i){
    return 2 * i + 1;
}

// 获取结点i的右儿子
BinaryHeap.prototype.right = function(i){
    return 2 * i + 2;
}
// 根据数组元素的索引进行交换
BinaryHeap.prototype.swap = function(i, j){
    var temp = this.array[i];
    this.array[i] = this.array[j];
    this.array[j] = temp;
}
// 二叉堆push操作,往堆中添加一个新元素
BinaryHeap.prototype.push = function(T){
	// 没有容量了
    if(this.size == this.capacity){
        console.log("overflow: could not push key.");
        return;
    }
    // 错位,数组0索引处是树的根节点,也可以不给0赋值,把数组索引和树的位置对应
    // 只要把数组长度加1
    this.size++;
    var i = this.size - 1;
    this.array[i] = T;
    // 上浮操作,由下而上检查,新元素和它的父节点比较
    while(i != 0 && this.compare(this.array[i], this.array[this.parent(i)])){
    	// 交换新元素和父节点位置
        this.swap(i, this.parent(i));
        // 把父节点索引给i,继续向上比较
        i = this.parent(i);
    }
}
// 下沉操作,修复二叉堆的性质,从根节点自上而下检查,需要比较父节点和它两个子节点
BinaryHeap.prototype.heapify = function(i){
    var left = this.left(i);
    var right = this.right(i);
    // small存储值比较小的节点
    var small = i;
    // 顺序比较 父元素 左子元素 右子元素
    // 比较父元素和左子元素
    if(left < this.size && this.compare(this.array[left], this.array[i]))
        small = left;
    // 比较左子元素和右子元素
    if(right < this.size && this.compare(this.array[right], this.array[small]))
        small = right;
    // 父元素的值改变,交换
    if(small != i){
        this.swap(i, small);
        // 继续下浮,恢复堆结构
        this.heapify(small);
    }
}
// 获取堆顶元素
BinaryHeap.prototype.top = function(){
    return this.array[0];
}
// 释放堆顶元素,将最后一个元素替换堆顶元素,然后进行下沉堆化操作
BinaryHeap.prototype.pop = function(){
    if(this.size <= 0){
        console.log("heap is empty.");
        return null;
    }
    if(this.size == 1){
        this.size--;
        return this.array[0];
    }
    var root = this.array[0];
    this.array[0] = this.array[this.size - 1];
    this.size--;
    // 从顶部下浮恢复结构
    this.heapify(0);
    return root;
}
// 改变第i个结点的值
BinaryHeap.prototype.decreaseKey = function(i, NT){
    this.array[i] = NT;
    // 判断改变过后是否还符合二叉堆性质
    while(i != 0 && this.compare(this.array[i], this.array[this.parent(i)])){
        this.swap(i, this.parent(i));
        i = this.parent(i);
    }
}
// 删除元素
BinaryHeap.prototype.delete = function(i){
    if(this.size <= 0){
        console.log("heap is empty.");
        return;
    }
    if(this.size == 1){
        this.size--;
        return;
    }
    // 把最后一个值替换到要删除的位置上
    this.array[i] = this.array[this.size - 1];
    // 然后数组长度减一
    this.size--;
    // 再下沉,恢复结构
    this.heapify(i);
}
// 构建堆,用于使用一个数组构建堆的时候进行堆化操作
BinaryHeap.prototype.buildHeap = function(){
	// 从数组中间的索引位置开始下浮
    for(var i = Math.floor(this.size / 2);i >= 0;i--)
        this.heapify(i);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章