STL复习-序列式容器和容器适配器部分

news2024/11/23 21:12:28

STL复习

1. 常见的容器

如何介绍这些容器,分别从常见接口,迭代器类型,底层实现

在这里插入图片描述


序列式容器

string

string严格来说不属于stl,它是属于C++标准库

**底层实现:**string本质是char类型的顺序表,因为不同编译器下的具体实现不同,这里只提供一个我的简答框架

class string
{
public:
    typedef char* iterator;
    typedef const char* const_iterator; 
private:
    char* _str; 		// 堆上开辟的顺序表空间
    size_t _size; 		// 有效字符个数
    size_t _capacity; 	// _str的空间大小

    static const size_t npos; // 最大字符串大小
};

const size_t string::npos = -1;

实际在VS系列下,string是包含一个指针,一个联合体(一个数组和一个指针,如果字符串长度小于16就会使用提前开辟好的数组,如果大于16字节再去堆上申请空间使用指针指向),size和capacity

在g++下string只包含一个指针,指向堆上的一段空间,包含一个指针指向为字符串开辟的空间,引用计数,size和capacity,这个引用计数可以让赋值拷贝这些对象只需要浅拷贝增加引用计数即可

迭代器类型: 随机访问迭代器

常用接口:

函数名功能
size / length返回字符串有效字符个数
clear / reserver / resize清空有效字符 / 预留空间 / 将有效字符的个数该成n个,多出的空间用字符c填充
operator[]返回pos位置的字符
push_back / append / operator+=在字符串后尾插字符c / 字符串 / 字符串
c_str返回C格式字符串
find / rfind + npos从字符串pos位置开始往(后 / 前)找字符c,返回该字符在字符串中的位置,没有返回npos
substr在str中从pos位置开始,截取n个字符,然后将其返回
operator<< / operator>> / getline从iostream中写 / 读 (空格/回车),getline可自定义分隔符
// 剪去报头 length\r\n*******\r\nlength\r
std::string Decode(std::string& str)
{
	// 判断条件是否满足,不满足返回空串
	size_t pos = str.find(SEP);
	if (pos == std::string::npos)
		return "";
	size_t lenght = atoi(str.substr(0, pos).c_str());

	if (lenght > str.size() - pos - SEP_LEN * 2)
		return "";

	str.erase(0, pos + SEP_LEN);
	std::string ret = str.substr(0, lenght);
	str.erase(0, lenght + SEP_LEN);
	return ret;
}
// 简单实现

class string
{
	friend std::ostream& operator<<(std::ostream& out, const string& str);
	//friend istream& operator>>(istream& in, const string& str);
public:
	string() : _str(nullptr), _size(0), _capacity(0)
	{}

	string(const char* str) :string()
	{
		size_t sz = strlen(str);
		_str = new char[sz + 1] {0};
		strcpy(_str, str);
		_size = sz;
		_capacity = sz + 1;
	}

	string(const string& str)
		: string(str.c_str())
	{}

	string& operator==(string str)
	{
		if(this != &str)
			swap(str);
		return *this;
	}

	~string()
	{
		delete[] _str;
	}

	string& operator+=(const string& str)
	{
		size_t sz = str._size;
		if (sz + _size + 1 > _capacity)
		{
			reserve(sz > _capacity ? _capacity + sz : 2 * _capacity);
		}
		strcpy(_str + _size, str.c_str());
		_size += sz;
		return *this;
	}

	char& operator[](size_t pos)
	{
		return *(_str + pos);
	}

	void reserve(size_t newsize)
	{
		if (newsize > _capacity)
		{
			char* str = new char[newsize] {0};
			strcpy(str, _str);
			delete[] _str;
			_str = str;
			_capacity = newsize;
		}
	}

	void swap(string& str)
	{
		std::swap(_str, str._str);
		std::swap(_size, str._size);
		std::swap(_capacity, str._capacity);
	}

	const char* c_str() const 
	{
		return _str;
	}

	size_t size() const
	{
		return _size;
	}

private:
	char* _str;
	size_t _size;
	size_t _capacity;

