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

概念

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

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

空队列

插入一个元素

插入第二个元素

 删除一个节点

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

 运行结果:

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode刷题详解——两两交换链表中的节点

1. 题目链接&#xff1a;24. 两两交换链表中的节点 2. 题目描述&#xff1a; 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff08;即&#xff0c;只能进行节点交换&#xff09;。 …

【机器学习】一、机器学习概述与模型的评估、选择

机器学习简介 由来 阿瑟.萨缪尔Arthur Samuel,1952年研制了一个具有自学习能力的西洋跳棋程序&#xff0c;1956年应约翰.麦卡锡John McCarthy&#xff08;人工智能之父&#xff09;之邀&#xff0c;在标志着人工智能学科诞生的达特茅斯会议上介绍这项工作。他发明了“机器学习…

GitHub项目监控

目录 github开放平台接口限流 监控某个仓库的更新状态 对于常用Github的用户来说&#xff0c;经常有一些自动化的需求。比如监控某些项目的更新情况并实时拉取&#xff0c;比如监控github全网上传的代码是否携带了公司的APIKEY&#xff0c;SECRETKEY等… github开放平台 gith…

线性代数 第一章 行列式

一、概念 不同行不同列元素乘积的代数和&#xff08;共n!项&#xff09; 二、性质 经转置行列式的值不变&#xff0c;即&#xff1b; 某行有公因数k&#xff0c;可把k提到行列式外。特别地&#xff0c;某行元素全为0&#xff0c;则行列式的值为0&#xff1b; 两行互换行列式…

STM32智能小车(循迹、跟随、避障、测速、蓝牙、wife、4g、语音识别)总结

目录 1.电机模块开发 1.1 让小车动起来 1.2 串口控制小车方向 1.3 如何进行小车PWM调速 1.4 PWM方式实现小车转向 2.循迹小车 2.1 循迹模块使用 2.2 循迹小车原理 2.3 循迹小车核心代码 2.4 循迹小车解决转弯平滑问题 3.跟随/避障小车 3.1 红外壁障模块分析​编辑 …

SpringCloud Gateway实现请求解密和响应加密

文章目录 前言正文一、项目简介二、核心代码2.1 自定义过滤器2.2 网关配置2.3 自定义配置类2.4 加密组件接口2.5 加密组件实现&#xff0c;AES算法2.6 启动类&#xff0c;校验支持的算法配置 三、请求报文示例四、测试结果4.1 网关项目启动时4.2 发生请求时 前言 本文环境使用比…

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

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

软考中项集成如何画图?计算题怎么考的?

2023下半年软考集成一共考6个批次&#xff0c;10月28日、29日软考集成考了第一、二、三、四批次&#xff0c;11月4日软考集成再考第五批和第六批。 先说一下通过10.28-29得出的软考机考注意事项&#xff1a; 1、草稿纸不能自带&#xff0c;考试现场会发放草稿纸&#xff0c;草…

钡铼技术ARM工控机在机器人控制领域的应用

ARM工控机是一种基于ARM架构的工业控制计算机&#xff0c;用于在工业自动化领域中进行数据采集、监控、控制和通信等应用。ARM&#xff08;Advanced RISC Machine&#xff09;架构是一种低功耗、高性能的处理器架构&#xff0c;广泛应用于移动设备、嵌入式系统和物联网等领域。…

关于pytorch张量维度转换及张量运算

关于pytorch张量维度转换大全 1 tensor.view()2 tensor.reshape()3 tensor.squeeze()和tensor.unsqueeze()3.1 tensor.squeeze() 降维3.2 tensor.unsqueeze(idx)升维 4 tensor.permute()5 torch.cat([a,b],dim)6 torch.stack()7 torch.chunk()和torch.split()8 与tensor相乘运算…

预处理详解(一)

1 预定义符号 __FILE__ //进行编译的源文件 __LINE__ //文件当前的行号 __DATE__ //文件被编译的日期 __TIME__ //文件被编译的时间 __STDC__ //如果编译器遵循ANSI C&#xff0c;其值为1&#xff0c;否则未定义 这些预定义符号都是…

Linux纯C串口开发

为什么要用纯C语言 为了数据流动加速&#xff0c;实现低配CPU建立高速数据流而不用CPU干预&#xff0c;避免串口数据流多次反复上升到软件应用层又下降低到硬件协议层。 关于termios.h 麻烦的是&#xff0c;在 Linux 中使用串口并不是一件最简单的事情。在处理 termios.h 标头…

怎么检测开关电源质量的好坏?测试的方法是什么?

开关电源的工作原理 开关电源(简称SMPS)是常见的一种电源供应器&#xff0c;是高频化的电能转换装置&#xff0c;可以将电压透过不同形式的架构转换为用户端所需求的电压或电流。具有体积小、功耗小、效率高、高可靠性的特点&#xff0c;被广泛应用在工业、军工设备、科研设备、…

机器学习---支持向量机的初步理解

1. SVM的经典解释 改编自支持向量机解释得很好 |字节大小生物学 (bytesizebio.net) 话说&#xff0c;在遥远的从前&#xff0c;有一只贪玩爱搞破坏的妖怪阿布劫持了善良美丽的女主小美&#xff0c;智勇双全 的男主大壮挺身而出&#xff0c;大壮跟随阿布来到了妖怪的住处&…

Docker compose容器编排

Docker compose容器编排 1、Docker compose简介 docker-compose是docker的编排工具&#xff0c;用于定义和运行一个项目&#xff0c;该项目包含多个docker容器&#xff0c;在如今的微服务时代&#xff0c;一个项目会存在多个服务&#xff0c;使用docker一个个部署操作的话就会…

React中的状态管理

目录 前言 1. React中的状态管理 1.1 本地状态管理 1.2 全局状态管理 Redux React Context 2. React状态管理的优势 总结 前言 当谈到前端开发中的状态管理时&#xff0c;React是一个备受推崇的选择。React的状态管理机制被广泛应用于构建大型、复杂的应用程序&#xf…

计算机毕业设计选题推荐-超市售货微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

注册中心ZK、nameServer、eureka、Nacos介绍与对比

前言 注册中心的由来 微服务架构是存在着很多跨服务调用,每个服务都存在着多个节点,如果有多个提供者和消费者,当提供者增加/减少或者消费者增加/减少,双方都需要感知发现。所以诞生了注册中心这个中间件。 市面上有很多注册中心,如 Zookeeper、NameServer、Eureka、Na…

OpenCV 笔记(4):图像的算术运算、逻辑运算

Part11. 图像的算术运算 图像的本质是一个矩阵&#xff0c;所以可以对它进行一些常见的算术运算&#xff0c;例如加、减、乘、除、平方根、对数、绝对值等等。除此之外&#xff0c;还可以对图像进行逻辑运算和几何变换。 我们先从简单的图像加、减、逻辑运算开始介绍。后续会有…

企业 Tomcat 运维 部署tomcat反向代理集群

一、Tomcat 简介 Tomcat服务器是一个免费的开放源代码的Web应用服务器&#xff0c;属于轻量级应用服务器&#xff0c; Tomcat和Nginx、Apache(httpd)、Web服务器一样&#xff0c;具有处理HTML页面的功能不过Tomcat处理静态HTML的能力不如Nginx/Apache服务器 一个tomcat默认并…