【容器适配器的认识与模拟】

news2024/12/29 11:44:01

目录:

  • 前言
  • 一、引入
  • 二、容器适配器
    • (一)stack
      • deque
      • stack模拟实现
    • (二)queue
      • queue模拟实现
      • 为什么栈和队列要使用deque
    • (三)priority_queue
      • priority_queue模拟实现
  • 总结

前言

打怪升级:第78天
在这里插入图片描述

一、引入

朋友们大家好,今天我们来探究一下stl中的适配器,作为stl六大组件之一,适配器的重要性显而易见。
适配,就是可以和其他工具一起配合着使用,并且只要满足适配条件就都可以使用。
我们生活中又有哪些物品可以称为适配器?
例如:排插、手机充电器等

在这里插入图片描述
以三孔插座为例:只要你的插头是三叉的,不管你是电脑还是电冰箱或者是电视机,都可以使用同一个插孔进行充电,但是如果你不是三孔插头就另说。

生活中的适配器比比皆是,stl中的适配器也是这么个意思,表示我们可以为适配器传入不同的容器模板,那么,
stl中都有哪些是适配器?
– stack、queue、priority_queue
下面我们一一介绍。

二、容器适配器

(一)stack

在这里插入图片描述

上方为stack的定义,模板参数一表示存储数据的类型,模板参数二表示所传容器的类型。

deque

deque:双端队列,底层为数组与链表的结合体,deque的数据存储并非连续的一段空间。
由于deque的底层实现比较复杂, – 如下图,我们一般不单独使用,只作为stack与queue的默认模板参数使用,所以我们只需要看第二张逻辑图就足够。

物理图:

在这里插入图片描述

逻辑图:
在这里插入图片描述

我们不需要去深究deque的底层实现,可以简单理解为一个指针数组,每个指针指向堆区的一个小数组。


stack模拟实现

栈和队列的操作由于受到限制,只能在一段插入和删除数据,不能进行下标访问,不能遍历、查找等,因此实现起来十分简单。

using namespace std;
#include<vector>
#include<list>
#include<iostream>
#include<algorithm>
#include<deque>

namespace kz
{
	template<class T, class container = vector<T>> // 我们这里换一换,使用vector作为默认容器
	class stack 
	{
		typedef stack<T, container> self;

	public:
		stack() = default;

		stack(const self& s)
		{
			container tmp = s._arr; // 利用vector自己的拷贝构造
			_arr = tmp;
		}

		self& operator=(const self s) // 利用深拷贝
		{
			swap(s._arr);
		}

		void push(const T& val) { _arr.push_back(val); }

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

		T& top() { return _arr.back(); }

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

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

		size_t capacity() const { return _arr.capacity(); }

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

		void swap(self& s) { std::swap(_arr, s._arr); }

	private:
		container _arr;
	};
}

