STL——容器及容器适配器

很长,但很多东西没写,不过对于STL的初始已经完全够用了,很多东西直接copy某位大佬博客的,如果正主发现并且不允许的话,联系秒删。

一、List

list是序列荣容器,它允许在序列的任何位置执行固定O(1)时间的插入和删除操作,由双向链表实现,但不能通过位置直接访问元素,需要用迭代器,而需要一些额外的内存空间来保持与其他元素相关联的链接信息。使用需要添加头文件#include<list>

1. 定义与初始化

list<int> list1;//创建空的list
list<int> list2(2);//创建含有两个元素的list
list<int> list3(4,2);//创建含有4个元素的list,且初始化为2
list<int> list4(list3);//用list3初始化list4
list<int> list5(list3.begin(),list3.end());//同上

2. 常用操作函数

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

3. list和vector

  1. vector底层实现是数组,list是双向链表
  2. vector支持随机访问,list不支持
  3. vector是顺序内存,list不是
  4. vector在中间节点进行插入和删除会导致内存拷贝,list不会
  5. vector一次分配好内存,不够进行2倍(1.5倍)扩容,list不会
  6. vector随机访问性能好,但插入删除性能差;list随机访问性能差,但插入删除性能好。
  7. vector插入删除元素都会导致迭代器失效,list插入元素会导致迭代器失效,但删除元素只会导致当前迭代器失效,其他的正常。

二、deque

deque是由一块块固定大小的连续空间组成(块与块之间是不连续的)。一旦需要在它的头端或尾端增加新的空间,便配置一块固定大小的连续空间。deque最大的任务是使这些固定大小的连续空间看起来整体连续,并提供随机存取的接口(随机迭代器)。它采用一块所谓的_M_map作为主控(不是STL容器的map),_M_map是一小块连续空间,其中每个元素都是指针,指向另一段较大的连续线性空间,称为缓冲区,这个才是deque的存储空间主体。

1. 定义与初始化

#include<deque>  // 头文件
deque<type> deq;  // 声明一个元素类型为type的双端队列que
deque<type> deq(size);  // 声明一个类型为type、含有size个默认值初始化元素的的双端队列que
deque<type> deq(size, value);  // 声明一个元素类型为type、含有size个value元素的双端队列que
deque<type> deq(mydeque);  // deq是mydeque的一个副本
deque<type> deq(first, last);  // 使用迭代器first、last范围内的元素初始化deq

2. 常用的操作函数

  1. deq[ ]:用来访问双向队列中单个的元素。
  2. deq.front():返回第一个元素的引用。
  3. deq.back():返回最后一个元素的引用。
  4. deq.push_front(x):把元素x插入到双向队列的头部。
  5. deq.pop_front():弹出双向队列的第一个元素。
  6. deq.push_back(x):把元素x插入到双向队列的尾部。
  7. deq.pop_back():弹出双向队列的最后一个元素。

3.deque和vector

  1. deque两端都可以快速插入删除(O(1)),但vector只能尾端进行插入和删除(O(1))
  2. deque的元素存取和迭代器操作会稍微慢一些,因为它的内部结构多一个间接过程
  3. deque的迭代器是一个特殊的智能指针而不是一般的指针,它需要在两个不同区块之间进行跳跃,因为deque使用的不止一块内存,而vector必须使用一块连续内存
  4. deque不支持对容量和内存重分配时机的控制,除了首尾两端安插删除元素外,其他地方安插删除元素都将导致元素的pointer(指针)、reference(引用)、iterator(迭代器)失效,不过deque的内存分配机制优于vector,因为deque不必在内存重分配时复制所有元素
  5. deque的内存区块不再被使用时,就会被释放。

三、vector

vector是动态空间,随着元素增加,它的内部会自动扩充空间以容纳新元素,vector的运用对于内存的合理利用与运用的灵活性有很大的帮助。Vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率,一旦vector旧空间满了,如果客户每新增一个元素,vector内部只是扩充一个元素的空间,实为不智,因为所谓的扩充空间(不论多大),一如刚所说,是”配置新空间-数据移动-释放旧空间”的大工程,时间成本很高。

1. vector迭代器

vector的迭代器可以看作是一个普通指针,普通指针可以做的事情,它都可以,是一个随机访问迭代器。

迭代函数功能
iterator begin()返回指向起始的迭代器
iterator end()返回指向末尾的迭代器
reverse_iterator rbegin()返回指向起始的逆向迭代器
reverse_iterator rend()返回指向末尾的逆向迭代器
Vector<int>::iterator it1;

2. vector的扩容

