C++STL的容器的底层实现详解

顺序容器

在这里插入图片描述

vector(向量容器)

  • 特点
  • 内存可2倍增长的动态数组
  • 数据结构:线性连续空间
  • 维护三个迭代器:start、finish、end_of_storage
    在这里插入图片描述

注意:动态增加大小,并不是在原空间之后接续新空间(因为无法保证之后尚有可供分配的空间),而是每次再分配原大小两倍的内存空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。

  • 成员函数
函数作用
assign(first,last)用迭代器first,last所指定的元素取代容器中的元素
assign(num,val)用val的num份副本取代2元素
at(n)等价于[]运算符,返回容器中位置n的元素
front()返回容器中第一个元素的引用
back()返回容器中最后一个元素的引用
begin()返回容器中第一个元素的迭代器
end()返回容器中最后一个元素的下一个迭代器(不可解引用)
max_size()返回容器类型的最大容量(2^30-1=0x3FFFFFFF)
capacity()返回容器当前开辟的空间大小
size()返回容器中现有元素的个数(<=capacity)
clear()清空容器中所有元素
empty()如果容器为空,返回真
erase(start,end)删除迭代器[start ,end)所指定范围内的元素
erase(i)删除迭代器i所指向的元素,返回指向删除元素下一位置的迭代器
insert(i,x)把x插入到迭代器i所指定的位置之前
insert(i,n,x)把x的n份副本插入到迭代器i所指定的位置之前
insert(i,start,end)在i位置插入在[start,end)区间的数据,无返回值。
push_back(x)尾插
op_back()删除容器最后一个元素
rbegin()返回一个反向迭代器,该迭代器指向的元素越过了容器中的最后一个元素
rend()返回一个反向迭代器,该迭代器指向容器中第一个元素
reverse()反转元素顺序
resize(n,x)把容器的大小改为n,新元素的初值赋为x
swap(vector1)交换2个容器的内容

deque(双端队列)

  • 特点

  • 数据结构:一种双向开口的存储空间分段连续的数据结构,每段数据空间内部是连续的,而每段数据空间之间则不一定连续

  • deque与vector的最大差异 :
    1.允许常数时间内对起首端进行元素的插入和删除
    2.deque没有所谓的容量概念,因为它是以分段连续空间组合而成,随时可以增加一段新的空间并连接起来
    在这里插入图片描述
    上图deque有四部分数据空间,这些空间都是程序运行过程中在堆上动态分配的。
    中控器(map)保存着一组指针,每个指针指向一段数据空间的起始位置,通过中控器可以找到所有的数据空间。如果中控器的数据空间满了,会重新申请一块更大的空间,并将中控器的所有指针拷贝到新空间中。

  • deque采用一块所谓的map(注意,不是STL的map容器)作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体
    在这里插入图片描述

  • deque 的迭代器

deque的迭代器由四个属性组成,这四个属性是我们随机访问一个容器内元素的必要成分。

1.node用于指向的“第一维”的某个位置,该位置对应要访问元素的入口地址
2.剩下的三个属性则用于“第二维”,[first,last)规定了访问本区间元素的边界条件,而curr则指向实际我们要访问的元素。

在这里插入图片描述
1.start迭代器:绑定到第一个有效的map结点和该结点对应的缓冲区。

2.finish迭代器:绑定到最后一个有效的map结点和该结点对应的缓冲区。

举例:
假设deque中存储20个元素,每个缓冲区大小为8,则需要20/8=3个缓冲区,所以map中会运用3个节点。deque的begin()和end()始终会返回map中节点的头和尾,名为start和finish,其中,start的cur指向第一个缓冲区中的首元素,finish的cur指向最后一个缓冲区的最后一个元素的下一位置。
在这里插入图片描述

  • deque缓冲区扩充

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意:从上图来看,如果无穷尽的向deque中存储数据,map所含有的缓冲区也会填满的。也就是说,当map的前端节点或后端节点备用空间不足时,map也需要重新配置,依然是经过三个步骤:配置更大的、拷贝原来的、释放原空间。

  • 成员函数
