当前位置: 首页 > news >正文

c++初阶--Stack,Queue和PriorityQueue的实现

今天我们来学习一下c++中的栈和队列,我们之前学过的vector和list是模板容器,而今天我们要学习的是容器适配器,那么什么是容器适配器呢?

目录

1. 容器适配器

1.1 适配器

1.2 Stack和Queue

2. deque的介绍

2.1 deque的原理介绍

2.2 deque的缺陷

2.3 为什么选择deque作为stack和queue的底层默认容器

3. Stack和Queue的实现

3.1 Stack的实现

3.2 Queue的实现

4. Priority Queue优先级队列

4.1 优先级队列的介绍

 4.2 仿函数

4.3 优先级队列的实现


1. 容器适配器
1.1 适配器
适配器是一种设计模式(设计模式是一套被反复使用的、多数人知晓的、经过分类编目的、代码设
计经验的总结),该种模式是将一个类的接口转换成客户希望的另外一个接口,比如说不同国家的电压不相同,我们在国内使用的充电器到了国外需要用到转接器,这个转接器就是一个适配器。
1.2 Stack和Queue
对于Stack和Queue,它们是对其他容器进行了封装,因此我们在实现的时候在定义模板的时候还需要添加一个模板参数:容器。容器这个参数和其他参数一样可以给定缺省值,在实例化时,容器(Container)是哪种容器就使用哪种容器。
2. deque的介绍
但对于官方库中,Stack和Queue的这个容器参数缺省值给了一个容器deque,那么deque是哪种容器呢?
2.1 deque的原理介绍
deque(双端队列):是一种双开口的"连续"空间的数据结构,双开口的含义是:可以在头尾两端
进行插入和删除操作,且时间复杂度为O(1),与vector比较,头插效率高,不需要搬移元素;与
list比较,空间利用率比较高。
对于deque的结构,deque使用了一个中控数组,中控数组是一个指针数组,数组中每个元素都是一个buff数组的地址,所以 deque并不是真正连续的空间,而是由一段段连续的小空间拼接而成的,实际deque类似于一个动态的二维数组,与list比较,其底层是连续空间,空间利用率比较高,不需要存储额外字段。
2.2 deque的缺陷
但是,deque有一个致命缺陷:不适合遍历,因为在遍历时,deque的迭代器要频繁的去检测其
是否移动到某段小空间的边界,导致效率低下,而序列式场景中,可能需要经常遍历,因此在实
际中,需要线性结构时,大多数情况下优先考虑vector和list,deque的应用并不多,而目前能看
到的一个应用就是,STL用其作为stack和queue的底层数据结构。
2.3 为什么选择deque作为stackqueue的底层默认容器
stack是一种后进先出的特殊线性数据结构,因此只要具有push_back()和pop_back()操作的线性
结构,都可以作为stack的底层容器,比如vector和list都可以;queue是先进先出的特殊线性数据
结构,只要具有push_back和pop_front操作的线性结构,都可以作为queue的底层容器,比如
list。但是STL中对stack和queue默认选择deque作为其底层容器,主要是因为:

1. stack和queue不需要遍历(因此stack和queue没有迭代器),只需要在固定的一端或者两端进

行操作。

2. 在stack中元素增长时,deque比vector的效率高(扩容时不需要搬移大量数据);queue中的

元素增长时,deque不仅效率高,而且内存使用率高。

结合了deque的优点,而完美的避开了其缺陷。
3. Stack和Queue的实现
3.1 Stack的实现
#include<deque>

template<class T,class container= deque<T>>
class Stack {
public:
	Stack()
	{}

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

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

	void push(const T& x)
	{
		S.push_back(x);
	}

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

	T& top()
	{
		return S.back();
	}
private:
	container S;
};
3.2 Queue的实现
#include<deque>

template<class T,class container= deque<T>>
class Queue {
public:
	Queue()
	{}

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

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

	void push(const T& x)
	{
		Q.push_back(x);
	}

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

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

	const T& back() const
	{
		return Q.back();
	}
private:
	container Q;
};
4. Priority Queue优先级队列
4.1 优先级队列的介绍
1. 优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素
中最大的。
2. 此上下文类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶
部的元素)。
3. 优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue
提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的
顶部。
4. 标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue
类实例化指定容器类,则使用vector。
5. 需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数make_heap、push_heap和pop_heap来自动完成此操作。
 4.2 仿函数

