优先队列的基本算法(使用二叉堆构建优先队列)

ⅰ亾dé卋堺 提交于 2019-12-02 02:22:56

一、介绍

堆,也是计算中一种很常用的数据结构,它以树的形式存在。 对于树的结构,因为有父节点和子节点的概念,所以一般通过父指针和子指针来实现。但是,也有一种特殊的树不需要使用指针而可以直接通过数组来实现,这种树就是完全二叉树(除了最后一层不用全满,其他层必须全满,而且最后一层的叶子结点都靠左对齐)。今天我们要讨论的二叉堆就是一棵完全二叉树,二叉堆也是堆的一种,只不过二叉堆拥有自己的特点,它分为最大堆和最小堆。

 

二叉堆特点:

最大堆:所有的父节点值都不小于其孩子节点值。(注意“其”字,仅限于当前父节点和它的子节点的比较)

最小堆:所有的父节点值都不大于其孩子节点值。(注意“其”字,仅限于当前父节点和它的子节点的比较)

算法: parent = floor(i/2),leftChild = 2*i,  rightChild = 2*i+1   [注意: 存储的角标从1开始]。

 

二叉堆图示:

  

 

二、使用

之前的篇幅介绍过链队列和顺序队列,它们都是普通的队列,采用先进先出的方式来对元素进行操作。可是,有的时候需求并不是仅仅如此,可能会来一个优先级更高的元素操作,这个时候把它按照普通队列的方式进行处理明显是不合适的。而且,普通队列的入队和出队的时间复杂度也不是最优的。此时采用二叉堆构建优先队列,是一个折中的选择,时间复杂度是最优的,如图所示:

 

 

三、代码

定义

#include <iostream>
#include <stdlib.h>
#include <math.h>
#include <algorithm>

using namespace std;

#define QUEUE_OVERFLOW  -1
#define OK               1
#define ERROR            0

typedef int Status;
typedef int QElemType;

typedef struct BinaryHeap {
    int capacity;      //容量
    int count;         //个数
    QElemType *array;  //数组

}BinaryHeapQueue;

//构建二叉堆优先队列
Status constructBinaryHeapQueue(BinaryHeapQueue &bq, int capacity);

//将元素添加到队列
Status enQueueByShiftUp(BinaryHeapQueue &bq, QElemType e);

//从队列中取出元素
Status deQueueByShiftDown(BinaryHeapQueue &bqt);

//获取队列元素个数
QElemType eleCount(BinaryHeapQueue &bq);

//判断队列是否为空
Status isEmpty(BinaryHeapQueue &bq);

//判断队列是否已满
Status isFull(BinaryHeapQueue &bq);

实现

//构建一个二叉堆
Status constructBinaryHeapQueue(BinaryHeapQueue &bq, int capacity) {

    assert(capacity > 0);

    //数组元素从下标1的位置开始存储,所以多分配一块内存
    bq.array = (QElemType *)malloc((capacity + 1) * sizeof(QElemType));
    bq.capacity = capacity+1;
    bq.count = 0;

    if (!bq.array) exit(QUEUE_OVERFLOW); //存储分配失败

    return OK;
}

//将元素e添加队列
Status enQueueByShiftUp(BinaryHeapQueue &bq, QElemType e) {

    if (isFull(bq)){
        cout<<"队列已满,当前元素: "<<e<<" 无法被添加到队列"<<endl;
        return ERROR;
    }

    //保存元素
    bq.array[bq.count+1] = e;
    cout<<"入队列元素: "<<"array["<<bq.count+1<<"] = "<<e<<endl;

    //如果这个入队元素的值是比父节点值大,则往上移动
    int chiIndex = bq.count + 1;
    int parIndex = floor(chiIndex/2);
    while (bq.array[parIndex] < bq.array[chiIndex] && parIndex > 0) {
        swap(bq.array[parIndex], bq.array[chiIndex]);
        chiIndex = parIndex;
        parIndex = floor(chiIndex / 2);
    }

    //个数自增
    bq.count ++;

    return OK;
}


