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


    一、介绍

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

    二叉堆特点:

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

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

    算法: 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; } //取出元素,并将尾部元素放到下标1的位置 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
  • 相关阅读:
    一个配置引发的血案
    软件工程之学习方法篇
    开篇
    重拾《 两周自制脚本语言 》- 中文关键字与原生函数
    重拾《 两周自制脚本语言 》- 支持中文标识符
    将《 两周自制脚本语言 》测试中使用的接口中文化
    5分钟入门LingaScript-尝鲜中文版TypeScript
    为《 两周自制脚本语言 》添加中文测试代码
    2019-02-18 扩展Python控制台实现中文反馈信息之二-正则替换
    2019-02-14 1992年日本对母语编程的可读性比较实验
  • 原文地址:https://www.cnblogs.com/XYQ-208910/p/11725718.html
Copyright © 2020-2023  润新知