一. STL 标准模板库
- STL(Standard Template Library,标准模板库)
- STL 从广义上分为: 容器(container) 算法(algorithm) 迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接。
- STL 几乎所有的代码都采用了模板类或者模板函数
1. STL六大组件
STL大体分为六大组件,分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如vector、list、deque、set、map等,用来存放数据。
- 算法:各种常用的算法,如sort、find、copy、for_each等
- 迭代器:扮演了容器与算法之间的胶合剂。
- 仿函数:行为类似函数,可作为算法的某种策略。
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
- 空间配置器:负责空间的配置与管理。
2. STL中容器、算法、迭代器概念
(1). 容器:置物之所也
STL容器就是将运用最广泛的一些数据结构实现出来
常用的数据结构:数组, 链表,树, 栈, 队列, 集合, 映射表 等
这些容器分为序列式容器和关联式容器两种:
**序列式容器**:强调值的排序,序列式容器中的每个元素均有固定的位置。
**关联式容器**:二叉树结构,各元素之间没有严格的物理上的顺序关系
(2). 算法:问题之解法也
有限的步骤,解决逻辑或数学上的问题,这一门学科我们叫做算法(Algorithms)
算法分为:质变算法和非质变算法。
质变算法:是指运算过程中会更改区间内的元素的内容。例如拷贝,替换,删除等等
非质变算法:是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等等
(3). 迭代器:容器和算法之间粘合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
每个容器都有自己专属的迭代器
迭代器使用非常类似于指针,初学阶段我们可以先理解迭代器为指针
迭代器种类:
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、--, |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、--、[n]、-n、<、<=、>、>= |
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
二. string容器
1. 创建一个string
构造函数原型:
-
string();
//创建一个空的字符串 例如: string str;
string(const char* s);
//使用字符串s初始化 -
string(const string& str);
//使用一个string对象初始化另一个string对象 -
string(int n, char c);
//使用n个字符c初始化
#include <string>
//string构造
void test01()
{
string s1; //创建空字符串,调用无参构造函数
cout << "str1 = " << s1 << endl;
const char* str = "hello world";
string s2(str); //把c_string转换成了string
cout << "str2 = " << s2 << endl;
string s3(s2); //调用拷贝构造函数
cout << "str3 = " << s3 << endl;
string s4(10, 'a');
cout << "str3 = " << s3 << endl;
}
int main() {
test01();
system("pause");
return 0;
}
2. string的赋值
方法一 : 赋值
方法二 : assign方法
#include <iostream>
#include <string>
using namespace std;
void test01()
{
string str1;
str1 = "hello world";
cout << "str1 = " << str1 << endl;
string str2;
str2 = str1;
cout << "str2 = " << str2 << endl;
string str3;
str3 = 'a';
cout << "str3 = " << str3 << endl;
string str4;
str4.assign("hello c++");
cout << "str4 = " << str4 << endl;
string str5;
str5.assign("hello c++",5);
cout << "str5 = " << str5 << endl;
string str6;
str6.assign(str5);
cout << "str6 = " << str6 << endl;
string str7;
str7.assign(5, 'x');
cout << "str7 = " << str7 << endl;
}
int main() {
test01();
return 0;
}
3. string字符串拼接
方法一 : 相加是拼接
方法二 : append方法
//字符串拼接
void test01()
{
string str1 = "我";
str1 += "爱玩游戏";
cout << "str1 = " << str1 << endl;
str1 += ':';
cout << "str1 = " << str1 << endl;
string str2 = "LOL DNF";
str1 += str2;
cout << "str1 = " << str1 << endl;
string str3 = "I";
str3.append(" love ");
str3.append("game abcde", 4);
//str3.append(str2);
str3.append(str2, 4, 3); // 从下标4位置开始 ,截取3个字符,拼接到字符串末尾
cout << "str3 = " << str3 << endl;
}
int main() {
test01();
system("pause");
return 0;
}
4. string查找(find)和替换(replace)
查找 : find
替换 : replace
//查找和替换
void test01()
{
//查找
string str1 = "abcdefgde";
int pos = str1.find("de");
if (pos == -1)
{
cout << "未找到" << endl;
}
else
{
cout << "pos = " << pos << endl;
}
pos = str1.rfind("de");
cout << "pos = " << pos << endl;
}
void test02()
{
//替换
string str1 = "abcdefgde";
str1.replace(1, 3, "1111");
cout << "str1 = " << str1 << endl;
}
int main() {
//test01();
//test02();
system("pause");
return 0;
}
5. string字符串比较
字符串比较是按字符的ASCII码进行对比
6. string字符存取
方法一 : 按索引取值法
方法二 : at方法
void test01()
{
string str = "hello world";
for (int i = 0; i < str.size(); i++)
{
cout << str[i] << " ";
}
cout << endl;
for (int i = 0; i < str.size(); i++)
{
cout << str.at(i) << " ";
}
cout << endl;
//字符修改
str[0] = 'x';
str.at(1) = 'x';
cout << str << endl;
}
int main() {
test01();
system("pause");
return 0;
}
7. string插入(insert)和删除(erase)
插入 : insert
删除: erase
string str = "hello";
str.insert(1, "111");
cout << str << endl;
str.erase(1, 3); //从1号位置开始3个字符
cout << str << endl;
8. string切片 ( 子串 )
用substr(起始索引, 长度);
//子串
void test01()
{
string str = "abcdefg";
string subStr = str.substr(1, 3);
cout << "subStr = " << subStr << endl;
}
int main() {
test01();
return 0;
}
三. Vector容器
- vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别:
- 不同之处在于数组是静态空间,而vector可以动态扩展 ( 并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间 )
1. vector的创建
vector<T> v;
//采用模板实现类实现,默认构造函数
2. vector的赋值
方法1 : =号赋值
方法2 : assign(头,尾迭代器)
方法3 : assign(多少个, 什么)
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//赋值操作
void test01()
{
vector<int> v1; //无参构造
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
//
vector<int>v2;
v2 = v1;
printVector(v2);
vector<int>v3;
v3.assign(v1.begin(), v1.end());
printVector(v3);
vector<int>v4;
v4.assign(10, 100);
printVector(v4);
}
int main() {
test01();
system("pause");
return 0;
}
3. vector数据存取
方法 | 说明 |
---|---|
[]; | 索引取值 |
at(int idx); |
索引取值 |
front(); |
第一个数据元素 |
back(); |
最后一个数据元素 |
4. 容量和大小相关方法
方法 | 说明 |
---|---|
empty(); | 判断容器是否为空 |
capacity(); | 容器的容量 |
size(); | 返回容器中元素的个数 |
resize(int num); | 重新指定容器的长度为num, 若容器变长,则以默认值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除。 |
resize(int num, elem); | 重新指定容器的长度为num, 若容器变长,则以elem值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除 |
5. vector的增删
方法 | 说明 |
---|---|
push_back | 尾插 |
pop_back | 尾删 |
insert | 插入 |
erase (位置迭代器) | 删除 |
clear | 清空 |
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
//插入和删除
void test01()
{
vector<int> v1;
//尾插
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
v1.push_back(50);
printVector(v1);
//尾删
v1.pop_back();
printVector(v1);
//插入
v1.insert(v1.begin(), 100);
printVector(v1);
v1.insert(v1.begin(), 2, 1000);
printVector(v1);
//删除
v1.erase(v1.begin());
printVector(v1);
//清空
v1.erase(v1.begin(), v1.end());
v1.clear();
printVector(v1);
}
int main() {
test01();
return 0;
}
6. vector数据互换 swap
#include <vector>
void printVector(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test01()
{
vector<int>v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
vector<int>v2;
for (int i = 10; i > 0; i--)
{
v2.push_back(i);
}
printVector(v2);
//互换容器
cout << "互换后" << endl;
v1.swap(v2);
printVector(v1);
printVector(v2);
}
void test02()
{
vector<int> v;
for (int i = 0; i < 100000; i++) {
v.push_back(i);
}
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
v.resize(3);
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
//收缩内存
vector<int>(v).swap(v); //匿名对象
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
7. vector的遍历
容器: vector
算法: for_each
迭代器: vector<int>::iterator
示例:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// vector遍历的回调函数
void PrintItem(int val)
{
cout << val << endl;
}
int main()
{
/*-------------创建容器-------------------------*/
//创建vector容器对象,并且通过模板参数指定容器中存放的数据的类型
vector<int> v;
//向容器中放数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
//每一个容器都有自己的迭代器,迭代器是用来遍历容器中的元素
// v.begin()返回迭代器,这个迭代器指向容器中第一个数据
// v.end()返回迭代器,这个迭代器指向容器元素的最后一个元素的下一个位置
// vector<int>::iterator 拿到vector<int>这种容器的迭代器类型
/*-------------创建迭代器-------------------------*/
vector<int>::iterator pBegin = v.begin();
vector<int>::iterator pEnd = v.end();
/*-------------遍历容器-------------------------*/
//使用STL提供标准遍历算法 头文件 algorithm
for_each(pBegin, pEnd, PrintItem);
return 0;
}
其实我们也可以不用foreach 自己用循环操作迭代器来遍历
#include <iostream>
#include <vector>
using namespace std;
int main()
{
/*-------------创建容器-------------------------*/
//创建vector容器对象,并且通过模板参数指定容器中存放的数据的类型
vector<int> v;
//向容器中放数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
v.push_back(50);
/*-------------创建迭代器-------------------------*/
vector<int>::iterator pBegin = v.begin();
vector<int>::iterator pEnd = v.end();
/*-------------遍历容器-------------------------*/
for (vector<int>::iterator it = pBegin; it != pEnd; it++)
{
cout << *it << endl;
}
return 0;
}
8. vector也可以存放 对象 / 地址 等
#include <vector>
#include <string>
//自定义数据类型
class Person {
public:
Person(string name, int age) {
mName = name;
mAge = age;
}
public:
string mName;
int mAge;
};
//存放对象
void test01() {
vector<Person> v;
//创建数据
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {
cout << "Name:" << (*it).mName << " Age:" << (*it).mAge << endl;
}
}
//放对象指针
void test02() {
vector<Person*> v;
//创建数据
Person p1("aaa", 10);
Person p2("bbb", 20);
Person p3("ccc", 30);
Person p4("ddd", 40);
Person p5("eee", 50);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
v.push_back(&p5);
for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++) {
Person * p = (*it);
cout << "Name:" << p->mName << " Age:" << (*it)->mAge << endl;
}
}
int main() {
test01();
test02();
system("pause");
return 0;
}
四. list 列表(链表)
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
list是一个线性链表结构,它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针。它无需分配指定的内存大小且可以任意伸缩,这是因为它存储在非连续的内存空间中,并且由指针将有序的元素链接起来。
list的优点:
- 采用动态存储分配,不会造成内存浪费和溢出
- 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
- 链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
- 由于其结构的原因,list 随机检索的性能非常的不好,因为它不像vector那样直接找到元素的地址,而是要从头一个一个的顺序查找,这样目标元素越靠后,它的检索时间就越长。检索时间与目标元素的位置成正比。
0. list和vector的区别
- vector底层实现是数组;list是双向 链表。
- vector支持随机访问,list不支持。
- vector是顺序内存,list不是。
- vector在中间节点进行插入删除会导致内存拷贝,list不会。
- vector一次性分配好内存,不够时才进行2倍扩容;list每次插入新节点都会进行内存申请。
- vector随机访问性能好,插入删除性能差;list随机访问性能差,插入删除性能好。
- vector拥有一段连续的内存空间,因此支持随机访问,如果需要高效的随即访问,而不在乎插入和删除的效率,使用vector。
- list拥有一段不连续的内存空间,如果需要高效的插入和删除,而不关心随机访问,则应使用list。
1. list的创建
list<T> lst;
//采用模板实现类实现,默认构造函数
2. list的赋值
方法1 : =号赋值
方法2 : assign(头,尾迭代器)
方法3 : assign(多少个, 什么)
3. list数据存取
方法 | 说明 |
---|---|
front(); |
第一个数据元素 |
back(); |
最后一个数据元素 |
注意 : list容器中不可以通过[]或者at方式访问数据
4. 容量和大小相关方法
方法 | 说明 |
---|---|
empty(); | 判断容器是否为空 |
size(); | 返回容器中元素的个数 |
resize(int num); | 重新指定容器的长度为num, 若容器变长,则以默认值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除。 |
resize(int num, elem); | 重新指定容器的长度为num, 若容器变长,则以elem值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除 |
list没有容量的概念
5. list的增删
两端插入操作:
-
push_back(elem);
//在容器尾部添加一个数据 -
push_front(elem);
//在容器头部插入一个数据 -
pop_back();
//删除容器最后一个数据 -
pop_front();
//删除容器第一个数据
指定位置操作:
-
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位置的数据,返回下一个数据的位置。 -
remove(elem);
//删除容器中所有与elem值匹配的元素。
多了一个remove
6. list数据互换 swap
同vector
7. list的遍历
容器: list
算法: for_each
迭代器: list<int>::iterator
8. list的排序
sort(iterator beg, iterator end)
//对beg和end区间内元素进行排序
9. list的翻转
reverse(iterator beg, iterator end)
//对beg和end区间内元素进行排序
五. deque容器 (队列)
- 双端数组,可以对头端进行插入删除操作
deque与vector区别:
- vector对于头部的插入删除效率低,数据量越大,效率越低
- deque相对而言,对头部的插入删除速度回比vector快
- vector访问元素时的速度会比deque快,这和两者内部实现有关
- deque没有容量的概念
0. 一般很少使用deque
一般很少使用deque
只是简单的存储元素使用vector即可,如果对元素任意位置进行插入或者删除操作比较多,使用list即可,所以一般很少使用deque;deque的最大应用,就是用其作为标准库中stack和queue的底层结构。
关于deque的话了解即可,将重点放在vector、list、map、set等容器上即可。
1. deque的创建
deque<T> d;
//采用模板实现类实现,默认构造函数
2. deque的赋值
方法1 : =号赋值
方法2 : assign(头,尾迭代器)
方法3 : assign(多少个, 什么)
3. deque数据存取
方法 | 说明 |
---|---|
[]; | 索引取值 |
at(int idx); |
索引取值 |
front(); |
第一个数据元素 |
back(); |
最后一个数据元素 |
4. 容量和大小相关方法
方法 | 说明 |
---|---|
empty(); | 判断容器是否为空 |
size(); | 返回容器中元素的个数 |
resize(int num); | 重新指定容器的长度为num, 若容器变长,则以默认值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除。 |
resize(int num, elem); | 重新指定容器的长度为num, 若容器变长,则以elem值填充新位置。 如果容器变短,则末尾超出容器长度的元素被删除 |
deque没有容量的概念
5. deque的增删
两端插入操作:
-
push_back(elem);
//在容器尾部添加一个数据 -
push_front(elem);
//在容器头部插入一个数据 -
pop_back();
//删除容器最后一个数据 -
pop_front();
//删除容器第一个数据
指定位置操作:
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位置的数据,返回下一个数据的位置。
6. deque数据互换 swap
同vector
7. deque的遍历
容器: deque
算法: for_each
迭代器: deque<int>::iterator
示例:
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
void printItem(int val)
{
cout << val << endl;
}
int main()
{
deque<int> v1;
for (int i = 0; i < 10; i++)
v1.push_back(i);
for_each(v1.begin(), v1.end(), printItem);
return 0;
}
8. deque的排序
sort(iterator beg, iterator end)
//对beg和end区间内元素进行排序
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
void printItem(int val)
{
cout << val << endl;
}
int main()
{
deque<int> d1;
d1.push_back(5);
d1.push_back(2);
d1.push_back(3);
d1.push_back(1);
d1.push_back(4);
sort(d1.begin(), d1.end());
for_each(d1.begin(), d1.end(), printItem);
return 0;
}
9. deque的翻转
reverse(iterator beg, iterator end)
//对beg和end区间内元素进行排序