一、vector容器
1、vector容器实现机理
vector 是可变大小的动态数组,属于顺序容器,在内存中是连续存储的,因此可以支持快速随机访问;此外 vector/string 通常会分配比新空间更大的内存空间(具体实现细节与编译器的版本有关),器会将多分配的剩余空间作为备用,用来存储新的元素。这样,就不需要每次增加新元素都重新分配容器的内存空间,提高性能;
基于上述机制的vector内存扩张操作通常要比list,deque要好的多,因此在能用vector的前提下,尽量多使用vector;
2、vector容器常见方法
empty(); //判断容器是否为空 size(); //返回容器中元素的个数 capacity(); //容器的容量 push_back(ele); //尾部插入元素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(); //删除容器中所有元素 at(int idx); //返回索引idx所指的数据 operator[]; //返回索引idx所指的数据 front(); //返回容器中第一个数据元素 back(); //返回容器中最后一个数据元素 swap(vec); // 将vec与本身的元素互换 resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置 //如果容器变短,则末尾超出容器长度的元素被删除 resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置 //如果容器变短,则末尾超出容器长度的元素被删除
二、list 容器
1、list容器实现机理
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的;
STL中的链表是一个双向循环链表, 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器;
list 的优点:
采用动态存储分配,不会造成内存浪费和溢出;
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素;
list 的缺点:
链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大;
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的;
2、list容器常见方法
size(); //返回容器中元素的个数 empty(); //判断容器是否为空 push_back(elem); //在容器尾部加入一个元素 pop_back(); //删除容器中最后一个元素 push_front(elem); //在容器开头插入一个元素 pop_front(); //从容器开头移除第一个元素 front(); //返回第一个元素 back(); //返回最后一个元素 reverse(); //反转链表 sort(); //链表排序 insert(pos,elem); //在pos位置插elem元素的拷贝,返回新数据的位置 insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值 insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值 clear(); //移除容器的所有数据 swap(lst); //将lst与本身的元素互换 erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置 erase(pos); //删除pos位置的数据,返回下一个数据的位置 remove(elem); //删除容器中所有与elem值匹配的元素 resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置 //如果容器变短,则末尾超出容器长度的元素被删除 resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置
三、stack 容器
1、stack容器实现机理
stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为;
2、stack容器常用方法
stack<T> stk; //stack采用模板类实现, stack对象的默认构造形式 stack(const stack &stk); //拷贝构造函数 stack& operator=(const stack &stk); //重载等号操作符 push(elem); //向栈顶添加元素 pop(); //从栈顶移除第一个元素 top(); //返回栈顶元素 empty(); //判断堆栈是否为空 size(); //返回栈的大小
注:常常可以利用栈的性质,用两个栈实现一个队列;
四、queue 容器
1、queue容器实现机理
Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口队列容器允许从一端新增元素,从另一端移除元素队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为;
2、queue容器常用方法
queue<T> que; //queue采用模板类实现,queue对象的默认构造形式 queue(const queue &que); //拷贝构造函数 queue& operator=(const queue &que); //重载等号操作符 push(elem); //往队尾添加元素 pop(); //从队头移除第一个元素 front(); //返回第一个元素 back(); //返回最后一个元素 empty(); //判断堆栈是否为空 size(); //返回栈的大小
五、deque 容器
1、deque容器实现机理
双端数组,可以对头端进行插入删除操作
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低;
deque相对而言,对头部的插入删除速度回比vector快;
vector访问元素时的速度会比deque快,这和两者内部实现有关;
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间;
deque容器的迭代器也是支持随机访问的;
2、deque容器常用方法
deque.empty(); //判断容器是否为空 deque.size(); //返回容器中元素的个数 push_back(elem); //在容器尾部添加一个数据 push_front(elem); //在容器头部插入一个数据 pop_back(); //删除容器最后一个数据 pop_front(); //删除容器第一个数据 front(); //返回容器中第一个数据元素 back(); //返回容器中最后一个数据元素 at(int idx); //返回索引idx所指的数据 operator[]; //返回索引idx所指的数据 insert(pos,elem); //在pos位置插入一个elem元素的拷贝,返回新数据的位置 insert(pos,n,elem); //在pos位置插入n个elem数据,无返回值 insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值 clear(); //清空容器的所有数据 erase(beg,end); //删除[beg,end)区间的数据,返回下一个数据的位置 erase(pos); //删除pos位置的数据,返回下一个数据的位置 sort(iterator beg, iterator end) //对beg和end区间内元素进行排序 deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置 //如果容器变短,则末尾超出容器长度的元素被删除 deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置 //如果容器变短,则末尾超出容器长度的元素被删除
六、set 与 unordered_set
1、set容器实现机理
基于红黑树实现,红黑树具有自动排序的功能,因此set内部所有的数据,在任何时候,都是有序的;
(1)set/multiset 容器常见方法:
set<T> st; //默认构造函数 set(const set &st); //拷贝构造函数赋值 set& operator=(const set &st); //重载等号操作符 size(); //返回容器中元素的数目 empty(); //判断容器是否为空 swap(st); //交换两个集合容器 insert(elem); //在容器中插入元素 clear(); //清除所有元素 erase(pos); //删除 pos 迭代器所指的元素,返回下一个元素的迭代器 erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器 erase(elem); //删除容器中值为elem的元素 find(key); //查找key是否存在,若存在返回该键的元素的迭代器;若不存在,返回 //set.end(); count(key); //统计key的元素个数 // set 和 multiset 的区别 set不可以插入重复数据,而multiset可以 set插入数据的同时会返回插入结果,表示插入是否成功 multiset不会检测数据,因此可以插入重复数据
2、unordered_set容器实现机理
基于哈希表,数据插入和查找的时间复杂度很低,几乎是常数时间,而代价是消耗比较多的内存,无自动排序功能。底层实现上,使用一个下标范围比较大的数组来存储元素,形成很多的桶,利用hash
函数对key
进行映射到不同区域进行保存;
(1)unordered_set容器常见方法
begin(); //返回指向容器中第一个元素的正向迭代器 end(); //返回指向容器中最后一个元素之后位置的正向迭代器 empty(); //若容器为空,则返回 true;否则 false size(); //返回当前容器中存有元素的个数 max_size(); //返回容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同 find(key); //查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一 //个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器) count(key); //在容器中查找值为 key 的元素的个数 insert(); //向容器中添加新元素 erase(pos); //删除pos迭代器所指元素,返回下一个元素的迭代器 clear(); //清空容器,即删除容器中存储的所有元素 swap(); //交换 2 个 unordered_map 容器存储的元素,前提是必须保证这2个容器的类型完全相等
3、set使用情况
(1)我们需要有序数据(不同的元素);
(2)我们必须打印/访问数据(按排序顺序);
(3)我们需要元素的前身/后继者;
4、unordered_set使用情况
(1)我们需要保留一组不同的元素,不需要排序;
(2)我们需要单个元素访问,即没有遍历;
七、map 与 unordered_map
1、map/multimap
容器实现机理
map/multimap属于关联式容器,底层结构是用二叉树实现
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
优点:
有序性,这是map结构最大的优点,其元素的有序性在很多应用中都会简化很多的操作;红黑树,内部实现一个红黑书使得map的很多操作在logn的时间复杂度下就可以实现,因此效率非常的高;
缺点:
空间占用率高,因为map内部实现了红黑树,虽然提高了运行效率,但是因为每一个节点都需要额外保存父节点,孩子节点以及红/黑性质,使得每一个节点都占用大量的空间;
适用处,对于那些有顺序要求的问题,用map会更高效一些;
map和multimap区别:
map不允许容器中有重复key值元素
multimap允许容器中有重复key值元素
(1)map/multimap 容器常见方法:
map<T1, T2> mp; //map默认构造函数 map(const map &mp); //拷贝构造函数 eg: map<int,int>m; //默认构造 m.insert(pair<int, int>(1, 10)); size(); //返回容器中元素的数目 empty(); //判断容器是否为空 swap(st); //交换两个集合容器 insert(elem); //在容器中插入元素 clear(); //清除所有元素 erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器 erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器 erase(key); //删除容器中值为key的元素 find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end() count(key); //统计key的元素个数 //map容器默认排序规则为 按照key值进行 从小到大排序,掌握如何改变排序规则 //利用仿函数,可以改变排序规则
2、unordered_map
容器实现机理
优点:
- 因为内部实现了哈希表,因此其查找速度非常的快;
缺点:
- 哈希表的建立比较耗费时间;
- 适用处,对于查找问题,unordered_map会更加高效一些;
(1)unordered_map
容器常见方法:
empty() //若容器为空,则返回 true;否则 false size() //返回当前容器中存有键值对的个数 max_size() //返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同 operator[key] //该模板类中重载了 [] 运算符,其功能是可以向访问数组中元素那样,只要给定某个键值对的键 key,就可以获取该键对应的值。注意,如果当前容器中没有以 key 为键的键值对,则其会使用该键向当前容器中插入一个新键值对 at(key) //返回容器中存储的键 key 对应的值,如果 key 不存在,则会抛出 out_of_range 异常 find(key) //查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器) count(key) //在容器中查找以 key 键的键值对的个数 equal_range(key) //返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围 emplace() //向容器中添加新键值对,效率比 insert() 方法高 emplace_hint() //向容器中添加新键值对,效率比 insert() 方法高 insert() //向容器中添加新键值对 erase() //删除指定键值对,map.erase(val); clear() //清空容器,即删除容器中存储的所有键值对 swap() //交换 2 个 unordered_map 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等
八、string 容器
1、string容器实现机理
(1)string和char * 区别:
- char * 是一个指针;
- string是一个类,类内部封装了char*,管理这个字符串,是一个char*型的容器;
(2)特点:
string 类内部封装了很多成员方法;
例如:查找find,拷贝copy,删除delete 替换replace,插入insert;
string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责;
2、string容器常见方法
string(); //创建一个空的字符串 例如: string str; string(const char* s); //使用字符串s初始化 string(const string& str); //使用一个string对象初始化另一个string对象 string(int n, char c); //使用n个字符c初始化 //eg:string s4(10, 'a'); 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& 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个字符连接到字符 int find(const string& str, int pos = 0) const; //查找str第一次出现位置,从pos开始查找 int find(const char* s, int pos = 0) const; //查找s第一次出现位置,从pos开始查找 int find(const char* s, int pos, int n) const; //从pos位置查找s的前n个字符第一次位置 int find(const char c, int pos = 0) const; //查找字符c第一次出现位置 int rfind(const string& str, int pos = npos) const; //查找str最后一次位置,从pos开始查找 int rfind(const char* s, int pos = npos) const; //查找s最后一次出现位置,从pos开始查找 int rfind(const char* s, int pos, int n) const; //从pos查找s的前n个字符最后一次位置 int rfind(const char c, int pos = 0) const; //查找字符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
- 字符串之间的比较
比较方式:
- 字符串比较是按字符的ASCII码进行对比
= 返回 0
> 返回 1
< 返回 -1
int compare(const string &s) const; //与字符串s比较 int compare(const char *s) const; //与字符串s比较 //字符的获取 char& operator[](int n); //通过[]方式取字符 char& at(int n); //通过at方法获取字符 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个字符组成的字符串
注:此文章内容仅是自己在学习过程中的一个小小总结(且仅是为了以后的学习使用提供方便),在后面的学习过程中会不断地对STL进行完善;
在刷算法题的时候可以根据各个容器的特性做出合适的选择;