很长,但很多东西没写,不过对于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
- vector底层实现是数组,list是双向链表
- vector支持随机访问,list不支持
- vector是顺序内存,list不是
- vector在中间节点进行插入和删除会导致内存拷贝,list不会
- vector一次分配好内存,不够进行2倍(1.5倍)扩容,list不会
- vector随机访问性能好,但插入删除性能差;list随机访问性能差,但插入删除性能好。
- 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. 常用的操作函数
- deq[ ]:用来访问双向队列中单个的元素。
- deq.front():返回第一个元素的引用。
- deq.back():返回最后一个元素的引用。
- deq.push_front(x):把元素x插入到双向队列的头部。
- deq.pop_front():弹出双向队列的第一个元素。
- deq.push_back(x):把元素x插入到双向队列的尾部。
- deq.pop_back():弹出双向队列的最后一个元素。
3.deque和vector
- deque两端都可以快速插入删除(O(1)),但vector只能尾端进行插入和删除(O(1))
- deque的元素存取和迭代器操作会稍微慢一些,因为它的内部结构多一个间接过程
- deque的迭代器是一个特殊的智能指针而不是一般的指针,它需要在两个不同区块之间进行跳跃,因为deque使用的不止一块内存,而vector必须使用一块连续内存
- deque不支持对容量和内存重分配时机的控制,除了首尾两端安插删除元素外,其他地方安插删除元素都将导致元素的pointer(指针)、reference(引用)、iterator(迭代器)失效,不过deque的内存分配机制优于vector,因为deque不必在内存重分配时复制所有元素
- 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操作
- 构造函数
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem);//构造函数将n个elem拷贝给本身。
vector(const vector &vec);//拷贝构造函数。
- 常用赋值操作
assign(beg, end);//将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem);//将n个elem拷贝赋值给本身。
vector& operator=(const vector &vec);//重载等号操作符
swap(vec);// 将vec与本身的元素互换。
- 容量操作
size();//返回容器中元素的个数
empty();//判断容器是否为空
resize(int num);//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem);//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。如果容器变短,则末尾超出容器长度的元素被删除。
capacity();//容器的容量
reserve(int len);//容器预留len个元素长度,预留位置不初始化,元素不可访问。
- 数据存取
at(int idx); //返回索引idx所指的数据,如果idx越界,抛出out_of_range异常。
operator[];//返回索引idx所指的数据,越界时,运行直接报错
front();//返回容器中第一个数据元素
back();//返回容器中最后一个数据元素
- 插入删除操作
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风格字符串对比:
- Char是一个指针,String是一个类string封装了char,管理这个字符串,是一个char*型的容器。
- String封装了很多实用的成员方法。查找find,拷贝copy,删除delete 替换replace,插入insert等
- 不用考虑内存释放和越界。string管理char*所分配的内存。每一次string的复制,取值都由string类负责维护,不用担心复制越界和取值越界等。
string容器的常用API操作
- 构造函数
string();//创建一个空的字符串 例如: string str;
string(const string& str);//使用一个string对象初始化另一个string对象
string(const char* s);//使用字符串s初始化
string(int n, char c);//使用n个字符c初始化
- 基本赋值操作
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个字符赋值给字符串
- 存取操作
char& operator[](int n);//通过[]方式取字符
char& at(int n);//通过at方法获取字符
- 拼接操作
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
- 比较操作
/*
compare函数在>时返回 1,<时返回 -1,==时返回 0。
比较区分大小写,比较时参考字典顺序,排越前面的越小。
大写的A比小写的a小。
*/
int compare(const string &s) const;//与字符串s比较
int compare(const char *s) const;//与字符串s比较
- 插入删除操作
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个字符
- 取子串
string substr(int pos = 0, int n = npos) const;//返回由pos开始的n个字符组成的字符串
- 与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函数
- 构造函数
stack<T> stkT;//stack采用模板类实现, stack对象的默认构造形式:
stack(const stack &stk);//拷贝构造函数
- 赋值操作
stack& operator=(const stack &stk);//重载等号操作符
- 数据处理
push(elem);//向栈顶添加元素
pop();//从栈顶移除第一个元素
top();//返回栈顶元素
- 容量函数
empty();//判断堆栈是否为空
size();//返回堆栈的大小
六、queue
一种先进先出的数据结构,无迭代器,允许从队尾新增元素,从队头移除元素。
常用API函数
- 构造函数
queue<T> queT;//queue采用模板类实现,queue对象的默认构造形式:
queue(const queue &que);//拷贝构造函数
- 赋值函数
queue& operator=(const queue &que);//重载等号操作符
- 数据操作
push(elem);//往队尾添加元素
pop();//从队头移除第一个元素
back();//返回最后一个元素
front();//返回第一个元素
- 容量操作
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操作
- 构造函数
set<T> st;//set默认构造函数:
mulitset<T> mst; //multiset默认构造函数:
set(const set &st);//拷贝构造函数
- 赋值操作
set& operator=(const set &st);//重载等号操作符
swap(st);//交换两个集合容器
- 插入删除操作
insert(elem);//在容器中插入元素。
clear();//清除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem);//删除容器中值为elem的元素。
- 容量操作
size();//返回容器中元素的数目
empty();//判断容器是否为空
- 查找操作
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函数
- 构造函数
map<T1, T2> mapTT;//map默认构造函数:
map(const map &mp);//拷贝构造函数
- 赋值操作
map& operator=(const map &mp);//重载等号操作符
swap(mp);//交换两个集合容器
- 容量操作
size();//返回容器中元素的数目
empty();//判断容器是否为空
- 插入元素操作
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] = "小王";
- 删除操作
clear();//删除所有元素
erase(pos);//删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg,end);//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(keyElem);//删除容器中key为keyElem的对组。
- 查找操作
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相等的上下限的两个迭代器。
九、容器的选择
- vector的使用场景:比如软件历史操作记录的存储,我们经常要查看历史记录,比如上一次的记录,上上次的记录,但却不会去删除记录,因为记录是事实的描述。
- deque的使用场景:比如排队购票系统,对排队者的存储可以采用deque,支持头端的快速移除,尾端的快速添加。如果采用vector,则头端移除时,会移动大量的数据,速度慢。
- list的使用场景:比如公交车乘客的存储,随时可能有乘客下车,支持频繁的不确实位置元素的移除插入。
- set的使用场景:比如对手机游戏的个人得分记录的存储,存储要求从高分到低分的顺序排列。
- map的使用场景:比如按ID号存储十万个用户,想要快速要通过ID查找对应的用户。二叉树的查找效率,这时就体现出来了。如果是vector容器,最坏的情况下可能要遍历完整个容器才能找到该用户。