函数作用
c.assign(beg,end)将[beg; end)区间中的数据赋值给c。
c.assign(n,elem)将n个elem的拷贝赋值给c。
c.at(idx)传回索引idx所指的数据,如果idx越界,抛出out_of_range。
c.back()返回最后一个数据,不检查这个数据是否存在。
c.begin()返回迭代器的第一个数据。
c.clear()移除容器中所有数据。
deque创建一个空的deque。
deque c1(c2)c复制一个deque。
deque c(n)创建一个deque,含有n个数据,数据均已缺省构造产生。
deque c(n, elem)创建一个含有n个elem拷贝的deque。
deque c(beg,end)创建一个以[beg;end)区间的deque。
c.~deque()销毁所有数据,释放内存。
c.empty()判断容器是否为空。
c.end()指向迭代器中的最后一个数据地址。
c.erase(pos)删除pos位置的数据,返回下一个数据的位置。
c.erase(beg,end)删除[beg,end)区间的数据,返回下一个数据的位置。
c.front()返回容器的第一个元素。
get_allocator使用构造函数返回一个拷贝。
c.insert(pos,elem)在pos位置插入一个elem拷贝,传回新数据位置。
c.insert(pos,n,elem)在pos位置插入>n个elem数据。无返回值。
c.insert(pos,beg,end)在pos位置插入在[beg,end)区间的数据。无返回值。
c.max_size()返回容器中最大数据的数量。
c.pop_back()删除最后一个数据。
c.pop_front()删除头部数据。
c.push_back(elem)在尾部加入一个数据。
c.push_front(elem)在头部插入一个数据。
c.rbegin()传回一个反向队列的第一个数据。
c.rend()传回一个反向队列的最后一个数据的下一个位置。
c.resize(num)重新指定队列的长度。
c.size()返回容器中实际数据的个数。
c1.swap(c2)将c1和c2元素互换
swap(c1,c2)将c1和c2元素互换。

list

  • 特点

  • 有效利用空间

  • 数据结构:环状双向链表

  • 插入(insert)和接合(splice)操作都不会造成原来list的迭代器失效

  • 删除(erase)操作仅仅使“指向被删除元素”的迭代器失效,其它迭代器不受影响

  • 随机访问比较慢

在这里插入图片描述
在这里插入图片描述
插入一个结点
在这里插入图片描述

  • 成员函数
函数作用
assign()给list赋值
back()返回最后一个元素
begin()返回指向第一个元素的迭代器
clear()删除所有元素
empty()如果list是空的则返回true
end()返回末尾的迭代器
erase()删除一个元素
front()返回第一个元素
get_allocator()返回list的配置器
insert()插入一个元素到list中
max_size()返回list能容纳的最大元素数量
merge()合并两个list
pop_back()删除最后一个元素
pop_front()删除第一个元素
push_back()在list的末尾添加一个元素
push_front()在list的头部添加一个元素
rbegin()返回指向第一个元素的逆向迭代器
remove()从list删除元素
remove_if()按指定条件删除元素
rend()指向list末尾的逆向迭代器
resize()改变list的大小
reverse()把list的元素逆序
size()返回list中的元素个数
sort()给list排序
splice()合并两个list
swap()交换两个list
unique()删除list中重复的元素

关联容器

set(集合)

  • 特点
  • 数据结构:底层使用平衡的搜索树——红黑树实现
    在这里插入图片描述
  • 插入删除操作时仅仅需要指针操作节点即可完成,不涉及到内存移动和拷贝
  • set中元素都是唯一的,而且默认情况下会对元素自动进行升序排列
  • 支持集合的交(set_intersection),差(set_difference) 并(set_union),对称差 (set_symmetric_difference) 等一些集合上的操作

在这里插入图片描述

  • 成员函数
函数作用
begin()返回容器的第一个迭代器
end()返回容器的最后元素的下一个迭代器
clear()删除容器中的所有元素
empty()判断容器是否为空
insert()插入一个元素
erase()删除一个元素
size()返回当前容器的元素个数
rbegin()返回尾元素的逆向迭代器指针
reverse_iterator rend()返回首元素前一个位置的迭代器指针
find()查找一个元素,不存在返回s.end()
lower_bound()返回第一个大于或等于给定关键值的元素
upper_bound()返回第一个大于给定关键值的元素
swap()交换两个集合元素

注意:

  • set内部元素也是以键值对的方式存储的,只不过它的键值与实值相同
  • set中不允许存放两个实值相同的元素
  • 迭代器是被定义成const iterator的,说明set的键值是不允许更改的,并且不允许通过迭代器进行修改set里面的值

multiset

  • 特点
  • 数据结构:底层实现与set一样,也采用了红黑树
  • 允许插入重复的键值,使用insert_equal机制
  • 插入、删除操作的时间复杂度为O(log2n)
    在这里插入图片描述
  • 成员函数用法同set

