1、STL简介
1.1基本概念
可复用利用的东西!
面向对象和泛型编程(模板)的 目的->提升复用性
为了建立数据结构和算法的一套标准->STL横空出世
- STL(Standard Template Liberary)标准模板库
- 广义分:容器、算法、迭代器
- 容器和算法之间通过迭代器连接、STL几乎所有的代码均采用函数模板和类模板
1.2六大组件
- 容器:各种数据结构,如vector、list、deque、map、set等由于数据存储。
序列式排序:强调值的排序,序列式容器中的每个元素均有固定的位置。
关联式排序:二叉树结构,各元素之间没有严格的物理的顺序关系。
- 算法:各种算法,如:sort、find、copy、for_each等。
- 迭代器:容器和算法连接桥梁。
提供一种方法,使之间能够依次序访问某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有自己专属的迭代器。类似于指针,可简单理解为指针。
迭代器种类:
种类 | 功能 | 支持运算 |
---|---|---|
输入迭代器 | 对数据的只读访问 | 只读,支持++、==、!= |
输出迭代器 | 对数据的只写访问 | 只写,支持++ |
前向迭代器 | 读写操作,并能向前推进迭代器 | 读写,支持++、==、!= |
双向迭代器 | 读写操作,并能向前和向后操作 | 读写,支持++、--, |
随机访问迭代器 | 读写操作,可以以跳跃的方式访问任意数据,功能最强的迭代器 | 读写,支持++、--、[n]、-n、<、<=、>、>= |
常用的容器中迭代器种类为双向迭代器,和随机访问迭代器
- 仿函数:作为算法的某种策略。
- 适配器(配接器):一种用来修饰容器或者仿函数或迭代接口的东西。
- 空间配置器:负责空间的配置与管理。
1.3容器、算法、迭代器案例(Vector)
创建、插入、遍历
1.3.1vector存放内置数据类型
案例代码:
//.h
#pragma once
#include "iostream"
#include"vector"
#include "algorithm"
class STL_Vector
{
public:void test_exp();
};//.cpp
#include "STL_Vector.h"using namespace std;
void func(int val)
{cout << val << endl;
}
void STL_Vector::test_exp()
{//创建vector <int> v;//添加v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);v.push_back(5);v.push_back(6);//遍历vector<int>::iterator it_begin = v.begin();vector<int>::iterator it_end = v.end();//方式一while (it_begin != it_end){cout << *it_begin << endl;it_begin++;}//方式二for (vector<int>::iterator it = v.begin(); it != v.end(); it++){cout << *it << endl;}//方式三for_each(v.begin(), v.end(), func);}//mian
#include "iostream"
using namespace std;
#include "STL_Vector.h"int main()
{STL_Vector sv = STL_Vector();sv.test_exp();system("pause");return 0;
}
运行结果:
1.3.2vector存放自定义数据类型及自定义对象指针
代码示例:
//.h
#pragma once
#include "iostream"
#include"vector"
#include "algorithm"
class STL_Vector
{
public:void test_exp();
};//.cpp#include "STL_Vector.h"
using namespace std;class Animal
{
public:Animal(int age,string name){m_age = age;m_name = name;}
public:int m_age;string m_name;
};
void func(int val)
{cout << val;
}
void STL_Vector::test_exp()
{//创建vector<Animal>v_p;vector<Animal *>v_point;//添加Animal a1(12,"僧面侯");Animal a2(12, "熊猫");Animal a3(12, "考拉");Animal a4(12, "袋鼠");v_p.push_back(a1);v_p.push_back(a2);v_p.push_back(a3);v_p.push_back(a3);v_point.push_back(&a1);v_point.push_back(&a2);v_point.push_back(&a3);v_point.push_back(&a4);//遍历for (vector<Animal>::iterator it = v_p.begin(); it != v_p.end(); it++){cout << "年龄:" << it->m_age << "姓名:" << (*it).m_name << endl;}for (vector<Animal *>::iterator it = v_point.begin(); it != v_point.end(); it++){cout << "年龄:" << (*it)->m_age << "姓名:" << (*it)->m_name << endl;}}//main
#include "iostream"
using namespace std;
#include "STL_Vector.h"int main()
{STL_Vector sv = STL_Vector();sv.test_exp();system("pause");return 0;
}
运行结果:
1.3.3容器嵌套-有点二维数组的意思
代码示例:
//.h
#pragma once
#include "iostream"
#include"vector"
#include "algorithm"
class STL_Vector
{
public:void test_exp();
};//.cpp
#include "STL_Vector.h"
using namespace std;class Animal
{
public:Animal(int age,string name){m_age = age;m_name = name;}
public:int m_age;string m_name;
};
void func(int val)
{cout << val;
}
void STL_Vector::test_exp()
{//创建vector<vector<string>> v_nest;vector<string> v_nest1;vector<string> v_nest2;vector<string> v_nest3;vector<string> v_nest4;//添加for (int i = 0; i < 5; i++){if (i % 2 == 0){v_nest1.push_back("我里个豆");v_nest2.push_back("我里个豆");v_nest3.push_back("我里个豆");v_nest4.push_back("我里个豆");}else{v_nest1.push_back("我里个乖");v_nest2.push_back("我里个乖");v_nest3.push_back("我里个乖");v_nest4.push_back("我里个乖");}}v_nest.push_back(v_nest1);v_nest.push_back(v_nest2);v_nest.push_back(v_nest3);v_nest.push_back(v_nest4);//遍历for (vector<vector<string>>::iterator it = v_nest.begin(); it != v_nest.end(); it++){for (vector<string>::iterator itt = (*it).begin(); itt != (*it).end(); itt++){cout << (*itt) ;}cout << endl;}
}
//mian
#include "iostream"
using namespace std;
#include "STL_Vector.h"int main()
{STL_Vector sv = STL_Vector();sv.test_exp();system("pause");return 0;
}
2、常用容器
2.1string
本质:string是C++风格的字符串,string是一个类。
char *是一个指针,是C风格的字符串
string是一个类,封装了char*,管理和维护char*的容器
2.1.1string构造函数
- 无参构造string();构造语句 string str;
- 字符串初始化string(const char * str);
- 拷贝构造string(const string& str);
- n个字符构造string(int n,char c);
代码示例:
//.h
#pragma once
#include "iostream"
using namespace std;
class string_
{
public:void test();};//.cpp
#include "string_.h"
#include "string"
void string_::test()
{//string str1;string str2 = "无语";string str3 = string(str2);string str4 = string(4, 'c');cout << "str1:" << str1 << endl;cout << "str2:" << str2 << endl;cout << "str3:" << str3 << endl;cout << "str4:" << str4 << endl;}int main()
{string_ t;t.test();system("pause");return 0;
}
运行结果:
2.1.2string的赋值操作
-
string& operator=(const char* s);
//char*类型字符串 赋值给当前的字符串 -
string& operator=(const string &s);
//把字符串s赋给当前的字符串 -
string& operator=(char c);
//字符赋值给当前的字符串 -
string& assign(const char *s);
//把字符串s赋给当前的字符串 -
string& assign(const char *s, int n);
//把字符串s的前n个字符赋给当前的字符串 -
string& assign(const string &s);
//把字符串s赋给当前字符串 -
string& assign(int n, char c);
//用n个字符c赋给当前字符串
代码示例:
//.h
#pragma once
#include "iostream"
using namespace std;
class string_
{
public:void test01();};//.cpp
#include "string_.h"
#include "string"
using namespace std;
//string & operator=(const char* s); //char*类型字符串 赋值给当前的字符串
//
//string& operator=(const string& s); //把字符串s赋给当前的字符串
//
//string& operator=(char c); //字符赋值给当前的字符串
//
//string& assign(const char* s); //把字符串s赋给当前的字符串
//
//string& assign(const char* s, int n); //把字符串s的前n个字符赋给当前的字符串
//
//string& assign(const string& s); //把字符串s赋给当前字符串
//
//string& assign(int n, char c); //用n个字符c赋给当前字符串
void string_::test01()
{string str1 = "无语考拉";string str2 = str1;string str3;str3 = 'a';string str4;str4.assign("无语拉拉");string str5;str5.assign(str4);string str6;str6.assign(4, 'c');cout << "str1 = " << str1 << endl;cout << "str2 = " << str2 << endl;cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;cout << "str5 = " << str5 << endl;cout << "str6 = " << str6 << endl;
}int main()
{string_ t;t.test01();system("pause");return 0;
}
运行结果:
2.1.3string字符串拼接
-
string& operator+=(const char* str);
//重载+=操作符 -
string& operator+=(const char c);
//重载+=操作符 -
string& operator+=(const string& str);
//重载+=操作符 -
string& append(const char *s);
//把字符串s连接到当前字符串结尾 -
string& append(const char *s, int n);
//把字符串s的前n个字符连接到当前字符串结尾 -
string& append(const string &s);
//同operator+=(const string& str) -
string& append(const string &s, int pos, int n);
//字符串s中从pos开始的n个字符连接到字符串结尾
代码示例:
//.h
#pragma once
#include "iostream"
using namespace std;
class string_
{
public:void test02();};//.cpp
#include "string_.h"
#include "string"
using namespace std;
//*`string& operator+=(const char* str); ` //重载+=操作符
//* `string& operator+=(const char c); ` //重载+=操作符
//* `string& operator+=(const string& str); ` //重载+=操作符
//* `string& append(const char* s); ` //把字符串s连接到当前字符串结尾
//* `string& append(const char* s, int n); ` //把字符串s的前n个字符连接到当前字符串结尾
//* `string& append(const string& s); ` //同operator+=(const string& str)
//* `string& append(const string& s, int pos, int n); `/ / 字符串s中从pos开始的n个字符连接到字符串结尾
void string_::test02()
{string str1 = "WC!";string str2;str2 += str1;string str3;str3 += str2;str3 += 'm';string str4;str4 += str2;str4 += "md";string str5;str5.append(str2);string str6;str6.append(str1, 2);str6.append(str2, 0, 2);cout << "str1 = " << str1 << endl;cout << "str2 = " << str2 << endl;cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;cout << "str5 = " << str5 << endl;cout << "str6 = " << str6 << endl;}int main()
{string_ t;t.test02();system("pause");return 0;
}
运行结果:
2.1.4string查找和替换
-
int find(const string& str, int pos = 0) const;
//查找str第一次出现位置,从pos开始查找 -
int find(const char* s, int pos = 0) const;
//查找s第一次出现位置,从pos开始查找 -
int find(const char* s, int pos, int n) const;
//从pos位置查找s的前n个字符第一次位置 -
int find(const char c, int pos = 0) const;
//查找字符c第一次出现位置 -
int rfind(const string& str, int pos = npos) const;
//查找str最后一次位置,从pos开始查找 -
int rfind(const char* s, int pos = npos) const;
//查找s最后一次出现位置,从pos开始查找 -
int rfind(const char* s, int pos, int n) const;
//从pos查找s的前n个字符最后一次位置 -
int rfind(const char c, int pos = 0) const;
//查找字符c最后一次出现位置 -
string& replace(int pos, int n, const string& str);
//替换从pos开始n个字符为字符串str -
string& replace(int pos, int n,const char* s);
//替换从pos开始的n个字符为字符串s
总结:
-
find查找是从左往后,rfind从右往左
-
find找到字符串后返回查找的第一个字符位置,找不到返回-1
-
replace在替换时,要指定从哪个位置起,多少个字符,替换成什么样的字符串(全换进去)
2.1.5string比较
-
字符串比较是按字符的ASCII码进行对比
= 返回 0
> 返回 1
< 返回 -1
函数原型:
-
int compare(const string &s) const;
//与字符串s比较 -
int compare(const char *s) const;
//与字符串s比较
2.1.6string插入和删除
-
string& insert(int pos, const char* s);
//插入字符串 -
string& insert(int pos, const string& str);
//插入字符串 -
string& insert(int pos, int n, char c);
//在指定位置插入n个字符c -
string& erase(int pos, int n = npos);
//删除从Pos开始的n个字符
插入和删除的起始坐标0开始!
2.1.7string截取子串
函数原型:
string substr(int pos = 0, int n = npos) const;
//返回由pos开始的n个字符组成的字符串
代码示例:
void string_::test04()
{string str = "sefgsfsdfdsfg";string substr = str.substr(1, 5);cout << "substr:" << substr<<endl;//截取信息string str1 = "rwerwefgewf1313@163.cn";int pos = str1.find('@');cout << "emial:" << str1 << endl;string substr_email = str1.substr(0, pos);cout << "substr_email:" << substr_email;
}
运行结果:
2.1.8string字符中字符获取与修改
string中单个字符存取方式有两种
-
char& operator[](int n);
//通过[]方式取字符 -
char& at(int n);
//通过at方法获取字符
代码示例:
void string_::test03()
{string str = "卧槽了,我去!";str[2] = 'a';for (int i = 0; i < str.size(); i++){cout << str[i] ;}cout << endl;str.at(2) = 'a';for (int i = 0; i < str.size(); i++){cout << str.at(i);}
}
string字符串中单个字符存取有两种方式,利用 [ ] 或 at
2.2vector容器
2.2.1vector基本概念
又称单端数组
与数组的最大区别,数组静态的,vector可动态申请空间,采用的方式是动态扩展
即复制原数据到新开辟空间的上,并释放原空间
常用函数及迭代器:
vector容器是支持随机访问的迭代器
2.2.2vector构造函数
-
vector<T> v;
//采用模板实现类实现,默认构造函数 -
vector(v.begin(), v.end());
//将v[begin(), end())前闭后开区间中的元素拷贝给本身。 -
vector(n, elem);
//构造函数将n个elem拷贝给本身。 -
vector(const vector &vec);
//拷贝构造函数。
2.2.3vector赋值操作
-
vector& operator=(const vector &vec);
//重载等号操作符
-
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。 -
assign(n, elem);
//将n个elem拷贝赋值给本身。
2.2.4vector容量
-
empty();
//判断容器是否为空,空返回为true,否则为false -
capacity();
//容器的容量capacity >= size; -
size();
//返回容器中元素的个数 -
resize(int num);
//重新指定容器的长度为num,若容器变长,则以默认值(0)填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
-
resize(int num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除
2.2.5 vector插入和删除
-
push_back(ele);
//尾部插入元素ele -
pop_back();
//删除最后一个元素 -
insert(const_iterator pos, ele);
//迭代器指向位置pos插入元素ele -
insert(const_iterator pos, int count,ele);
//迭代器指向位置pos插入count个元素ele -
erase(const_iterator pos);
//删除迭代器指向的元素 -
erase(const_iterator start, const_iterator end);
//删除迭代器从start到end之间的元素 -
clear();
//删除容器中所有元素
2.2.6vector数据获取
-
at(int idx);
//返回索引idx所指的数据 -
operator[];
//返回索引idx所指的数据 -
front();
//返回容器中第一个数据元素 -
back();
//返回容器中最后一个数据元素
注意:是迭代器参数!
2.2.7vector互换
功能描述:
-
实现两个容器内元素进行互换
函数原型:
-
swap(vec);
// 将vec与本身的元素互换
实际用途:收缩内存空间
v.swap(v);
v的申请空间很大,但实际用的很少,内存浪费!于是vector<T> (v).swap(v);
vector<T> (v)为匿名对象,其占用内存和v一样,但申请空间和占用空间一致,最后再换给v,使得空间压缩,不浪费了!
2.2.8vector预留空间
功能描述:
-
减少vector在动态扩展容量时的扩展次数
函数原型:
-
reserve(int len);
//容器预留len个元素长度,预留位置不初始化,元素不可访问。
2.3deque容器
2.3.1deque基本概念和原理
功能:
-
双端数组,可以对头端进行插入删除操作
deque与vector区别:
-
vector对于头部的插入删除效率低,数据量越大,效率越低
-
deque相对而言,对头部的插入删除速度回比vector快
-
vector访问元素时的速度会比deque快,这和两者内部实现有关
常用迭代器和函数:
deque内部工作原理:
deque内部有个中控器,维护每段缓冲区中的内容,缓冲区中存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间
deque容器的迭代器也是支持随机访问的。
2.3.2deque构造函数
-
deque<T>
deqT; //默认构造形式 -
deque(beg, end);
//构造函数将[beg, end)区间中的元素拷贝给本身。 -
deque(n, elem);
//构造函数将n个elem拷贝给本身。 -
deque(const deque &deq);
//拷贝构造函数
注意:
//参数是const只读模式,则迭代器也应该变为只读迭代器
void printDeque(const deque<int>& d)
{
for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
cout << *it << " ";}
cout << endl;
}
2.3.4 deque赋值
-
deque& operator=(const deque &deq);
//重载等号操作符
-
assign(beg, end);
//将[beg, end)区间中的数据拷贝赋值给本身。 -
assign(n, elem);
//将n个elem拷贝赋值给本身。
2.3.4deque大小获取、大小更改、判空
-
deque.empty();
//判断容器是否为空 -
deque.size();
//返回容器中元素的个数 -
deque.resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值(0)填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
-
deque.resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
注意:
deque没有capacity属性!
2.3.5deque插入与删除
-
push_back(elem);
//在容器尾部添加一个数据 -
push_front(elem);
//在容器头部插入一个数据 -
pop_back();
//删除容器最后一个数据 -
pop_front();
//删除容器第一个数据
指定位置操作:
pos是迭代器位置
-
insert(pos,elem);
//在pos位置插入一个elem元素的拷贝,返回新数据的位置。 -
insert(pos,n,elem);
//在pos位置插入n个elem数据,无返回值。 -
insert(pos,v.beg,v.end);
//在pos位置插入迭代器[beg,end)区间的数据,无返回值。 -
clear();
//清空容器的所有数据 -
erase(beg,end);
//删除迭代器[beg,end)区间的数据,返回下一个数据的位置。 -
erase(pos);
//删除pos位置的数据,返回下一个数据的位置。
2.3.6数据获取
-
at(int idx);
//返回索引idx所指的数据 -
operator[];
//返回索引idx所指的数据 -
front();
//返回容器中第一个数据元素 -
back();
//返回容器中最后一个数据元素
2.3.7排序
-
sort(iterator beg, iterator end)
//对beg和end区间内元素进行排序
注意:#include ”algorithm“;
2.4容器案例
有5名选手,10个评委分别对每一名选手打分,去除最高分,去除评委中最低分,取平均分。
代码示例:
//.h
#pragma once
#include "vector"
#include "deque"
#include "string"
using namespace std;
class player
{
public:string m_name;deque<float> m_scores;
};
class Exa_evaluate_score
{
public:Exa_evaluate_score();void eval(int index);void summarizing(int index);
public:vector<player> m_p;
};//.cpp
#include "Exa_evaluate_score.h"
#include "string"
#include "deque"
#include <iostream>
#include "algorithm"
using namespace std;Exa_evaluate_score::Exa_evaluate_score()
{m_p.resize(10);
}
void Exa_evaluate_score::eval(int index)
{cout << "请输入第" << index+1 << "位选手得分:" << endl;cout << "输入姓名:" ;string name;getline(cin, name);m_p[index].m_name= name;for (int j = 0; j < 10; j++){cout << "第" << j + 1 << "位评委请对" << index+1 << "位选手打分:" << endl;float temp_score;cin >> temp_score;m_p[index].m_scores.push_back(temp_score);}
}
void Exa_evaluate_score::summarizing(int index)
{sort(m_p[index].m_scores.begin(), m_p[index].m_scores.end());cout << "最低分为:" << m_p[index].m_scores.front() << endl;cout << "最高分为:" << m_p[index].m_scores.back() << endl;m_p[index].m_scores.pop_back();m_p[index].m_scores.pop_front();float sum = 0;for (deque<float>::iterator it = m_p[index].m_scores.begin(); it != m_p[index].m_scores.end(); it++){sum += *it;}cout << "去除最低分最高分后平均分为:" <<(float)sum / m_p[index].m_scores.size() << endl;}
int main()
{Exa_evaluate_score e;for (int i = 0; i < 2; i++){e.eval(i);e.summarizing(i);}system("pause");return 0;
}
运行结果:
2.5stack 容器
2.5.1基本概念
FILO(First In Last Out)
2.5.2stack构造函数
-
stack<T> stk;
//stack采用模板类实现, stack对象的默认构造形式 -
stack(const stack &stk);
//拷贝构造函数
2.5.3stack赋值函数
-
stack& operator=(const stack &stk);
//重载等号操作符
2.5.4stack出栈、入栈、取栈顶
-
push(elem);
//向栈顶添加元素 -
pop();
//从栈顶移除第一个元素 -
top();
//返回栈顶元素
2.5.5stack判空、获取大小
-
empty();
//判断堆栈是否为空 -
size();
//返回栈的大小
2.6queue容器
2.6.1queue基本概念
FIFO(First In First Out)
常用函数:尾入头出
2.6.2queue构造函数
-
queue<T> que;
//queue采用模板类实现,queue对象的默认构造形式 -
queue(const queue &que);
//拷贝构造函数
2.6.7queue赋值操作
-
queue& operator=(const queue &que);
//重载等号操作符
2.6.8queue入队、出队、获取队尾对头元素
-
push(elem);
//往队尾添加元素 -
pop();
//从队头移除第一个元素 -
back();
//返回最后一个元素 -
front();
//返回第一个元素
2.6.9queue判空、获取大小
-
empty();
//判断堆栈是否为空 -
size();
//返回栈的大小
2.7list容器
2.7.1list基本概念
双向循环链表:list容器的迭代器是双向迭代器,不支持随机访问!
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
list的优点:
-
采用动态存储分配,不会造成内存浪费和溢出
-
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
-
链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
STL中List和vector是两个最常被使用的容器,各有优缺点
2.7.2 list构造函数
-
list<T> lst;
//list采用采用模板类实现,对象的默认构造形式: -
list(beg,end);
//构造函数将[beg, end)区间中的元素拷贝给本身。 -
list(n,elem);
//构造函数将n个elem拷贝给本身。 -
list(const list &lst);
//拷贝构造函数。
2.7.3 list赋值操作
-
assign(beg, end);
//将迭代器[beg, end)区间中的数据拷贝赋值给本身。 -
assign(n, elem);
//将n个elem拷贝赋值给本身。 -
list& operator=(const list &lst);
//重载等号操作符
2.7.4 list交换操作
swap(lst);
//将lst与本身的元素互换。
注:不要求两个交换的list的数据个数相同!
2.7.5 list获取大小、判空、更改容量
-
size();
//返回容器中元素的个数 -
empty();
//判断容器是否为空 -
resize(num);
//重新指定容器的长度为num,若容器变长,则以默认值填充新位置。//如果容器变短,则末尾超出容器长度的元素被删除。
-
resize(num, elem);
//重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
2.7.6 list插入与删除
-
push_back(elem);//在容器尾部加入一个元素
-
pop_back();//删除容器中最后一个元素
-
push_front(elem);//在容器开头插入一个元素
-
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值匹配的元素。
2.7.7 list 获取第一个元素、最后一个元素
list不支持随机访问,即无 [] ,at等随机访问的方法;
-
front();
//返回第一个元素。 -
back();
//返回最后一个元素。
2.7.8 list反转和排序
-
reverse();
//反转链表 -
sort();
//链表排序 参数为一个回调函数,其中声明了排序次序即可更改默认 次序(从小到大)的次序。
L.sort(myCompare); //指定规则,从大到小
bool myCompare(int val1 , int val2)
{
return val1 > val2;
}
2.7.9 list排序案例
案例描述:将NuiMa自定义数据类型进行排序,NuiMa中属性有姓名、身份排名、薪资水平
排序规则:按照身份排名进行升序,如果身份排名相同按照薪资水平进行升序
代码示例:
//.h
#pragma once
#include "string"
#include "list"
using namespace std;
//牛马类
class NiuMa
{
public:NiuMa(string name,int salary,int status){m_name = name;m_status = status;m_salary = salary;};
public:string m_name;int m_salary;int m_status;
};
class list_sort_niuma
{
public:list<NiuMa> m_NM;
};//.cpp
#include "list_sort_niuma.h"
#include "iostream"bool sort_relu(NiuMa &nm1 , NiuMa &nm2)
{if (nm1.m_status == nm2.m_status){return nm1.m_salary > nm2.m_salary;}return nm1.m_status < nm2.m_status;
}
int main()
{NiuMa nm1("小牛马1",5,1);NiuMa nm2("小牛马2", 50, 2);NiuMa nm3("小牛马3", 50, 2);NiuMa nm4("小牛马4", 500, 3);NiuMa nm5("小牛马5", 500, 1);NiuMa nm6("小牛马6", 5000, 1);list_sort_niuma lsn;lsn.m_NM.push_back(nm1);lsn.m_NM.push_back(nm2);lsn.m_NM.push_back(nm3);lsn.m_NM.push_back(nm4);lsn.m_NM.push_back(nm5);lsn.m_NM.push_back(nm6);cout << "排序前:" << endl;for (list<NiuMa>::iterator it = lsn.m_NM.begin(); it != lsn.m_NM.end(); it++){cout << "姓名: " << it->m_name << " 社会地位排名: " << it->m_status<< " 薪资水平: " << it->m_salary << endl;}lsn.m_NM.sort(sort_relu);cout << "排序后:" << endl;for (list<NiuMa>::iterator it = lsn.m_NM.begin(); it != lsn.m_NM.end(); it++){cout << "姓名: " << it->m_name << " 社会地位排名: " << it->m_status<< " 薪资水平: " << it->m_salary << endl;}system("pause");return 0;
}
运行结果:
2.8set、multiset 容器
2.8.1set及multiset的基本概念和区别
简介所有的元素会被插入后自动进行排序
本质:关联式容器,底层二叉树实现
set与multiset区别:
- set不允许有重复元素
- multiset允许有重复元素
注:两个包含头文件,只需包含set即可,即#include ”set“;
2.8.2set构造函数
-
set<T> st;
//默认构造函数: -
set(const set &st);
//拷贝构造函数
2.8.3set赋值操作
-
set& operator=(const set &st);
//重载等号操作符
2.8.4set判空、获取大小、交换
-
size();
//返回容器中元素的数目 -
empty();
//判断容器是否为空 -
swap(st);
//交换两个集合容器
2.8.5set插入与删除
-
insert(elem);
//在容器中插入元素。 -
clear();
//清除所有元素 -
erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。 -
erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。 -
erase(elem);
//删除容器中值为elem的元素。
2.8.6set查找与统计
-
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end(); -
count(key);
//统计key的元素个数
2.8.7set、multiset的区别
-
set不可以插入重复数据,而multiset可以
-
set插入数据的同时会返回插入结果,表示插入是否成功
-
multiset不会检测数据,因此可以插入重复数据
2.8.8pair对组创建
成对出现的数据,利用对组可返回两个数据
对组创建方式:
pair<type,type> p (val1,val2);
pair<type,type> p = make_pair(val1,val2);
代码示例:
pair<string, int> p(string("Tom"), 20);
pair<string, int> p2 = make_pair("Jerry", 10);
2.8.9set排序
2.8.9.1内置数据类型排序
主要技术点:
-
利用仿函数,可以改变排序规则
-
注意:一定在插入之前就利用仿函数进行指定排序顺序
代码示例:
#include "set"
class MyCompare
{
public:bool operator()(int v1, int v2) {return v1 > v2;}
};
void test01()
{ set<int> s1;s1.insert(10);s1.insert(40);s1.insert(20);s1.insert(30);s1.insert(50);//默认从小到大for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {cout << *it << " ";}cout << endl;//指定排序规则set<int,MyCompare> s2;s2.insert(10);s2.insert(40);s2.insert(20);s2.insert(30);s2.insert(50);for (set<int, MyCompare>::iterator it = s2.begin(); it != s2.end(); it++) {cout << *it << " ";}cout << endl;
}int main() {test01();system("pause");return 0;
}
2.8.9.2自定义数据类型排序
对于自定义数据类型,set必须指定排序规则才可以插入数据
代码示例:
#include <set>
#include <string>class Person
{
public:Person(string name, int age){this->m_Name = name;this->m_Age = age;}string m_Name;int m_Age;};
class comparePerson
{
public:bool operator()(const Person& p1, const Person &p2){//按照年龄进行排序 降序return p1.m_Age > p2.m_Age;}
};void test01()
{set<Person, comparePerson> s;Person p1("刘备", 23);Person p2("关羽", 27);Person p3("张飞", 25);Person p4("赵云", 21);s.insert(p1);s.insert(p2);s.insert(p3);s.insert(p4);for (set<Person, comparePerson>::iterator it = s.begin(); it != s.end(); it++){cout << "姓名: " << it->m_Name << " 年龄: " << it->m_Age << endl;}
}
int main() {test01();system("pause");return 0;
}
2.9map、multimap容器
2.9.1map基本概念
简介:
- map中所有的元素都是pair
- pair中第一个元素为key,起到索引作用,第二个元素为value
- 所有的元素会根据元素key自动排序
本质:
-
map/multimap属于关联式容器,底层结构是用二叉树实现
优点:
-
可以根据key值快速找到value值
map和multimap区别:
-
map不允许容器中有重复key值元素
-
multimap允许容器中有重复key值元素
2.9.2map的构造和赋值
-
map<T1, T2> mp;
//map默认构造函数: -
map(const map &mp);
//拷贝构造函数 -
map& operator=(const map &mp);
//重载等号操作符
void test01()
{map<int,int>m; //默认构造m.insert(pair<int, int>(1, 10));m.insert(pair<int, int>(2, 20));m.insert(pair<int, int>(3, 30));printMap(m);map<int, int>m2(m); //拷贝构造printMap(m2);map<int, int>m3;m3 = m2; //赋值printMap(m3);
}
2.9.3map判空、获取大小、交换
-
size();
//返回容器中元素的数目 -
empty();
//判断容器是否为空 -
swap(st);
//交换两个集合容器
2.9.4map插入、删除
-
insert(elem);
//在容器中插入元素。 -
clear();
//清除所有元素 -
erase(pos);
//删除pos迭代器所指的元素,返回下一个元素的迭代器。 -
erase(beg, end);
//删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。 -
erase(key);
//删除容器中值为key的元素。
void test01()
{//插入map<int, int> m;//第一种插入方式-对组方式一m.insert(pair<int, int>(1, 10));//第二种插入方式-对组方式二m.insert(make_pair(2, 20));//第三种插入方式,不常用m.insert(map<int, int>::value_type(3, 30));//第四种插入方式//若key不存在则,会创建一个key为该值,value为默认参数的数据,不好//所以[]常用于根据key获取valuem[4] = 40; printMap(m);//删除m.erase(m.begin());printMap(m);//按照key值删除m.erase(3);printMap(m);//清空m.erase(m.begin(),m.end());m.clear();printMap(m);
}
2.9.5 map查找与统计
-
find(key);
//查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end(); -
count(key);
//统计key的元素个数
注意:
-
查找 --- find (返回的是迭代器)
-
统计 --- count (对于map,结果为0或者1)由于map不允许有重复的key对组!multimap而言就是具体的个数。
2.9.6map排序
-
map容器默认排序规则为 按照key值进行 从小到大排序,掌握如何改变排序规则
主要技术点:
-
利用仿函数,可以改变排序规则
代码示例:
#include <map>
class MyCompare {
public:bool operator()(int v1, int v2) {return v1 > v2;}
};
void test01()
{//默认从小到大排序//利用仿函数实现从大到小排序map<int, int, MyCompare> m;
m.insert(make_pair(1, 10));m.insert(make_pair(2, 20));m.insert(make_pair(3, 30));m.insert(make_pair(4, 40));m.insert(make_pair(5, 50));
for (map<int, int, MyCompare>::iterator it = m.begin(); it != m.end(); it++) {cout << "key:" << it->first << " value:" << it->second << endl;}
}
int main() {
test01();
system("pause");
return 0;
}
总结:
-
利用仿函数可以指定map容器的排序规则
-
对于自定义数据类型,map必须要指定排序规则,同set容器
2.9.7map案例
案例描述:
-
公司今天招聘了10个员工(ABCDEFGHIJ),10名员工进入公司之后,需要指派员工在那个部门工作
-
员工信息有: 姓名 工资组成;部门分为:策划、美术、研发
-
随机给10名员工分配部门和工资
-
通过multimap进行信息的插入 key(部门编号) value(员工)
-
分部门显示员工信息
代码示例:
//.h
#pragma once#include <iostream>
#include <random>
#include "string"
#include "vector"
#include "map"
using namespace std;
class employer
{
public:string m_Name;int m_DepId;float m_Salary;
public:employer(string name){m_Name = name;m_DepId = rand() % 3;// 创建一个随机数引擎std::random_device rd;std::mt19937 gen(rd());// 定义浮点数范围float lower_bound = 0.0f;float upper_bound = 100.0f;// 创建一个均匀分布的随机数生成器std::uniform_real_distribution<float> dist(lower_bound, upper_bound);// 生成随机浮点数m_Salary = dist(gen);}
};
class exp_map
{
public:vector<employer> m_emp;multimap<int, employer> m_DepMap;
};//.cpp
#include "exp_map.h"
#include "iostream"
using namespace std;
#define CEHUA 0
#define MEISHU 1
#define YANFA 2
void insert_exp(exp_map &em)
{employer e1("A");employer e2("B");employer e3("C");employer e4("D");employer e5("E");employer e6("F");employer e7("G");employer e8("H");employer e9("I");employer e10("J");em.m_emp.push_back(e1);em.m_emp.push_back(e2);em.m_emp.push_back(e3);em.m_emp.push_back(e4);em.m_emp.push_back(e5);em.m_emp.push_back(e6);em.m_emp.push_back(e7);em.m_emp.push_back(e8);em.m_emp.push_back(e9);em.m_emp.push_back(e10);
}
void insert_multimap(exp_map& em)
{for (vector<employer>::iterator it = em.m_emp.begin(); it != em.m_emp.end(); it++){em.m_DepMap.insert(pair<int,employer>((*it).m_DepId,*it));}
}void show_dep_emp(multimap<int, employer> &m)
{//map会按照key进行排序,因此返回pos即第一个符合的位置,其后count个均为其满足条件的结果cout << "策划部门:" << endl;multimap<int, employer>::iterator pos = m.find(CEHUA);int count = m.count(CEHUA);int index = 0;for (; pos != m.end() && index < count; pos++, index++){cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;}cout << "----------------------" << endl;cout << "美术部门: " << endl;pos = m.find(MEISHU);count = m.count(MEISHU); // 统计具体人数index = 0;for (; pos != m.end() && index < count; pos++, index++){cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;}cout << "----------------------" << endl;cout << "研发部门: " << endl;//range是一个符合find条件的迭代器集合auto range = m.equal_range(YANFA);//range.first 指向范围的开始(第一个匹配元素),而 range.second 则指向范围的末尾(最后一个匹配元素的下一个位置)//auto 用于自动推导变量的类型。使用 auto 可以使编译器自动推断变量的类型,而不需要手动指定for (auto pos = range.first; pos != range.second; ++pos) {cout << "姓名: " << pos->second.m_Name << " 工资: " << pos->second.m_Salary << endl;}}
int main()
{exp_map em;insert_exp(em);insert_multimap(em);show_dep_emp(em.m_DepMap);system("pause");return 0;
}
运行结果:
3、函数对象
3.1函数对象概念
概念:
-
重载函数调用操作符的类,其对象常称为函数对象
-
函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质:
函数对象(仿函数)是一个类,不是一个函数
特点:
-
函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
-
函数对象超出普通函数的概念,函数对象可以有自己的状态
-
函数对象可以作为参数传递
3.2函数对象的使用
1、函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
其实就是仿函数,本质是一个类重载了()运算符,然后通过匿名对象调用()。
2、函数对象可以有自己的状态
其实就是因为其本质是类,所以可以拥有成员变量即“自己的状态标识”
3、函数对象可以作为参数传递
其实就是因为其本质是类,则参数即为该类的对象实例!
3.3谓词
3.3.1谓词概念
- 返回bool类型的仿函数被称为谓词
- operator()重载()运算符,接受一个参数,那么叫一元谓词
- operator()重载()运算符,接受两个参数,那么叫二元谓词
目前看与仿函数挂钩
3.4内建函数对象-可直接拿来用的仿函数
3.4.1概念
STL内建了一些函数对象
- 算术仿函数
- 关系仿函数
- 逻辑仿函数
注意:
需要引入头文件 #include<functional>
3.4.2算术仿函数
仿函数原型:
-
template<class T> T plus<T>
//加法仿函数 -
template<class T> T minus<T>
//减法仿函数 -
template<class T> T multiplies<T>
//乘法仿函数 -
template<class T> T divides<T>
//除法仿函数 -
template<class T> T modulus<T>
//取模仿函数 -
template<class T> T negate<T>
//取反仿函数-一元仿函数,其余均为二元仿函数
//negate
void test01()
{negate<int> n;cout << n(50) << endl;
}//plus
void test02()
{plus<int> p;cout << p(10, 20) << endl;
}
3.4.3关系仿函数
-
template<class T> bool equal_to<T>
//等于 -
template<class T> bool not_equal_to<T>
//不等于 -
template<class T> bool greater<T>
//大于 -
template<class T> bool greater_equal<T>
//大于等于 -
template<class T> bool less<T>
//小于 -
template<class T> bool less_equal<T>
//小于等于
//伪代码
class MyCompare
{
public:bool operator()(int v1,int v2){return v1 > v2;}
};
//自己实现仿函数
//sort(v.begin(), v.end(), MyCompare());
//STL内建仿函数 大于仿函数
sort(v.begin(), v.end(), greater<int>());
3.4.5逻辑仿函数
-
template<class T> bool logical_and<T>
//逻辑与 -
template<class T> bool logical_or<T>
//逻辑或 -
template<class T> bool logical_not<T>
//逻辑非
逻辑仿函数实际应用较少,了解即可
4、STL-常用算法
4.1常用头文件
-
算法主要是由头文件
<algorithm>
<functional>
<numeric>
组成。 -
<algorithm>
是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等 -
<numeric>
体积很小,只包括几个在序列上面进行简单数学运算的模板函数 -
<functional>
定义了一些模板类,用以声明函数对象。
4.2常用遍历算法
4.2.1for_each 很常用
功能:遍历容器元素
for_each(iterator beg, iterator end, _func);
// beg 开始迭代器
// end 结束迭代器
// _func 函数或者函数对象
4.2.2transform
功能:搬运容器到另一个容器中
transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数或者函数对象,这可以对搬运的数据进行一些运算,实现在仿函数中
//仿函数,不对搬运数据进行处理,直接返回
class TransForm
{
public:int operator()(int val){return val;}};
4.3常用查找算法
4.3.1find
功能:查找指定元素(内置类型、自定义类型),找到返回指定元素的迭代器,找不到返回结束迭代器end()
find(iterator beg, iterator end, value);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// value 查找的元素
注意:利用find可以在容器中找指定的元素,返回值是迭代器
对于自定义数据类型应该重载operator==,告诉如何对比查找。
//重载==bool operator==(const Person& p) {if (this->m_Name == p.m_Name && this->m_Age == p.m_Age) {return true;}
4.3.2find_if
功能:按照条件查找元素
find_if(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// _Pred 函数或者谓词(返回bool类型的仿函数)
4.3.3adjacent_find
功能:查找相邻重复元素
adjacent_find(iterator beg, iterator end);
// 查找相邻重复元素,返回相邻元素的第一个位置的迭代器
// beg 开始迭代器
// end 结束迭代器
4.3.4count
功能:统计元素个数
count(iterator beg, iterator end, value);
// 统计元素出现次数
// beg 开始迭代器
// end 结束迭代器
// value 统计的元素
注意:value也可以是自定义数据类型,需要在自定义数据类型重载operator==
bool operator==(const Person & p){if (this->m_Age == p.m_Age){return true;}else{return false;}}
4.3.5count_if
功能:按条件统计元素个数
count_if(iterator beg, iterator end, _Pred);
// 按条件统计元素出现次数
// beg 开始迭代器
// end 结束迭代器
// _Pred 谓词
4.3.6binary_search
功能:查找指定元素是否存在
bool binary_search(iterator beg, iterator end, value);
// 查找指定的元素,查到 返回true 否则false
// 注意: 在无序序列中不可用,因为底层是二分查找
// beg 开始迭代器
// end 结束迭代器
// value 查找的元素
4.4常用排序算法
4.4.1sort 常用
功能:对容器内元素进行排序
sort(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// _Pred 谓词
4.4.2random_shuffle
功能:洗牌、指定范围的元素打乱次序
random_shuffle(iterator beg, iterator end);
// 指定范围内的元素随机调整次序
// beg 开始迭代器
// end 结束迭代器
注意:用之前需要加种子,才能真实随机。
随机种子:srand((unsigned int) time(NULL));
4.4.3merge
功能:两个容器元素合并,并存储到另一个容器中
merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 容器元素合并,并存储到另一容器中
// 注意: 两个容器必须是有序的,记得给放入的容器resize扩容一下。
// beg1 容器1开始迭代器
// end1 容器1结束迭代器
// beg2 容器2开始迭代器
// end2 容器2结束迭代器
// dest 目标容器开始迭代器
4.4.4reverse
功能:容器内元素进行反转
reverse(iterator beg, iterator end);
// 反转指定范围的元素
// beg 开始迭代器
// end 结束迭代器
4.5常用拷贝喝替换函数
4.5.1copy
功能:容器内指定范围的元素拷贝到另一容器中
copy(iterator beg, iterator end, iterator dest);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// dest 目标起始迭代器
4.5.2replace
功能:将容器内指定范围的旧元素替换为新的元素
replace(iterator beg, iterator end, oldvalue, newvalue);
// 将区间内旧元素 替换成 新元素
// beg 开始迭代器
// end 结束迭代器
// oldvalue 旧元素
// newvalue 新元素
4.5.3replace_if
功能:
将区间内满足条件的元素,替换成指定元素
replace_if(iterator beg, iterator end, _pred, newvalue);
// 按条件替换元素,满足条件的替换成指定元素
// beg 开始迭代器
// end 结束迭代器
// _pred 谓词
// newvalue 替换的新元素
4.5.4swap
功能:互换两个容器的元素
swap(container c1, container c2);
// 互换两个容器的元素
// c1容器1
// c2容器2
注意:两个容器数据类型必须相同
4.6常用算术生成算法
4.6.1accumulate
功能:计算区间内容器元素累计总和
accumulate(iterator beg, iterator end, value);
// 计算容器元素累计总和
// beg 开始迭代器
// end 结束迭代器
// value 起始值 一般为0
注意:accumulate使用时头文件注意是 numeric,这个算法很实用
4.6.2fill
功能:向容器中填充指定的元素
fill(iterator beg, iterator end, value);
// 向容器中填充元素
// beg 开始迭代器
// end 结束迭代器
// value 填充的值
4.7常用集合算法-交并差集
4.7.1set_intersectionn
功能:求两个容器的交集
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的交集
// 注意:两个集合必须是有序序列
// beg1 容器1开始迭代器
// end1 容器1结束迭代器
// beg2 容器2开始迭代器
// end2 容器2结束迭代器
// dest 目标容器开始迭代器
注意:
-
求交集的两个集合必须的有序序列
-
目标容器开辟空间需要从两个容器中取小值
-
set_intersection返回值既是交集中最后一个元素的位置
遍历用返回的最后的迭代器itEnd.
set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());for_each(vTarget.begin(), itEnd, myPrint());cout << endl;
4.7.2set_union
功能:求并集
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的并集
// 注意:两个集合必须是有序序列
// beg1 容器1开始迭代器
// end1 容器1结束迭代器
// beg2 容器2开始迭代器
// end2 容器2结束迭代器
// dest 目标容器开始迭代器
注意:
-
求并集的两个集合必须的有序序列
-
目标容器开辟空间需要两个容器相加
-
set_union返回值既是并集中最后一个元素的位置
4.7.3set_difference
功能:两个集合差集
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的差集
// 注意:两个集合必须是有序序列
// beg1 容器1开始迭代器
// end1 容器1结束迭代器
// beg2 容器2开始迭代器
// end2 容器2结束迭代器
// dest 目标容器开始迭代器
注意:
-
求差集的两个集合必须的有序序列
-
目标容器开辟空间需要从两个容器取较大值
-
set_difference返回值既是差集中最后一个元素的位置