优先队列----数据结构

概念

不知道你玩过英雄联盟吗?英雄联盟里面的防御塔会攻击离自己最近的小兵,但是如果有炮车兵在塔内,防御塔会优先攻击炮车(因为炮车的威胁性更大),只有没有兵线在塔内时,防御塔才会攻击英雄。所以你可以得出优先级:距离最近的炮车 > 炮车 > 距离最近的小兵 > 小兵 > 距离最近的英雄 > 英雄。

那什么是优先队列?首先它是一个队列,它的入队顺序没有发生改变,但是出队的顺序是根据优先级的高低来实现的,遍历队列,优先级高的先出队,有多个节点具有最高的优先级,选取遇到的第一个具有最高的优先级的节点。 

空队列

插入一个元素

插入第二个元素

 删除一个节点

上列情况是最普通的情况(无需多余的操作),显然如果删除的是队列中的最后一个节点,尾指针需要手动移动;如果删除的是队列中的第一个节点,头指针会自动移动。如果删除的队列只有一个节点,那头尾指针需要手动置空。所以总共有 2 种情况需要考虑。

队列的算法实现

队列的结构体定义

其中优先级的高低是自己定义的,你也可以令 0 为最高优先级,优先级数也不只有这 9 个。

#define MAX_SIZE 15
typedef int DateElem;typedef struct _QNode
{int priority; //节点的优先级,9为最高优先级,0为最低优先级,优先级相同的取第一个DateElem date;struct _QNode* next;
}QNode;typedef QNode* QueuePtr; //QueuePtr a; 就定义了一个指向结构体QNode的指针typedef struct _Queue
{int length;    //队列长度QueuePtr head; //头指针QueuePtr tail; //尾指针
}Queue;

队列的初始化、判空、判满、插入

//队列的初始化,初始化为空队列
void initQueue(Queue* q)
{if (!q) //指向队头的指针为空{return;}q->head = NULL;q->tail = NULL;q->length = 0;
}//判断队列是否为空
bool IsEmpty(Queue* q)
{if (!q) return false;if (q->head == NULL) //条件用 q->tail == NULL 也行{return true;}return false; //不为空
}//判断队列是否为满
bool IsFull(Queue* q)
{if (!q) return false;if (q->length >= MAX_SIZE) //也可以用 q->length == MAX_SIZE{return true;}return false;
}//入队
bool enterQueue(Queue* q, DateElem e, int priority)
{if (!q || IsFull(q)){cout << "队列已满" << endl;return false;}QNode* p = new QNode;//if (!q) return false; 一般不会生成失败p->priority = priority;p->date = e;p->next = NULL;//插入有两种情况if (IsEmpty(q)) //空队列{q->head = p;q->tail = p;}else //队列中已有元素{q->tail->next = p; //队列中的最后一个节点的next指针指向新加节点q->tail = p;	   //更新尾指针}q->length++;return true;
}

出队

唯一与普通队列有较大差别的就是队列的出队,其他的操作变化很小。

//遍历队列
bool popQueue(Queue* q,DateElem *out)
{if (!q || IsEmpty(q)){cout << "队列为空" << endl;return false;}if (!out){cout << "无法传递删除节点的值" << endl;return false;}QNode** prev_node_next = NULL; //二级指针,指向优先级最高的节点的前一个节点的next指针QNode* prev_node = NULL; //指向优先级最高的节点的前一个节点QNode* temp = NULL,*last = NULL; //temp遍历队列,last指向temp指向的前一个节点prev_node_next = &(q->head); //最开始指向队头指针(也就是第一个节点的前一个节点的next指针),解引用就是指向第一个节点last = q->head; temp = last->next; while (temp != NULL){if (temp->priority > (*prev_node_next)->priority){cout << "找到了一个更高的优先级:" << temp->priority << endl;prev_node_next = &(last->next); //指向temp的前一个节点的next指针prev_node = last; //指向temp的前一个节点}last = temp;temp = temp->next;}*out = (*prev_node_next)->date; //传递出队元素的值temp = *prev_node_next;  // temp指向要删除节点*prev_node_next = (*prev_node_next)->next; //或者是 prev_node_next = & (*prev_node_next)->next;delete temp;q->length--;//情况一:删除节点后为空队列if (q->length == 0){q->head = q->tail = NULL;}//情况二:删除的是尾节点else if ( *prev_node_next == NULL && prev_node != NULL){q->tail = prev_node;}//情况三:删除的是首节点,与情况一不同的是删除节点后,队列不为空//情况四:普通情况//这两种情况遍历结束后的调整中头尾指针就弄好了return true;
}