	static size_t npos;
};

size_t string::npos = -1;

std::ostream& operator << (std::ostream& out, const string& d)
{
	out << d.c_str();
	return out;
}

vector

底层实现:

vector本质是模板类型的顺序表,对于底层数据结构是顺序表类型的容器在每次增容都需要极大的代价,需要开辟更大空间,并对之前的数据进行拷贝,,vs下capacity是按1.5倍增长的,g++是按2倍增长的

reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解string和vector增容的代价缺陷问题

与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list 统一的迭代器和引用更好

template<class T>
class vector
{
public:
	typedef T* iterator;
	typedef const T* const_iterator;
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _end_of_storage = nullptr;
};

迭代器类型: 随机访问迭代器

常用接口:

函数名功能
size / capacity / empty数据个数 / 容量大小 / 是否为空
reserve / resize / clear改变vector的capacity / size / 清空
push_back / pop_back尾插 / 尾删
find + v.end() (不常用)查找,注意这个是算法模块实现,不是vector的成员接口
insert / erase在pos位置插入删除
emplace / empalce_back构造和插入
operator[]返回pos位置的对象

深拷贝问题,在拷贝构造时如果对像是自定义类型,就会调用自定义类型的拷贝构造

// 简单实现

template<class T>
class vector
{
	typedef T* iterator;
public:
	iterator begain() { return _start; }
	iterator end() { return _finish; }

	vector():_start(nullptr), _finish(nullptr), _end(nullptr)
	{}

	vector(size_t n, T val = T()) :vector()
	{
		reserve(n);
		for (size_t i = 0; i < n; ++i)
		{
			_start[i] = val;
		}
		_finish = _start + n;
	}

	vector(const vector& v) :vector()
	{
		reserve(v.size());
		_finish = _start + v.size();
		for (size_t i = 0; i < v.size(); ++i)
		{
			operator[](i) = v[i];
		}
	}

	~vector(){delete[] _start; }

	void push_back(T val) {insert(_finish, val); }

	void pop_back() {erase(_finish - 1); }

	void insert(iterator pos, T val = T())
	{
		assert(pos >= _start && pos <= _finish);
		if (_finish + 1 > _end)
		{
			size_t pos_size = pos - _start;
			reserve(size() + 1);
			pos = _start + pos_size;
		}
		iterator cur = _finish;
		while (cur != pos)
		{
			*cur = *(cur - 1);
			--cur;
		}
		*pos = val;
		_finish += 1;
	}

	iterator erase(iterator pos)
	{
		assert(pos >= _start && pos < _finish);

		iterator cur = pos;
		while (cur != _finish)
		{
			*cur = *(cur + 1);
			++cur;
		}
		_finish -= 1;
		return pos;
	}

	T& operator[](size_t pos)
	{
		assert(pos < size());
		return _start[pos];
	}

	const T& operator[](size_t pos) const 
	{
		assert(pos < size());
		return _start[pos];
	}

	void reserve(size_t newcapacity)
	{
		size_t capacity = _end - _start;
		size_t size = _finish - _start;
		if (capacity < newcapacity)
		{
			newcapacity = newcapacity < 2 * capacity ? 2 * capacity : newcapacity;
			T* newv = new T[newcapacity];
			if(_start)
				memcpy(newv, _start, size * sizeof(T));
			delete[] _start;
			_start = newv;
			_end = newv + newcapacity;
			_finish = _start + size;
		}
	}

	size_t size() const { return _finish - _start; }
	size_t capacity() const { return _end - _start; }


private:
	iterator _start;
	iterator _finish;
	iterator _end;
};

list

底层实现:

list的底层是带头双向循环链表结构,链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素

与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好

最大的缺陷是不支持任意位置的随机访问,list还需要一些额外的空间,以保存每个节点的相关联信息

template<class T>
struct list_node
{
	typedef list_node<T> node;
	node* _next;
	node* _prev;
	T _date;
};

template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef list_node<T> node;
	typedef __list_iterator<T, Ref, Ptr> self;
	node* _it;
};