所谓动态增加大小,并不是在原空间之后续接新空间(因为无法保证原空间之后尚有可配置的空间),而是一块更大的内存空间,然后将原数据拷贝新空间,并释放原空间。因此,对vector的任何操作,一旦引起空间的重新配置,指向原vector的所有迭代器就都失效了。这是程序员容易犯的一个错误,务必小心。

3. vector常用API操作

  1. 构造函数
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem);//构造函数将n个elem拷贝给本身。
vector(const vector &vec);//拷贝构造函数。
  1. 常用赋值操作
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
vector& operator=(const vector  &vec);//重载等号操作符
swap(vec);// 将vec与本身的元素互换。
  1. 容量操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
  1. 数据存取
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
operator[];//返回索引idx所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
  1. 插入删除操作
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele.
push_back(ele); //尾部插入元素ele
pop_back();//删除最后一个元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
erase(const_iterator pos);//删除迭代器指向的元素
clear();//删除容器中所有元素

4. resize、reserve、assign

resize函数重新分配大小,改变容器的大小并创建对象
reserve函数分配出来的内存空间未初始化对象,只表示vector可以利用这部分内存空间,但不能有效的访问,如果访问就会出现越界现象,导致程序崩溃。
assign将元素赋值给vector容器,或者在指定空间的元素赋值到vector容器中,会清除掉以前的内容

四、string

C++定义了一种string类来代替C风格字符串。

String和c风格字符串对比:

  1. Char是一个指针,String是一个类string封装了char,管理这个字符串,是一个char*型的容器。
  2. String封装了很多实用的成员方法。查找find,拷贝copy,删除delete 替换replace,插入insert等
  3. 不用考虑内存释放和越界。string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。

string容器的常用API操作

  1. 构造函数
string();//创建一个空的字符串 例如: string str;      
string(const string& str);//使用一个string对象初始化另一个string对象
string(const char* s);//使用字符串s初始化
string(int n, char c);//使用n个字符c初始化 
  1. 基本赋值操作
string& operator=(const char* s);//char*类型字符串 赋值给当前的字符串
string& operator=(const string &s);//把字符串s赋给当前的字符串
string& operator=(char c);//字符赋值给当前的字符串
string& assign(const char *s);//把字符串s赋给当前的字符串
string& assign(const char *s, int n);//把字符串s的前n个字符赋给当前的字符串
string& assign(const string &s);//把字符串s赋给当前字符串
string& assign(int n, char c);//用n个字符c赋给当前字符串
string& assign(const string &s, int start, int n);//将s从start开始n个字符赋值给字符串
  1. 存取操作
char& operator[](int n);//通过[]方式取字符
char& at(int n);//通过at方法获取字符
  1. 拼接操作
string& operator+=(const string& str);//重载+=操作符
string& operator+=(const char* str);//重载+=操作符
string& operator+=(const char c);//重载+=操作符
string& append(const char *s);//把字符串s连接到当前字符串结尾
string& append(const char *s, int n);//把字符串s的前n个字符连接到当前字符串结尾
string& append(const string &s);//同operator+=()
string& append(const string &s, int pos, int n);//把字符串s中从pos开始的n个字符连接到当前字符串结尾
string& append(int n, char c);//在当前字符串结尾添加n个字符c
  1. 比较操作
/*
compare函数在>时返回 1,<时返回 -1,==时返回 0。
比较区分大小写,比较时参考字典顺序,排越前面的越小。
大写的A比小写的a小。
*/
int compare(const string &s) const;//与字符串s比较
int compare(const char *s) const;//与字符串s比较
  1. 插入删除操作
string& insert(int pos, const char* s); //插入字符串
string& insert(int pos, const string& str); //插入字符串
string& insert(int pos, int n, char c);//在指定位置插入n个字符c
string& erase(int pos, int n = npos);//删除从Pos开始的n个字符 
  1. 取子串
string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串
  1. 与C类型字符串的转换
//string 转 char*
string str = "it";
const char* cstr = str.c_str();
//char* 转 string 
char* s = "it";
string str(s);

在c++中存在一个从const char到string的隐式类型转换,却不存在从一个string对象到C_string的自动类型转换。对于string类型的字符串,可以通过c_str()函数返回string对象对应的C_string。
通常,程序员在整个程序中应坚持使用string类对象,直到必须将内容转化为char
时才将其转换为C_string。

五、stack

stack是一种先进后出的数据结构,只有一个出口,只允许增加、移除、取得栈顶元素,且只能取得栈顶元素。如图
在这里插入图片描述
它只提供先进后出的功能,不提供迭代器。

