[C++初阶]栈和队列_优先级队列的模拟实现 deque类 的理解

news2024/11/25 22:43:28

为了更好的理解优先级队列priority_queue,这里会同时进行栈和队列的提及

文章目录

  • 简要概念(栈和队列)
  • 栈和队列的模拟实现与使用
    • stack(栈)
    • deque的理解和操作
    • queue
  • priority_queue(优先级队列)
    • 框架
    • 具体实现
      • push()
      • adjust_up()(向上调整)
      • pop()
      • adjust_down()(向下调整)
      • top()
      • empty()
      • size()
    • priority_queue 的 验证 / 使用

简要概念(栈和队列)

  • 栈:后进先出的结构,通常使用数组栈的形式
  • 队列:先进先出的结构,通常使用链表式的队列

在这里插入图片描述

  • 优先级队列:优先级队列是基于堆实现的一种数据结构。在 C++ STL 中,我们将实现的priority_queue 类就是通过堆来实现的。
  • 这里不再对堆进行详细介绍,具体在这:堆详解

栈和队列的模拟实现与使用

stack(栈)

模拟实现

进行stack的实现前先介绍一下deque<T>

deque的理解和操作

概念(简单看看)

概念简单看看就行

  1. deque 是 C++ STL 中的一种双端队列(double-ended queue),它允许在队列两端高效地添加、删除元素,同时支持随机访问
  2. 与vector的不同:deque 具有良好的内存分配策略,可以避免 vector 扩容时带来的大量元素复制操作。此外,deque 也具有更好的迭代器安全性,因为它不能像 vector 那样通过引起扩容而使得旧迭代器失效。
  3. deque 内部使用了一个中央控制器,管理了一系列固定大小的连续空间块,每个块内部存储多个元素。当 deque 需要增加或删除元素时,中央控制器会根据需要进行块的扩容或收缩。

操作(重要)

这里介绍deque与stack && queue的不同处,介绍为什么要用deque实现栈和队列

  1. 插入和删除方式不同:栈只能在栈顶进行插入和删除元素,而队列只能在队尾插入,在队头删除;而 deque 可以在队列的两端都插入和删除元素。
  2. 访问方式不同:栈只能访问栈顶元素,队列只能访问队头元素;而 deque 可以随机访问任意位置的元素。
  3. 功能上不同:栈的主要功能是实现“后进先出”(LIFO),队列的主要功能是实现“先进先出”(FIFO),而 deque 不仅可以实现这两种功能,还能够满足双向遍历、支持随机插入和删除等操作。

继续对stack进行模拟实现:

这里需要注意的:

  1. 对于模板template: T 表示栈中元素的类型,Container 表示底层容器的类型,默认为 deque<T>
  2. 成员变量: _con为Container类型,如果调用者不给Container类型,_con默认为deque,剩下的增删查改调用_con的操作函数即可
namespace aiyimu 
{
	//Container 表示用于存储队列元素的底层容器类型,默认值为 deque<T>
	template<class T, class Container = deque<T>>
	class stack
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_back();
		}

		T& top()
		{
			return _con.back();
		}
		
		const T& top() const
		{
			return _con.back();
		}

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

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

	private:
		Container _con;
	};
}

queue

同理stack,这里不作过多赘述,使用_con的操作函数实现即可

namespace aiyimu
{
	template<class T, class Container = deque<T>>
	class queue
	{
	public:
		void push(const T& x)
		{
			_con.push_back(x);
		}

		void pop()
		{
			_con.pop_front();
		}

		T& back()
		{
			return _con.back();
		}

		T& front()
		{
			return _con.front();
		}

		const T& back() const
		{
			return _con.back();
		}

		const T& front() const
		{
			return _con.front();
		}


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

		size_t size() const
		{
			return _con.size();
		}
	private:
		Container _con;
	};
}

priority_queue(优先级队列)

框架

  • Container:在上文stack有解,同理
  • Compare:用于选择该堆为大堆还是小堆,默认为std::less则为小堆
  • std::less: 是一个函数对象,用于比较两个参数的大小关系。重载了小于运算符,用于比较两个类型为 T 的对象的大小。当 a < b 时,std::less()(a, b) 返回 true,否则返回 false。
  • 构造函数,操作函数
