C++ 提高编程 第二章 STL之容器

一、STL基本概念

STL(Standard Template Library,标准模板库)
从广义上分为,容器(container)、算法(algorithm)、迭代器(iterator)
容器算法之间通过迭代器进行无缝连接
STL几乎所有的代码都采用了模板类或者模板函数

STL六大组件

容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器

  1. 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据
  2. 算法:各种常用的算法,如sort、find、copy、for_each等
  3. 迭代器:扮演了容器与算法之间的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
  6. 空间配置器:负责空间的配置与管理。

容器

STL容器就是将运用最广泛的一些数据结构实现出来
序列式容器:强调值的排序,序列式容器中每个元素均有固定的位置
关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

算法

质变算法:运算过程中会更改区间的元素内容。如拷贝,替换,删除等
非质变算法:指运算过程中不会更改区间内的元素内容,例如查找,计数,遍历,寻找极值等

迭代器

提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式
每个容器都有自己的专属迭代器,迭代器使用非常类似于指针

分类

种类 功能 支持运算
输入迭代器 对数据的只读访问 只读,支持++、==、!=
输出迭代器 对数据的只写访问 只写、支持++
前向迭代器 读写操作,并能向前推进迭代器 读写,支持++、==、!=
双向迭代器 读写操作,并能向前和向后操作 读写、支持++、--
随机访问迭代器 读写操作,可以以跳跃的方式访问数据,功能最强的迭代器 读写支持++、--、[n]、-n、<、<=、>、>=

常用的容器中迭代器种类分为双向迭代器,和随机访问迭代器

二、vector

容器:vector
算法:for_each
迭代器:vector<int>::iterator

1.构造函数

函数原型
vector<T>v;:采用模板类实现,默认的构造函数
vector(v.begin(),v.end());:将v[begin(),end())区间中的元素拷贝给本身
vector(n,elem);:构造函数将n个elem拷贝给本身
vector(const vector &vec);:拷贝构造函数

2.赋值操作

函数原型
vector& operator=(const vector &vec);:重载等号运算符
assign(beg,end);:将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem):将n个elem拷贝赋值给本身

3.容量和大小

函数原型
empty():判空
capacity():容器的容量
size():元素个数
resize(int num):重新指定容器的长度为num,若容器变长,则以默认值填充新位置;若容器变短,则末尾超出的元素被删除
resize(int num,elem):重新指定容器的长度为num,若容器变长,则以elem填充新位置;若容器变短,则末尾超出的元素被删除

4.插入和删除

函数原型
push_back(ele)
pop_back()
insert(const_iterator pos,ele);:迭代器指向位置pos插入元素ele
insert(const_iterator pos,int count,ele):迭代器指向位置pos插入count个元素ele
erase(const_iterator pos);:删除迭代器指向的元素
erase(const_iterator start,const_iterator end);:删除迭代器从start到end之间的元素
clear():清空容器

vector<int>v;
	for (int i = 1; i <= 10; i++)
	{
		v.push_back(i);
	}
	v.insert(v.begin() + 3, -1);
	v.erase(v.begin() + 3);

5.数据存取

at(int idx):返回索引idx下标的元素
operator[]:同上
front():返回第一个元素
back():返回最后一个元素

6.互换容器

swap(vec):将vec与本身的元素互换
实际用途:巧用swap可以收缩内存空间

vector<int>v;
	for (int i = 1; i <= 10; i++)
	{
		v.push_back(i);
	}
	v.resize(3);
	cout << v.capacity()<<endl;
	vector<int>(v).swap(v);//匿名对象
	cout << v.capacity() << endl;

7.预留空间

减少vector在动态扩展容量时的扩展次数
函数原型
reserve(int len);:容器预留len个元素空间,预留位置不初始化,元素不可访问

vector<int>v;
	int cnt = 0;//统计开辟次数
	int* p = NULL;
	for (int i = 1; i <= 1e5; i++)
	{
		v.push_back(i);
		if (p != &v[0])
		{
			p = &v[0];
			cnt++;
		}
	}
	cout << cnt << endl;

若不进行预留空间,向容器插入1e5个元素大约需要30次扩容操作
若提前预留空间,则只需1次扩容操作

三种遍历方式

