STL的诞生
- 长久以来,软件界一直希望建立一种可重复利用的东西
- c++的面向对象和泛型编程思想,目的就是复用性的提升
- 大多数情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
- 为了建立数据结构和算法的一套标准,诞生了STL
STL基本概念
- STL(Standard Template Library)标准模板库
- STL 从广义上分为:容器(container)、算法(algorithm)、迭代器(iterator)
- 容器和算法之间通过迭代器进行无缝连接
- STL几乎所有的代码都采用了模板类或者模板函数
STL六大组件
六大组件分别是:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器
- 容器:各种数据结构,如 vector、list、deque、set、map 等,用来存放数据
- 算法:各种常用的算法,如 sort、find、copy、for_each 等
- 迭代器:扮演了容器与算法之间的胶合剂
- 仿函数:行为类似函数,可作为算法的某种策略
- 适配器:一种用来修饰容器或者仿函数或迭代器接口的东西
- 空间配置器:负责空间的配置与管理
String 容器
基本概念
本质
- string 是 C++ 风格的字符串,而 string 本质上是一个类
string 和 char* 区别
- char* 是一个指针,通过这个指针管理一连串的 char 类型的空间
- string 是一个类,类内部封装了 char*,管理这个字符串,是一个 char* 类型的容器
特点
string 类内部封装了很多成员方法
例如:查找 find,拷贝 copy,删除 delete,替换 replace,插入 insert
string 管理 char* 所分配的内存,不用担心复制越界和取值越界等问题,这些问题由类内部进行负责
构造函数
#include <iostream>
#include <string>
using namespace std;
void test()
{
string s1; // 背后会调用默认的构造函数
const char* s2 = "hello world"; // C语言风格的字符串
string s3(s2); // 背后会调用拷贝构造函数
cout << s3 << endl;
string s4(5, 'a');
cout << s4 << endl; // 输出结果:aaaaa
}
int main()
{
test();
}
赋值操作
#include <iostream>
#include <string>
using namespace std;
void test()
{
string s1;
s1 = "hello world";
cout << s1 << endl;
string s2;
s2 = s1;
cout << s2 << endl;
string s3;
s3 = 'a';
cout << s3 << endl;
string s4;
s4.assign("hello world");
cout << s4 << endl;
string s5;
s5.assign("hello world", 5); // 前5个字符将会赋值给字符串
cout << s5 << endl;
string s6;
s6.assign(s5);
cout << s6 << endl;
string s7;
s7.assign(6, 'a'); // 等价于 s7 = "aaaaaa"
cout << s7 << endl;
}
int main()
{
test();
}
字符串拼接
#include <iostream>
#include <string>
using namespace std;
void test()
{
string s1 = "我";
s1 += "爱唱歌"; // 追加字符串
s1 += 'a'; // 追加一个字符
string s2 = "666";
s1 += s2;
string s3 = "I";
s3.append(" Love ");
s3.append("game abcde", 4); // 只追加前面4个字符
cout << s3 << endl; // 输出结果:I Love game
s3.append(s2);
s3.append(s2, 0, 4); // 从第0个位置开始追加字符,只追加4个
}
int main()
{
test();
}
查找和替换
#include <iostream>
#include <string>
using namespace std;
void test()
{
// 查找
string s1 = "abcdefg";
int position = s1.find("de"); // 第一次出现"de"的位置
cout << position << endl; // 输出结果:3
position = s1.rfind("ef"); // right-find 从右开始查找
// 替换
string s2 = "abcde";
s2.replace(1, 3, "kkkk"); // 从1号位置起的3个字符,替换为 kkkk
}
int main()
{
test();
}
字符串比较
#include <iostream>
#include <string>
using namespace std;
void test()
{
// 字符串比较大小---ASCII码 比较大小
string s1 = "hello";
string s2 = "abcdef";
int ret = s1.compare(s2);
if (ret == 0)
{
cout << "s1 == s2" << endl;
}
else if (ret > 0)
{
cout << "s1 > s2" << endl;
}
else
{
cout << "s1 < s2" << endl;
}
}
int main()
{
test();
}
单个字符存取
#include <iostream>
#include <string>
using namespace std;
void test()
{
string s1 = "hello";
// 1. 通过 [] 访问单个字符
for (int i = 0; i < s1.size(); i++)
{
cout << s1[i];
}
cout << endl;
// 2. 通过 at 方式访问单个字符
for (int i = 0; i < s1.size(); i++)
{
cout << s1.at(i);
}
// 修改单个字符
s1[0] = 'k';
s1.at(0) = 'm';
}
int main()
{
test();
}
插入与删除
#include <iostream>
#include <string>
using namespace std;
void test()
{
string s = "hello";
// 插入
s.insert(1, "vvv");
cout << s << endl; // hvvvello
// 删除
s.erase(1, 3); // 从1号位开始删除,删除3个
cout << s << endl; // hello
}
int main()
{
test();
}
子串获取
#include <iostream>
#include <string>
using namespace std;
void test()
{
// 获取子串
string s1 = "hello";
string s2 = s1.substr(1, 3);
cout << s2 << endl; // 输出结果:ell
// 应用
string email = "jack@qq.com";
int position = email.find("@");
string username = email.substr(0, position);
cout << "username: " << username << endl; // 输出结果:jack
}
int main()
{
test();
}
vector 容器
存放内置数据类型
#include <iostream>
#include <vector>
#include <algorithm> // 标准算法头文件
using namespace std;
// vector 容器存放内置数据类型
// myPrint函数声明
void myPrint(int val);
void test()
{
// 创建一个vector容器
vector<int> v;
// 向容器中插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
// 通过迭代器访问容器中的数据
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函数实现
void myPrint(int val)
{
cout << val << endl;
}
int main()
{
test();
}
存放自定义数据类型
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
// vector容器中存放自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test()
{
// 准备好vector容器
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 << "姓名:" << (*it).m_Name << " 年龄:" << (*it).m_Age << endl;
}
}
int main()
{
test();
}
vector 容器的嵌套
#include <iostream>
#include <vector>
using namespace std;
// 容器中嵌套容器
void test()
{
// 准备一个外层容器
vector< vector<int> > v;
// 准备一些内层容器
vector<int> v1;
vector<int> v2;
vector<int> v3;
vector<int> v4;
vector<int> v5;
// 向内层容器添加数据
for (int i = 0; i < 4; i++)
{
v1.push_back(i + 1);
v2.push_back(i + 1);
v3.push_back(i + 1);
v4.push_back(i + 1);
v5.push_back(i + 1);
}
// 将内层容器插入到外层容器中
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
v.push_back(v5);
// 遍历取出数据
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++)
{
for (vector<int>::iterator itt = (*it).begin(); itt != (*it).end(); itt++)
{
cout << *itt << " ";
}
cout << endl;
}
}
int main()
{
test();
}
/*
运行结果:
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
*/
构造函数
#include <iostream>
#include <vector>
using namespace std;
// vector 容器构造
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// 通过默认的无参构造函数来实现构造
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1);
// 通过区间方式进行构造
vector<int> v2(v1.begin(), v1.end());
printVector(v2);
// n个element方式构造
vector<int> v3(10, 100); // 10 个 100
printVector(v3);
// 拷贝构造
vector<int> v4(v3);
printVector(v4);
}
int main()
{
test();
}
赋值操作
#include <iostream>
#include <vector>
using namespace std;
// vector 赋值操作
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1); // 0 1 2 3 4 5 6 7 8 9
// 通过符号"="实现
vector<int> v2;
v2 = v1;
printVector(v2); // 0 1 2 3 4 5 6 7 8 9
// 通过 assign 实现
vector<int> v3;
v3.assign(v1.begin(), v1.end());
printVector(v3); // 0 1 2 3 4 5 6 7 8 9
// 通过 n 个 element 实现
vector<int> v4;
v4.assign(10, 99);
printVector(v4); // 99 99 99 99 99 99 99 99 99 99
}
int main()
{
test();
}
容量和大小
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// vector 容器的容量和大小操作
vector<int> v1;
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1); // 0 1 2 3 4 5 6 7 8 9
if (v1.empty())
{
cout << "v1为空" << endl;
}
else
{
cout << "v1不为空" << endl;
cout << "v1的容量为:" << v1.capacity() << endl;
cout << "v1的大小为:" << v1.size() << endl;
}
// 重新指定大小
v1.resize(15, 0); // 第二个参数可以不写,默认填充值为0
printVector(v1); // 如果重新指定的比原来的长,默认用0填充
v1.resize(5);
printVector(v1); // 如果重新指定的比原来的短,超出部分将会被删除
}
int main()
{
test();
}
插入和删除
#include <iostream>
#include <vector>
using namespace std;
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// vector 插入与删除
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); // 输出结果:10 20 30 40 50
// 尾删
v1.pop_back();
printVector(v1); // 输出结果:10 20 30 40
// 插入 <-第一个参数是迭代器->
v1.insert(v1.begin(), 99); // 在头部插入数据 99
printVector(v1); // 输出结果:99 10 20 30 40
v1.insert(v1.begin(), 2, 888); // 在头部插入两个数据 888
printVector(v1); // 输出结果:888 888 99 10 20 30 40
// 删除 <-第一个参数是迭代器->
v1.erase(v1.begin()); // 在头部删除一个数据
printVector(v1); // 输出结果:888 99 10 20 30 40
v1.erase(v1.begin(), v1.end()); // 从头部开始删除,一直删除到尾部(也就是清空)
v1.clear(); // 直接清空
printVector(v1); // 输出结果:""
}
int main()
{
test();
}
数据存取
#include <iostream>
#include <vector>
using namespace std;
void test()
{
vector<int> v1;
// ----------- 存取数据 -----------
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
// ----------- 输出数据 -----------
for (int i = 0; i < v1.size(); i++)
{
// 利用 [] 方式访问数组中的元素
cout << v1[i] << " ";
}
cout << endl;
// ----------- 输出数据 -----------
for (int i = 0; i < v1.size(); i++)
{
// 利用 at 方式访问元素
cout << v1.at(i) << " ";
}
cout << endl;
// 获取第一个元素
cout << "第一个元素为:" << v1.front() << endl;
cout << "最后一个元素为:" << v1.back() << endl;
}
int main()
{
test();
}
/*
运行结果:
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
第一个元素为:0
最后一个元素为:9
*/
互换容器
#include <iostream>
#include <vector>
using namespace std;
// 输出容器内容
void printVector(vector<int>& v)
{
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1()
{
// 容器1
vector<int> v1;
// 添加数据
for (int i = 0; i < 10; i++)
{
v1.push_back(i);
}
printVector(v1); // 0 1 2 3 4 5 6 7 8 9
// 容器2
vector<int> v2;
for (int i = 10; i > 0; i--)
{
v2.push_back(i);
}
printVector(v2); // 10 9 8 7 6 5 4 3 2 1
// 交换容器
/*
v1 指向 容器1
v2 指向 容器2
------------
交换容器之后
------------
v1 指向 容器2
v2 指向 容器1
*/
v1.swap(v2);
printVector(v1); // 10 9 8 7 6 5 4 3 2 1
printVector(v2); // 0 1 2 3 4 5 6 7 8 9
}
void test2()
{
// 交换容器的实际应用:巧用swap可以收缩空间内存
// 1. 创建一个vector容器
vector<int> v;
// 2. 向容器里面添加1000个数据
for (int i = 0; i < 1000; i++)
{
v.push_back(i);
}
cout << endl;
cout << "v的容量为:" << v.capacity() << endl; // 输出结果:v的容量为:1066
cout << "v的大小为:" << v.size() << endl; // 输出结果:v的大小为:1000
// 3. 重新指定大小
v.resize(30); // 意味着原来使用了1000个数据,现在只用了30个,剩下的大量空间都被浪费了
cout << endl;
cout << "v的容量为:" << v.capacity() << endl; // 输出结果:v的容量为:1066
cout << "v的大小为:" << v.size() << endl; // 输出结果:v的大小为:30
// 4. 巧用swap收缩内存
/*
原理:利用"匿名对象"创建出来没多久就会被系统自动回收的特点
有名对象:
vector<int> v // 无参
vector<int> v(...) // 含参
匿名对象:
vector<int>() // 无参
vector<int>(v) // 含参or拷贝
具体实现:
1. 复制v容器的30个数据,创建一个匿名对象容器 (到时候系统会自动回收)
2. 马上让它和v容器进行互换
v ---> 容量为1066的容器
匿名 ---> 容量为30的容器
---------- 互换后 ------------
v ---> 容量为30的容器
匿名 ---> 容量为1066的容器
3. 语句结束,系统马上回收匿名对象容器 (即: 回收了容量为1066的容器)
*/
vector<int> (v).swap(v); // 创建匿名对象容器,并马上交换容器
cout << endl;
cout << "v的容量为:" << v.capacity() << endl; // 输出结果:v的容量为:30
cout << "v的大小为:" << v.size() << endl; // 输出结果:v的大小为:30
}
int main()
{
test1();
test2();
}
预留空间
#include <iostream>
#include <vector>
using namespace std;
void test()
{
vector<int> v;
v.reserve(1000); // 利用reserve预留空间,可以减少因为容器容量不足而重新开辟内存的次数
int num = 0; // 统计开辟次数
int* p = NULL;
for (int i = 0; i < 1000; i++)
{
v.push_back(i);
if (p != &v[0])
{
p = &v[0];
num++;
}
}
cout << "开辟次数 num = " << num << endl; // 开辟次数 num = 1
cout << "容器容量:" << v.capacity() << endl; // 容器容量:1000
cout << "容器大小:" << v.size() << endl; // 容器容量:1000
}
int main()
{
test();
}
deque 容器
构造函数
#include <iostream>
#include <deque>
using namespace std;
void printDeque(deque<int>& d)
{
for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
deque<int> d1;
for (int i = 0; i < 10; i++)
{
d1.push_back(i);
}
printDeque(d1);
deque<int> d2(d1.begin(), d1.end());
printDeque(d2);
deque<int> d3(10, 99);
printDeque(d3);
deque<int> d4(d3);
printDeque(d4);
}
int main()
{
test();
}
赋值操作
- 与 vector 操作一样
容量和大小
- deque 没有容量的概念
- 判断是否为空 — empty
- 返回元素个数 — size
- 重新指定个数 — resize
插入和删除
#include <iostream>
#include <deque>
using namespace std;
// 输出容器数据
void printDeque(deque<int>& d)
{
for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
deque<int> d1;
// 尾插
d1.push_back(10);
d1.push_back(20);
printDeque(d1); // 10 20
// 头插
d1.push_front(66);
d1.push_front(88);
printDeque(d1); // 88 66 10 20
// 尾删
d1.pop_back();
printDeque(d1); // 88 66 10
// 头删
d1.pop_front();
printDeque(d1); // 66 10
// 插入
d1.insert(d1.begin(), 99);
printDeque(d1); // 99 66 10
d1.insert(d1.begin(), 2, 77);
printDeque(d1); // 77 77 99 66 10
// 按照区间进行插入
deque<int> d2;
d2.push_back(111);
d2.push_back(222);
d2.push_back(333);
printDeque(d2); // 111 222 333
d1.insert(d1.begin(), d2.begin(), d2.end());
printDeque(d1); // 111 222 333 77 77 99 66 10
// 删除
deque<int>::iterator it = d1.begin();
it++;
d1.erase(it);
printDeque(d1); // 111 333 77 77 99 66 10
// 按照区间的方式进行删除
d1.erase(d1.begin(), d1.end()); // 等价于清空操作: d1.clear();
printDeque(d1); // 空
}
int main()
{
test();
}
存取操作
- 除了用迭代器获取 deque 容器中的元素,[ ] 和 at 也可以
- front 返回容器第一个元素
- back 返回容器最后一个元素
排序
#include <iostream>
#include <deque>
#include <algorithm>
using namespace std;
// 输出容器数据
void printDeque(deque<int>& d)
{
for (deque<int>::iterator it = d.begin(); it != d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
deque<int> d;
d.push_back(10);
d.push_back(20);
d.push_back(30);
d.push_front(100);
d.push_front(200);
d.push_front(300);
printDeque(d); // 300 200 100 10 20 30
// 排序
// 1. 默认排序规则:从小到大的升序排序
// 2. 对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序,使用它之前记得 #include <algorithm>
sort(d.begin(), d.end());
printDeque(d); // 10 20 30 100 200 300
}
int main()
{
test();
}
vector 与 deque 案例
#include <iostream>
#include <vector>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;
/*
有5名选手:选手ABCDE,10个评委分别对每一位选手打分
去除最高分,去除评委中最低分,取平均分
*/
// 选手类:负责维护选手信息
class Person
{
public:
Person(string name, int score) :m_Name(name), m_Score(score)
{
cout << "成功构造了一名选手!" << endl;
}
string m_Name;
int m_Score;
};
// 创建选手,并且放入到容器中
void createPerson(vector<Person>& v)
{
string nameSeed = "ABCDE";
int score = 0;
for (int i = 0; i < 5; i++)
{
string name = "选手";
name += nameSeed[i];
Person p(name, score);
v.push_back(p); // 将创建的Person对象 放入到容器中
}
}
// 输出容器中的数据
void printVector(vector<Person>& v)
{
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
cout << "{" << (*it).m_Name << ", " << (*it).m_Score << "}" << endl;
}
}
// 给选手打分
void setScore(vector<Person>& v)
{
// 从容器中,获取每一个选手,修改选手里面默认的score数据
for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
{
// 1. 准备score数据:
// 1.1. 准备好一个容器
deque<int> d;
// 1.2. 随机出10个数,将它们放到容器中
for (int i = 0; i < 10; i++)
{
int score = rand() % 41 + 60; // 60 ~ 100
d.push_back(score);
}
// 1.3. 将分数进行排序
sort(d.begin(), d.end()); // 默认升序-由小到大
// 1.4. 去除最高和最低分
d.pop_back();
d.pop_front();
// 1.5. 取平均分
int sum = 0;
for (deque<int>::iterator dit = d.begin(); dit != d.end(); dit++)
{
sum += (*dit);
}
int average = (int)(sum / d.size());
// 2. 修改score数据
(*it).m_Score = average;
}
}
void test()
{
// 随机数种子
srand((unsigned int)time(NULL));
// 1. 创建5名选手
vector<Person> v; // 创建容器
createPerson(v); // 创建选手,并放入容器中
cout << "-----------------------" << endl;
printVector(v); // 插入容器中的数据(选手)
// 2. 给5名选手打分
setScore(v);
cout << "-----------------------" << endl;
// 3. 显示最后得分
printVector(v);
}
int main()
{
test();
}
/*
运行结果:
成功构造了一名选手!
成功构造了一名选手!
成功构造了一名选手!
成功构造了一名选手!
成功构造了一名选手!
-----------------------
{选手A, 0}
{选手B, 0}
{选手C, 0}
{选手D, 0}
{选手E, 0}
-----------------------
{选手A, 78}
{选手B, 82}
{选手C, 74}
{选手D, 80}
{选手E, 77}
*/
stack 容器
#include <iostream>
#include <stack>
using namespace std;
void test()
{
// stack(栈)容器
stack<int> s;
// 查看栈的大小
cout << "栈的大小:" << s.size() << endl;
// 入栈
s.push(10);
s.push(20);
s.push(30);
// 查看栈的大小
cout << "栈的大小:" << s.size() << endl;
// 只要栈不为空,查看栈顶,并且执行出栈操作
while (!s.empty())
{
// 查看栈顶元素
cout << "栈顶元素为:" << s.top() << endl;
// 出栈
s.pop();
}
// 查看栈的大小
cout << "栈的大小:" << s.size() << endl;
}
int main()
{
test();
}
/*
运行结果:
栈的大小:0
栈的大小:3
栈顶元素为:30
栈顶元素为:20
栈顶元素为:10
栈的大小:0
*/
queue 容器
#include <iostream>
#include <queue>
using namespace std;
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
void test()
{
// 创建队列
queue<Person> q;
// 准备数据
Person p1("Jack", 20);
Person p2("Tom", 30);
Person p3("Peter", 40);
Person p4("Jim", 50);
cout << "队列大小:" << q.size() << endl;
// 入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
cout << "队列大小:" << q.size() << endl;
// 判断只要队列不为空,查看队头,查看队尾,然后出队
while (!q.empty())
{
// 查看对头
cout << "队头元素:{" << q.front().m_Name << ", " << q.front().m_Age << "}\t";
// 查看队尾
cout << "队尾元素:{" << q.back().m_Name << ", " << q.back().m_Age << "}" << endl;
// 出队
q.pop();
}
cout << "队列大小:" << q.size() << endl;
}
int main()
{
test();
}
list 容器
基本认识
功能:将数据进行链式存储
链表(list):是一种物理存储单元上非连续的存储结构
STL中的链表是双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
构造函数
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// 默认构造
list<int> l1; // 创建list容器
// 添加数据
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
// 遍历容器
printList(l1); // 10 20 30 40
// 区间方式构造
list<int> l2(l1.begin(), l1.end());
printList(l2); // 10 20 30 40
// 拷贝构造
list<int> l3(l2);
printList(l3); // 10 20 30 40
// n个element
list<int> l4(10, 99);
printList(l4); // 99 99 99 99 99 99 99 99 99 99
}
int main()
{
test();
}
赋值与交换
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// 准备一个list容器
list<int> l1;
// 装入数据
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
l1.push_back(40);
printList(l1); // 10 20 30 40
list<int> l2;
l2 = l1; // operator= 赋值
printList(l2); // 10 20 30 40
list<int> l3;
l3.assign(l2.begin(), l2.end()); // assign 赋值
printList(l3); // 10 20 30 40
list<int> l4;
l4.assign(10, 88); // 10个88
printList(l4); // 88 88 88 88 88 88 88 88 88 88
// 交换
list<int> l5, l6;
l5.assign(6, 111);
l6.assign(6, 222);
l5.swap(l6);
printList(l5); // 222 222 222 222 222 222
printList(l6); // 111 111 111 111 111 111
}
int main()
{
test();
}
大小操作
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// 创建list容器,并存入数据
list<int> l1;
l1.push_back(10);
l1.push_back(20);
l1.push_back(30);
printList(l1); // 10 20 30
// 判断容器是否为空
if (l1.empty())
{
cout << "l1为空" << endl;
}
else
{
cout << "l1不为空" << endl; // l1不为空
cout << "l1的元素个数为:" << l1.size() << endl; // l1的元素个数为:3
}
// 重新指定大小
l1.resize(10, 666); // 第一个参数是重新指定大小为10,第二个参数是使用666填充,如果第二个参数不传入,默认填充值是0
printList(l1);
}
int main()
{
test();
}
插入和删除
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
list<int> l;
// 尾插
l.push_back(10);
l.push_back(20);
l.push_back(30);
// 头插
l.push_front(11);
l.push_front(22);
l.push_front(33);
printList(l); // 33 22 11 10 20 30
// 尾删
l.pop_back();
l.pop_front();
printList(l); // 22 11 10 20
// 插入
l.insert(l.begin(), 3333);
printList(l); // 3333 22 11 10 20
// 删除
l.erase(l.begin());
printList(l); // 22 11 10 20
// 移除
l.remove(10); // 移除容器中所有的和指定内容匹配的值
printList(l); // 22 11 20
}
int main()
{
test();
}
数据存取
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
list<int> l;
l.push_back(10);
l.push_back(20);
l.push_back(30);
// list容器不可以用 [] 访问容器中的元素
// list容器不可以用 at 访问容器中的元素
// 原因:list容器本质是一个链表,不是用连续的线性空间存储数据,迭代器也是不支持随机访问的
cout << "第一个元素:" << l.front() << endl;
cout << "最后一个元素:" << l.back() << endl;
// list容器的迭代器不支持随机访问
// 例如:it+3、it+19 是不允许的
// 但是它支持双向访问,即 it++ 与 it--
list<int>::iterator it = l.begin();
it++;
it--;
}
int main()
{
test();
}
反转和排序
#include <iostream>
#include <list>
using namespace std;
void printList(const list<int> &l)
{
for (list<int>::const_iterator it = l.begin(); it != l.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
list<int> l;
l.push_back(20);
l.push_back(50);
l.push_back(40);
l.push_back(10);
l.push_back(30);
cout << "反转前:" << endl;
printList(l); // 20 50 40 10 30
l.reverse();
cout << "反转后:" << endl;
printList(l); // 30 10 40 50 20
// 所有不支持随机访问迭代器的容器,不可以使用标准算法
// 不支持随机访问迭代器的容器,内部会提供对应的一些算法
// sort(l.begin(), l.end());
cout << "排序前:" << endl;
printList(l); // 30 10 40 50 20
l.sort(); // 默认排序规则:从小到大,升序
cout << "排序后:" << endl;
printList(l); // 10 20 30 40 50
}
int main()
{
test();
}
排序案例
#include <iostream>
#include <list>
using namespace std;
class Person
{
public:
Person(string name, int age, int height)
{
this->m_Name = name;
this->m_Age = age;
this->m_Height = height;
}
string m_Name;
int m_Age;
int m_Height;
};
// 排序规则函数
bool comparePerson(Person& p1, Person& p2)
{
// 按照年龄从小到大的顺序进行排序--升序
// 如果年龄相同,则按照身高升序进行排序
if (p1.m_Age == p2.m_Age)
{
return p1.m_Height < p2.m_Height;
}
return p1.m_Age < p2.m_Age;
}
void test()
{
// 创建容器
list<Person> l;
// 准备数据
Person p1("Peter", 28, 180);
Person p2("Jack", 18, 170);
Person p3("Tom", 18, 160);
// 插入数据
l.push_back(p1);
l.push_back(p2);
l.push_back(p3);
cout << "排序前:" << endl;
// 查看容器中的数据
for (list<Person>::iterator it = l.begin(); it != l.end(); it++)
{
cout << "{姓名:" << (*it).m_Name << ", 年龄:" << (*it).m_Age << ", 身高:" << (*it).m_Height << "cm}" << endl;
}
// 排序
l.sort(comparePerson); // 编写"排序规则函数"
cout << "排序后:" << endl;
// 查看容器中的数据
for (list<Person>::iterator it = l.begin(); it != l.end(); it++)
{
cout << "{姓名:" << (*it).m_Name << ", 年龄:" << (*it).m_Age << ", 身高:" << (*it).m_Height << "cm}" << endl;
}
}
int main()
{
test();
}
Set 容器
基本认识
简介:
所有元素都会在插入时自动被排序
本质:
set/multiset 属于关联式容器,底层结构是用二叉树实现
set和multiset区别:
set不允许容器中有重复的元素
multiset允许容器中有重复的元素
构造和赋值
#include <iostream>
#include <set>
using namespace std;
void printSet(set<int>& s)
{
for (set<int>::iterator it = s.begin(); it != s.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test()
{
// 默认构造
set<int> s1;
// 插入数据 只有insert方法
// set容器的特点:所有元素插入时候自动被排序
// set容器不允许插入重复值
s1.insert(30);
s1.insert(10);
s1.insert(40);
s1.insert(20);
printSet(s1); // 10 20 30 40
// 拷贝构造
set<int> s2(s1);
printSet(s2); // 10 20 30 40
// 赋值
set<int> s3;
s3 = s2;
printSet(s3); // 10 20 30 40
}
int main()
{
test();
}
大小和交换
size(); 返回容器中元素的数目
empty(); 判断容器是否为空
swap(st); 交换两个集合容器
插入和删除
insert(element); 在容器中插入元素
clear(); 清除所有元素
erase(position); 删除position迭代器所指向的元素,返回下一个元素的迭代器
erase(beg, end); 删除区间[beg, end)的所有元素,返回下一个元素的迭代器
erase(element); 删除容器中值为element的元素
查找和统计
#include <iostream>
#include <set>
using namespace std;
// set容器 查找和统计
void test()
{
// 创建容器
set<int> s1;
// 插入数据
s1.insert(20);
s1.insert(40);
s1.insert(10);
s1.insert(30);
// 查找
set<int>::iterator pos = s1.find(30);
if (pos != s1.end())
{
cout << "找到元素:" << *pos << endl;
}
else
{
cout << "未找到元素" << endl;
}
// 统计30的个数(对于set而言,统计的结果要么是0,要么是1)
int num = s1.count(30);
cout << "num = " << num << endl;
}
int main()
{
test();
}
set 和 multiset 区别
#include <iostream>
#include <set>
using namespace std;
// set 不可以插入重复数据,而multiset可以
// set 插入数据的同时会返回插入结果,表示插入是否成功
// multiset 不会检测数据,因此可以插入重复数据
void test()
{
// 这个容器允许插入重复的值
multiset<int> ms;
ms.insert(10);
ms.insert(10);
ms.insert(10);
// 输出容器中的数据
for (multiset<int>::iterator it = ms.begin(); it != ms.end(); it++)
{
cout << *it << " ";
}
cout << endl; // 10 10 10
}
int main()
{
test();
}
pair 对组
功能描述:成对出现的数据,利用对组可以返回两个数据
#include <iostream>
#include <set>
using namespace std;
// pair对组的创建
void test()
{
// 第一种方式
pair<string, int> p1("Tom", 28);
cout << p1.first << " " << p1.second << endl; // Tom 28
// 第二种方式
pair<string, int> p2 = make_pair("Jerry", 30);
cout << p2.first << " " << p2.second << endl; // Jerry 30
}
int main()
{
test();
}
map 容器
基本介绍
简介:
- map 中所有元素都是 pair
- pair 中第一个元素 key(键值),起到索引作用,第二个元素 value(实值)
- 所有元素都会根据元素的键值自动排序
本质:
- map / multimap 属性关联式容器,底层结构是用二叉树实现
优点:
- 可以根据 key 值快速找到 value 值
map和multimap区别:
- map 不允许容器中有重复的 key 值元素
- multimap 允许容器中有重复的 key 值元素
构造和赋值
#include <iostream>
#include <map>
using namespace std;
void printMap(map<int, int> &m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << (*it).first << ":" << (*it).second << ", ";
}
cout << endl;
}
void test()
{
// 默认构造
map<int, int> m; // 创建map容器
// 插入数据
m.insert(pair<int, int>(4, 19));
m.insert(pair<int, int>(1, 45));
m.insert(pair<int, int>(3, 33));
m.insert(pair<int, int>(2, 12));
printMap(m);
// 拷贝构造
map<int, int> m2(m);
printMap(m2);
// 赋值
map<int, int> m3;
m3 = m2;
printMap(m3);
}
int main()
{
test();
}
大小和交换
#include <iostream>
#include <map>
using namespace std;
void printMap(map<int, int>& m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << (*it).first << ":" << (*it).second << ", ";
}
cout << endl;
}
void test()
{
// 创建map容器
map<int, int> m;
// 插入数据
m.insert(pair<int, int>(4, 19));
m.insert(pair<int, int>(1, 45));
m.insert(pair<int, int>(3, 33));
m.insert(pair<int, int>(2, 12));
if (m.empty())
{
cout << "m为空" << endl;
}
else
{
cout << "m不为空" << endl;
cout << "m的大小:" << m.size() << endl;
}
map<int, int> m1;
m1.insert(pair<int, int>(5, 29));
m1.insert(pair<int, int>(3, 46));
m1.insert(pair<int, int>(9, 13));
cout << "交换前:" << endl;
printMap(m); // 1:45, 2:12, 3:33, 4:19,
printMap(m1); // 3:46, 5:29, 9:13,
m.swap(m1);
cout << "交换后:" << endl;
printMap(m); // 3:46, 5:29, 9:13,
printMap(m1); // 1:45, 2:12, 3:33, 4:19,
}
int main()
{
test();
}
插入和删除
insert(elem) // 在容器中插入元素
clear(); // 清除所有元素
erase(pos); // 删除pos迭代器所指向的元素,返回下一个元素的迭代器
erase(beg, end); // 删除区间[beg, end]的所有元素,返回下一个元素的迭代器
erase(key); // 删除元素中值为key的元素
查找和统计
find(key); // 查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); // 统计key元素个数
排序
map 容器默认排序规则为:按照key值进行"从小到大排序",掌握如何改变排序规则
利用仿函数,可以改变排序规则
#include <iostream>
#include <map>
using namespace std;
// 仿函数
class MyCompare
{
public:
bool operator()(int v1, int v2)
{
// 降序排列
return v1 > v2;
}
};
void test()
{
// 创建map容器
map<int, int, MyCompare> m;
// 插入数据
m.insert(make_pair(4, 19));
m.insert(make_pair(1, 45));
m.insert(make_pair(3, 33));
m.insert(make_pair(2, 12));
// 输出容器中的数据
for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key: " << (*it).first << " value: " << (*it).second << endl;
}
}
int main()
{
test();
}
函数对象 (仿函数)
#include <iostream>
#include <string>
using namespace std;
// 函数对象(又叫仿函数)
class MyAdd
{
public:
int operator()(int v1, int v2)
{
return v1 + v2;
}
};
// 1. 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
void test1()
{
MyAdd add;
cout << add(10, 20) << endl; // add() 实际上是对象调用()运算符进行重载,因为很像函数,所以也可以称之为"仿函数"
}
// 2. 函数对象超出普通函数的概念,函数对象可以有自己的状态
class MyPrint
{
public:
void operator()(string text)
{
cout << text << endl;
this->count++;
}
int count = 0; // 内部自己的状态
};
void test2()
{
MyPrint print;
print("hello world");
print("hello world");
print("hello world");
print("hello world");
print("hello world");
cout << "一共打印的次数:" << print.count << endl;
}
// 3. 函数对象可以作为参数传递
void doPrint(MyPrint& mp, string text)
{
mp(text);
}
void test3()
{
MyPrint print;
doPrint(print, "Hello World");
}
int main()
{
test1();
test2();
test3();
}
谓词
基本概念
- 返回 bool 类型的仿函数称为谓词
- 如果 operator() 接收一个参数,那么叫做一元谓词
- 如果 operator() 接收两个参数,那么叫做二元谓词
具体内容
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 返回值类型是bool数据类型的仿函数称为谓词
// 一元谓词
class GreaterFive
{
public:
bool operator()(int val)
{
return val > 5;
}
};
void test1()
{
vector<int> v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
// 查找容器中有没有大于5的数字
// GreaterFive() -- 匿名函数对象
vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
if (it == v.end())
{
cout << "未找到" << endl;
}
else
{
cout << "找到了大于5的数字,它是:" << *it << endl;
}
}
// 二元谓词
class MyCompare
{
public:
bool operator()(int val1, int val2)
{
return val1 > val2;
}
};
void test2()
{
vector<int> v;
v.push_back(20);
v.push_back(50);
v.push_back(30);
v.push_back(40);
v.push_back(10);
sort(v.begin(), v.end());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
// 使用函数对象,改变算法策略,变为排序规则为从大到小
sort(v.begin(), v.end(), MyCompare()); // MyCompare() -- 匿名函数对象
}
int main()
{
test1();
test2();
}
内建函数对象
基本介绍
概念:STL 内部建立了一些函数对象
分类:
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
用法:
- 这些仿函数所产生的对象,用法和一般函数完全相同
- 使用内建函数对象,需要引入头文件
#include <functional>
算术仿函数
#include <iostream>
#include <functional> // 内建的函数对象头文件
using namespace std;
void test1()
{
// 取反 仿函数
negate<int> n;
cout << n(20) << endl;
// 加法 仿函数
plus<int> p;
cout << p(10, 20) << endl;
}
int main()
{
test1();
}
关系仿函数
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 内建的函数对象头文件
using namespace std;
class MyCompare
{
public:
bool operator()(int val1, int val2)
{
return val1 > val2;
}
};
void test1()
{
vector<int> v;
v.push_back(20);
v.push_back(50);
v.push_back(30);
v.push_back(10);
v.push_back(40);
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
// 降序
// 方法1:使用自己写的规则【即:使用自己写的仿函数】
sort(v.begin(), v.end(), MyCompare()); // MyCompare() -- 匿名函数对象
// 方法2:使用内建仿函数【greater是"大于"关系的内建仿函数】
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test1();
}
逻辑仿函数
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // 内建的函数对象头文件
using namespace std;
void test1()
{
vector<bool> v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(true);
v.push_back(false);
// 输出容器内容
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it << " ";
}
cout << endl;
vector<bool> v2;
v2.resize(v.size());
// 利用逻辑非 将容器v搬运到容器v2中,并执行取反操作
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
// 输出容器内容
for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
int main()
{
test1();
}
STL常用算法简介
- 算法主要是由头文件 组成
- 是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等
- 体积很小,只包括几个在序列上面进行简单数学运算的模板函数
- 定义了一些模板类,用以声明函数对象
遍历算法
- for_each // 遍历容器
- transform // 搬运容器到另一个容器中
for_each
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 普通函数
void print(int val)
{
cout << val << endl;
}
// 仿函数
class MyPrint
{
public:
void operator()(int val)
{
cout << val << endl;
}
};
void test()
{
// 创建容器
vector<int> v;
// 添加数据
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
// 遍历数据
for_each(v.begin(), v.end(), print);
for_each(v.begin(), v.end(), MyPrint()); // MyPrint() -- 匿名函数对象
}
int main()
{
test();
}
transform
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class Transform
{
public:
int operator()(int v)
{
v *= 10;
return v;
}
};
class MyPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test()
{
// 创建原始容器
vector<int> v;
// 向原始容器中添加数据
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
// 查看原始容器中的内容
for_each(v.begin(), v.end(), MyPrint()); // MyPrint() -- 匿名函数对象
cout << endl;
// 创建目标容器
vector<int> vTarget;
// 开辟好空间,准备存放数据
vTarget.resize(v.size());
// 将原始容器的每一个元素乘以10再放到目标容器中
transform(v.begin(), v.end(), vTarget.begin(), Transform()); // Transform() -- 匿名函数对象
// 输出目标容器的内容
MyPrint print;
for_each(vTarget.begin(), vTarget.end(), print); // print -- 有名函数对象、或者就叫函数对象、或者对象
}
int main()
{
test();
}
/*
运行结果:
0 1 2 3 4 5 6 7 8 9
0 10 20 30 40 50 60 70 80 90
*/
查找算法
find // 查找元素
find_if // 按条件查找元素
adjacent_find // 查找相邻重复元素
binary_search // 二分法查找
count // 统计元素个数
count_if // 按照条件统计元素个数
排序算法
sort // 对容器内元素进行排序
random_shuffle // 洗牌,指定范围内的元素随机调整次序
merge // 容器元素合并,并存储到另一个容器中
reverse // 反转指定范围的元素
拷贝和替换算法
copy // 容器内指定范围的元素拷贝到另一容器中
replace // 将容器内指定范围的旧元素修改为新元素
replace_if // 将容器内指定范围满足条件的元素替换为新元素
swap // 互换两个容器的元素
算术生成算法
使用时,请包含头文件 #include <numeric>
- accumulate // 计算容器元素累计总和
- fill // 向容器中添加元素
集合算法
set_intersection // 求两个容器的交集
set_union // 求两个容器的并集
set_difference // 求两个容器的差集