C++之STL
1 STL概论
STL(标准模板库):STL的分类:容器,算法和迭代器。
STL提供了6大组件:容器,算法和迭代器,仿函数、适配器(配接器)、空间配置器。
2 三大组件的初识
容器:
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
// 容器 vector
// 迭代器 遍历的功能
// 普通的指针 也是一种迭代器
void test01() {
int array[5] = {1,3,5,6,8};
int *p = array; // 指向array[0]
for (int i = 0; i < 5;i++) {
//cout << array[i] << " ";
cout << *(p++) << " ";
}
}
void test02() {
// 声明一个容器
vector<int> v; // 声明一个容器 这个容器中存放int类型数据 对象名称
// 向容器中添加数据
v.push_back(1);
v.push_back(2);
v.push_back(3);
v.push_back(4);
// 遍历容器中的数据
// 利用迭代器
// 声明迭代器
vector<int>::iterator itBegin = v.begin(); // itBegin指向的是v容器中的起始位置
vector<int>::iterator itEnd = v.end(); // itEnd指向的是v容器中最后一个位置的下一个地址
while (itBegin != itEnd) {
cout << *itBegin << " ";
itBegin++;
}
}
int main()
{
test02();
system("pause");
return EXIT_SUCCESS;
}
总结: 普通的指针也是一种迭代器
C++容器类 vector ,vector 自带的迭代器vector <T> ::iterator
四种遍历方法:
第1种:直接使用while循环
// 声明迭代器
vector<int>::iterator itBegin = v.begin(); // itBegin指向的是v容器中的起始位置
vector<int>::iterator itEnd = v.end(); // itEnd指向的是v容器中最后一个位置的下一个地址
// 第1种遍历方式
/*while (itBegin != itEnd) {
cout << *itBegin << " ";
itBegin++;
}*/
第2种:使用for循环
for (vector<int>::iterator start = v.begin(); start != v.end(); start++) {
cout << *start << " ";
}
第3种:使用foreach循环
for ( int a: v) {
cout << a << " ";
}
第4种: 使用algorithm头文件中的for_each函数
for_each(v.begin(),v.end(),myPrint);
总结: 迭代器可以看作指针。 vector对象的begin()函数返回的值就是指向vector容器第一个元素的指针。
3 string容器
3.1 构造和赋值
// 1. string 构造函数
string str;
string str2(str); // 拷贝构造
string str3 = str2;
string str4("abc"); // 使用n个字符串初始化
string str5(10, 'a'); // 用10个字符a来初始化
cout << str4 << endl;
cout << str5 << endl;
// 2. string 基本的赋值
str = "Hello";
cout << str << endl;
str2 = str4; // 拷贝赋值
str = 'a';
cout << str << endl;
str3.assign("fyeuryue",5); //将字符串的前5个数放到str3中
cout << str3 << endl;
str3.assign("fyeuryue", 1,5); // 将字符串的第1个字符到第5个字符拿出来赋值给str3
cout << str3 << endl;
3.2 存储
从string容器中取出元素的两种方法:1. 使用方括号 [idnex]
2. 使用at(index)函数
3.3 字符串拼接
直接使用”+”运算符,或者调用append()函数
string s1 = "hello";
string s2;
s2 += " world"; // 字符串拼接
s1.append("lofly"); // 字符串拼接
cout << s1 + s2 << endl;
3.4 字符串查找
调用find()
int position = s1.find('o', 0);
cout << position << endl; // 输出5 找不到返回-1
position = s1.rfind('o');
cout << position << endl; // 输出5 找不到返回-1
3.5 字符串替换
// 字符替换
s1.replace(0, 3, "llh"); // 从0开始到3位置结束的字符 替换为llh
cout << s1 << endl; //
3.6 字符串比较
// 字符串比较
string t1 = "abc";
string t2 = "abc";
if (t1.compare(t2) == 0) {
cout << "字符串相等" << endl;
}
else if(t1.compare(t2) == 1){
cout << "t1大于t2" << endl;
}
else {
cout << "t1小于t2" << endl;
}
3.7 string子串
// 子串
string ss1 = "csadhfu";
string ss2 = ss1.substr(2,5);
cout << ss2 << endl;
3.8 string 的插入和删除
// 插入和删除
string str1 = "hello";
str1.insert(1,"SB");
cout << str1 << endl;
str1.erase(1,2); // 从位置1开始的两个字符会被删除
cout << str1 << endl;
3.9 string 和 c-style类型转换
//string 和 c-style字符串转换
string s1 = "abc";
const char *cstr = s1.c_str();
cout << cstr << endl;
printf("%s \n", cstr);
string s2(cstr);
cout << s2 << endl;
3.10 string赋值重新分配内存问题
string s = "abcdefghi";
char &a = s[1];
char &b = s[2];
a = 'm';
b = 'n';
cout << s << endl;
cout << "字符串的地址" << (int*)s.c_str() << endl;
s = "ppppp";// 原始开辟的内存够用, 不重新分配内存
s = "pppppppppppppppppppppppppppp"; // 原始开辟的内存不够用,重新分配内存
cout << s << endl;
cout << "字符串的地址" << (int*)s.c_str() << endl;
// 将所有的字母转成大写
string s = "abRdefg";
for (int i = 0; i < s.size();i++) {
s[i] = toupper(s[i]);
}
cout << s << endl;
转小写使用函数 tolower()
4 vector容器
vector的容器容量并非是线性增加的。
4.1 vector的数据结构
vector采用的是线性连空间。
注:动态增加大小的原理:为了能有更合适的控件,需要重新开辟一个更大的空间,将原来的数据拷贝到这个空间中。对vector的操作,一但空间重新分配了,指向原vector的所有的迭代器都会失效。
4.2 常用API操作
// 构造函数
vector<int> v; // 调用默认构造
int a[] = {1,2,3,4,5};
vector<int> v1(a,a+sizeof(a)/sizeof(int)); // 将a中的元素作为构造函数的参数
vector<int> v2(v1.begin(),v1.end()); // 将v1中的原始作为构造函数的参数
printVector(v2);
vector<int> v3(10,100); // 10个100
printVector(v3);
// 赋值操作
vector<int> v4(v3.begin(),v3.end()-1);
printVector(v4);
v4.swap(v2); // 容器中的元素进行互换
cout << "v4和v2中的元素进行互换" << endl;
printVector(v4);
cout << v4.size() << endl; //统计容器中的元素个数
if (v4.empty()) {
cout << "空容器" << endl;
}
else {
cout << "非空容器" << endl;
}
// resize(n)
// resize(n,m) 重新设置容器的大小 如果过长 用0填充 如果过短就删除长度以外的元素
v4.resize(10,-1);
printVector(v4);
// reserve(int len) // 预留len个长度空间 但预留的空间没有数据
4.3 巧用swap
vector<int> v;
for (int i = 0; i < 100000;i++) {
v.push_back(i);
}
cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量 138225 大小100000
v.resize(3);
cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量 138225 大小3
// 巧用swap
vector<int>(v).swap(v); // 使用v来初始化匿名对象 再用匿名对象来调用swap(v)函数
cout << "容量" << v.capacity() << "大小" << v.size() << endl;// 容量3大小3
4.4 预留空间reserve函数
vector<int> v;
int *p = NULL;
int num = 0;
for (int i = 0; i < 100000;i++) {
v.push_back(i);
if (p != &v[0]) {
p = &v[0];
num++;
}
}
cout << num << endl; // 输出30 表示重新开辟了30次空间
预留空间
vector<int> v;
v.reserve(100000); // 预留100000个空间
int *p = NULL;
int num = 0;
for (int i = 0; i < 100000;i++) {
v.push_back(i);
if (p != &v[0]) {
p = &v[0];
num++;
}
}
cout << num << endl; // 输出1
4.5 vector存取元素
vector<int> v;
v.push_back(10);
v.push_back(11);
v.push_back(12);
v.push_back(13);
cout << v.front() << endl; // 取第一个数
cout << v.back() << endl; // 取最后一个数
// 插入
v.insert(v.begin(), 10000); // 第一个参数是一个迭代器
// 删除最后一个元素
v.pop_back();
printVector(v);
// 删除元素
v.erase(v.begin()); // 参数是迭代器
v.erase(v.begin(),v.end()); // 删除指定范围的元素
if (v.empty()) {
cout << "空的容器" << endl;
}
// 清空容器
v.clear();
4.6 逆序遍历
// 使用逆序的迭代器进行遍历
for (vector<int>::reverse_iterator r = v.rbegin(); r != v.rend();r++) {
cout << *r << " ";
}
cout << endl;
5 deque容器
deque头插效率更高。
Deque没有容量的概念的,是动态分段空间组成。
原理图:
5.1 常用API
三种迭代器
void printDeque(const deque<int> &d) {
// 普通的迭代器 iterator
// 逆序迭代器 reverse_iterator
// const_iterator只读迭代器
for (deque<int> ::const_iterator begin = d.begin(); begin != d.end(); begin ++) {
cout << *begin << " ";
}
构造函数跟vector类似。
deque<int> d;
d.push_back(10);
d.push_back(20);
d.push_back(30);
d.push_back(100);
printDeque(d);
deque<int> d2(d.begin(),d.end());
d2.push_back(10000);
printDeque(d2);
// 交换两个deque 双向队列
d2.swap(d);
printDeque(d);
if (d2.empty()) { // 判断双向队列是否为空
cout << "为空" << endl;
}
else {
cout << "不为空" << endl;
}
// 读取头数据 和 尾数据
cout << d.front() << endl; // 头数据
cout << d.back() << endl; // 尾数据
// 插入数据
deque<int> d2(10,1);
d.insert(d.begin(), 10); // 在队列头添加10
d.insert(d.begin(), d2.begin(), d2.end());
排序:#include <algorithm>
deque<int> d;
d.push_back(1);
d.push_back(20);
d.push_back(12);
d.push_back(3);
// 排序
sort(d.begin(),d.end()); // 升序排序
// 从大到小 排序
sort(d.begin(), d.end(), myCampare); // 降序排序
printDeque(d);
随机数讲解:
导入头文件: #include <ctime>
srand((unsigned int) time(NULL)); // srand(30);
cout << "随机数:" << rand() << endl;
6 栈容器
导入头文件 #include <stack>
压栈:push(ele)
栈大小:size()
返回栈顶元素:top()
// 读取栈顶元素
cout << "栈顶元素为:" << s.top() << endl;
// 弹出栈顶元素
s.pop();
7 队列
导入头文件
#include <queue>
queue<int> q;
q.push(1);
q.push(2);
q.push(3);
q.push(4);
cout << "最后一个元素" << q.back() << endl;
cout << "第一个元素" << q.front() << endl;
while (q.size() != 0)
{
cout << q.front() << endl; // 输出 队头
q.pop();
}
cout <<"队列的长度="<< q.size() << endl;
8 list容器
list容器(上)
list是基于(双向循环)链表的数据结构,vector是线性结构的.
list<int> ls1(10,10);
list<int> ls2(ls1.begin(),ls1.end());
printList(ls1);
ls2.push_back(11);
for (list<int>::reverse_iterator rbegin = ls2.rbegin(); rbegin != ls2.rend();rbegin ++) {
cout << *rbegin << " ";
}
cout << endl;
list 不支持随机访问。只能每次向后访问一个元素。
list<int> l3;
l3.push_back(10); // 尾插法
l3.push_back(20);
l3.push_back(30);
l3.push_back(40);
l3.push_front(1000); // 头插法
// 删除两端数据
l3.pop_front(); // 删除头部数据
l3.pop_back(); // 删除尾部数据
// 移除元素
l3.remove(10); // 移除所有等于10的元素
printList(l3);
// 赋值
list<int > L4;
L4.assign(l3.begin(),l3.end());
printList(L4);
list容器(下)
list翻转排序
bool ageCompare(Person &p1,Person &p2) {
return p1.m_age < p2.m_age; // 升序排序
}
class Person;
void printPersonList(list<Person> &p) {
for (const Person p1:p) {
cout << p1.m_name << ":" << p1.m_age << " ";
}
cout << endl;
}
list<Person> p;
Person p1("孙悟空",500);
Person p2("猪八戒",30);
Person p3("唐僧",23);
Person p4("沙僧",300);
p.push_back(p1);
p.push_back(p2);
p.push_back(p3);
p.push_back(p4);
// 按照年龄的降序输出
p.sort(ageCompare);
printPersonList(p);
8.1 删除操作:
// 自定义数据类型
class Person {
public :
Person(string name,int age,int height) {
this->m_name = name;
this->m_age = age;
this->m_Height = height;
}
// 重载== 使得remove方法可以删除自定义的person类型
bool operator==(const Person &p) {
if (this->m_name == p.m_name && this->m_age == p.m_age && this->m_Height == p.m_Height) {
return true;
}
return false;
}
string m_name;
int m_age;
int m_Height;
};
// 删除
p.remove(p6);
printPersonList(p);
9 set容器(加入的元素自动排序)
multiset允许插入重复的值,set和multiset底层都是采用红黑树数据结构。set容器是关联式容器,自动按照顺序加入容器。(自动排序)
set容器不能插入重复的值。
set<int> s;
// 关联式容器 自动排序
s.insert(1); // 加入元素 1
s.insert(13);
s.insert(7);
s.insert(5);
s.insert(9);
s.insert(11);
printSet(s);
if (s.empty()) {
cout << "空的容器!" << endl;
}
else {
cout << "容器的大小:" << s.size() << endl;
}
// 两种删除元素的方法
s.erase(s.begin());// 1.使用迭代器的方式
s.erase(11); // 2.直接删除指定的元素
printSet(s)
查找操作:
set<int> s1;
s1.insert(3);
s1.insert(5);
s1.insert(8);
s1.insert(6);
set<int>::iterator pos = s1.find(5);// 如果value存在 返回迭代器,如果不存在返回s.end()
if (pos != s1.end()) {
cout << "找到,值为" << *pos << endl;
}
else {
cout << "没找到" << endl;
}
// 对于set而言 count函数的返回值要么是1 要么是0
cout <<"key值个数:" <<s1.count(8) << endl; // 查找key的个数 输出1
cout << s1.count(1) << endl;
set<int>::iterator it1 = s1.lower_bound(1); // 返回第一个大于等于1的迭代器
set<int>::iterator it2 = s1.upper_bound(6); // 返回第一个大于6的迭代器
cout << *it1 << "\t" << *it2 << endl;
// 返回上下限的两个迭代器(分别对应上面的lower_bound和upper_bound)
pair<set<int>::iterator,set<int>::iterator> ret= s1.equal_range(3);
10 pair对组的创建方式
//// 第1种
//pair<string, int> p(string("Tom"),13);
//cout <<"姓名:" << p.first << endl;
//cout << "年龄:" << p.second << endl;
// 第2种
pair<string, int> p = make_pair(string("张三"),34);
cout <<"姓名:" << p.first << endl;
cout << "年龄:" << p.second << endl;
10.1 set容器是否可以插入成功判断
set<int> s;
pair<set<int>::iterator, bool> p;
p= s.insert(4);
cout << "插入是否成功=" << p.second << endl; // 插入成功
p = s.insert(4);
cout << "插入是否成功=" << p.second << endl; // 插入失败
10.2 set容器排序(指定set的排序规则)
// 指定set容器的排序规则 从大到小
// 仿函数
class myCompare {
public:
// 重载
bool operator()(int v1,int v2) {
return v1 > v2;
}
};
// 从大到小排序(set一但设定好就不能对元素进行移动)
// 只能在插入之前指定
for (set<int, myCompare>::iterator start = s1.begin(); start != s1.end();start ++) {
cout << *start << " ";
} // 输出 8 6 5 3
cout << endl;
11 set容器插入自定义数据类型
插入自定义数据时,需要指定排序规则。
class myComparePerson {
public:
bool operator()(const Person &p1,const Person &p2) {
if (p1.m_age > p2.m_age) { // 降序排序
return true;
}
else {
return false;
}
}
};
void test06() {
set<Person,myComparePerson> s1;
Person p1("张三",12);
Person p2("李四", 22);
Person p3("王五", 42);
Person p4("赵六", 32);
s1.insert(p1);
s1.insert(p2);
s1.insert(p3);
s1.insert(p4);
// 显示
for (set<Person, myComparePerson>::iterator begin = s1.begin(); begin != s1.end();begin ++) {
cout << " 姓名:" << begin->m_name << " 年龄:" << begin->m_age <<" ";
}
cout << endl;
}
7 map/multimap容器
所有元素都会根据元素的键值进行排序。Map所有元素都是pair,第一个元素视为键值,第二个元素视为实值。不允许有两个相同的key值。
map<int, int> m;
// 4种 插入值
// 第1种 插入值的方式
m.insert(pair<int,int>(1,10));
// 第2种插入值的方式(推荐)
m.insert(make_pair(2,10));
// 第3种插入值的方式(不推荐)
m.insert(map<int, int>::value_type(3, 10));
// 第4种插入值的方式(不推荐)
m[4] = 100;
for (map<int, int>::iterator begin = m.begin(); begin != m.end(); begin ++) {
cout << "key=" << begin->first << " value=" << begin->second << endl;
}
//判断是否非空
if (m.empty()) {
cout << "为空" << endl;
}
else {
cout << "非空" << endl;
}
// 查找
map<int, int>::iterator it = m.find(5);
if (it != m.end()) {
cout << "找到"<<it->second << endl;
}
else {
cout << "没有找到" << endl;
}
// lower_bound 返回第一个 key>=keyEle元素的迭代器
map<int, int> ::iterator ret = m.lower_bound(3);
if (ret != m.end()) {
cout << ret->first << " " << ret->second << endl; // 3 10
}
ret = m.upper_bound(3);
if (ret != m.end()) {
cout << ret->first << " " << ret->second << endl; // 4 100
}
pair<map<int,int>::iterator, map<int, int>::iterator> ret2 = m.equal_range(3);
if (ret2.first != m.end()) {
cout << "查找到equal_range中的lower_bound" << ret2.first->first << " "
<< ret2.first->second
<<endl;
}
if (ret2.second != m.end()) {
cout << "查找到equal_range中的upper_bound" << ret2.second->first << " "
<< ret2.second->second << endl;
}
指定排序规则
class myCompare { // 按照key的值从大到小排序
public:
bool operator()(int v1, int v2) {
return v1 > v2;
}
};
// 指定排序规则
void test02() {
map<int, int, myCompare> m;
m.insert(pair<int, int>(1, 10));
m.insert(pair<int, int>(4, 40));
m.insert(pair<int, int>(2, 80));
m.insert(make_pair(3, 11));
m.insert(map<int, int>::value_type(5, 15));
for (map<int, int, myCompare>::iterator begin = m.begin(); begin != m.end(); begin++) {
cout << "key=" << begin->first << " value=" << begin->second << endl;
}
}