【STL源码剖析】priority_queue 优先队列的简单实现

水到绝处是风景

人到绝境是重生


目录

priority_queue的模拟实现 

源码剖析:

代码测试:

 契子✨ 


我们之前不仅讲过 队列queue 还有 双端队列deque 而我们今天所讲的依旧是队列家族的成员 -- 优先队列priority_queue

顾名思义,priority_queue是一个拥有权值观念的 queue,它允许增删元素、访问元素等功能。由于这是一个 queue,所以只允许在低端加入元素,并从顶端取出元素,除此之外别无其他存取元素的途径

priority_queue 带有权值观念,其内的元素并非依照推入的顺序排序,而是自动依照元素的权值排序(权值通常以实值表示)。权值最高者,排在前面

大家想象一下,我们之前学过的数据结构有哪一种具有类似的性质?

是不是像我们学过的 -- 堆(heap),我们可以利用 heap 的特性完成 [依权值高低自动递增排序] priority_queue

优先队列 priority_queue 是一种容器适配器,默认使用 vector 作为其底层存储数据的容器,在 vector 上又使用了堆算法将 vector 中元素构造成堆的结构,因此 priority_queue 就是堆,所有需要用到堆的位置,都可以考虑使用 priority_queue。注意: 默认情况下 priority_queue 是 大堆

 priority_queue 没有迭代器

priority_queue 的所有元素,进出都有一定的规则,只有 queue 的顶端元素(权值最高元素),才有机会被外界取用,priority_queue 不提供遍历功能,也不提供迭代器功能


priority_queue的模拟实现 

通过对 priority_queue 的底层结构默认就是 vector ,然后我们处理一下形成堆,因此此处只需对对进行通用的封装即可。操作非常简单,源码很简短,这里就完整的列出吧 ~ 然后在讲一下细节

#include<vector>
#include<iostream>
using std::vector;
using std::swap;

namespace Mack
{
	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

	template<class T,class Sequence = vector<T>, class Comapre = less<T> >
	class priority_queue
	{
	public:
		
		priority_queue() = default;

		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				c.push_back(*first);
				++first;
			}
			for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--)
			{
				AdjustDown(i);
			}
		}

		const T& top() const
		{
			return c.front();
		}

 		bool empty() const
		{
			return c.empty();
		}

		size_t size() const
		{
			return c.size();
		}

		void AdjustUP(size_t child)
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (comp(c[parent] , c[child]))
				{
					swap(c[parent], c[child]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else
				{
					break;
				}
			}
		}

		void push(const T& val)
		{
			c.push_back(val);
			AdjustUP(size()-1);
		}

		void AdjustDown(size_t parent)
		{
			size_t child = parent * 2 + 1;
			while (child < size())
			{
				while (child + 1 < size() && comp(c[child] , c[child+1]))
				{
					child++;
				}
				if (comp(c[parent] , c[child]))
				{
					swap(c[parent], c[child]);
					parent = child;
					child = parent * 2 + 1;
				}
				else
				{
					break;
				}
			}
		}

		void pop()
		{
			swap(c[0], c[size() - 1]);
			c.pop_back();
			AdjustDown(0);
		}

	private:
		Sequence c;
		Comapre comp;
	};

}

我们先来分析一下库里面的优先队列

对啦 ~ 头文件依然是 #include<queue>

源码剖析:

#include<queue>
#include<iostream>
using namespace std;
void priority_queue_test()
{
	priority_queue<int>  str;
	str.push(10);
	str.push(30);
	str.push(20);
	str.push(50);
	str.push(35);
	while (!str.empty())
	{
		cout << str.top() << " ";
		str.pop();
	}
}

我们发现库里的优先队列默认排的是降序也就是大堆 ~ 

所以我们写优先队列时,也要按照大堆的方式去写

关于算法,老铁们可以借鉴一下这个:二叉堆

我们重点讲一下关于 priority_queue 的自动排序,我们知道我们现在的优先队列排的是降序,那我们想排升序怎么办呢?难道要将堆中的比较符号都改一下吗?

我们先来看一下库里的算法:

#include<queue>
#include<iostream>
using namespace std;

