优先队列(二叉堆)的基本实现

什么是二叉堆

对于优先队列的实现,最常用的数据结构就是二叉堆。二叉堆是一棵完全二叉树, 因此借用完全二叉树的规律我们可以自上而下,从左到右地对每一个节点进行标记。这时,二叉堆便可以用一个数组表示而不必使用树形结构。

             ① A
              / \                  |0|1|2|3|4|5|6|7|8|
           ② B   C ③      --->     |_|A|B|C|D|E|_|_|_|
            / \                  
        ④  D   E

如上图所示,一棵完全二叉树可以用数组表示(数组从下标1开始)。
另外,根据完全二叉树的性质,可以得到对于非根节点,他们的父节点编号等于该节点编号除以2,即index(t) = index(t->left)/2 或 index(t) = index(t->right)/2
如果一个节点有两个孩子,那么左孩子的编号一定小于当前堆中节点的数量(currentSize),用index(t)*2 != currentSize判断。如果左孩子的编号等于当前节点的数量(即最大的编号),那么该节点只有左孩子,没有右孩子。

二叉堆的堆序性质

对于最小堆中的任一节点X,X的子节点的值总大于或等于X的值。所以对于一个最小堆,得到堆中最小的元素只需要从堆顶取出即可,但是无法获得最大值。
同样对于最大堆,性质和最小堆相似,对于最大堆中的任一节点X,X的子节点的值总小于或等于X的值。

二叉堆的基本实现

以最小堆为例

#include<iostream>
#include<vector>
using namespace std;

template<typename T>
class binaryHeap{
public:
    explicit binaryHeap(int capacity = 100){
        array.resize(capacity + 1);
    }
    explicit binaryHeap(const vector<T> & items){
        array.resize(items.size() + 10);
        currentSize = items.size();
        //插入数值
        for (int i = 0; i < items.size(); i++){
            array[i + 1] = items[i];
        }
        //整理成最小堆
        buildHeap();
    }
    bool isEmpty(){
        return currentSize == 0;
    }
    const T & findMin()const{
        return array[1];
    }
    //插入一个新节点(上滤)
    void insert(const T & x){
        //如果数组空间满了
        //就扩大一倍的空间
        if (currentSize == array.size() - 1){
            array.resize(array.size() * 2);
        }
        //在二叉堆的最后建立一个空穴
        //hole代表空穴的位置
        int hole = ++currentSize;
        //当穴的父节点比x大的时候,且穴不在堆顶
        while (hole > 1 && array[hole / 2] > x){
            //父节点下移
            array[hole] = array[hole / 2];
            //穴上移
            hole = hole / 2;
        }
        //当穴的父节点比x小,或穴移动到了堆顶
        //就把x填进去
        array[hole] = x;
    }
    //取出最小值
    void deleteMin(){
        if (isEmpty()){
            return;
        }
        //把最后一个位置的数填入堆顶
        array[1] = array[currentSize];
        //删除最后一个位置
        currentSize--;
        //对堆顶进行下滤
        percolateDown(1);
    }
    //取出最小值,赋值给x
    void deleteMin(T & x){
        if (isEmpty()){
            return;
        }
        x = array[1];
        array[1] = array[currentSize--];
        //对堆顶下滤
        percolateDown(1);
    }
    //打印整个堆
    void printHeap(){
        for (int i = 1; i <= currentSize; i++){
            cout << array[i] << ' ';
        }
        cout << endl;
    }

private:
    int currentSize;
    vector<T> array;

    //对无序的堆下滤排序
    void buildHeap(){
        //从第一个非叶子节点开始下滤
        //第一个非叶子节点的位置是size/2
        //完全二叉树的性质
        for (int i = currentSize / 2; i > 0; i--){
            percolateDown(i);
        }
    }

    /*
    *下滤(堆重整)
    *把以该节点为根的子树变成小根堆
    */
    void percolateDown(int hole){
        //穴的孩子位置
        int child;

        //获得穴的值
        T temp = array[hole];

        //当hole不是叶子节点的时候
        while (hole * 2 <= currentSize){
            child = hole * 2;
            //如果有两个孩子,让child指向值最小的孩子
            if (child != currentSize && array[child] > array[child + 1]){
                child++;
            }
            //如果最小的孩子比穴小
            //就将穴下移
            if (temp > array[child]){
                array[hole] = array[child];
                hole = child;
            }
            //否则,即找到穴应在的位置
            //退出循环
            else{
                break;
            }
        }
        //将值填入穴
        array[hole] = temp;

    }
};

int main(){
    vector<int>a = { 150, 80, 40, 30, 10, 70, 110, 100, 20, 90, 60, 50, 120, 140, 130 };
    binaryHeap<int>bh(a);
    for (int i = 0; i < a.size(); i++){
        cout << a[i] << ' ';
    }
    cout << endl;
    bh.printHeap();
    bh.deleteMin();
    bh.printHeap();
    system("pause");
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值