示例:

	void Test_stack()
	{
		stack<int>s1;
		s1.push(1);
		s1.push(2);
		s1.push(3);
		s1.push(4);
		

		stack<int>s2;
		s2.push(10);
		s2.push(20);
		s2.push(30);
		cout << s1.size() << endl;
		cout << s2.size() << endl;
		cout << endl;
		
		s1.swap(s2);
		cout << s1.size() << endl;
		cout << s2.size() << endl;
		while (!s1.empty())
		{
			cout << s1.top() << endl;
			s1.pop();
		}
		cout << endl;

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

运行实例:
在这里插入图片描述


(二)queue

在这里插入图片描述

queue模拟实现

namespace kz
{
	template<class T, class Con = deque<T>>
	class queue
	{
	public:

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

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

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

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

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

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

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

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

	private:

		Con _arr;

	};
}

示例:

	void Test_queue()
	{
		queue<int>q1;
		q1.push(10);
		q1.push(20);
		q1.push(30);
		q1.push(50);

		queue<int>q2(q1);

		while (!q1.empty())
		{
			cout << q1.front() << ' ';
			q1.pop();
		}
		cout << endl << endl;

		while (!q2.empty())
		{
			cout << q2.front() << ' ';
			q2.pop();
		}
		cout << endl << endl;
	}

运行实例:
在这里插入图片描述


为什么栈和队列要使用deque

vector的尾插尾删效率高,无需挪动数据,但是头插头删效率低下;
list的头插头删、尾插尾删效率都很高,但是,数据存储不是连续的,所以访问数据的效率低下;
由于双端队列的数据存储是从中间开始的,头插头删、尾插尾删效率都很高,并且读取数据时效率也可以接受,
整合了vector与list的优点,但是在实际应用中没有vector与list那么极致,或者说:短板拉长了,长板变短了。
但是在栈和队列这样只需要进行头插头删,尾插尾删,不需要进行遍历的限制下使用deque不失为一种好的选择。


(三)priority_queue

这里是引用

相比于stack与queue,priority_queue 的底层要复杂许多,前两个参数相同,但优先级队列最后多了一个比较仿函数,而这个仿函数是选最值。
优先级队列的底层是,因此我们一般使用vector作为默认容器,而且一般不会改变它,
默认比较仿函数是less,按照我们平时的思路排出的应该是升序,但是这里默认堆顶是最大值,这是第一个需要注意的地方;
第二个需要注意的就是后两个参数的位置,我们一般不会改变vector容器,但是比较函数则不一定,当数据元素为自定义类型时就必须自定义比较仿函数,但是在改变比较仿函数时我们还必须先传第二个参数,因此当我们需要改变比较仿函数时需要传三个参数,这个确实有些多此一举,但是标准已经确定就不会再更改,因此我们以后使用也需要注意。

priority_queue模拟实现

#pragma once
#include<iostream>
using namespace std;
#include<vector>

namespace kz
{
	template<class T>
	struct less
	{
		bool operator()(const T& e1, const T& e2) const// 小于
		{
			return e1 < e2;
		}
	};

	template<class T>
	struct greater
	{
		bool operator()(const T& e1, const T& e2) const // 大于
		{
			return e1 > e2;
		}
	};

	template<class T, class container = vector<T>, class compare  = less<T>> // 适配器
	class priority_queue
	{
	public:
		priority_queue() = default; // 使用默认构造

		template<class iteratortype> // 迭代器区间构造
		priority_queue(iteratortype begin, iteratortype end)
			:_a(begin, end)
		{
			MakeHeap();
		}

		void push(const T& val)
		{
			_a.push_back(val);
			//  向上调整 
			AdjustUp(_a.size() - 1);
		}

		const T& top() // 堆中的数据不允许修改,防止破坏堆的结构
		{
			return _a.front();
		}

		void pop()
		{
			SwapTop(0, _a.size() - 1);
			_a.pop_back();
		}

		size_t size()
		{
			return _a.size();
		}

		bool empty()
		{
			return _a.empty();
		}

	private:
		void SwapTop(size_t begin, size_t end)
		{
			swap(_a[begin], _a[end]);
			AdjustDown(0, _a.size() - 1);
		}
		// 初始化 -- 建堆
		void MakeHeap()
		{
			for (int i = (_a.size() - 2) / 2; i >= 0; --i) // 此处如果使用size_t 要注意控制结束条件,避免无限循环
				AdjustDown(i, _a.size());
		}
		//		向上调整
		void AdjustUp(size_t child) // 配合标准库实现:小堆降序
		{
			size_t parent = (child - 1) / 2;
			while (child > 0)
			{
				if (cmp(_a[parent], _a[child]))
				{
					swap(_a[child], _a[parent]);
					child = parent;
					parent = (child - 1) / 2;
				}
				else // 孩子不大于父亲就退出
				{
					break;
				}
			}
		}
		// 向下调整
		void AdjustDown(size_t parent, size_t capacity)
		{
			size_t child = parent * 2 + 1;
			while (child < capacity)
			{
				// 右孩子存在且 大
				if (child + 1 < capacity && cmp(_a[child], _a[child + 1]))
					++child;
				// 孩子比双亲大
				if (cmp(_a[parent], _a[child]))
				{
					swap(_a[child], _a[parent]);
					parent = child;
					child = parent * 2 + 1;
				}
				else// 否则调整结束
				{
					break;
				}
			}
		}

	private:
		container _a;
		compare cmp;
	};

}

示例:

	void TestPriorityQueue_1()
	{
		priority_queue<int>q;
		for (int i = 0; i < 6; ++i) q.push(i);
		while(!q.empty())
		{
			cout << q.top() << " ";
			q.pop();
		}
		cout << endl << endl;

	}

	void TestPriorityQueue_2()
	{
		srand((unsigned)time(nullptr));
		vector<int>v;
		for (int i = 0; i < 10; ++i) v.push_back(rand()%100);
		priority_queue<int, vector<int>, greater<int>>q(v.begin(), v.end());
		while (!q.empty())
		{
			cout << q.top() << " ";
			q.pop();
		}
		cout << endl << endl;

	}

	struct Person
	{
		int _a;
		int _b;

		bool operator<(const Person& p)const // 告诉对方我的比较规则
		{
			return _a < p._a;
		}
	};

	void TestPriorityQueue_3()
	{
		srand((unsigned)time(nullptr));
		vector<Person>v;
		for (int i = 0; i < 10; ++i) v.push_back({ rand() % 100 , i});
		priority_queue<Person>q(v.begin(), v.end());

		while (!q.empty())
		{
			cout << q.top()._a << " " << q.top()._b << endl;
			q.pop();
		}
		cout << endl << endl;

	}

运行实例:
在这里插入图片描述


总结

适配器就是在容器的外面再嵌套一层容器,通过对普通容器的限制,达到我们想要的结果。
stl中的容器适配器一共有三个stack、queue以及priority_queue,它们的操作很少,并且接口十分类似,今天我们已经见识了它们的函数接口以及接口的实现,如果你可以自行写出它们的模拟实现,那你对于容器适配器的理解就会迈入很高的层次。
希望对有需要的朋友带来帮助。

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

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

相关文章

小程序获取用户信息实现一键登录

文章目录 旧版获取用户信息实现登录流程login页面代码个人中心页面代码全局app.vue代码下面是小程序获取用户信息最新调整的方式 温馨提示 &#xff1a;以下小程序登录方式只适用于2.27.1版本库以下使用 详情请看微信官方文档调整 旧版获取用户信息实现登录流程 由于我是在hbu…

公司招了一个腾讯拿30K的人,让我见识到了什么是天花板···

前言 人人都有大厂梦&#xff0c;对于软件测试人员来说&#xff0c;BAT 为首的一线互联网公司肯定是自己的心仪对象&#xff0c;毕竟能到这些大厂工作&#xff0c;不仅薪资高待遇好&#xff0c;而且能力技术都能够得到提升&#xff0c;最关键的是还能够给自己镀上一层金&#…

Linux系统防火墙iptables(你委屈什么,爱而不得的又不止你一个)

文章目录 一、iptables防火墙概述1.简介2.netfilter/iptables关系3.iptables的四表五链&#xff08;1&#xff09;四表&#xff08;2&#xff09;五链 4.数据包过滤的匹配流程&#xff08;1&#xff09;入站&#xff08;2&#xff09;转发 二、iptables防火墙配置1.下载相关服务…

一个有点好用的信息收集工具

功能 domainscan 调用 subfinder 被动收集&#xff0c;调用 ksubdoamin 进行 dns 验证 泛解析、CDN 判断 获取 domain 相关的 web&#xff08;host:port&#xff09;资产&#xff0c;使用 webscan 扫描 webscan 支持 http/https scheme 自动判断 获取 statusCode、contentL…

OpenAI新作Shap-e算法使用教程

一、知识点 Shap-e是基于nerf的开源生成3d模型方案。它是由如今热火朝天的Open AI公司&#xff08;chatgpt&#xff0c;Dell-E2&#xff09;开发、开源的。Shap-e生成的速度非常快&#xff0c;输入关键词即可生成简单模型&#xff08;限于简单单体模型&#xff09;。 二、环境…

TFTLCD显示实验

实验内容 通过 STM32 的 FSMC 接口来控制 TFTLCD 的显示。 TFTLCD简介 TFT-LCD 即薄膜晶体管液晶显示器。其英文全称为&#xff1a;Thin Film Transistor-Liquid Crystal Display。TFT-LCD 与无源 TN-LCD、STN-LCD 的简单矩阵不同&#xff0c;它在液晶显示屏的每一个象素上都…

Docker笔记7 | 如何使用 Docker Compose 搭建一个拥有权限 认证、TLS 的私有仓库?

7 | 如何使用 Docker Compose 搭建一个拥有权限 认证、TLS 的私有仓库&#xff1f; 1 准备工作2 准备站点证书2.1 创建CA私钥2.2 创建CA根证书请求文件2.3 配置CA根证书2.4 签发根证书2.5 生成站点SSL私钥2.6 私钥生成证书请求文件2.7 配置证书2.8 签署站点SSL证书 3 配置私有仓…

一五一、web+小程序骨架屏整理

骨架屏介绍 请点击查看智能小程序骨架屏 车载小程序骨架屏 车载小程序为方便开发者设置骨架屏&#xff0c;在智能小程序的基础上抽取出骨架屏模板&#xff0c;开发者只需要在 skeleton 文件夹下配置config.json&#xff08;page 和骨架屏的映射关系文件&#xff09;即可生效骨…

web自动化测试进阶篇02 ——— BDD与TDD的研究实践

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

Apollo oracle适配

前言&#xff1a;公司数据库统一切换为oracle&#xff0c;减少部署mysql&#xff0c;现需要将Apollo的数据库做oracle适配&#xff0c;当前使用版本为Apollo2.0.0&#xff0c;网上找到最新版本的适配oracle的版本也仅为1.4.0&#xff0c;现决定自己适配。 部分参考了官方介绍的…

使用FFMPEG加载外挂或内封字幕小记

ffmpeg版本&#xff1a; FFMEPEG 4.4 继上一篇实现音视频播放器后&#xff0c;将加载字幕的过程和遇到的坑记录如下&#xff1a; 字幕初识 视频字幕分为三种。 内嵌字幕&#xff0c;字幕与视频图像合二为一&#xff0c;成为视频帧的一部分&#xff0c;也叫硬字幕。 内封字…

数据库完整性

完整性概述 数据库的完整性是指数据库的正确性、一致性、相容性 正确性&#xff1a;数据库的数据符合语义约束 一致性&#xff1a;数据间的逻辑关系是正确的&#xff0c;从一个一致性状态转移到另一个一致性状态 相容性&#xff1a;同一事物的两个数据应当是一致的 约束的分类…

Zookeeper(一)

简介 设计模式角度 Zookeeper&#xff1a;是一个基于观察者模式设计的分布式服务管理框架&#xff0c;它负责存储和管理大家都关心的数据&#xff0c;然后接受观察者的注册&#xff0c;一旦这些数据的状态发生变化&#xff0c;Zookeeper就将负责通知已经在Zookeeper上注册的那…

JDBC从入门到精通

1 JDBC概述 在开发中我们使用的是java语言&#xff0c;那么势必要通过java语言操作数据库中的数据。这就是接下来要学习的JDBC。 1.1 JDBC概念 JDBC 就是使用Java语言操作关系型数据库的一套API 全称&#xff1a;( Java DataBase Connectivity ) Java 数据库连接 我们开发的同…

两年时间,成为测试组老大....

看到行业的前辈都分享一些过往的经历来指导我们这些测试人员&#xff0c;我很尊敬我们的行业前辈&#xff0c;没有他们在前面铺路&#xff0c;如今我们这帮年轻的测试人估计还在碰壁或摸着石头过河&#xff0c;结合前辈们的经验&#xff0c;作为年轻的测试人也有自己的一些职场…

IMX6ULL裸机篇之DDR3的时钟配置

一. MMDC 控制器 对于 I.MX6U 来说&#xff0c;有 DDR 内存控制器&#xff0c;否则的话它怎么连接 DDR 呢&#xff1f;MMDC控制器 就是 I.MX6U 的 DDR内存控制器。 MMDC 外设包含一个内核(MMDC_CORE)和 PHY(MMDC_PHY)&#xff0c;内核和 PHY 的功能如下&#xff1a; MMDC 内…

nacos服务端源码集群同步源码分析

nacos集群状态同步源码分析 ServerStatusReporter ServerStatusReporter 是 ServerListManager的内部类 通过Component注解被解析到spring容器中 再通过PostConstruct初始化执行init方法 上边代码启动了一个延时2秒的线程 private class ServerStatusReporter implements Run…

信号时域分析方法

主要参考&#xff1a; 时域分析——有量纲特征值含义一网打尽 信号时域分析方法的理解&#xff08;峰值因子、脉冲因子、裕度因子、峭度因子、波形因子和偏度等&#xff09; 重要笔记如下&#xff1a; 建议跟参考笔记同步看。 有量纲特征值8个——最大值、最小值、峰峰值、均值…

Unable to resolve resource vscode-vfs://github%2B7b2276223a312c22726566223a7

github无法访问&#xff1f;vscode 无法使用github登录同步? 改 hosts 吧 Unable to resolve resource vscode-vfs://github%2B7b2276223a312c22726566223a7一、无法访问 github.com &#xff1f; 想要去 github.com 上拿来主义&#xff0c;结果访问不了&#xff0c;或者 np…

Go基础篇:接口

目录 前言✨一、什么是接口&#xff1f;二、空接口 interface{}1、eface的定义2、需要注意的问题 三、非空接口1、iface的定义2、itab的定义3、itab缓存 前言✨ 前段时间忙着春招面试&#xff0c;现在也算告一段落&#xff0c;找到一家比较心仪的公司实习&#xff0c;开始慢慢回…