如果你觉得我这里写得不好,嘻嘻,因为明明只需要用一级指针,我偏要用二级指针,这就是我与明明的区别,哈哈,好了不开玩笑,可以看看下图帮助理解。

队列的打印、清空、获取队首元素

//打印队列
bool Print(Queue* q)  
{if (!q) return false;if (IsEmpty(q)){cout << "队列为空" << endl;}QNode* p = q->head;cout << "队列中的元素:";while (p != NULL){printf("%d[优先级%d] ", p->date,p->priority);p = p->next;}cout << endl;return true;
}
//清空队列
bool ClearQueue(Queue* q)
{if (!q || IsEmpty(q)) return false;QNode* temp = q->head, * tmp = NULL;while (temp != NULL){tmp = temp->next;delete temp;temp = tmp;}q->length = 0;q->head = NULL;q->tail = NULL;return true;
}//获取队头
bool GetHead(Queue* sq, DateElem* date)
{if (!date || !sq || IsEmpty(sq))return false;*date = sq->head->date; return true;}

主函数测试代码

int main(void)
{Queue* q = new Queue;DateElem e = -1;initQueue(q);for (int i = 0; i < 10; i++){enterQueue(q, i + 2, i);}printf("队列中有%d个元素\n", q->length);Print(q);for (int i = 0; i < 5; i++){if (popQueue(q, &e)){cout << "出队的元素是:" << e << endl;}else{cout << "出队失败" << endl;}}cout << "出队后,";Print(q);cout << "清空队列后,";ClearQueue(q);Print(q);//清理资源delete q;return 0;
}

 运行结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/126796.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

css选择器,布局,BFC

一、选择器的优先关系 !important > 行内样式style > #id > .class > 标签div > 元素选择符 > *通用选择器 二、元素选择符有哪些 包含选择符:E F&#xff08;E所有的F包含子代&#xff0c;孙代&#xff0c;子子孙孙等等&#xff09;子选择符:E > F&am…

java多线程异步处理并获取处理后的返回值

示例部分代码&#xff1a; String param "hello"//举例用的线程池&#xff0c;一般建议自定义线程池ExecutorService executorService Executors.newFixedThreadPool(5);CompletionService<Object> completionService new ExecutorCompletionService<&g…

实在智能携手品牌商家,在活动会面中共谋发展

金秋十月&#xff0c;丰收的季节&#xff0c;也是商家们在双11大展拳脚的时刻。为迎战一年一度的双11大促&#xff0c;品牌商家在10月份卯足劲&#xff0c;制定一系列营销方案&#xff0c;争取为店铺带来更多流量和订单。 其中&#xff0c;舍得、同科医药、梅子熟了、宝洁、维…

回归预测 | Matlab实现RIME-CNN-SVM霜冰优化算法优化卷积神经网络-支持向量机的多变量回归预测

回归预测 | Matlab实现RIME-CNN-SVM霜冰优化算法优化卷积神经网络-支持向量机的多变量回归预测 目录 回归预测 | Matlab实现RIME-CNN-SVM霜冰优化算法优化卷积神经网络-支持向量机的多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.RIME-CNN-SVM霜冰优化算…

大坝水库安全监测终端MCU,智能化管理的新篇章!

我国目前拥有超过9.8万座水库大坝&#xff0c;其中超过95%为土石坝&#xff0c;这些大坝主要是在上世纪80年代以前建造的。这些水库大坝在保障防洪、发电、供水、灌溉等方面发挥了巨大的作用&#xff0c;但是同时也存在一定的安全风险&#xff0c;比如坝体结构破损、坝基渗漏、…

掌握 JavaScript 的基本语法

一、javascript书写位置 JavaScript 可以写在 HTML 页面中的以下三个位置&#xff1a; 内联脚本&#xff1a;将 JavaScript 代码直接写在 HTML 元素的 onclick、onload、onsubmit 等事件属性中。例如&#xff1a; <button onclick"alert(Hello World)">点击我…

【设计模式】第19节:行为型模式之“中介模式”

一、简介 中介模式定义了一个单独的&#xff08;中介&#xff09;对象&#xff0c;来封装一组对象之间的交互。将这组对象之间的交互委派给与中介对象交互&#xff0c;来避免对象之间的直接交互。 中介模式的设计思想跟中间层很像&#xff0c;通过引入中介这个中间层&#xf…

el-dialog中嵌套iframe之后拿不到iframe的id 的解决办法

在vueelement项目中想用到el-dialog弹窗加iframe嵌套外部页面的方法,但是这时候要获取iframe里面的ID 但是这时候怎么也获取不到 <el-dialog ref"middleFlag" v-if"middleFlag" width"1100px" height1200px title"文章管理" :visib…