template<class T, class Container = vector<T>, class Compare = std::less<T>>
	class priority_queue
	{
	public:
		// 默认构造
		priority_queue()
		{}
		
		// 利用迭代器构造函数
		template <class InputIterator>
		priority_queue(InputIterator first, InputIterator last)
		{}

		// O(logN)
		// 向上调整
		void adjust_up(size_t child)
		{}

		//向下调整
		void adjust_down(size_t parent)
		{}
		
		// 其余操作函数
		void push(const T& x)
		{}

		void pop()
		{}

		const T& top()
		{}

		bool empty() const
		{}

		size_t size() const
		{}
	private:
		Container _con;
	};

具体实现

push()

  1. 插入操作即为堆的插入操作,所以执行_con.push_back()后进行向上调整(从尾部_con.size()-1进行调整)
void push(const T& x)
{
	_con.push_back(x);
	adjust_up(_con.size() - 1);
}

adjust_up()(向上调整)

  1. 向上调整即堆heap的操作,具体思路在上文给到的堆详解中
  2. 主要对if语句中的内容进行解释:

(小堆)向上调整过程中,当父节点的值小于子节点时,交换父子节点

  • 原始方法直接进行<判断:if (_con[parent] < _con[child])
  • 但这种写法此时就锁定小堆了, 为了使调用者可以根据需要进行大堆小堆的更改
  • 所以这里创建一个Compare对象com,将比较改写为:if(com(_con[parent], _con[child]))
  • 该写法当调用者不做更改时默认为小堆(std::less),当调用者给出std::greater(比较大于),此时的priority_queue就变为大堆
void adjust_up(size_t child)
{
	Compare com;	// 

	size_t parent = (child - 1) / 2;

	while (child > 0)
	{
		//if (_con[parent] < _con[child])
		if(com(_con[parent], _con[child]))// 默认为less,调用者可以自行指定
		{
			std::swap(_con[parent], _con[child]);
			child = parent;
			parent = (child - 1) / 2;
		}
		else
		{
			break;
		}
	}
}

pop()

  • 交换堆顶和堆底的元素,删除堆顶元素,向下调整
void pop()
{
	// 交换堆顶和堆底的元素,删除堆顶元素,向下调整
	std::swap(_con[0], _con[_con.size() - 1]);
	_con.pop_back();

	adjust_down(0);
}

adjust_down()(向下调整)

  • 向下调整需要注意的仍为com(_con[child], _con[child + 1])写法的意义
  • 具体内容在adjust_up中已经提到