void sum(int x)
{
	s += x;
}
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		cout << *it << " ";
		it++;
	}
	puts("");
	for (auto it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	for_each(v.begin(), v.end(), sum);
	puts("");
	cout << s << endl;

三、string

string和char*的区别

char是一个指针
string是一个类,类内部封装了char
,管理这个字符串,是一个char*的容器

1.构造函数

函数原型
string();:创造一个空的字符串
string(const char* s);:使用字符串s初始化
string(const string& str);:使用一个string对象初始化另一个string对象
string(int n,char c);:使用n个字符c初始化

2.赋值操作

函数原型
string& operator(const char* s);:char字符串s赋值给当前字符串
string& operator=(const string& s);:字符串s赋值给当前字符串
string& operator=(char c);:字符赋值给当前字符串
string& assign(const char *s);:char
字符串s赋值给当前字符串
string& assign(const char *s,int n);:char*字符串s的前n个字符赋值给当前字符串
string& assign(const string& s);:把字符串s赋给当前字符串
string& assign(int n,char c);:用n个字符c赋值给字符串

3.字符串拼接

实现在字符串末尾拼接字符串
函数原型
string& operator+=(const char* str):重载+=操作符
string& operator+=(const char c):重载+=操作符
string& operator+=(const string& str):重载+=操作符
string& append(const char* s):把字符串s连接到当前字符串末尾
string& append(const char *s,int n):把字符串s的前n个字符连接到末尾
string& append(const string &s):同operator+=(const string& str)
string& append(const string &s,int pos,int n):字符串s的第pos个字符开始的n个字符接到末尾

4.字符串查找与替换

函数原型
int find(const string& str,int pos = 0) const:从pos开始查找str第一次出现位置
int find(const char* s,int pos = 0) const:从pos开始查找s第一次出现位置
int find(const char* s,int pos,int n) const:从pos开始查找s的前n个字符的第一次位置
int find(const char c,int pos = 0) const:从pos开始查找字符c的第一次出现位置
int rfind(const string& str,int pos = npos) const:从pos开始查找str的最后一次出现位置
int rfind(const char* s,int pos = npos) const:从pos开始查找s的最后一次出现位置
int rfind(const char* s,int pos,int n)const:从pos开始查找s的前n个字符最后一次位置
int rfind(const char c,int pos = 0) const:从pos开始查找字符c的最后一次出现位置
string& replace(int pos,int n,const string& str):替换从pos开始n个字符为str
string& replace(int pos,int n,const char* s):替换从pos开始n个字符为s

5.字符串比较

字符串比较是按ASCII码进行对比
=返回0 >返回1 <返回-1
函数原型
int compare(const string &s) const:与字符串s比较
int compare(const char* s) const:与字符串s比较

6.string字符存取

  1. 通过[]访问单个字符
  2. 通过at()访问单个字符
    函数原型
    char& operator[](int n):通过[]访问单个字符
    char& at(int n):通过at()访问单个字符

7.字符串插入和删除

函数原型
string& insert(int pos,const char* s);:在pos位置插入字符串
string& insert(int pos,const string& str):在pos位置插入字符串
string& insert(int pos,int n,char c):在pos位置插入n个字符c
string& erase(int pos,int n = npos):删除从Pos开始的n个字符

8.子串获取

函数原型
string& substr(int pos = 0,int n = npos);:返回由pos开始的n个字符组成的字符串
实战演示:从邮箱地址提取出用户名

	string email = "yxc@qq.com";
	int pos = email.find('@');
	string name = email.substr(0, pos);
	cout << name << endl;
	return 0;

四、deque容器

1.基本概念

功能

双端数组,可以实现对两端进行插入删除操作
image

与vector的区别

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度比vector快
  • vector访问元素时的速度比deque快,这和内部实现有关

内部工作原理

deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque像一片连续的内存空间
image
deque迭代器支持随机访问

2.构造函数

函数原型
deque<T>deq:默认构造形式
deque(beg,end);:构造函数将[beg,end)区间中的元素拷贝给本身
deque(n,elem);:构造函数将n个elem拷贝给本身
deque(const deque &deq);:拷贝构造函数

3.赋值操作

函数原型
deque& operator(const deque &deq);:重载等号操作符
assign(beg,end):将[beg,end)区间中的数据拷贝赋值给本身
assign(n,elem):将n个elem拷贝赋值给本身

4.大小操作

deque没有容量的概念
empty()
size()
resize(num)
resize(num,elem)

5.插入和删除

函数原型
两端插入操作:
push_back(elem):在容器尾部添加一个数据
push_front:在容器头部插入一个数据
pop_back():删除容器最后一个数据
pop_front():删除容器第一个数据
指定位置操作:
insert(iterator pos,elem):pos位置插入一个元素elem,返回新数据的位置
insert(iterator pos,n,elem):pos位置插入n个elem数据,无返回值
insert(pos,beg,end):pos位置插入[beg,end)区间的位置,无返回值
clear():清空
erase(beg,end):删除区间的数据,返回下一个数据的位置
erase(pos):删除pos位置的数据,返回下一个数据的位置

6.数据存取

函数原型
at(int idx):返回索引idx所指的数据
operator[]:同上
front():容器第一个元素
back():容器最后一个元素

7.排序

sort(iterator beg,iterator end)

五、stack容器

构造函数

stack<T> stk
stack(const stack& stk):拷贝构造函数

赋值操作

stack& operator=(const stack &stk)

数据存取

push(elem)
pop()
top()
empty()
size()

六、queue容器

构造函数

queue<T> que
queue(const queue& que):拷贝构造函数

赋值操作

queue& operator=(const queue &queue)

数据存取

push(elem)
pop()
front()
back()
empty()
size()

七、list容器

list是一个双向循环链表
image
由于链表的存储方式并不是连续的内存空间,因此list中的迭代器只支持前移和后移,属于双向迭代器
list的优点

  • 采用动态存储分配,不会浪费空间
  • 插入和删除效率高,无需移动大量元素
  • 插入和删除操作都不会使原有list迭代器的失效,这在vector是不成立的

list的缺点

  • 空间(指针域)和时间(遍历)额外耗费较大

构造函数

类似于vector

赋值和交换

类似于vector

大小操作

类似于deque

插入和删除

两端插入操作:
push_back(elem):在容器尾部添加一个数据
push_front:在容器头部插入一个数据
pop_back():删除容器最后一个数据
pop_front():删除容器第一个数据
指定位置操作:
insert(iterator pos,elem):pos位置插入一个元素elem,返回新数据的位置
insert(iterator pos,n,elem):pos位置插入n个elem数据,无返回值
insert(pos,beg,end):pos位置插入[beg,end)区间的位置,无返回值
clear():清空
erase(beg,end):删除区间的数据,返回下一个数据的位置
erase(pos):删除pos位置的数据,返回下一个数据的位置
remove(elem):删除容器中所有与elem值匹配的元素

数据存取

front()
back()
不可以用[]/at()的方式随机访问

反转和排序

reverse()
sort()

	list<double>l;
	for (int i = 10; i >= 0; i--)
	{
		l.push_back(i);
	}
	//sort(l.begin(), l.end());//错误:所有不支持随机访问迭代器的容器,不能使用标准算法
	l.sort();//默认升序,可传入bool函数改变排序规则
	l.reverse();
	for (auto x : l)
	{
		cout << x << " ";
	}
	return 0;

七、set/multiset容器

简介

所有元素都会在插入时自动被排序
set/multiset容器属于关联式容器,底层结构是用二叉树实现的

set和multiset的区别

set容器不允许容器中有重复的元素,multiset允许容器中有重复的元素
set插入数据的同时会返回插入结果,表示插入是否成功;multiset不会检测,因为可以插入重复元素

1.构造和赋值

set<T> st:默认构造函数
set(const set &st):拷贝构造函数
set& operator=(const set &st):重载等号赋值运算符

2.大小和交换

size():返回容器中元素的数目
empty():判空
swap(st):交换两个集合容器

3.插入和删除

insert(elem):插入元素,set返回pair<iterator,bool>表示是否插入成功
erase(pos):删除迭代器指定的元素,返回下一个元素的迭代器
erase(beg,end):删除区间[beg,end)的所有元素,返回下一个元素的迭代器
erase(elem):删除容器中值为elem的元素
clear():清空

	set<int>s;
	for (int i = 0; i < 10; i++)
	{
		cout << s.insert(i).second << endl;
	}
	cout << s.insert(9).second << endl;
	return 0;

4.查找和统计

find(key):查找key是否存在,返回key的迭代器,若不存在,返回set.end()
count(key):统计key的元素个数

5.排序规则的改变

利用仿函数,可以改变排序规则

class Mycom
{
public:
	bool operator()(int a,int b) const //此处必须定义为常函数,否则报错
	{
		return a > b;
	}
};
int main()
{
	set<int,Mycom>s;
	for (int i = 0; i < 10; i++)
	{
		s.insert(i);
	}
	for (auto x : s)
	{
		cout << x << " ";
	}
	return 0;
}

自定义数据类型

重写比较符号即可

八、map/multimap容器

1.简介

map中所有元素都是pair
pair第一个元素是key键值,起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序

本质

map/multimap属于关联式容器,底层结构是用二叉树实现。

优点

可以根据key值快速找到value值

map与multimap的区别

map不允许容器中有重复key值元素
multimap允许容器中有重复key值元素

2.构造和赋值

函数原型
map<T1,T2>mp:默认构造函数
map(const map &mp):拷贝构造函数
map& operator=(const map &mp):重载等号运算符

3.大小和交换

函数原型
size():返回容器中键值对个数
empty():判空
swap(mp):交换

4.插入和删除

insert(elem)
erase(iterator pos):删除迭代器所指的元素,返回下一个元素的迭代器
erase(beg,end):删除[beg,end)的所有元素,返回下一个元素的迭代器
erase(key):删除容器中值为key的元素
clear():清空

5.查找和统计

find(key):查找key是否存在,若是,返回该键元素的迭代器,否则返回set.end()
count(key):统计key元素的个数

6.改变排序规则

利用仿函数,可以改变排序规则

class com
{
public:
    bool operator()(int a, int b) const
    {
        return a > b;
    }
};
int main() {
    map<int, int,com>mp;
    mp.insert({ 3,2 });
    mp.insert({ 1,0 });
    for (auto& [key, value] : mp)
    {
        cout << key << ":" << value << endl;
    }
    return 0;
}

自定义数据类型

重写比较符号即可

posted @   安河桥北i  阅读(34)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示