STL
基本概念
STL(Standard Template Library,标准模板库)
STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
容器和算法之间通过迭代器进行无缝连接
STL几乎所有代码都采用了模板类或者模板函数
六大组件
容器,算法,迭代器,仿函数,适配器,空间配置器
1.容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据
2.算法:各种常用算法,如sort,find,copy,for_each等
3.迭代器:扮演了容器与算法间的胶合剂
4.仿函数:行为类似函数,可作为算法的某种策略
5.适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
6.空间配置器:负责空间的配置与管理
容器
常用数据结构:数组,链表,树,栈,队列,集合,映射表等
这些容器分为序列式容器和关联式容器两种:
序列式容器:强调值的排序
关联式容器:二叉树结构,各元素见没有严格的物理上的顺序关系
算法
分为:质变算法和非质变算法
质变算法:运算过程中会更改元素内容
非质变算法:不会改变
迭代器
提供一种方法,使之能够依序寻访某个容器所含的各个元素,又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
容器
vector
初识
void myPrint(int val) {
cout<<val<<endl;
}
void test() {
vector<int> v; //创建vector容器
//向容器中插入数据
v.push_back(10);
v.push_back(20);
//通过迭代器访问容器中的数据
//初学时可以把迭代器看成是指针
vector<int>::iterator itBegin = v.begin(); //起始迭代器指向容器中第一个元素
vector<int>::iterator itEnd = v.end(); //结束迭代器指向容器最后一个元素的下一个位置
//第一种遍历方式
while(itBegin!=itEnd) {
cout<<*itBegin<<endl;
itBegin++;
}
//第二种遍历方式
for(vector<int>::iterator it = v.begin();it!=v.end;it++) {
cout<<*it<<endl;
}
//第三种遍历方式
//STL遍历算法
for_each(v.begin(),v.end(),myPrint); //回调myPrint
}
vector数据结构与数组非常相似,也称为单端数组,不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:找一块更大的内存空间将原数据拷贝到新空间,释放原空间
常用方法
构造函数
vector<T> v,vector(v.begin,v.end) 将v[begin(),end())这个区间中的元素拷贝,vector(n,elem)将n个elem拷贝,vector(vec)拷贝构造
赋值
vector1=vector2;vector.assign(v.begin(),v.end())
容量和大小
vector.empty() 判断容器是否为空
vector.capacity() 返回容器容量
vector.size() 返回容器中元素个数
vector.resize() 重新指定容器长度,若容器变长以默认值填充新位置,若变短则末尾长处容器长度的元素删除
插入和删除
vector.push_back() 尾部插入元素
vector.pop_back() 删除最后一个元素
vector.insert(iterator,ele) 迭代器指向位置添加元素
vector.erase(iterator) 删除迭代器指向的元素
vector.clear() 删除容器中所有元素
数据存取
vector.at();vector[];vector.front() 返回第一个元素;vector.back()返回最后一个
互换容器
vector.swap(vec) vector与vec元素互换
用途:巧用swap可以收缩内存空间
预留空间
vector.reserve(len) 容器预留len个元素长度,预留位置不初始化,元素不可访问
用途:减少vector在动态拓展容量时的拓展次数
string
string和char*的区别
char*是一个指针,string是一个类,类内部封装了char*,管理这个字符串,是一个char*型容器。
常用方法
赋值
string = "",string.assign()
字符串拼接
string += "",string.append()
查找和替换
string.find(),string.rfind()
find是从左往右找,rfind是从右往左找
string.replace(start,n,s)s中从start开始的n个字符换为s
字符串比较
按字符串的ASCII码进行比较
=返回0,>返回1,<返回-1
string.compare()
字符串存取
string[],string.at()
插入删除
string.insert()
string.erase()
子串
string.substr(start,number)
deque
双端数组,可以对头和尾进行插入删除
与vector的区别:vector对于头部的插入删除效率低,deque相对而言对头部的插入删除速度比vector块,vector访问元素时的速度会比deque块,这和两者的内部实现有关
常用方法
构造函数
类似vector
deque<T>deq; deque<T>deq(iterator1,iterator2);deque<T>deq(number,element);
deque<T>deq(deq2)
赋值
deque1=deque2;deque.assign()
容器和大小
deque.empty();deque.size();deque.resize()
deque没有容量的概念
插入和删除
deque.push_back();deque.push_front()头插;deque.pop_back();deque.pop_front头删
deque.insert();deque.clear();deque.erase()
数据存取
deque[];deque.at();deque.front();deque.back()
排序操作
sort(iterator1,iterator2)根据迭代器对区间元素进行排序
用的时候要包含头文件algorithm
小练习
//有5名选手ABCDE。10个评委分别对每一名选手打分,去最高最低,取平均
class Person {
public:
Person(string name,int score) {
this->name = name;
this->score = score;
}
string name;
int score;
};
void createPerson(vector<Person>&g) {
string nameSeed = "ABCDE";
for(int i=0;i<5;i++) {
string name = "选手";
name += nameSeed[i];
int score = 0;
Person p(name,score);
g.push_back(p);
}
}
void setScore(vector<Person> &g) {
for(vector<Person>::iterator i=g.begin();i<g.end();i++) {
//将评委的分数放到deque
deque<int> d;
for(int i=0;i<10;i++) {
int score=rand()%41+60; //60-100
d.push_back(score);
}
sort(d.begin(),d.end());
d.pop_back();
d.pop_front();
int sum = 0;
for(deque<int>::iterator i=d.begin();i<d.end();i++) {
sum += *i;
}
i->score = sum/d.size();
}
}
stack
概念:一种先进后出(first in last out,FILO)的数据结构,它只有一个出口
因为只有顶端元素才可被外界使用,所以栈不允许有遍历行为
常用方法
构造函数
stack<T>;stack(stk)
赋值
stack1=stack2
数据存取
push() 向栈顶存数据;pop() 从栈顶移除第一个元素;top() 返回栈顶元素
大小操作
empty() 判断堆栈是否为空;size() 返回栈的大小
queue
概念:queue是一种先进先出(First in First out,FIFO)的数据结构,有两个出口
队尾只能进数据,队头只能出数据
只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为
常用方法
构造函数
queue<T> que;queue(que);
赋值
que1=que2
数据存取
push(ele) 往队尾添加ele;pop() 从队头移除一个元素;back() 返回队尾元素;front() 返回队头元素
大小操作
empty() 判断堆栈是否为空 size() 返回栈的大小
list(链表)
功能:将数据进行链式存储
数据域存放数据,指针域存放下一个数据的地址。方便对任意位置进行快速插入或删除元素
但是遍历速度没有数组块,占用空间比数组大
STL中的list是双向循环链表,前面数据的地址也存。最后一个节点的下一个节点地址记录的是第一个节点的地址,第一个节点记录的上一个节点的地址是最后一个节点的地址(与图片不符)
list的迭代器只支持前移和后移属于双向迭代器
常用方法
构造函数
list<T> lst ; list(iterator1,iterator2) ; list(n,element) ; list(list1)
赋值和交换
list.assign(iterator1,iterator2);list.assign(n,elem)
list1 = list2;
list.swap(lst) 与lst元素互换
大小操作
list.size() ; list.empty() ; list.resize();
插入和删除
list.push_back(); list.pop_back() ; list.push_front() ; list.pop_front() ;
list.insert(pos,ele) ; list(pos,n,ele) ; list(pos,iterator1,iterator2) 插入数据在pos位置
list.clear(); list.erase(iterator1,iterator2) ; list.erase(pos) ;
list.remove(elem) 删除容器中所有与elem值向匹配的元素
数据存取
不可以用[ ]访问
list.front(); 返回第一个元素
list.back(); 返回最后一个元素
反转和排序
list.reverse(); 反转
list.sort(); 排序 sort(iterator1,iterator2)用不了
所有不支持随机访问迭代器的容器,不可以用标准算法
set/multiset
所有元素都会在插入时自动被排序,属于关联式容器,底层结构是用二叉树实现
两者区别:set不允许有重复元素,multiset允许有重复元素
常用方法
构造函数
赋值
添加数据
set.insert()
大小和交换
set.empty() ; set.swap(set1) ; set.size();
删除
set.clear();set.erase()
查找和统计
set.find(ele) ; 查找ele元素是否存在若存在返回该元素的迭代器;不存在返回set.end()
set.count(ele) ; 统计ele元素的个数
排序
set默认从小到大排序,利用仿函数可以改变排序规则
//仿函数:是一个定义了一个含有重载()函数的类
class MyCompare {
public:
bool operator()(int v1,int v2) const {
return v1>v2;
}
};
void test01() {
//指定排序规则从大到小
set<int,MyCompare> s2; //在插入数据之前就要指定好排序规则 利用仿函数
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(50);
s2.insert(30);
for(set<int,MyCompare>::iterator i=s2.begin();i != s2.end();i++) {
cout<< *i << "";
}
}
map/multimap
map中所有元素都是pair,pair的第一个元素为key,第二个元素为value。所有元素都会根据元素的键值自动排序。是关联式容器,底层用二叉树实现。
map不允许有重复key元素,multimap允许
常用方法
构造函数
map<T1,T2> m ;map<>m1(m2)
大小和交换
map.empty();map.size();map.swap()
插入和删除
map.insert(pair<T1,T2>(t1,t2))
map.insert(make_pair(a,b))
//pair<T1,T2> 对组 获取第一个元素用 pair.first()第二个用pair.second()
map.clear();
map.erase(iterator) ;map.erase(iterator1,iterator2);map.erase(key)
查找和统计
map.find(key)
map.count(key)
排序
map.sort() //同样要修改排序规则利用仿函数