void priority_queue_test()
{
	priority_queue<int, vector<int>,greater<int>>  str;
	str.push(10);
	str.push(30);
	str.push(20);
	str.push(50);
	str.push(35);
	while (!str.empty())
	{
		cout << str.top() << " ";
		str.pop();
	}
}

用惯排序 sort 的老铁可能会有些不习惯,为什么中间还要加一个参数,因为库里就是以下的格式,就跟传缺省一样不能隔代相传

template<class T,class Sequence = vector<T>, class Comapre = less<T> >

我们回到重点!!!

在我们我们 C语言 阶段的话频繁的比较大小我们一般都会写成一个函数 

bool Compare(int x, int y)
{
	return x < y;
}

int main()
{
	int x = 0, y = 1;
	if (Compare(x, y))
	{
		printf("y>x");
	}
	else
	{
		printf("y<x");
	}
	return 0;
}

如果比较 int 我们写一个专门比较 int 类型的函数,char 类型则专门写一个char 类型的函数

当我们学了 C++ 就开摆了,编程的进步就是变懒的过程 -- 我们可以利用模板来控制类型的比较

而要使用模板的前提必须是一个类,或者类中的函数

	template<class T>
	struct less
	{
		bool operator()(const T& x, const T& y)
		{
			return x < y;
		}
	};
	
	template<class T>
	struct greater
	{
		bool operator()(const T& x, const T& y)
		{
			return x > y;
		}
	};

所以我们用一个类去包含比较函数在利用模板,而我们重载()的原因就是想写成这样一种函数的形式:Compare(x, y) -- 这样方便比较

我们调用类中的函数是不是都是 类对象+点运算符,我们将()重载便可以写成函数的形式

这样的函数形式我们称之为伪函数

为了让我们得初始化方便,库里提供了迭代器区间构造

有些老铁可能会疑惑,不是不提供迭代器吗,怎么还会有迭代器区间构造?
嘿嘿 ~ 其实我们的数组也可以进行迭代

		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				c.push_back(*first);
				++first;
			}
			for (int i = (c.size() - 1 - 1) / 2; i >= 0; i--)
			{
				AdjustDown(i);
			}
		}

先将数据尾插到对象中,在向下调整建堆,因为向下调整要找到第一个非叶子节点  

这里放张动图以便老铁理解:

(_con.size() - 1 - 1) / 2 的 _con.size() - 1 是找到最后一个节点,(_con.size() - 1 - 1) / 2,则是套公式 parent  = (child-1) /2 找到最后一个节点的双亲也就是第一个非叶子节点


代码测试:

别的不说先来测试一下代码,不要哔哔了一大段文字结果代码都是错的

void priority_queue_test()
{
	int arr[] = {1,3,5,7,9,2,4,6,8,0};
	priority_queue<int, vector<int>> str(arr, arr + 9);

	while (!str.empty())
	{
		std::cout << str.top() << " ";
		str.pop();
	}
}

 

#include"priority_queue.h"
#include<iostream>
using std::iostream;
using std::ostream;
using namespace Mack;

class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
		: _year(year)
		, _month(month)
		, _day(day)
	{}
	bool operator<(const Date& d)const
	{
		return (_year < d._year) ||
			(_year == d._year && _month < d._month) ||
			(_year == d._year && _month == d._month && _day < d._day);
	}
	bool operator>(const Date& d)const
	{
		return (_year > d._year) ||
			(_year == d._year && _month > d._month) ||
			(_year == d._year && _month == d._month && _day > d._day);
	}
	friend ostream& operator<<(ostream& _cout, const Date& d)
	{
		_cout << d._year << "-" << d._month << "-" << d._day;
		return _cout;
	}
private:
	int _year;
	int _month;
	int _day;
};

void TestPriorityQueue()
{
	priority_queue<Date> q1;
	q1.push(Date(2018, 10, 29));
	q1.push(Date(2018, 10, 28));
	q1.push(Date(2018, 10, 30));
	std::cout << q1.top() << std::endl;
	priority_queue<Date, vector<Date>, greater<Date>> q2;
	q2.push(Date(2018, 10, 29));
	q2.push(Date(2018, 10, 28));
	q2.push(Date(2018, 10, 30));
	std::cout << q2.top() << std::endl;
}