template<class T>
class list
{
	typedef list_node<T> node;
public:
	typedef __list_iterator<T, T&, T*> iterator;
	typedef __list_iterator<T, const T&, const T*> const_iterator;
private:
	node* _head;
};

迭代器类型: 双向访问迭代器

常用接口:

函数名功能
empty / size判空 / 返回节点个数
front / back返回最后 / 最前节点值的引用
push_front / pop_front头插 / 头删
push_front / pop_back尾插 / 尾删
insert / erase插入 / 删除
emplace / emplace_front / emplace_back构造 + 插入
sort链表的排序
template<class T>
struct list_node
{
	list_node* _prev;
	list_node* _next;
	T _val;

	list_node(T val = T()) :_prev(nullptr), _next(nullptr), _val(val)
	{}
};

template<class T, class Ref, class Ptr>
struct list_iterator
{
	typedef list_node<T> node;
	typedef list_iterator<T, Ref, Ptr> Self;
	node* _it;

	list_iterator(node* it) :_it(it) 
	{}

	list_iterator(const list_iterator<T, T&, T*>& iterator) :_it(iterator._it)
	{}

	Ptr operator->() {return &(_it->_val);}
	Ref operator*() {return _it->_val; }
	Self operator++() {return _it = _it->_next; }
	Self operator--() {return _it = _it->_prev; }
	bool operator!=(const Self& iterator) const {return _it != iterator._it; }
};

template<class T>
struct list
{
	typedef list_node<T> node;
	typedef list_iterator<T, T&, T*> iterator;
	typedef list_iterator<T, const T&, const T*> const_iterator;
public:
	iterator begin() { return iterator(_head->_next); }
	const_iterator begin() const { return const_iterator(_head->_next); }
	iterator end() { return iterator(_head); }
	const_iterator end() const { return const_iterator(_head); }

	list():_head(new node)
	{
		_head->_next = _head;
		_head->_prev = _head;
	}

	~list()
	{
		while (!empty())
		{
			pop_front();
		}
		delete _head;
	}

	void insert(iterator pos, const T& val)
	{
		node* next = pos._it;
		node* prev = next->_prev;
		node* newnode = new node(val);
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = next;
		next->_prev = newnode;

		_size++;
	}

	void erase(iterator pos)
	{
		assert(pos._it != _head);
		node* next = pos._it->_next;
		node* prev = pos._it->_prev;
		delete pos._it;
		next->_prev = prev;
		prev->_next = next;

		_size--;
	}

	void push_back(const T& val) { insert(end(), val);  }
	void push_front(const T& val) { insert(begin(), val); }

	void pop_back(){erase(_head->_prev); }
	void pop_front(){erase(begin()); }

	bool empty() const
	{
		return _size == 0;
	}
private:
	list_node<T>* _head;
	size_t _size = 0;
};

deque

底层实现:

双端队列由一个指针数组和多个相同长度的定长数组组成

在这里插入图片描述

双端队列,头插尾插,头删尾删效率高,这点比vector要好,但是不如list在任意位置都可以O(1)时间复杂度插入删除(增删)

通过迭代器支持operator[]随机访问,在随机访问上比list要好,但是不如vector(查改)

因为头尾的插入删除效率高,所以他就非常适合stack和queue

迭代器类型: 随机访问迭代器

常用接口:

函数名功能
size / resize / empty大小 / 改变大小 / 判空
operator[] / front / back读数据
push_front / pop_front头插 / 头删
push_front / pop_back尾插 / 尾删
insert / erase插入 / 删除

关联式容器

map/set
unordered_map/unordered_set
bitset

容器适配器

stack

stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行元素的插入与提取操作。

stack是作为容器适配器被实现的,容器适配器即是对特定类封装作为其底层的容器,并提供一组特定的成员函数来访问其元素,将特定类作为其底层的,元素特定容器的尾部(即栈顶)被压入和弹出。