void adjust_down(size_t parent)
{
	Compare com;
	size_t child = parent * 2 + 1;	//先假设右孩子大
	while (child < _con.size())
	{
		//if (child + 1 < _con.size() && _con[child] < _con[child + 1])
		if (child + 1 < _con.size() && com(_con[child], _con[child + 1]))
		{
			++child;
		}

		//if (_con[parent] < _con[child])
		if(com(_con[parent], _con[child]))
		{
			std::swap(_con[parent], _con[child]);
			parent = child;
			child = parent * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

top()

  • 返回堆顶元素,即_con[0]
const T& top()
{
	return _con[0];
}

empty()

  • bool类型:判断堆是否为空
bool empty() const
{
	return _con.empty();
}

size()

  • 返回堆大小(堆元素个数)
size_t size() const
{
	return _con.size();
}

priority_queue 的 验证 / 使用

插入元素 && 打印priority_queue

// 头文件

void test_priority_queue1()
{
	aiyimu::priority_queue<int> pq;
	//priority_queue<int> pq;

	// 插入元素
	pq.push(3);
	pq.push(2);
	pq.push(5);
	pq.push(9);
	pq.push(4);
	pq.push(6);
	pq.push(1);

	// 打印pq的所有元素
	while (!pq.empty())
	{
		cout << pq.top() << " ";
		pq.pop();
	}
	cout << endl;
}

输出结果:
在这里插入图片描述

降序 / 升序

int arr[] = { 3,5,6,8,19,7,1,0,9,1,20,4,12 };

	// 不传入Compare类型,默认less类型:小于,即降序
	aiyimu::priority_queue<int> heap_less(arr, arr + sizeof(arr) / sizeof(arr[0]));
	
	// 传入Compare类型,为greater类型:大于,即升序
	aiyimu::priority_queue<int, vector<int>, greater<int>> heap_greater(arr, arr + sizeof(arr) / sizeof(arr[0]));


	while (!heap_less.empty())
	{
		cout << heap_less.top() << " ";
		heap_less.pop();
	}
	cout << endl;


	while (!heap_greater.empty())
	{
		cout << heap_greater.top() << " ";
		heap_greater.pop();
	}
	cout << endl;

输出结果:
在这里插入图片描述

由此可以看出,当使用std::less时堆为降序,std::greater时堆为升序

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

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

相关文章

悲观锁、乐观锁、自旋锁和读写锁

悲观锁和乐观锁 悲观锁&#xff1a;在每次取数据时&#xff0c;总是担心数据会被其他线程修改&#xff0c;所以会在取数据前先加锁&#xff08;读锁&#xff0c;写锁&#xff0c;行 锁等&#xff09;&#xff0c;当其他线程想要访问数据时&#xff0c;被阻塞挂起。&#xff08…

金融贷款行业如何高效获客,积累意向客户群体——运营商大数据

现如今贷款行业面对的运营压力日益扩大&#xff0c;顾客贮备是生存的关键&#xff0c;传统式的陌生拜访&#xff0c;一切随缘销售市场已不能满足其要求。互联网消费行为的融合与转变是在销售市场端反映&#xff0c;直接影响着广告推广广告策略的确立与运用。 可是&#xff0c;…

移除元素【数组】

⭐前言⭐ ※※※大家好&#xff01;我是同学〖森〗&#xff0c;一名计算机爱好者&#xff0c;今天让我们进入练习模式。若有错误&#xff0c;请多多指教。更多有趣的代码请移步Gitee &#x1f44d; 点赞 ⭐ 收藏 &#x1f4dd;留言 都是我创作的最大的动力&#xff01; 题目 27…

IPEmotion 2023 R1支持在线能量分析

新发布的IPEmotion 2023 R1提供了许多新功能&#xff0c;其中最重要的是新的“在线功率计算&#xff08;Online Power Calculation&#xff09;”功能。该功能允许使用预定义的功率计算来进行测量任务和数据分析。此外&#xff0c;IPEmotion 2023 R1现在支持一种新的存储模式&a…

Maya英文界面怎么改为中文界面

Maya是一款3D动画和视觉效果软件&#xff0c;用于创建逼真的角色和大片般的效果&#xff0c;也是受到电影、电视和游戏行业的 3D 建模师、动画师、照明艺术家和 VFX 艺术家等多数人喜爱的一款3D软件。我们在使用Maya的过程中&#xff0c;常常会遇到一些小阻碍&#xff0c;比如M…

【Python爬虫实战】你不必到处找数据,你完全可以自己爬之Python批量采集图虫网摄影师高清美照,听说~你喜欢御姐...(爬图神器)

前言 怎么批量保存网页图片&#xff1f; 有时候在网页中看到很多美图其中有很多自己喜欢的图片素材或壁纸&#xff0c;一张纸一张下载保存未 免太低效了。 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码福利&#xff0c;请移步至CSDN社区或文末公众ha…

C++哈希应用——位图布隆过滤器

C布隆过滤器 文章目录 C布隆过滤器概念实质用途控制误判率实现插入和查找布隆过滤器的删除 布隆过滤器优点布隆过滤器缺陷相关大数据题目 用哈希表存储用户记录&#xff0c;缺点是需要消耗较大的内存&#xff1b;用位图存储用户记录&#xff0c;缺点是位图一般处理整形&#xf…

P1039 [NOIP2003 提高组] 侦探推理

题目描述 明明同学最近迷上了侦探漫画《柯南》并沉醉于推理游戏之中&#xff0c;于是他召集了一群同学玩推理游戏。游戏的内容是这样的&#xff0c;明明的同学们先商量好由其中的一个人充当罪犯&#xff08;在明明不知情的情况下&#xff09;&#xff0c;明明的任务就是找出这…

Java-设计模式中事件与委托Java版本

目录 背景介绍 实现过程 类图 NS图 代码 客户端 业务封装类 委托类 事件类 猫类 老鼠类 运行结果 总结提升 背景介绍 相信大家在学习大话设计模式的时候都有接触过事件与委托&#xff0c;但是对于事件与委托具体的业务逻辑也不是很清楚&#xff0c;只能照猫画虎去使用…

SEO机制算是让我玩明白了

获取当前时间时间戳&#xff0c;返回遵循ISO 8601扩展格式的日期 new Date(Date.now()).toISOString() 使用moment库转换回来 this.moment(new Date(Date.now()).toISOString()).format("YYYY-MM-DD") js去掉富文本中html标签和图片 filterHtmlTag(val) {if(!val){…

Shell编程规范与使用

一、Shell脚本概述 1&#xff09;Shell的作用——命令解释器&#xff0c;“翻译官” Linux 系统中的 Shell 是一个特殊的应用程序&#xff0c;它介于操作系统内核与用户之间&#xff0c;充当 了一个“命令解释器”的角色&#xff0c;负责接收用户输入的操作指令&#xff08;命…

接口协作--apipost接口协作工具

接口协作 apipost支持接口在线协作编辑功能&#xff0c;打开apipost创业一个团队&#xff0c;在创建一个项目。 在把需要一起协作的人员添加到团队中 在进行项目编辑把需要进行协作的人员拉取到项目中 之后在进入项目创建接口就可以进行接口协作了

scratch猫捉老鼠 少儿编程 电子学会图形化编程scratch编程等级考试二级真题和答案解析2023年3月

目录 scratch猫捉老鼠 一、题目要求 1、准备工作 2、功能实现 二、案例分析

kafka调试脚本的使用

创建名称为test的topic且副本数量3&#xff0c;partition数量6 /etc/kafka/kafka/bin/kafka-topics.sh --create --bootstrap-server 10.1.60.112:9092 --replication-factor 3 --partitions 6 --topic test 查看名称为test的topic信息 /etc/kafka/kafka/bin/kafka-topics.sh -…

uniapp微信小程序图片预览PreviewImage

一、说明 功能&#xff1a;点击图片预览大图&#xff0c;并且可以通过滑动查看不同图片的预览大图。 点击预览大图后&#xff1a; 二、上代码 参考uniapp官方文档 其提供了预览大图的函数uni.previewImage(OBJECT). //放大查看推荐图片enlargePicture(index) {console.log…

【Unity-ML】Unity机器学习(一)

安装环境&#xff1a;Windows10 Anaconda3(64-bit)&#xff0c;网上很多教程&#xff0c;例如这个anaconda下载及安装(保姆级教程) - 知乎anaconda包管理器和环境管理器&#xff0c;强烈建议食用 1.下载官网下载太慢可选用镜像下载 官网下载&#xff1a; Anaconda | Individua…

Softing FiberXpert 700光纤测试套件助力一级多模和单模光纤认证

FiberXpert 700是用于多模和单模的四路波长测试套件&#xff0c;不仅可以对光纤链路进行直观、灵活和快速地认证&#xff0c;而且可以导出数据报告。 测试网络安装以确保其符合指定标准的过程称为认证&#xff0c;并且这通常需要纸质文件作为符合标准的证明。而FiberXpert 700光…

Docker 的数据管理

一、Docker 的数据管理 管理 Docker 容器中数据主要有两种方式&#xff1a;数据卷&#xff08;Data Volumes&#xff09;和数据卷容器&#xff08;DataVolumes Containers&#xff09;。 1&#xff0e;数据卷 数据卷是一个供容器使用的特殊目录&#xff0c;位于容器中。可将宿…

为什么说网络安全行业是IT的风口行业?

前言 2023年网络安全行业的前景看起来非常乐观。根据当前的趋势和发展&#xff0c;一些趋势和发展可能对2023年网络安全行业产生影响&#xff1a; 5G技术的广泛应用&#xff1a;5G技术的普及将会使互联网的速度更快&#xff0c;同时也将带来更多的网络威胁和安全挑战。网络安全…

eBPF技术介绍

前言 eBPF起源于linux内核&#xff0c;它可以以砂箱程序运行在操作系统内核的特权上下文&#xff0c;高效&#xff0c;安全&#xff0c;易于扩展而不需要修改内核源码或者加载内核模块。 操作系统一直是实现观测&#xff0c;安全和网络功能的最理想的地方&#xff0c;因为内核的…