map(key,value)

  • 特点

  • map中key的值是唯一的

  • 数据结构:红黑树变体的平衡二叉树数据结构

  • 提供基于key的快速检索能力

  • 元素插入是按照排序规则插入的,不能指定位置插入

  • 对于迭代器来说,可以修改实值,而不能修改key。

  • 根据key值快速查找,查找的复杂度基本是log2n

  • 成员函数

函数作用
begin()返回指向map头部的迭代器
clear()删除所有的元素
count()返回指定元素出现的次数
empty()判断容器是否为空
end()返回指向map末尾的迭代器
get_allocator()返回map的配置器
insert()添加元素
lower_bound()返回键值>=给定元素的第一个位置
upper_bound()返回>给定元素的第一个元素
max_size()返回可以容纳的最大元素个数
rbegin()返回一个指向map尾部的逆向迭代器
rend()返回一个指向map头部的逆向迭代器
find(k)返回指向第一个与键 k 匹配的 pair 的迭代指针,没找到返回指向map尾部的迭代器
erase()删除迭代器所指向的元素
swap()两个容器交换
size()返回map中元素的个数

注意:
map在进行插入的时候是不允许有重复的键值的,如果新插入的键值与原有的键值重复则插入无效,可以通过insert的返回的pair中第二个bool型变量来判断是否插入成功来判断是否成功插入.

pair<iterator, bool> insert(const value_type& x)

举例:

#include<iostream>
#include<map>
using namespace std;
int main()
{
	map<int, int> m;
	m.insert(pair<int, int>(1, 10));
	pair<map<int, int>::iterator, bool> ret;
	ret = m.insert(pair<int, int>(1, 20));
	if (ret.second)
	{
		cout << "成功" << endl;
	}
	else
	{
		cout << "失败" << endl;
	}
	

	return 0;
}

在这里插入图片描述

multimap

  • 特点
  • 与 map 不同,multimap 可以包含重复键

比如:

1.在电话簿中相同的人可以有两个以上电话号码
2.文件系统中可以将多个符号链接映射到相同的物理文件
3.DNS服务器可以将几个URL映射到相同的IP地址
  • 成员函数
函数作用
begin()返回指向第一个元素的迭代器
clear()删除所有元素
count()返回一个元素出现的次数
empty()如果multimap为空则返回真
end()返回一个指向multimap末尾的迭代器
equal_range(k)该函数查找所有与 k 关联的值。返回迭代指针的 pair,它标记开始和结束范围
erase()删除元素
find()查找元素
get_allocator()返回multimap的配置器
insert()插入元素
key_comp()返回比较key的函数
lower_bound()返回键值>=给定元素的第一个位置
max_size()返回可以容纳的最大元素个数
rbegin()返回一个指向mulitmap尾部的逆向迭代器
rend()返回一个指向multimap头部的逆向迭代器
size()返回multimap中元素的个数
swap()交换两个multimaps
upper_bound()返回键值>给定元素的第一个位置
value_comp()返回比较元素value的函数

equal_range()函数应用举例:

#include<iostream>
#include<map>
using namespace std;

int main()
{
	multimap<int, int> m;
	m.insert(pair<int,int>(1, 10));
	m.insert(pair<int, int>(1, 20));
	m.insert(pair<int, int>(1, 30));
	m.insert(pair<int, int>(2, 21));
	m.insert(pair<int, int>(2, 22));
	m.insert(pair<int, int>(3, 31));
	m.insert(pair<int, int>(3, 32));

	multimap<int, int>::iterator  it;
	pair<multimap<int, int>::iterator, multimap<int, int>::iterator> ret;
	for (it = m.begin(); it != m.end(); )
	{
		cout << it->first << "==>";
		ret = m.equal_range(it->first);
		for (it = ret.first; it != ret.second; ++it)
		{
			cout << " " << (*it).second;
		}
		cout << endl;
	}
	
	cout << m.count(1) << endl;//与键1关联的值的数量
	return 0;
}

在这里插入图片描述

注意:

  1. multimap::insert() 和 map::insert() 的返回值不同。
  2. multimap::insert():返回指向新插入元素的迭代器(multimap::insert()总是能执行成功)
  3. map::insert() :返回 pair<iterator, bool>,此处 bool 值表示插入操作是否成功。
  • 33
    点赞
  • 210
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值