int main()
{	
	TestPriorityQueue();
	std::cout << std::endl;
	system("pause");
	return 0;
}


 

 有问题的话可以提出来哦 ~ 

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

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

相关文章

网络实用技术答案

&#xff08; C &#xff09;不属于计算机网络四要素。A. 计算机系统 B. 传输介质C. 用户 D. 网络协议计算机网络中广域网和局域网的分类是以&#xff08; D &#xff09;来划分的。A. 信息交换方式 B&#xff0e;传输控制方法C. 网络使用习惯 D&#xff0e;网络覆盖范围计算机…

蓝桥云课第12届强者挑战赛

第一题&#xff1a;字符串加法 其实本质上就是一个高精度问题&#xff0c;可以使用同余定理的推论 &#xff08;ab&#xff09;%n((a%n)(b%n))%n; #include <iostream> using namespace std; const int mod1e97; int main() {string a,b;cin>>a>>b;ab;int …

CorelDRAW2024最新版本有哪些功能?揭秘设计界最新神器!

“设计”一词最早来源于拉丁语“designare”&#xff0c;意为计划&#xff0c;构思。随着时代的发展&#xff0c;人们将“设计”理解为一种创造性活动&#xff0c;通过这种活动&#xff0c;人们可以创造出新的产品、新的场景以及新的体验。 「CorelDRAW汉化版下载」&#xff0c…

Spring Boot框架基础

文章目录 1 Spring Boot概述2 Spring Boot入门2.1 项目搭建2.2 入门程序 3 数据请求与响应3.1 数据请求3.2 数据响应 4 分层解耦4.1 三层架构4.2 控制反转4.3 依赖注入 5 参考资料 1 Spring Boot概述 Spring是Java EE编程领域的一个轻量级开源框架&#xff0c;是为了解决企业级…

AI日报|文生语音大模型国内外均有突破,Pika完成6亿新融资,视频大模型也不远了!

文章推荐 AI搜索哪家强&#xff1f;16款产品实战测评&#xff0c;效率飙升秘籍&#xff01; AI日报&#xff5c;智谱AI再降价&#xff0c;同时开源9B系列模型&#xff1b;国内外气象大模型竞逐升级 字节推出文本到语音模型家族Seed-TTS&#xff1a;擅长情感表达&#xff0c;…

前端 JS 经典:打印对象的 bug

1. 问题 相信这个 console 打印语句的 bug&#xff0c;其实小伙伴们是遇到过的&#xff0c;就是你有一个对象&#xff0c;通过 console&#xff0c;打印一次&#xff0c;然后经过一些处理&#xff0c;再通过 console 打印&#xff0c;发现两次打印的结果是一样的&#xff0c;第…

定个小目标之每天刷LeetCode热题(12)

这是一道简单题&#xff0c;使用位运算中的异或运算即可&#xff0c;异或运算有以下性质&#xff1a; 1、任何数异或 0 结果仍然是原来的数&#xff0c;即 a⊕0a 2、任何数和其自身做异或运算&#xff0c;结果是 0 所以我们只需要让数组里的所有元素进行异或运算得到的结果就…

30、matlab现代滤波:维纳滤波/LMS算法滤波/小波变换滤波