stack的底层容器可以是任何标准的容器类模板或者一些其他特定的容器类,这些容器类应该支持以下操作:

  • empty:判空操作
  • back:获取尾部元素操作
  • push_back:尾部插入元素操作
  • pop_back:尾部删除元素操作

标准容器vector、deque、list均符合这些需求,默认情况下,如果没有为stack指定特定的底层容器, 默认情况下使用deque

在这里插入图片描述

函数名功能
stack()构造空的栈
empty检测stack是否为空
size返回stack中元素的个数
top返回栈顶元素的引用
push将元素val压入stack中
pop将stack中尾部的元素弹出

queue

队列是一种容器适配器,专门用于在FIFO上下文(先进先出)中操作,其中从容器一端插入元素,另一端提取元素。

队列作为容器适配器实现,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从队尾入队列,从队头出队列。

底层容器可以是标准容器类模板之一,也可以是其他专门设计的容器类。该底层容器应至少支持以下操作:

  • empty:检测队列是否为空
  • size:返回队列中有效元素的个数
  • front:返回队头元素的引用
  • back:返回队尾元素的引用
  • push_back:在队列尾部入队列
  • pop_front:在队列头部出队列

标准容器类deque和list满足了这些要求。默认情况下,如果没有为queue实例化指定容器类,则使用标准容器deque

在这里插入图片描述

函数名功能
queue()构造空的队列
empty检测queue是否为空
size返回queue中元素的个数
front返回queue顶元素的引用
back返回queue尾元素的引用
push将元素val压入queue中
pop将queue中尾部的元素弹出

priority_queue

优先队列是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的

类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)

优先队列被实现为容器适配器,容器适配器即将特定容器类封装作为其底层容器类,queue提供一组特定的成员函数来访问其元素。元素从特定容器的“尾部”弹出,其称为优先队列的顶部。

底层容器可以是任何标准容器类模板,也可以是其他特定设计的容器类。容器应该可以通过随机访问迭代器访问,并支持以下操作:

  • empty():检测容器是否为空
  • size():返回容器中有效元素个数
  • front():返回容器中第一个元素的引用
  • push_back():在容器尾部插入元素
  • pop_back():删除容器尾部元素

标准容器类vector和deque满足这些需求。默认情况下,如果没有为特定的priority_queue类实例化指 定容器类,则使用vector(模板参数第二位)

需要支持随机访问迭代器,以便始终在内部保持堆结构。容器适配器通过在需要时自动调用算法函数 make_heap、push_heap和pop_heap 来自动完成此操作

默认情况下priority_queue是大堆,使用less(模板参数第三位)

在这里插入图片描述

一句话,少的点挪动的次数多 时间复杂度约等于 T(N)

在这里插入图片描述

多的点挪动次数多 时间复杂度 T(N*LogN)

若是pop n次用来排序,时间复杂度 T(N*LogN)

优先级队列,使用迭代器建堆,比遍历push,要好

函数名功能
priority_queue()构造一个空的优先级队列
priority_queue(first,last)根据迭代器区间构造优先级队列
empty()检测优先级队列是否为空
top()返回优先级队列中最大(最小元素),即堆顶元素
size()返回优先级队列里的元素个数
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

杂度 T(N*LogN)

优先级队列,使用迭代器建堆,比遍历push,要好

函数名功能
priority_queue()构造一个空的优先级队列
priority_queue(first,last)根据迭代器区间构造优先级队列
empty()检测优先级队列是否为空
top()返回优先级队列中最大(最小元素),即堆顶元素
size()返回优先级队列里的元素个数
push(x)在优先级队列中插入元素x
pop()删除优先级队列中最大(最小)元素,即堆顶元素

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

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

相关文章

windows系统本地端口被占用的问题

第一步&#xff1a;查找所有运行的端口 按住“WindowsR”组合键&#xff0c;打开命令窗口&#xff0c;输入【cmd】命令&#xff0c;回车。在弹出的窗口中输入 命令【netstat -ano】&#xff0c;再按一下回车键 Win系统端口被占用-查找所有运行的端口 第二步&#xff1a;查看…

