优先队列的基本算法(使用二叉堆构建优先队列)
时间:2019-10-23
本文章向大家介绍优先队列的基本算法(使用二叉堆构建优先队列),主要包括优先队列的基本算法(使用二叉堆构建优先队列)使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
一、介绍
堆,也是计算中一种很常用的数据结构,它以树的形式存在。 对于树的结构,因为有父节点和子节点的概念,所以一般通过父指针和子指针来实现。但是,也有一种特殊的树不需要使用指针而可以直接通过数组来实现,这种树就是完全二叉树(除了最后一层不用全满,其他层必须全满,而且最后一层的叶子结点都靠左对齐)。今天我们要讨论的二叉堆就是一棵完全二叉树,二叉堆也是堆的一种,只不过二叉堆拥有自己的特点,它分为最大堆和最小堆。
二叉堆特点:
最大堆:所有的父节点值都不小于其孩子节点值。(注意“其”字,仅限于当前父节点和它的子节点的比较)
最小堆:所有的父节点值都不大于其孩子节点值。(注意“其”字,仅限于当前父节点和它的子节点的比较)
算法: 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
原文地址:https://www.cnblogs.com/XYQ-208910/p/11725718.html
- 详解c++指针的指针和指针的引用
- QTableView 一列添加两个按钮
- [个人翻译]Redis 集群教程(上)
- Docker实践--部署Nodejs应用
- 【权限的思考】(一)使用反射实现动态权限
- 【swift学习笔记】一.页面转跳的条件判断和传值
- 【swift学习笔记】二.页面转跳数据回传
- 【swift学习笔记】三.使用xib自定义UITableViewCell
- 【swift学习笔记】四.swift使用Alamofire和swiftyJson
- 【swift学习笔记】五.使用枚举优雅的管理Segue
- 【swift学习笔记】六.访facebook登录页面
- Docker Stack 部署web集群
- Docker部属Nsq集群
- Go 单例模式[个人翻译]
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法