优先级队列就是数据结构中的堆,由于堆只能定义为大堆或者小堆,优先级队列默认是元素大的优先级高,我们在使用的时候不能说要找出最小的元素就去再创建一个新的优先级队列,所以这里要使用仿函数。

仿函数是一个类,可以用class或者struct来定义,类中有一个重载函数operator()(),该函数重载的是()运算符。仿函数没有成员变量,是一个空类,所以仿函数的大小为1。

对于仿函数的对象,可以像函数一样调用,所以仿函数对象也叫函数对象。在实际使用的时候,仿函数作用于函数内部用来修饰条件。

4.3 优先级队列的实现
#include<vector>

template<class T>

class Less {
public:
	bool operator()(const T& x, const T& y)
	{
		return x < y;
	}
};

template<class T>

class Greater {
public:
	bool operator()(const T& x, const T& y)
	{
		return x > y;
	}
};

template<class T, class container = vector<T>, class compare = Less<T>>

class Priority_queue {
public:
	Priority_queue()
	{}

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

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

	const T& top()
	{
		return con[0];
	}

	void push(const T& x)
	{
		con.push_back(x);
		adjust_up(con.size() - 1);
	}

	void adjust_up(int child)
	{
		compare com;
		int parent = (child - 1) / 2;
		while (child > 0)
		{
			if (com(con[parent], con[child]))
			{
				swap(con[parent], con[child]);
				child = parent;
				parent=(child - 1) / 2;
			}
			else {
				break;
			}
		}
	}

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

	void adjust_down(int parent)
	{
		int child = parent * 2 + 1;
		compare com;
		while (child < con.size())
		{
			if ((child + 1 < con.size()) && (com(con[child], con[child + 1])))
			{
				child++;
			}
			if (com(con[parent], con[child]))
			{
				swap(con[parent], con[child]);
				parent = child;
				child = parent * 2 + 1;
			}
			else {
				break;
			}
		}
	}
private:
	container con;
};

其中,Less和Greater两个类就是仿函数,它们作用于堆的向上调整和向下调整两个函数中,当我们使用大堆时,模板参数给定Less仿函数,使用小堆时,只需要将Less改为Greater即可,大大增强了使用的便利。

有关c++的Stack和Queue及Priority Queue三个容器适配器就介绍到这里。

相关文章:

  • 大话西游2经典再续前缘单机版|无需虚拟机|操作简单+GM管理+可修改仙玉
  • 设计模式--spring中用到的设计模式
  • unity学习56:旧版legacy和新版TMP文本输入框 InputField学习
  • JAVA+MySQL实现分库分表及设计思路
  • 数据结构-直接插入和希尔排序
  • 生成SQL的模型与工具
  • netcore入门案例:netcore api连接mysql的完整记事本接口示例
  • 玄机-第二章 日志分析-mysql应急响应的测试报告
  • JWT+redis实现令牌刷新优化方案
  • STM32内存五区及堆栈空间大小设置(启动文件浅析)
  • yolov8乱改版(使用最新源码版本ultralytics-8.3.80——该项目库集成了yolov12)
  • Nuxt.js 3【详解】敏感信息处理 -- 环境变量配置
  • Spring Boot从入门到精通:一站式掌握企业级开发
  • linux里面的过滤符号 | 是如何实现的
  • Python语法糖教程第2天—Python装饰器深度解析与高阶应用指南
  • Element实现el-dialog弹框移动、全屏功能
  • 鸿蒙Next如何自定义标签页
  • Vue 表单优化:下拉框值改变前的确认提示与还原逻辑实现
  • C++ 的时间库之六:日历和时区
  • ArcGIS Pro技巧实战:高效矢量化天地图地表覆盖图
  • 免费微信小程序开店/河北网站seo地址
  • 如何破解网站后台密码/赣州seo
  • 传奇大气网站模板免费下载/如何推广一个项目
  • 做网站公司融资多少钱/个人开发app可以上架吗
  • 网页制作网站素材/人力资源培训
  • 网站建设与网页设计可行性分析报告/电子商务网站建设方案