基于Android Studio电影购票系统

目录 项目介绍 图片展示 运行环境 获取方式 项目介绍 主要实为了方便用户随时随地进行电影购票。在配色方面选择了一些富有电影元素的颜色。主要能够实现的功能与流程为&#xff1a; 1.用户首先需要注册用户名填写密码。 2.用户可以用之前注册的用户名和密码进行登录。 3.登…

ComfyUI+MuseV+MuseTalk图片数字人

电脑配置 GPU12G&#xff0c;如果自己电脑配置不够&#xff0c;选择云gpu&#xff0c;我就是用的这个&#xff0c;自己电脑太老配置跟不上 环境&#xff1a; Python 3.11.8 torch 2.2.1 cuda_12.1 资源提供&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1_idZbF…

跟《经济学人》学英文:2024年07月06日这期:Japan’s mind-bending bento-box economics

Japan’s mind-bending bento-box economics 日本令人费解的便当盒经济学 mind-bending&#xff1a;使人知觉不正常的&#xff1b;使人精神恍惚的&#xff1b;使人精神错乱的 bento&#xff1a;美 [bentoʊ] 盒饭&#xff1b;盒饭或饭盒 The paradox of red-hot labour mar…

浅尝Apache Mesos

文章目录 1. Mesos是什么2. 共享集群3. Apache Mesos3.1 Mesos主节点3.2 Mesos代理3.3 Mesos框架 4. 资源管理4.1 资源提供4.2 资源角色4.3 资源预留4.4 资源权重与配额 5. 实现框架5.1 框架主类5.3 实现执行器 6. 小结参考 1. Mesos是什么 Mesos是什么&#xff0c;Mesos是一个…

昇思25天学习打卡营第14天|Pix2Pix实现图像转换

Pix2Pix是基于条件生成对抗网络&#xff08;cGAN, Condition Generative Adversarial Networks &#xff09;实现的一种深度学习图像转换模型&#xff0c;该模型是由Phillip Isola等作者在2017年CVPR上提出的&#xff0c;可以实现语义/标签到真实图片、灰度图到彩色图、航空图到…

【js基础巩固】深入理解作用域与作用域链

作用域链 先看一段代码&#xff0c;下面代码输出的结果是什么&#xff1f; function bar() {console.log(myName) } function foo() {var myName "极客邦"bar() } var myName "极客时间" foo()当执行到 console.log(myName) 这句代码的时候&#xff0c…

acwing 291.蒙德里安的梦想

解法&#xff1a; 核心&#xff1a;先放横着的&#xff0c;再放竖着的。 总方案数&#xff0c;等于只放横着的小方块的合法方案数。 如何判断当前方案是否合法&#xff1f;所有剩余位置&#xff0c;能否填充满竖着的小方块。 即按列来看&#xff0c;每一列内部所有连续的空着的…

第六十八回 东平府误陷九纹龙 宋公明义释双枪将-文心大模型ernie-speed免费使用方法

宋江和卢俊义抓阄儿&#xff0c;宋江打东平府&#xff0c;卢俊义打东昌府&#xff0c;谁先打下谁做梁山泊主。宋江带领林冲、花荣、刘唐等二十八人&#xff0c;卢俊义带领吴用、公孙胜、关胜等二十八人。 宋江等人到了东平府外安山镇&#xff0c;郁保四和王定六自告奋勇去下战…

搭建NEMU与QEMU的DiffTest环境(动态库方式)

搭建NEMU与QEMU的DiffTest环境&#xff08;动态库方式&#xff09; 1 DiffTest原理简述2 编译NEMU3 编译qemu-dl-difftest3.1 修改NEMU/scripts/isa.mk3.2 修改NEMU/tools/qemu-dl-diff/src/diff-test.c3.3 修改NEMU/scripts/build.mk3.4 让qemu-dl-difftest带调试信息3.5 编译…

昇思12天