//从队列中取出队首元素保存到e中
Status deQueueByShiftDown(BinaryHeapQueue &bq) {

    if (isEmpty(bq)){
        cout<<"队列已空"<<endl;
        return ERROR;
    }

    //取出元素
    QElemType e = bq.array[1];
    bq.array[1] = bq.array[bq.count];
    cout<<"出队列元素: "<<"array[1] = "<<e<<endl;

    //个数自减
    bq.count --;

    //如果将尾部元素放到下标1位置的值是比孩子节点小,则往下移动
    int parIndex = 1;
    int l_chiIndex = parIndex * 2;
    int r_chiIndex = parIndex * 2 + 1;
    while (r_chiIndex <= bq.count && bq.count > 1){

        if (bq.array[parIndex] <= bq.array[l_chiIndex] || bq.array[parIndex] <= bq.array[r_chiIndex]) {

            //左孩子比右孩子值大,父节点和左孩子交换
            if (bq.array[l_chiIndex] > bq.array[r_chiIndex]) {
                swap(bq.array[parIndex],bq.array[l_chiIndex]);
                parIndex = l_chiIndex;

            } else {
                swap(bq.array[parIndex],bq.array[r_chiIndex]);
                parIndex = r_chiIndex;
            }

            l_chiIndex = parIndex * 2;
            r_chiIndex = parIndex * 2 + 1;
        }
    }

    return OK;
}


//获取队列元素个数
QElemType eleCount(BinaryHeapQueue &bq) {
    QElemType count = bq.count;
    cout<<"队列元素个数: count = "<<count<<endl;
    return count;
}


//判断队列是否为空
Status isEmpty(BinaryHeapQueue &bq) {
    return bq.count == 0;
}


//判断队列是否已满
Status isFull(BinaryHeapQueue &bq) {
    return bq.count == bq.capacity-1;
}

 

四、结果

测试

int main() {

    ///创建二叉堆优先队列(本代码以最大堆为例) ------ 最小堆实现逻辑与其类似
    BinaryHeapQueue heapQueue;
    constructBinaryHeapQueue(heapQueue, 5);

    ///添加元素到队列
    enQueueByShiftUp(heapQueue, 60);
    enQueueByShiftUp(heapQueue, 23);
    enQueueByShiftUp(heapQueue, 40);
    enQueueByShiftUp(heapQueue, 60);
    enQueueByShiftUp(heapQueue, 80);

    ///获取元素的个数
    eleCount(heapQueue);

    ///先取出一个元素,再入队两个元素
    cout<<endl;
    deQueueByShiftDown(heapQueue);
    enQueueByShiftUp(heapQueue, 100);
    enQueueByShiftUp(heapQueue, 200);

    ///获取元素的个数
    eleCount(heapQueue);
    cout<<endl;

    ///取出元素从队列
    deQueueByShiftDown(heapQueue);
    deQueueByShiftDown(heapQueue);
    deQueueByShiftDown(heapQueue);
    deQueueByShiftDown(heapQueue);
    deQueueByShiftDown(heapQueue);
    deQueueByShiftDown(heapQueue);

    ///获取元素的个数
    eleCount(heapQueue);

    return 0;
}

打印

/Users/xiayuanquan/CLionProjects/treeHeap/cmake-build-debug/treeHeap
入队列元素: array[1] = 60
入队列元素: array[2] = 23
入队列元素: array[3] = 40
入队列元素: array[4] = 60
入队列元素: array[5] = 80
队列元素个数: count = 5

出队列元素: array[1] = 80
入队列元素: array[5] = 100
队列已满,当前元素: 200 无法被添加到队列
队列元素个数: count = 5

出队列元素: array[1] = 100
出队列元素: array[1] = 60
出队列元素: array[1] = 60
出队列元素: array[1] = 40
出队列元素: array[1] = 23
队列已空
队列元素个数: count = 0

进程已结束,退出代码 0

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!