零、模板
一、STL基本概念
二、string
三、vector
四、list
五、vector与list
六、deque
七、set\multiset
八、map\multimap\unordered_map
8.1 map与unordered_map
九、仿函数
十、谓词
1.C++提供两种模板
函数模板和类模板
语法:
template
template
函数模板:
typename表示这个后面的符号是一种数据类型,
T表示一种通用的数据类型,名称可以替换,通常为大写字母
使用函数模板有两种方式:
自动类型推导,显示指定类型。
一般我们采用显示指定类型的方式,也就是在函数名后,括号前加上
类模板:
建立一个通用类,类中成员的数据类型可以不具体指定,用一个虚拟的类来代表。
2.模板的问题
如果T的数据类型是自定义类型,那么可以重载模板,提供具体化的模板。
3.类模板与函数模板的区别
1. 类模板没有自动类型推导的使用方式,必须声明类型
2. 类模板在模板参数列表中可以有默认参数
4.类模板中成员函数创建时机
普通类中的成员函数一开始就可以创建
类模板中的成员函数在调用时才创建
为了简化数据结构与算法的题韬标准,运用了面向对象和泛型编程思想,调高复用性,诞生了STL,标准模板库。
STL广义上由以下三部分组成:
1.容器
2.算法
3.迭代器
STL几乎所有的代码都采用模板类或模板函数
容器:
序列式容器:强调值的排序,每个元素均有固定的位置。
关联式容器:二叉树或哈希表结构,各个元素之间没有严格的物理顺序关系。
算法:
质变算法:改变区间内元素的内容,如拷贝,替换,删除
非质变算法:运算过程中不会更改区间内元素内容,如查找,遍历。
迭代器:
提供一种方法,使之能依序寻访某个容器中的各个元素,非常类似于指针。
string是C++风格的字符串,本质是一个类
他和char*的区别在于,
char*是一个字符类型的指针,
但是string是一个类,类的内部封装了char*。
string类管理char*所分配的内存,不用担心复制越界和取值越界,由类的内部进行负责。
vector的数据结构和数组非常相似,也称为单端数组。
他和数组的区别在于,数组是静态空间,而vector可以动态扩展。
vector的动态扩展并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,再释放原空间。
通过capacity():返回容器的容量
通过size():返回容器中元素的个数
通过resize():重新指定容器的长度为num
vector收缩内存:
利用匿名对象加swap:
举个例子
用一个vector v,
里面存放了1到100000的数
但是其实喜用帮助我们扩容了,总容量差不多有130000左右
这怎么办?
我们可以先用v.resize(100000),
调整v的元素长度为100000,这个时候什么都没有改变。
然后,我们使用拷贝构造函数,创建一个匿名对象:
vector(v).swap(v)
这个匿名对象会根据v的当前元素个数来确定自己的元素个数和容量,
也就是说,这个时候,匿名对象的容量和元素个数都是100000.
在调用他的交换函数,
将自己与原容器v进行交换,
这个时候,v就删去了之前多的容量,
而这个匿名对象指向的空间,
也会因为匿名对象的一个特点被系统回收。
这个特点是:匿名对象运行完本行,立即被销毁。
vector优点:
1.不需要指定内存大小就可以像数组一样操作,而且可以进行动态操作,如:push_back(),pop_back(),insert()等。
2.随机访问方便,可以随机访问vector内的任意一个元素。
3.节省空间
缺点:
1.插入和删除操作效率低,尤其是头部。
2.操作仅能在尾部进行push和pop
3.当动态添加的数据超过vector默认分配的大小时,要进行整体的重新分配、拷贝和释放。
功能:链式存储
底层:一个双向循环链表
每一个数据都有数据域,指向前一个元素的指针和指向后一个元素的指针。
优点:
1.动态存储分配,不会造成内存浪费
2.插入和删除效率高
缺点:
1.指针域占用了一定的空间
2.遍历元素,查找元素开销较大
1.如果需要高效的随机存取,不在乎插入和删除的效率,使用vector
2.如果需要大量的插入和删除元素,使用list
3.list里,进行插入和删除操作都不会造成原有的list迭代器失效,这在vector里是不成立的。
deque又叫做双端数组,它可以在头端和尾端进行插入删除操作。
deque相对于vector,对头部的插入和删除速度比vector快。
vector访问元素时的速度会比deque快。
set和multiset属于关联式容器,
底层实现:红黑树
map中的元素:对组
对组
成对出现的数据,利用队组可以返回两个数据。
pair p1 (string(“Tom”),20);
pair p2 = make_pair(“Jerry”,10);
特性:
1.set中每个元素必须是唯一的,不允许出现键值重复。可以理解为一个集合。
2.内部所有的元素都会被自动按照升序来排序。
可以改降序:使用仿函数,重写()括号运算符,
使用二元谓词,
bool operator()(int v1,int v2)
{
return v1>v2;
}
3.如果set中的元素为对组形式,可以根据key值快速找到value的值
.first,
.second
即可
4.multiset
multiset和set的区别在于multiset的元素可以相同,也就是内部可以出现重复的键值。
对multiset进行erase操作后,会删除所有值相同的key。
map提供键和值一对一的映射关系,
map中的所有元素都是对组。
其中第一个元素为key,键值,起到索引作用;
第二个元素为value,也就是实值。
每个键不允许重复,不允许修改。
但是map对应的值是可以修改的。
所有元素会根据元素的键值自动排序。
底层结构:红黑树
multimap允许容器中有重复的key值。
也就是说,一个键可以对应多个值,键和值一对多的映射关系。
unordered_map
内部是一个哈希表,也叫散列表,通过把关键key值映射到哈希表中的某一个位置来访问记录,时间复杂度可达O(1)。
其元素的排列顺序是无序的。
map
内部是一个红黑树,
红黑树不同于二叉平衡搜索树,他是非严格的二叉平衡搜索树,具有自动排序的功能。
红黑树的每个结点都代表map中的一个元素。
map优点:有序,且map底层为红黑树,使得很多操作在o(logn)的时间复杂度下就可以实现。
缺点:空间占用率高,因为红黑树的每一个结点都需要额外保存父节点,孩子节点以及红黑树本身的性质,使得空间占用率大。
unordered_map:
优点:因为内部实现了哈希表,所以查找速度非常快,性能优于基于红黑树的map;
缺点:哈希表建立比红黑树建立更费时。
所以对于查找问题,我们使用unordered_map会高效一些;
而如果我们想得到一个有序序列,那么使用map。
仿函数又叫做函数对象,
重载函数操作符的类,他的对象通常叫做函数对象。
因为函数对象使用重载的()运算符时,行为类似函数调用,
所以也称仿函数。
可以有参数,可以有返回值
class MyAdd
{
public :
int operator()(int v1,int v2)
{
return v1 + v2;
}
};
MyAdd myAdd;
cout << myAdd(10, 10) << endl;
返回bool类型的仿函数称为谓词;
如果重载的括号运算符接受一个参数,那么叫一元谓词;
如果重载的括号运算符接受两个参数,那么叫二元谓词。