FCN图像语义分割 1. 主题和背景 FCN是由UC Berkeley的Jonathan Long等人于2015年提出的&#xff0c;用于实现图像的像素级预测。 2. 语义分割的定义和重要性 语义分割是图像处理和机器视觉中的关键技术&#xff0c;旨在对图像中的每个像素进行分类。它在很多领域有重要应用…

【问题解决】 pyocd 报错 No USB backend found 的解决方法

pyocd 报错 No USB backend found 的解决方法 本文记录了我在Windows 10系统上遇到的pyocd命令执行报错——No USB backend found 的分析过程和解决方法。遇到类似问题的朋友可以直接参考最后的解决方法&#xff0c;向了解问题发送原因的可以查看原因分析部分。 文章目录 pyoc…

ChatGPT-4o大语言模型优化、本地私有化部署、从0-1搭建、智能体构建等高级进阶

目录 第一章 ChatGPT-4o使用进阶 第二章 大语言模型原理详解 第三章 大语言模型优化 第四章 开源大语言模型及本地部署 第五章 从0到1搭建第一个大语言模型 第六章 智能体&#xff08;Agent&#xff09;构建 第七章 大语言模型发展趋势 第八章 总结与答疑讨论 更多应用…

端口被占用,使用小黑框查杀

netstat -ano &#xff08;查看目前所有被占的端口&#xff09; netstat -ano|findstr " 8080" 查一下目前被占用的端口号 &#xff0c;目前我要查的端口号是&#xff1a;8080&#xff0c;注意 后面打8080的时候&#xff0c;要有空格&#xff0c;要不然报错 **task…

无人机便携式侦测干扰设备(定全向)技术详解

无人机便携式侦测干扰设备&#xff08;定全向&#xff09;是一种专门针对无人机进行侦测和干扰的设备。它具备定向和全向两种工作模式&#xff0c;能够覆盖较宽的频率范围&#xff0c;有效侦测并干扰无人机与遥控器之间的通信信号&#xff0c;从而达到控制或驱离无人机的目的。…

CRT工具

CRT工具 传输位置设置 打开SFTP alt p 命令 ls&#xff1a;远程机器当前目录内容 lls&#xff1a;传输位置文件的目录内容 pwd&#xff1a;远程机器的当前位置 lpwd&#xff1a;传输位置的位置 get 文件&#xff1a;ftp传输文件 get -r 文件夹&#xff1a;ftp传输文件…

AI中药处方模型构建与案例

在中医领域,人工智能(AI)可以生成各种指令来辅助诊断、治疗和研究。 1. 诊断辅助指令: 根据患者的症状和体征,自动分析并生成可能的中医证候诊断建议。利用中医望闻问切四诊信息,智能识别关键症状,提供对应的中医辨证思路。2. 治疗建议指令: 根据辨证结果,自动推荐相应…

2024吉他手的超级助手Guitar Pro8中文版本发布啦!

亲爱的音乐爱好者们&#xff0c;今天我要来和你们分享一款让我彻底沉迷的软件—Guitar Pro。如果你是一名热爱吉他的朋友&#xff0c;那么接下来的内容你可要瞪大眼睛仔细看哦&#xff01;&#x1f440;&#x1f3b6; Guitar Pro免费绿色永久安装包下载&#xff1a;&#xff0…

90元搭建渗透/攻防利器盒子!【硬件篇】

前言 以下内容请自行思考后进行实践。 使用场景 在某些情况下开软件进行IP代理很麻烦&#xff0c;并不能实现真正全局&#xff0c;而且还老容易忘记&#xff0c;那么为了在实景工作中&#xff0c;防止蓝队猴子封IP&#xff0c;此文正现。 正文 先说一下实验效果&#xff1…

鸿蒙开发管理:【@ohos.account.distributedAccount (分布式帐号管理)】

分布式帐号管理 本模块提供管理分布式帐号的一些基础功能&#xff0c;主要包括查询和更新帐号登录状态。 说明&#xff1a; 本模块首批接口从API version 7开始支持。后续版本的新增接口&#xff0c;采用上角标单独标记接口的起始版本。开发前请熟悉鸿蒙开发指导文档&#xff…