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

news2024/11/29 13:34:33

概念

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

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

空队列

插入一个元素

插入第二个元素

 删除一个节点

上列情况是最普通的情况(无需多余的操作),显然如果删除的是队列中的最后一个节点,尾指针需要手动移动;如果删除的是队列中的第一个节点,头指针会自动移动。如果删除的队列只有一个节点,那头尾指针需要手动置空。所以总共有 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.coloradmin.cn/o/1159832.html

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

相关文章

关于嵌入式rtthread系统与单片机芯片

简介 我估计已经有很久没更新了&#xff0c;近一年都在某个国企里工作&#xff0c;我做的就是嵌入式工程师的岗位&#xff0c;最近才刚刚退出来&#xff0c;想来说说自己的工作使用的软件和系统。 本身进公司的时候&#xff0c;其实做的就是写单片机的板子的程序的工作&#x…

8. 一文快速学懂常用工具——Linux命令(上)

本章讲解知识点 引言 指令学习 本专栏适合于软件开发刚入职的学生或人士&#xff0c;有一定的编程基础&#xff0c;帮助大家快速掌握工作中必会的工具和指令。本专栏针对面试题答案进行了优化&#xff0c;尽量做到好记、言简意赅。如专栏内容有错漏&#xff0c;欢迎在评论区指…

西门子PLC ModbusTcp通信访问网关后从站(梯形图篇)

这篇文章和之前的" 西门子PLC ModbusTcp通信访问网关后从站(SCL语音轮询状态机FB) "属于姊妹篇,这里我们主要介绍梯形图代码如何实现ModbusTcp轮询。 SCL代码篇 西门子PLC ModbusTcp通信访问网关后从站(SCL语言轮询状态机FB)-CSDN博客文章浏览阅读6次。西门子PLC的…

创建超过1G内存大小的程序

正常情况一个进程最大占用内存为1G一下&#xff0c;如果程序有需求要使用超过1G大小的程序&#xff0c;可进行如下操作 VS修改设置&#xff1a;属性--->链接器--->系统--->启用大地址 【选择是】 测试申请堆内存代码 #include <stdlib.h> #include <stdio…

【数据结构】树形结构所有路径复原为链表

目录 1. 树形结构可视化 2. 树形结构转为链表 此目标是要还原树形结构的所有路径。树形结构是一种常见的数据结构&#xff0c;它表示元素之间层次关系。在树形结构中&#xff0c;每个节点可能拥有一个或多个子节点&#xff0c;形成了一个分层的结构。为了还原树形结构的路径&…

Linux的账号管理

本章的学习感觉如果不做系统管理员&#xff0c;作为简单了解就可以了 前面介绍了&#xff0c;用户&#xff0c;组&#xff0c;other三个角色&#xff0c; 每个用户创建都会有uid与之对应&#xff0c;创建的用户基本信息在一下两个文件中&#xff0c;也是我们要介绍的内容&…

天猫双11:秋冬换季呼吸健康受关注 呼吸机、雾化器、血氧仪成交翻番

10月31日晚8点&#xff0c;天猫双11正式开售&#xff0c;健康消费热情集中释放。秋冬换季&#xff0c;呼吸健康备受关注&#xff0c;呼吸机、雾化器、血氧仪成交翻番&#xff0c;中药滋补经典方增长近400%。天猫健康数据显示&#xff0c;小仙炖、Ulike、Swisse、可复美、敷尔佳…

网课搜题小程序源码/小猿题库多接口微信小程序源码+自带流量主

网课搜题小程序源码&#xff0c;多接口小猿题库等综合网课搜题微信小程序源码带流量主&#xff0c;网课搜题小程序&#xff0c;可以开通流量主赚钱。 搭建教程 1、微信公众平台注册自己的小程序 2、下载微信开发者工具和小程序的源码 3、上传代码到自己的小程序 下载地址&…

车载网关让无人车栩栩如生:一款性价比车载终端案例

​无人车是汽车智能化的高形态,而连接无人车的大脑正是5G车载网关。今天让我们通过SV900-5G产品,感受一下无人车会带来怎样便利的生活场景。 详情查看&#xff1a;https://www.key-iot.com/iotlist/sv900.html 我们首先来到一座自动化工厂,几台小型AGV忙碌穿梭,它们都安装了SV…