常用API函数

  1. 构造函数
stack<T> stkT;//stack采用模板类实现, stack对象的默认构造形式: 
stack(const stack &stk);//拷贝构造函数
  1. 赋值操作
stack& operator=(const stack &stk);//重载等号操作符
  1. 数据处理
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素
  1. 容量函数
empty();//判断堆栈是否为空
size();//返回堆栈的大小

六、queue

一种先进先出的数据结构,无迭代器,允许从队尾新增元素,从队头移除元素。
在这里插入图片描述

常用API函数

  1. 构造函数
queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式:
queue(const queue &que);//拷贝构造函数
  1. 赋值函数
queue& operator=(const queue &que);//重载等号操作符
  1. 数据操作
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素
  1. 容量操作
empty();//判断队列是否为空
size();//返回队列的大小

七、set/mulitset

set

Set的特性是。所有元素都会根据元素的键值自动被排序。Set的元素不像map那样可以同时拥有实值和键值,set的元素即是键值又是实值。Set不允许两个元素有相同的键值。
我们不可以通过set的迭代器改变set元素的值,因为set元素值就是其键值,关系到set元素的排序规则。如果任意改变set元素值,会严重破坏set组织。换句话说,set的iterator是一种const_iterator
set拥有和list某些相同的性质,当对容器中的元素进行插入操作或者删除操作的时候,操作之前所有的迭代器,在操作完成之后依然有效,被删除的那个元素的迭代器必然是一个例外。

mulitset

它与set唯一的区别是允许键值重复,两个的底层实现都是红黑树。

常用API操作

  1. 构造函数
set<T> st;//set默认构造函数:
mulitset<T> mst; //multiset默认构造函数: 
set(const set &st);//拷贝构造函数
  1. 赋值操作
set& operator=(const set &st);//重载等号操作符
swap(st);//交换两个集合容器
  1. 插入删除操作
insert(elem);//在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem);//删除容器中值为elem的元素。
  1. 容量操作
size();//返回容器中元素的数目
empty();//判断容器是否为空
  1. 查找操作
find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key);//查找键key的元素个数
lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。
upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。
equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器。

八、map/mulitmap

Map的特性是,所有元素都会根据元素的键值自动排序。
Map所有的元素都是pair,同时拥有实值和键值,pair的第一元素被视为键值,第二元素被视为实值,map不允许两个元素有相同的键值。
我们不可以通过map的迭代器改变map的键值, 因为map的键值关系到map元素的排列规则,任意改变map键值将会严重破坏map组织。如果想要修改元素的实值,那么是可以的。
Map和list拥有相同的某些性质,当对它的容器元素进行新增操作或者删除操作时,操作之前的所有迭代器,在操作完成之后依然有效,当然被删除的那个元素的迭代器必然是个例外。
Multimap和map的操作类似,唯一区别multimap键值可重复。
Map和multimap都是以红黑树为底层实现机制。

常用API函数

  1. 构造函数
map<T1, T2> mapTT;//map默认构造函数: 
map(const map &mp);//拷贝构造函数
  1. 赋值操作
map& operator=(const map &mp);//重载等号操作符
swap(mp);//交换两个集合容器
  1. 容量操作
size();//返回容器中元素的数目
empty();//判断容器是否为空
  1. 插入元素操作
map.insert(...); //往容器插入元素,返回pair<iterator,bool>
map<int, string> mapStu;
// 第一种 通过pair的方式插入对象
mapStu.insert(pair<int, string>(3, "小张"));
// 第二种 通过pair的方式插入对象
mapStu.inset(make_pair(-1, "校长"));
// 第三种 通过value_type的方式插入对象
mapStu.insert(map<int, string>::value_type(1, "小李"));
// 第四种 通过数组的方式插入值
mapStu[3] = "小刘";
mapStu[5] = "小王";
  1. 删除操作
clear();//删除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(keyElem);//删除容器中key为keyElem的对组。
  1. 查找操作
find(key);//查找键key是否存在,若存在,返回该键的元素的迭代器;/若不存在,返回map.end();
count(keyElem);//返回容器中key为keyElem的对组个数。对map来说,要么是0,要么是1。对multimap来说,值可能大于1。
lower_bound(keyElem);//返回第一个key>=keyElem元素的迭代器。
upper_bound(keyElem);//返回第一个key>keyElem元素的迭代器。
equal_range(keyElem);//返回容器中key与keyElem相等的上下限的两个迭代器。

九、容器的选择

  1. vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。
  2. deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
  3. list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
  4. set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
  5. map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值