STL
STL
一、stl概述
STL是standard template library的简称----标准模板库。是c++的标准程序库的核心,它深刻的影响了标准程序库的整体结构,他是一种泛型编程。
从根本上来说,stl是一些“容器”的集合,这些“容器”list,vector,set,map
stl也是算法和其他一些组件的集合,程序员无需了解stl原理,便可享受数据结构和算法领域中的这一革命新成果
stl包含了算法和结构
template<class T>
void sort(T* parr,int len)
{
for(int i=0;i<len-1;i++)
{
for(int j=0;j<len-1-i;j++)
{
parr[j]=parr[j]^parr[j+1];
parr[j+1]=parr[j]^parr[j+1];
parr[j]=parr[j]^parr[j+1];
}
}
}
头文件
#include<vector>
#include<deque>
#include<list>
#include<map>
#include<set>
#include<functional>
二、STL容器
为了适应不同的需要,STL提供了不同的容器
1、序列式容器---可序
vector,list,deque等
可序:每个元素都有固定的位置,元素的位置取决于插入的时机的位置 (下标),和元素值无关
2、关系式容器--已序
set,multiset,map,multimap等
已序:元素的位置取决于元素值的大小,和插入的次序无关
3、可序与已序
可序的优点:元素进入容器比较简单,元素的查找相对比较麻烦
已序的优点:元素进入容器比较麻烦,元素的查找相对比较简单
4、容器接口
不管是已序容器还是可序容器,有一些共同的功能,也就是相同的函数接口。这些函数主要是用于进行数据比较,迭代和存储的
比如:size(),关系运算符重载,迭代器相关,数据的插入,删除等
三、STL容器满足的特性
1、外部数据外部管理,内部数据内部管理,容器进行元素插入操作时,内部实现的是拷贝操作,容器中每一个元素都要能被拷贝或赋值
2、容器总体而言所有的元素都形成次序(下表次序),多次遍历每个元素次序总是相同
3、各项操作并非绝对安全
四、迭代器--iterator
迭代可以理解为遍历。
每一个STL的容器当中都会有一个迭代器,迭代器是帮助我们去容器中查找所有的元素
1、迭代器定义
可遍历stl容器内全部或部分元素的对象,一个迭代器用来指出容器中的一个特定位置。他类似于智能指针,具有遍历复杂数据结构的能力,其运作机制取决于其所遍历的数据结构
每一个容器的内存模型可能并不相同,每一个容器都有自己的迭代器,该迭代器以嵌套的方式定义在容器的内部。这个迭代器可以帮助我们去该容器中查找元素
2、半开区间
假如在容器中有两个迭代器,会形成一个区间--半闭合区间(半开区间)
两个迭代器指向容器的不同位置,两个迭代器会形成区间,这个区间叫做半开区间
3、半开区间的优点
1.为遍历元素,提供循环的结束时机
2.不需要对空区间采取特殊的处理手段
五、vector---模拟动态数组
#include
1、vector的理解
封装了动态数组的顺序容器,可以简单的认为,vector是一个能够存放任意类型的动态数组,也称为向量
2、vector的特性
1.顺序序列,有序群集,逻辑位置和物理位置连续
2.支持随机存取
3.在末端添加或删除元素方便,性能好
4.头部或者中部插入元素或删除元素需要移位,性能差
5.可以扩容,但是会导致内存重新分配
六、vector里的基本函数
1、构造与析构
1;
vector();//创建一个空vector
2;
vector(int size);//创建一个vector,元素个数为size
3;
vector(int size,const t&t);//创建一个vector,元素个数为size,且值均为t
4;
vector(const vector&);//复制构造函数
5;
vector(begin,end);//复制[begin,end]区间内另一个数组的元素到vector中
6;
~vector();//删除所有元素,释放内存
2、非变动性函数
1;
int size()const;//返回向量中元素的个数
2;
bool empty()const;//判断向量是否为空,若为空,则向量中无元素
3;
int capaicity()const;//返回当前向量所能容纳的最大元素值,capacity--容量
4;
int max_size()const;//返回最大可允许的vector元素数量值
5;
int capaicity()const;//返??
3、赋值操作
1;
void swap(vector&);//交换两个同类型向量的数据
2;
void assign(int n,const T&x);//设置向量中第n个元素的值为x
3;
void assign(const_iterator first,const_iterator last);//向量中[fist,last]中元素设置为当前向量元素
4、元素存取
1;
reference at(int pos);//返回pos位置元素的引用,reference--引用
2;
operator[idx];//返回索引idx所标示的元素,不进行范围检查
3;
reference front();//返回首元素的引用
4;
reference back();//返回尾元素的引用
5、迭代器相关
1;
iterator begin();//返回向量头指针,指向第一个元素
2;
iterator end();//返回向量尾指针,指向向量最后一个元素的下一个位置
3;
reverse_iterator rbegin();//反向迭代器,指向最后一个元素
4;
reverse_iterator rend();//反向迭代器,指向第一个元素之前的位置
6、插入和删除
1;
void push_back(const T&x);//向量尾部增加一个元素x
2;
iterator insert(iterator it,const T&x);//向量中迭代器指向元素前增加1个元素x
3;
iterator insert(iterator it,int n,const T&x);//向量中迭代器指向元素前增加n个相同的元素x
4;
iterator insert(iterator it,const_iterator first,const_iterator last);//向量中迭代器指向元素前插入另一个相同类型向量的[first,last]间的数据
5;
iterator erase(iterator it);//删除向量中迭代器指向元素,erase-擦去
6;
iterator erase(iterator first,iterator last);//删除向量中[first,last]中的元素
7;
void pop_back();//删除向量中最后一个元素
8;
void clear();//清空向量中的所有元素
9;
void resize(int n);//将元素数量改为num
10;
void resize(int n,T elem);//将元素数量改为num,如果size()变大了。多出来的新元素都是elem的副本
7、实际使用
vector<double> v1(10);//调用构造函数
vector<int> v;//动态数组的容器
for (int i = 0; i < 10; i++)
{
v.push_back(i + 1);//尾部插入元素
}
printf("%d\n", v[5]);//operator[idx]返回idx所标示的元素,不进行范围检查
v.pop_back();//尾部弹出容器
vector<int>::iterator vit;//定义一个迭代器
vit = v.begin() + 3;//begin()返回的是指向第一个元素的指针,再向后面移动三位,带来容器下标为3的位置
v.insert(vit, 123);//插入,迭代器
vit=v.begin();//插入数据后,导致内存重分配,内存地址改变,必须重新让迭代器执行新的容器
vit=vit+5;//vit(迭代器)指向了容器下标为6的元素
v.erase(vit);//iterator erase(iterator it);//删除向量中迭代器指向元素,erase-擦去
//begin函数返回容器中第一个元素的位置(返回迭代器)
//end函数返回容器中最后一个元素的下一个位置(返回迭代器)
for (vit = v.begin(); vit != v.end(); vit++)
{
printf("%d\n", *vit);
}
8、二维数组的操作
int row = 5, col = 6;//5行,6列
vector<vector<int>>myvector(row);//定义二维动态数组大小为row行
for (size_t i = 0; i < myvector.size(); i++)//动态二维数组为row行col列,值全为0
{
myvector[i].resize(col);//表示调整容器的大小为col,扩容后的每个元素的值为element,默认为0,就是给每一行指定大小为col
//void resize(int n);//将元素数量改为num
//int size()const;//返回向量中元素的个数
}
for (size_t i = 0; i < myvector.size(); i++)//输出动态二维数组
{
for (size_t j = 0; j < myvector[i].size(); j++)
{
myvector[i][j] = i * 10 + j;
cout << myvector[i][j] << " ";
}
cout << endl;
}
七、deque--双端队列
1、deque的特性
deuqe是双向开口的连续线性空间(动态将多个连续空间通过指针数组接合在一起)
可以理解为:deque是双端数组,vector是单端数组
1.头尾开放,能在头部尾部进行快速插入和删除
2.支持随机存取
3.在中段部分插入,删除元素速度较慢,因为所有元素都需要移动以腾出或填补空间
4.专门实现队列,选用deque(仅在头尾进行操作)
5.和vector一样,扩容会导致内存重分配,内存重分配会导致迭代器失效
6.注意:在使用vector和deque时,如果进行了插入和删除操作,一定要重新给迭代器赋值
2、实际使用
deque<int> d;
for (int i = 0; i < 10; i++)
d.push_back(i + 1);//从尾部添加了10个元素
for (int i = 0; i < 5; i++)
d.push_front((i + 1) * 10);//从头部添加了5个元素
d.pop_back();//从尾部删除一个元素
d.pop_front();//从头部删除一个元素
deque<int>::iterator dit;
d[3] = 123;//将容器下标为3的元素改为123
dit = d.begin() + 2;//让迭代器指向容器下标为2的元素的位置
d.insert(dit, 100);//在容器下标为2的元素的位置插入元素100;
dit = d.begin() + 8;;//deque执行了删除和插入操作后,进行了内存重分配,所以必须给迭代器重新赋值
dit -= 2;//迭代器指向下标为6的元素的位置
d.erase(dit);//删除当前迭代器指向位置里的元素
for (dit = d.begin(); dit != d.end(); dit++)
printf("%d\n", *dit);//打印容器里面的元素
3、构造与析构函数
1、deque c;//产生一个空的deque
2、deque c1(c2);//针对某个deque 产生同类型副本(所有元素都被拷贝)
3、deque c(n);//产生一个deque,含有n个元素
4、deque c(n,elem);//产生一个deque,含有n个元素,这些元素均是elem的副本
5、deque c(beg,end);//产生一个deque,以区间[beg;end)内的元素作为初值
6、~deque();//销毁所有元素,释放内存
4、非变动性函数
1、size();//返回容器的实际元素个数
2、empty();//判断容器大小是否为零
3、max_size();//返回可容纳的最大元素数量
4、operator==
5、operator!=
6、operator<
7、operator>
8、operator<=
9、operator>=
10、at(idx);//返回索引idx所标示的元素,如果idx越界,抛出out_of_range异常
11、operator[idx];//返回索引idx所标示的元素,不进行范围检查
12、front();//返回第一个元素。不检查元素是否存在
13、back();//返回最后一个元素,不检查元素是否存在
14、begin();//返回一个随机迭代器,指向第一个元素
15、end();//返回一个随机迭代器,指向最后元素的下一个位置
16、rbegin();//返回一个逆向迭代器,指向逆向迭代器时的第一个元素
17、rend();//返回一个逆向迭代器,指向逆向迭代器时的最后元素的下一个位置
5、变动性函数
1、operator=;//将c2的所有元素赋值给c1
2、assign(n,elem);//将n个elem 副本赋值给c
3、assign(beg,end);//将区间[beg;end)中的元素赋值给c
4、c1.swap(c2);//将c1 和c2 的元素互换5、swap(c1,c2);//将c1 和c2 的元素互换,此为全局函数
6、insert(pos,elem);//在pos位置插入一个elem副本,并返回新元素的位置
7、insert(pos,n,elem);//在pos位置插入elem的n个副本,无返回值
8、insert(pos,beg,end);//在pos位置插入在区间[beg;end)所有元素的副本,无返回值
9、push_back(elem);//在尾部添加elem的一个副本
10、pop_back();//删除最后一个元素(但不返回删除的元素)
11、push_front(elem);//在头部插入elem的一个副本
12、pop_front();//删除头部元素(但不返回删除的元素)
13、erase(pos);//删除pos位置上的元素,返回下一个元素位置
14、erase(beg,end);//删除[beg, end)区间内的所有元素,返回下一个元素位置
15、resize(num);//将大小(元素个数)改为num,如果size()增长了,新增元素都以默认构造函数产生
出来
16、resize(num,elem);//将大小(元素个数)改为num,如果size()增长了,新增元素都是elem的副本
17、clear();//移除所有元素,将容器清空
八、list
使用一个双向链表来管理元素
list的内部结构和vector或deque截然不同。
基本操作
list<int> mylist;
for (int i = 0; i < 10; i++)
mylist.push_back(i + 1);//插入10个元素在list中
mylist.push_front(4);
mylist.push_front(4);
mylist.push_front(4);//从头部插入3个4到list中
mylist.push_back(4);
mylist.push_back(4);
mylist.push_back(4);//从尾部插入3个4到list中
mylist.remove(4);//删除list这个双向链表中所有值为4的节点
//bool isodd(int val){ return val % 2 != 0; }
mylist.remove_if(isodd);////删除所有“造成isodd(elem)结果为true”的元素,就是说将链表中的所有元素都传到isodd中,返回为true就将那个元素删除
//bool ismin(int a, int b) { return a < b; }
mylist.unique(ismin);//如果存在若干相邻元素,都使ismin()的结果为true,则删除重复元素,只留下一个.就是说将链表中的所有元素都传到ismin这个函数中,结构返回为true就将b数据删除。4>1,不删除,1<2,删除2,1<3,删除3......
list<int>::iterator lit;
list<int> mylist1;
lit = mylist1.begin();//物理位置不是连续的,所以不能加减
mylist1.splice(lit, mylist);//将mylist内的所有元素转移到mylist之内、迭代器lit之前.因为是转移不是拷贝,所以原来的容器里就没有东西了(只需要将链表指针断开)
for (lit = mylist.begin(); lit != mylist.end(); lit++)
printf("%d\n", *lit);
1、list的特性
1.不支持随机存取
2.在任何位置上,插入和删除都很快
3.插入和删除操作不会导致迭代器失效
2、构造与析构函数
1、list c;//产生一个空的list
2、list c1(c2);//产生一个与c2同类型的list(每个元素都被复制)
3、list c(n);//产生拥有n个元素的list,这些元素都以默认构造函数初始化
4、list c(n,elem);//产生拥有n个元素的list,每个元素都是elem的副本
5、list c(beg,end);//产生一个list并以 [beg;end)区间内的元素为初值
6、~list();//销毁所有元素,释放内存
3、非变动性函数
1、c.size();//返回元素个数
2、c.empty();//判断容器大小是否为零
3、c.max_size();//返回元素的最大可能数量
4、operator==
5、operator!=
6、operator<
7、operator>
8、operator<=
9、operator>=
4、赋值操作
1、operator=
2、c.assign(n,elem);//将elem的n个拷贝赋值给c
3、c.assign(beg,end);//将区间[beg;end)的元素赋值给c
4、c1.swap(c2);//将c1和c2的元素互换
5、swap(c1,c2);//将c1和c2的元素互换,此为全局函数
5、直接取操作
1、c.front();//返回第一个元素,不检查元素是否存在
2、c.back();//返回最后一个元素,不检查元素是否存在
注意:list不支持随机存取,只有上述2个函数才能直接取元素
6、迭代器相关
1、c.insert(pos,elem);//在迭代器pos所指位置上插入一个elem副本,并返回新元素的位置
2、c.insert(pos,n,elem);//在迭代器pos所指位置上插入n个elem 副本,无返回值
3、c.insert(pos,beg,end);//在迭代器pos所指位置上插入[beg;end)区间内的所有元素的副本,无返值
4、c.push_back(elem);//在尾部追加一个elem副本
5、c.pop_back();//删除最后一个元素(但不返回其值)6、c.push_front(elem);//在头部插入一个elem副本
7、c.pop_front();//删除第一个元素(但不返回其值)
8、c.remove(val);//删除所有值为val的元素
9、c.remove_if(op);//删除所有“造成op(elem)结果为true”的元素
10、c.erase(pos);//删除迭代器pos所指元素,返回下一个元素位置
11、c.erase(beg,end);//删除区间[beg;end)内的所有元素,返回下一个元素位置
12、c.resize(num);//将元素容量变为num。如果size()变大,则以默认构造函数构造所有新增元素
13、c.resize(num,elem);//将元素容量变为num。如果size()变大,则以elem副本作为新增元素的初值
14、c.clear();//删除全部元素,将整个容器清空
7、特殊变动性操作
1、c.unique();//如果存在若干相邻而数值相同的元素,就删除重复元素,只留下一个
2、c.unique(op);//如果存在若干相邻元素,都使op()的结果为true,则删除重复元素,只留下一个
3、c1.splice(pos,c2);//将c2内的所有元素转移到c1之内、迭代器pos之前
4、c1.splice(pos,c2,c2pos);//将c2内的c2pos所指元素转移到c1内的pos所指位置上(c1 和c2可相同)
5、c1.splice(pos,c2,c2beg,c2end);//将c2内的[c2beg;c2end)区间内所有元素转移到c1内的pos之前(c1
和c2可相同)
6、c.sort();//以operator< 为准则,对所有元素排序
7、c.sort(op);//以op()为准则,对所有元素排序
8、c1.merge(c2);//假设c1和c2容器都包含已排序元素,将c2的全部元素转移到c1,并保证合并后的list
仍为已排序
9、c1.merge(c2,op);//假设c1和c2容器都包含op()原则下的已排序元素,将c2的全部元素转移到c1,并
保证合并后的list在op()原则下仍为已排序
10、c.reverse();//将所有元素反序
九、map
二叉查找树
map将key/value当做元素(pair),进行管理。可以根据key的排序准则自动将元素排序
1、map的特性
1.map的元素类型为key和T,必须满足一下两个条件
2.key/value必须具备可赋值和可复制的性质
3.对排序准则而言,key必须是可以比较的
4.map根据元素的key自动对元素进行排序,根据已知的key搜寻某个元素的时候,就能有很好的性能,而根据已知value搜寻元素时,性能比较差
基本操作
//map容器里面的元素是一组,有key和value组成
//map已序,key来排序
//key和value可赋值和可复制
map<int, string> ms;
ms[4] = "张三";//key为4,value为"张三"
ms[2] = "李四";//把key和value做一个映射,通过key来关联value
ms[4] = "王五";//会将第一个key为4的数覆盖掉
ms.insert(make_pair(3, "name"));//理解为函数模板,可以通过make_pair()里的数据来推导出map的具体类型
//ms.insert(make_pair(3, "kisss"));不能这样用
map<int, string>::iterator mit;
for (mit = ms.begin(); mit != ms.end(); mit++)
{
printf("key=%d,value=%s\n", mit->first, mit->second.c_str());
}
multimap<int, double> m1;
//m1[2]=3.12这样是不行的
m1.insert(make_pair(5, 1.2));
m1.insert(make_pair(3, 3.12));
m1.insert(make_pair(3, 1.3243));//key可以重复一样,然后打印的顺序是根据key的大小来排序的,key小的在前面打印,大的在后面打印
multimap<int, double>::iterator mit1;
for (mit1 = m1.begin(); mit1 != m1.end(); mit1++)
{
printf("key=%d,value=%lf\n", mit1->first, mit1->second);
}
//使用头文件#include<functional>
multimap<int, double,greater<int>> m1;//本来打印的时候是按照key的大小升序来排的,不过现在也是按照key的大小,不过是降序排序
2、构造与析构函数
1、map c;//产生一个空的map,其中不含任何元素
2、map c1(c2);//产生某个map的副本,所有元素均被复制
3、map c(beg,end);//以区间[beg;end)内的元素产生一个map
4、~map();//销毁所有元素,释放内存
3、非变动性操作
1、c.size();
2、c.empty();
3、c.max_size();
4、operator==
5、operator!=
6、operator<
7、operator>
8、operator<=
9、operator>=
4、特殊搜寻操作--multimap
1、count(key);//返回“键值等于key”的元素个数
2、find(key);//返回“键值等于key”的第一个元素,找不到就返回end()
3、lower_bound(key);//返回“键值为key”的元素的第一个可插入的位置,也就是“键值 >= key”的第一个
元素位置
4、upper_bound(key);//返回“键值为key”的元素的最后一个可插入的位置,也就是“键值 > key”的第一
个元素位置
5、equal_range(key);//返回“键值为key”的元素的第一个可插入位置和最后一个可插入位置,也就是“键
值 == key”的元素区间
5、赋值操作
1、operator=
2、c1.swap(c2);
3、swap(c1,c2);
6、迭代器相关
1、c.begin();
2、c.end();
3、c.rbegin();
4、c.rend();
7、插入和删除
1、c.insert(elem);//插入一份elem 副本
2、c.insert(pos,elem);//插入一份elem 副本
3、c.insert(beg,end);//将区间[beg;end)内所有元素的副本安插到c(无返回值)
4、c.erase(elem);//移除“key与elem 相等”的所有元素,返回被移除的元素个数
5、c.erase(pos);//移除迭代器pos 所指位置上的元素,无返回值
6、c.erase(beg,end);//移除区间[beg;end)内的所有元素,无返回值
7、c.clear();//移除全部元素,将整个容器清空
8、元素存取
1、operator[key]
9、基本用法
1、定义 > 排序
#include<xfunctional>
map<int, string, std::greater<int>> m;//降序的排序
2、插入
//1
m.insert(std::pair<int,double>(2,3.14));
//2
m.insert(map<int, double>::value_type(6, 1.11));
//3
m.insert(std::make_pair(5, 9.99));
十、set
set和map的区别不大,只是key和value一样的