跨国文件传输为什么要用专业的大文件传输软件?

跨国文件传输是许多跨国企业需要的基础工作&#xff0c;对于传输的质量和速度要求也是很严格的&#xff0c;随着数据量的不断增加&#xff0c;寻常传统的传输方式肯定是不行&#xff0c;需要新的技术和方式来进行传输&#xff0c;大文件传输软件应运而出&#xff0c;那它有什么…

如何创建一个react项目

文章目录 前言前言打开小黑窗口npm init vite后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;react.js &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不断努力填补技术短板。(如果出现错误&am…

【hive 面试题】聚合操作时null和‘‘对结果的影响

1、HiveSQL中 聚合操作时null和对结果的影响 代码示例&#xff1a; with temp as (select null as aunion allselect 111 as a union allselect 222 as a union allselect 333 as aunion allselect as a )select sum(a), -- null 不会参与运算, 会转换成0avg(a), -- null …

图像二值化阈值调整——OTSU算法(大津法/最大类间方差法)

大津算法&#xff08;OTSU算法&#xff09;是一种常用的图像二值化方法&#xff0c;用于将灰度图像转化为二值图像。该算法由日本学者大津展之于1979年提出&#xff0c;因此得名。 大津算法的核心思想是通过寻找一个阈值&#xff0c;将图像的像素分为两个类别&#xff1a;前景…

【Amazon】AWS实战 | 快速发布安全传输的静态页面

文章目录 一、实验架构图二、实验涉及的AWS服务三、实验操作步骤1. 创建S3存储桶&#xff0c;存放网站网页2. 使用ACM建立域名证书3. 设置Cloudfront&#xff0c;连接S3存储桶✴️4. 设置Route53&#xff0c;解析域名服务5. 通过CLI工具上传网页更新内容【可选】 四、实验总结 …

Python小试牛刀:GUI(图形界面)实现计算器UI界面(二)

上一篇&#xff1a;Python小试牛刀&#xff1a;GUI&#xff08;图形界面&#xff09;实现计算器UI界面&#xff08;一&#xff09;-CSDN博客 在上一篇文章中介绍了Python GUI常用的库&#xff0c;以及运用GUI标准库tkinter仅设计了计算器的UI界面。 而在本篇文章&#xff0c;…

idea集成测试插件替代postman

idea集成测试插件替代postman 兄弟萌&#xff0c;你再测试接口是否无bug是否流畅的时候是否还在使用“postman”来回切换进行测试呢&#xff1f; 页面切换进行测试&#xff0c;有没有感觉很麻烦呢&#xff1f; 打开postman&#xff0c;输入接口地址&#xff0c;有没有感觉很麻烦…

如何选择云服务器?选择云服务器都要注意哪些关键点?

云服务器的选择对于企业和个人来说都是一个重要的决策&#xff0c;涉及到价格、周边生态、售后和续费等多个方面。本文将总结几个关键点&#xff0c;并推荐腾讯云作为一个性价比高、续费价格不贵的云服务器厂商。 价格&#xff1a;腾讯云的性价比高 在选择云服务器时&#xff…

自定义SpringBoot启动图标

在SpringBoot项目的resources目录下创建banner.txt文件 在https://www.bootschool.net/网站上复制Ascll艺术字&#xff08;图&#xff09;粘贴到banner.txt中保存。 启动项目就会加载 可以修改颜色&#xff0c;和版本号 ${application.version} 输出版本 ${spring-boot.v…

k8s-调度约束

目录 工作机制 调度过程 指定调度节点 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令&#xff0c;在 Node 节点上面…

土壤数据库辅助工具SPAW计算土壤导水率

土壤数据库辅助工具SPAW 首先下载SPAW工具 点击打开 根据之前的1比100土壤数据查表得到各个组分含量 其中 Field Capacity是田间持水量 Matric Bulk Density是基质粒密度 参考文章 【SWAT水文模型】ArcSWAT土壤数据库辅助工具SPAW简述