2023年11月1日蜻蜓C影视追剧系统v1.2.2更新-与时俱进调整微信登录授权获取方式-修复无法登陆授权

2023年11月1日蜻蜓C影视追剧系统v1.2.2更新-与时俱进调整微信登录授权获取方式-修复无法登陆授权 问题背景&#xff1a; 小程序用户头像昵称获取规则调整公告官方 微信团队2022-05-09 更新时间&#xff1a;2022年11月9日 由于 PC/macOS 平台「头像昵称填写能力」存在兼容性问…

518抽奖软件,安全稳定,不怕手抖误按键

518抽奖软件简介 518抽奖软件&#xff0c;518我要发&#xff0c;超好用的年会抽奖软件&#xff0c;简约设计风格。 包含文字号码抽奖、照片抽奖两种模式&#xff0c;支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 防误按功能 入口&#xff1a; 主界面上点右…

Unity Profiler 详细解析(二)

Profiler的主要参数详解 1. Memory Profiler Uesd Total : 当前帧的Unity内存&#xff0c;Mono内存&#xff0c;GfxDriver内存&#xff0c;Profiler内存以及额外内存的总和。 Reserved Total&#xff1a; 系统在当前帧申请的总体物理内存 Total System Memory Usage&#xff1…

opencv官网文档学习

1.图像处理基本使用 import cv2# 读取图像 image cv2.imread("images/1.png", cv2.IMREAD_GRAYSCALE) print("image:",image)# 显示图像 namedWindow cv2.namedWindow("images/1.png") cv2.imshow("images/1.png", image)# 等待按键…

MSQL系列(十一) Mysql实战-Inner Join算法底层原理及驱动表选择

Mysql实战-Inner Join算法驱动表选择 前面我们讲解了BTree的索引结构&#xff0c;及Mysql的存储引擎MyISAM和InnoDB,也详细讲解下 left Join的底层驱动表 选择, 并且初步了解 Inner join是Mysql 主动选择优化的驱动表&#xff0c;知道索引要建立在被驱动表上 那么对于Inner j…

Linux环境下运行selenium4.14

使用Python爬虫爬取数据时&#xff0c;需要用到selenium&#xff0c;在服务器上运行时&#xff0c;需要如下配置&#xff1a; 1、安装谷歌浏览器 yum install https://dl.google.com/linux/direct/google-chrome-stable_current_x86_64.rpm -y 2、安装chromedriver 1&#xf…

MTSC2023|深圳大会,你关心的技术话题都在这里

MTSC中国互联网测试开发大会 (Make Tester Super Cool&#xff0c; 简称MTSC&#xff09; 是由国内知名的测试技术社区TesterHome发起的软件测试行业技术会议&#xff0c;大会以“软件质量保障体系和测试研发技术交流”为主要目的&#xff0c;旨在为行业搭建一个深入探讨和交流…

Android NDK开发详解之调试和性能分析的ndk-gdb

Android NDK开发详解之调试和性能分析的ndk-gdb 要求用法选项 NDK 包含一个名为 ndk-gdb 的 Shell 脚本&#xff0c;可以启动命令行原生调试会话。偏好使用 GUI 的用户则应阅读在 Android Studio 中调试这篇文档。 要求 要运行命令行原生调试&#xff0c;必须满足以下要求&am…

curl 断点续传

如何断点续传&#xff1a; curl “xxxxurl” -o RealisticPhotography_v10.safetensors 要实现断点续传&#xff0c;您可以使用curl工具并添加一些参数&#xff0c;以便在下载中断后恢复下载。下面是如何使用curl进行断点续传的方法&#xff1a; 首先&#xff0c;确保您已经安…

【深度学习】Transformer、GPT、BERT、Seq2Seq什么区别?

请看vcr&#xff1a;https://transformers.run/back/transformer/

系列四十六、Spring的事务传播行为案例演示(六)#SUPPORTS

一、演示Spring的传播行为&#xff08;SUPPORTS&#xff09; 1.1、StockServiceImplSUPPORTS /*** Author : 一叶浮萍归大海* Date: 2023/10/30 15:43* Description: 演示SUPPORTS的传播行为* 外部不存在事务&#xff1a;不开启新的事务* 外部存在事务&#xff1a…

单链表练习

单链表练习 相关内容&#xff1a; 1.再理解&#xff1a;LNode、*LinkList 2.单链表的整表创建&#xff08;头插法和尾插法&#xff09; 3.单链表的读取、插入、删除 4.单链表的整表删除 //单链表的初始化、创建、插入、删除、查找 //结点的结构体&#xff1a;数据域、指针域 …