1、信号1和信号2的维纳滤波 实现代码 N 2000; %采样点数 Fs 2000; %采样频率 t 0:1 / Fs:1 - 1 / Fs; %时间序列 Signal1 sin(2*pi*20* t) sin(2*pi*40* t) sin(2*pi*60* t); Signal2[2*ones(1,50),zeros(1,50),-1*ones(1,100),zeros(1,50),-2*ones(1,50),zeros(1,50),1…

超详细的java Comparable,Comparator接口解析

前言 Hello大家好呀&#xff0c;在java中我们常常涉及到对象的比较&#xff0c;不同于基本数据类型&#xff0c;对于我们的自定义对象&#xff0c;需要我们自己去建立比较标准&#xff0c;例如我们自定义一个People类&#xff0c;这个类有name和age两个属性&#xff0c;那么问…

Day49 动态规划part08

LC139单词拆分(未掌握) 未掌握分析&#xff1a;将字符串s中的各个字符看成是背包&#xff0c;思考成了多重背包问题单词就是物品&#xff0c;字符串s就是背包&#xff0c;单词能否组成字符串s&#xff0c;就是问物品能不能把背包装满。拆分时可以重复使用字典中的单词&#xf…

RabbitMQ--Hello World(基础详解)

文章目录 先决条件RabbitMQ 初识RabbitMQ--Hello World发送接收 更多相关内容可查看 先决条件 本教程假定 RabbitMQ 已安装并在标准端口 &#xff08;5672&#xff09; 上运行。如果你 使用不同的主机、端口或凭据&#xff0c;连接设置将需要 调整。如未安装可查看Windows下载…

C++多线程同步总结

C多线程同步总结 关于C多线程同步 一、C11规范下的线程库 1、C11 线程库的基本用法&#xff1a;创建线程、分离线程 #include<iostream> #include<thread> #include<windows.h> using namespace std; void threadProc() {cout<<"this is in t…

Oracle EBS AP发票验证-计税期间出现意外错误解决方法

系统版本 RDBMS : 12.1.0.2.0 Oracle Applications : 12.2.6 问题症状: **打开发票题头或发票行“税详细信息”**错误提示如下: 由于以下原因而无法针对"税"窗口中所做的修改更新 Oraclee Payables信息: 尚未为税率或帐户来源税率设置可退回税/应纳税额帐户。请…

用 Notepad++ 写 Java 程序

安装包 百度网盘 提取码&#xff1a;6666 安装步骤 双击安装包开始安装。 安装完成&#xff1a; 配置编码 用 NotePad 写 Java 程序时&#xff0c;需要设置编码。 在 设置&#xff0c;首选项&#xff0c;新建 中进行设置&#xff0c;可以对每一个新建的文件起作用。 Note…

29网课交单平台 epay.php SQL注入漏洞复现

0x01 产品简介 29网课交单平台是一个专注于在线教育和知识付费领域的交单平台。该平台基于PHP开发,通过全开源修复和优化,为用户提供了高效、稳定、安全的在线学习和交易环境。作为知识付费系统的重要组成部分,充分利用了互联网的优势,为用户提供了便捷的支付方式、高效的…

CAD二次开发(8)-探索实现不重启CAD进行热部署代码

最近在研究CAD二次开发过程中&#xff0c;调试代码的过程中&#xff0c;需要频繁地重启CAD&#xff0c;非常浪费我们的开发时间&#xff0c;所以我就一直在想&#xff0c;怎么可以实现在不每次重启代码和CAD的情况下&#xff0c;实现代码的热部署效果。 我找到的方式&#xff…

【Linux】进程2——管理概念,进程概念

1.什么是管理&#xff1f; 那在还没有学习进程之前&#xff0c;就问大家&#xff0c;操作系统是怎么管理进行进程管理的呢&#xff1f; 很简单&#xff0c;先把进程描述起来&#xff0c;再把进程组织起来&#xff01; 我们拿大学为例子 最典型的管理者——校长最典型的被管理…

java线程变量共享

在Java中&#xff0c;线程变量共享可以通过几种方式实现&#xff1a; 1.实例变量&#xff1a;如果一个实例变量被多个线程共享&#xff0c;你需要确保适当的同步&#xff0c;以避免竞态条件。你可以使用synchronized关键字或者Lock接口来保护共享变量。 2.静态变量&#xff1a;…

【vuex小试牛刀】

了解vuex核心概念请移步 https://vuex.vuejs.org/zh/ # 一、初始vuex # 1.1 vuex是什么 就是把需要共享的变量全部存储在一个对象里面&#xff0c;然后将这个对象放在顶层组件中供其他组件使用 父子组件通信时&#xff0c;我们通常会采用 props emit 这种方式。但当通信双方不…

高考志愿填报有哪些技巧和方法

一年一度高考季&#xff0c;又高考志愿填报的时侯了。高考志愿填报的时侯&#xff0c;需要考虑的因素比较多&#xff0c;有的同学觉是离家越远越好&#xff0c;要放飞自我&#xff0c;家长再也管不了我了。有的同学觉得专业比学校牌子重要&#xff0c;只要报个好专业&#xff0…