你的c++学习路上明灯
目录
一,STL基本概念
二,STL六大组件
1,容器:
2,算法:
3,迭代器:
4,仿函数
三,详解STL中的容器
一,string容器
1,本质:string是c++风格的字符串,而string本质上是一个类。
2,
3,特点:
4,构造函数
5,赋值操作
6,字符串拼接
7,字符串查找和替换
8,字符串比较
9,字符存取
10,string的插入和删除
11,字串获取
二,vector容器
1,功能:
2,与普通数组的区别:
3,什么叫动态扩展?
4,vector容器存放数据
6,vector互换容器
7,预留空间
8,一个小知识点
9,resize用法
三,deque容器
1,功能:
2,deque与vector的区别:
3,deque内部工作原理:
6,deque排序
7,deque构造函数
四,案例
五,stack容器(栈容器)
六,queue容器(队列容器)
七,list容器(双向循环链表)
2,list的优点:
3,缺点:
5,反转和排序
6.list容器-排序案例
八,set/multiset容器(关联式容器)
1,本质:
2,二者的区别:
3,构造与赋值
4,查找与统计
5,排序规则
九,pair对组的创建
十,map/multimap容器(效率高)
1,简介:
2,本质:
3,优点:
十一,注意
十二,容器嵌套容器
十三,总结
哈哈哈,今天终于可以更新关于c++知识的最后一个部分的知识了,后面的话我应该就会更新一些关于python和机器学习还有数据结构方面的东西了,毕竟写这个东西也是很花时间的,希望大家多多支持,大家的认可,真的能让我大受鼓舞的,也是一直督促我前进的动力。
一,STL基本概念
1,STL(standard template library)标准模板库
2,STL从广义上分为:容器(container),算法(algorithm),迭代器(iterator)
3,STL几乎所有的代码都采用了模板类或者模板函数
4,容器和算法之间凭借迭代器进行连接
二,STL六大组件
接下来我来分别讲一讲容器,算法,迭代器和仿函数
STL容器就是将运用最广泛的一些数据结构凸显出来。
关联式容器概念介绍_遥远的歌s的博客-CSDN博客_关联式容器https://blog.csdn.net/qq_44819750/article/details/107785732?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164709939216780366580403%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164709939216780366580403&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-107785732.pc_search_result_cache&utm_term=%E5%85%B3%E8%81%94%E5%BC%8F%E5%AE%B9%E5%99%A8&spm=1018.2226.3001.4187STL之序列式容器(一)、什么是序列式容器_学无止尽,谨言慎行!-CSDN博客
https://blog.csdn.net/y601500359/article/details/105409417?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164709966416780271970753%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164709966416780271970753&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-105409417.pc_search_result_cache&utm_term=%E5%BA%8F%E5%88%97%E5%BC%8F%E5%AE%B9%E5%99%A8&spm=1018.2226.3001.4187序列式容器这一篇写的是真的不错(那两张图)总结的很好。
有限的步骤,解决逻辑或数学上的问题
说白了,算法就是解决问题的方法
容器和算法之间的粘合剂
实质:提供一种方法,使之能够访问某个容器所含的某些元素,而又无需暴露该容器的内部的表达方式。
每个容器都有自己的专属迭代器。
在STL中仿函数就是一种方法,改变一些规则的方法,其实我们学的也不多,简单了解即可。
在这里,我会着重讲几个容器,其他的容器就会泛泛而谈,因为他们是相似的。
char*是一个指针。
string是一个类,类内部封装了char*,管理这个字符串,是一个char*的容器
string类内部封装了很多成员方法。(所有STL容器都是),string管理char*所分配的内存,不用担心复制越界和取值越界等问题,由类内部进行负责。
#include
#include
using namespace std;
void test1() {
string s1="hello world";
const char* str = "hello world";
string s2(str);
cout << s2 << endl;
string s3(s2);
cout << s3 << endl;
string s4(8, 'a');
cout << s4 << endl;
}
int main() {
test1();
return 0;
}
#include
#include
using namespace std;
void test1() {
string str1;
str1 = "hello world";
string str2;
str2 = str1;
cout << str2 << endl;
//string str3='s';
//str3 = 's';
string str4;
str4.assign(str2);
//删除前五个
string str5;
str5.assign(str1, 5);
string str7;
str7.assign("hello c++", 5);
string str6;
str6.assign(10, 'w');
cout << str5 << endl;
cout << str7 << endl;
}
int main() {
test1();
return 0;
}
#include
using namespace std;
#include
int main() {
string str1 = "我";
str1 += "爱玩游戏";
str1 += ":";
cout << "str1 = " << str1 << endl;
string str2 = "LOL CF";
str1 += str2;
cout << "str1 = " << str1 << endl;
string str3 = "I";
str3.append(" love ");
str3.append(str2, 3);
cout << "str3 = " << str3 << endl;
str3.append(str2);
cout << "str3 = " << str3 << endl;
string str4 = "I love game";
str4.append(str2, 0, 3);
cout << "str4 = " << str4 << endl;
return 0;
}
#include
#include
using namespace std;
//字符串查找与替换
//1,查找
void test1() {
string str1 = "abcdefghde";
int pos = str1.find("df");
cout << "pos = " << pos << endl;
if (pos==-1) {
cout << "未找到" << endl;
}
else {
cout << "已找到,pos = " << pos << endl;
}
pos = str1.rfind("de");
cout << "pos = " << pos << endl;
}
//2.替换
void test2() {
string str1 = "abcdefghde";
//虽然只是要替换三个字符,但是后面接的字符串有四个,全部替换上去
str1.replace(1, 3, "1111");
cout << "str1 = " << str1 << endl;
}
int main() {
test1();
test2();
return 0;
}
#include
#include
using namespace std;
void test1() {
string str1 = "hello";
//1.利用【】访问
for (int i = 0; i < str1.size(); i++) {
cout << str1[i] << " ";
}
cout << endl;
//2,利用at访问
for (int i = 0; i < str1.size(); i++) {
cout << str1.at(i) << " ";
}
cout << endl;
}
int main() {
test1();
return 0;
}
#include
#include
using namespace std;
void test1() {
//1,插入字符串
string str1 = "hello";
str1.insert(1, "111");
cout << "str1 = " << str1 << endl;
//2,删除
str1.erase(1, 3);
cout << "str1 = " << str1 << endl;
}
int main() {
test1();
return 0;
}
#include
#include
using namespace std;
void test1() {
string str1 = "hello";
string subStr = str1.substr(1, 3);
cout << "subStr = " << subStr << endl;
//实用操作
string email = "[email protected]";
int pos = email.find("@");
cout << "用户名: " << email.substr(0, pos) << endl;
}
int main() {
test1();
return 0;
}
string容器是典型的序列式容器,其他的序列式容器的操作大多相同。例如下面要讲的vector容器。
二,vector容器
和数组十分相似,也称为单端数组
数组是静态的,而vector可以动态扩展。
并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝到新空间,释放原空间。(无法确定后续空间是否已经被使用)
除有与上面所有相同的操作外,还有其他的一些操作
1)存放内置数据类型
#define _CRT_SECURE_NO_WARNINGS 1
#include
using namespace std;
#include
#include//标准算法头文件
void MyPrint(int val) {
cout << val << endl;
}
void test() {
//创建一个vector数组
vector v;
//写入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
v.push_back(40);
//通过迭代器访问容器中的数据
vector::iterator itBegin = v.begin();
vector::iterator itEnd = v.end();
//第一种遍历方式
while (itBegin != itEnd) {
cout << *itBegin << endl;
itBegin++;
}
//第二种遍历方式
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << endl;
}
//第三种遍历方式
//利用STL中的遍历算法
for_each(v.begin(), v.end(), MyPrint);
}
int main() {
test();
return 0;
}
2)存放自定义数据类型
#include
#include
#include
using namespace std;
class person {
public:
string m_Name;
int m_Age;
person(string name, int age) {
this->m_Age = age;
this->m_Name = name;
}
};
//存放对象
void test1() {
vector v;
person p1("a", 1);
person p2("a", 2);
person p3("a", 3);
person p4("a", 4);
person p5("a", 5);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
for (vector::iterator it = v.begin(); it < v.end(); it++) {
cout << "年龄:" << (*it).m_Age << " 姓名:" << (*it).m_Name << endl;
cout << "年龄:" << it->m_Age << " 姓名:" << it->m_Name << endl;
}
}
//存放指针
void test2() {
vector v2;
person p1("a", 1);
person p2("a", 2);
person p3("a", 3);
person p4("a", 4);
person p5("a", 5);
v2.push_back(&p1);
v2.push_back(&p2);
v2.push_back(&p3);
v2.push_back(&p4);
v2.push_back(&p5);
for (vector::iterator it = v2.begin(); it < v2.end(); it++) {
cout << "年龄:" << (*it)->m_Age << " 姓名:" << (*it)->m_Name << endl;
}
}
int main() {
test1();
test2();
return 0;
}
5,vector容器的迭代器是支持随机访问的迭代器。
#include
#include
using namespace std;
void PrintVector(vector& v) {
for (vector::iterator it = v.begin(); it < v.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test1() {
cout << "互换前:" << endl;
vector v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
PrintVector(v1);
vector v2;
for (int i = 9; i >= 0; i--) {
v2.push_back(i);
}
PrintVector(v2);
//互换容器
cout << "互换后:" << endl;
v1.swap(v2);
PrintVector(v1);
PrintVector(v2);
}
//实用操作,收缩内存
void test2() {
vector v;
for (int i = 0; i < 10000; 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(v).swap(v);//匿名对象
cout << "v的容量为:" << v.capacity() << endl;
cout << "v的大小为:" << v.size() << endl;
}
int main() {
test1();
test2();
return 0;
}
#include
#include
using namespace std;
void test1() {
//reserve预留空间,预留位置不初始化,元素不可访问
vector v1;
int num = 0;
int* p = NULL;
v1.reserve(10000);
for (int i = 0; i < 10000; i++) {
v1.push_back(i);
if (p != &v1[0]) {
p = &v1[0];
num++;
}
}
cout << "num = " << num << endl;
}
int main() {
test1();
return 0;
}
vector的容量大于等于vector的大小。
c++ vector resize & reserve_jackywgw的博客-CSDN博客https://blog.csdn.net/jackywgw/article/details/6248342
双端数组,可以对头端进行插入删除操作
1)vector对于头部的插入效率低,数据量越大,效率越低。
2)deque容器对头部的插入速度更快
3)vector容器访问元素时会更快,这和两者内部实现有关
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区内存放数据
中控器维护的是每个缓冲区的地址,使得使用deque容器时像一片连续的空间。
4,deque容器的迭代器也是支持随机访问的
5,对于支持随机访问迭代器的容器,都可以利用sort算法排序。
#include
#include
using namespace std;
#include
void PrintDeque(const dequed) {
for (deque::const_iterator it = d.begin(); it < d.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test1() {
dequed;
d.push_back(10);
d.push_back(50);
d.push_back(9000);
d.push_back(570);
d.push_back(1);
//排序算法,默认为升序
//对于支持随机访问迭代器的容器,都可以利用sort算法直接对其排序
sort(d.begin(), d.end());
PrintDeque(d);
}
int main() {
test1();
return 0;
}
#include
using namespace std;
#include
void PrintDeque(const dequed) {
for (deque::const_iterator it = d.begin(); it < d.end(); it++)
{
cout << *it << " ";
}
cout << endl;
}
void test1() {
dequed1;
for (int i = 0; i < 10; i++) {
d1.push_back(i);
}
PrintDeque(d1);
dequed2(d1.begin(), d1.end());
PrintDeque(d2);
dequed3(10, 1000);
PrintDeque(d3);
dequed4(d3);
PrintDeque(d4);
}
int main() {
test1();
return 0;
}
四,案例
#include
#include
#include
using namespace std;
#include
#include
#include
//评委打分
class person {
public:
int m_Score;//分数
string m_Name;//姓名
person(string name, int score) {
this->m_Name = name;
this->m_Score = score;
}
};
void CreatePerson(vector&v) {
string nameseed = "ABCDE";
for (int i = 0; i < 5; i++) {
string name = "选手";
name += nameseed[i];
int score = 0;
person p(name, score);
v.push_back(p);
}
}
void SetScore(vector&v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
//存放评委打的分数
dequed;
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::iterator dit = d.begin(); dit != d.end(); dit++) {
sum += *dit;
}
int ave = sum / d.size();
it->m_Score = ave;
}
}
void showScore(vector&v) {
for (vector::iterator it = v.begin(); it != v.end(); it++) {
cout << "姓名:" << it->m_Name << " 平均分:" << it->m_Score << endl;
}
}
int main() {
//创造随机数种子
srand((unsigned int)time(NULL));
//1,创造5名选手
vectorv;//存放选手的容器
CreatePerson(v);
//2,给选手打分
SetScore(v);
//3.显示最后得分
showScore(v);
return 0;
}
概念:一种先进后出的数据结构,它只有一个出口,栈中只有顶端的元素才能够被外界使用。
(因此栈不允许有遍历行为)
概念:一种先进先出的数据结构,有两个出口;
队尾进数据,队头出数据。
队列中只有队头和队尾能被外界使用,因此队列不能被遍历
1,由于链表的存储方式并不是连续的内存空间,因此链表list只支持前移和后移,属于双向迭代器。
1)采用动态内存分配,不会造成内存的浪费和溢出
2)链表执行插入和删除操作十分方便,修改指针即可
链表灵活,但是空间(指针域)和时间(遍历)额外耗费较大
4.list有一个重要的性质,插入和删除操作都不会造成原有list迭代器的失效,这在vector中是不成立的。
#include
using namespace std;
#include
//排序案例
//将person自定义数据类型进行排序,
//排序规则:按照年龄进行升序,如果年龄相同按照升高进行降序
class person {
public:
person(string name, int age, int height) {
this->m_Age = age;
this->m_Height = height;
this->m_Name = name;
}
string m_Name;
int m_Age;
int m_Height;
};
int 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 test1() {
listL;
person p1("唐僧", 28, 180);
person p2("孙悟空", 567, 159);
person p3("猪八戒", 567, 179);
person p4("沙僧", 567, 167);
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
for (list::const_iterator it = L.begin(); it != L.end();it++) {
cout << " 姓名: " << it->m_Name << " "
<< " 年龄: " << it->m_Age << " " << " 身高: " << it->m_Height << endl;
}
cout << "------------------------------" << endl;
L.sort(ComparePerson);
for (list::const_iterator it = L.begin(); it != L.end(); it++) {
cout << "姓名: " << it->m_Name << " "
<< "年龄: " << it->m_Age << " " << "身高: " << it->m_Height << endl;
}
}
int main() {
test1();
return 0;
}
***不支持随机访问迭代器的容器,内部都会提供一些对应的算法
八,set/multiset容器(关联式容器)
所有元素都会在插入时自动排序,
底层结构是二叉树
1)set中不允许有重复元素
2)multiset中允许有重复元素
3)set插入数据的同时会返回插入结果,表示插入是否成功。
4)multiset不会检测数据,因此可以重复插入
#include
using namespace std;
#include
void test1() {
sets1;
pair::iterator, bool> it = s1.insert(10);
if (it.second) {
cout << "第一次插入成功" << endl;
}
else {
cout << "第一次插入失败" << endl;
}
it = s1.insert(10);
if (it.second) {
cout << "第二次插入成功" << endl;
}
else {
cout << "第二次插入失败" << endl;
}
}
int main() {
test1();
return 0;
}
插入数据只能用insert函数
#include
#include
using namespace std;
void PrintSet(set&s) {
for (set::iterator it = s.begin(); it != s.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
void test1() {
sets1;
//只能用insert方式
s1.insert(10);
s1.insert(10);
s1.insert(50);
s1.insert(40);
s1.insert(49);
//set容器的特点,所有元素插入时候自动被排序
//set容器不允许插入重复值
PrintSet(s1);
}
int main() {
test1();
return 0;
}
#include
#include
using namespace std;
//查找
void test1() {
sets;
s.insert(10);
s.insert(30);
s.insert(40);
s.insert(60);
s.insert(50);
set::iterator pos = s.find(30);
if (pos != s.end()) {
cout << "找到元素。" << endl;
}
else {
cout << "未找到元素。" << endl;
}
}
//统计元素个数
void test2() {
sets;
s.insert(10);
s.insert(30);
s.insert(40);
s.insert(60);
s.insert(50);
//查找元素的个数
int num = s.count(30);
cout << "num = " << num << endl;
}
int main() {
test1();
test2();
return 0;
}
利用仿函数可以改变set容器中的排序规则
1)内置数据类型
2)自定义数据类型
#include
#include
using namespace std;
class MyCompare {
public:
bool operator()(int a,int b) {
return a > b;
}
};
void test1() {
sets1;
s1.insert(10);
s1.insert(40);
s1.insert(20);
s1.insert(30);
for (set::iterator it = s1.begin(); it != s1.end(); it++) {
cout << *it << " ";
}
cout << endl;
sets2;
s2.insert(10);
s2.insert(40);
s2.insert(20);
s2.insert(30);
for (set::iterator it = s2.begin(); it != s2.end(); it++) {
cout << *it << " ";
}
cout << endl;
}
int main() {
test1();
return 0;
}
#include
#include
using namespace std;
void test1() {
//第一种方式
pairp1("Tom", 20);
cout << "姓名: " << p1.first << "年龄:" << p1.second << endl;
//第二种方式
pairp2("Jerry", 18);
cout << "姓名: " << p2.first << "年龄:" << p2.second << endl;
}
int main() {
test1();
}
map
1)map中所有元素都是pair
2)pair中的第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
3)所有元素都会根据元素的键值自动排序
该容器属于关联式容器,底层结构是二叉树实现
可以根据key值快速找到vlaue(高效的原因)
4,map不允许重复值
multimap允许重复值
1)所有容器的迭代器都可进行的操作
*iter 、 ++iter 、 --iter、 iter1==iter2、 iter1!=iter2
2)vector和deque容器的迭代器可进行的额外操作
iter+n、 iter-n 、 > 、 >= 、 < 、 <=
注:这是因为vector和deque容器实际上是两个数组,只有对数组才能执行额外操作;
#include
#include
#include
using namespace std;
class person {
public:
string m_Name;
int m_Age;
person(string name, int age) {
this->m_Age = age;
this->m_Name = name;
}
};
//存放对象
void test1() {
vector v;
person p1("a", 1);
person p2("a", 2);
person p3("a", 3);
person p4("a", 4);
person p5("a", 5);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
for (vector::iterator it = v.begin(); it < v.end(); it++) {
cout << "年龄:" << (*it).m_Age << " 姓名:" << (*it).m_Name << endl;
cout << "年龄:" << it->m_Age << " 姓名:" << it->m_Name << endl;
}
}
//存放指针
void test2() {
vector v2;
person p1("a", 1);
person p2("a", 2);
person p3("a", 3);
person p4("a", 4);
person p5("a", 5);
v2.push_back(&p1);
v2.push_back(&p2);
v2.push_back(&p3);
v2.push_back(&p4);
v2.push_back(&p5);
for (vector::iterator it = v2.begin(); it < v2.end(); it++) {
cout << "年龄:" << (*it)->m_Age << " 姓名:" << (*it)->m_Name << endl;
}
}
int main() {
test1();
test2();
return 0;
}
1,对于自定义数据类型,必须要指定排序规则,否则编译器不知道如何排序。
2,高级排序只是在排序规则上再进行一次逻辑规则制定即可,不是很复杂。