文章目录
C++提高编程
模板
c++另一种编程思想机制为泛型编程,主要利用的技术就是模板
分为两种模板机制:函数模板和类模板
函数模板
语法:
template<typename T>
函数申明或定义
template —— 申明创建模板
typename —— 表面其后面的符号是一种数据类型,可以用class代替
T —— 通用的数据类型,名称可以换
交换模板解释
#include<iostream>
using namespace std;
template<typename T>
void aswap(T &a, T &b) {
T temp = a;
a = b;
b = temp;
}
int main()
{
int a, b;
cin >> a >> b;
aswap(a, b);//自动类型推导
aswap<int>(a, b);//显示指定类型
cout << a << b;
}
模板注意事项
1.自动类型推导,需要推导出一致的类型T才可以使用
int main()
{
int a;
char b;
cin >> a >> b;
aswap(a, b);//错误,推导不出一致的T
cout << a << b;
}
2.模板必须确定出T的数据类型,才可以使用
template<class T>
void func() {
cout << "YES" << endl;
}
int main()
{
func();//错误
}
实用样例 排序模板
#include<iostream>
#include<cstring>
using namespace std;
template<class T>
void sswap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
template<class T>
void ssort(T a[], int len) {
for (int i = 0; i < len; i++) {
int max = i;
for (int j = i + 1; j < len; j++) {
if (a[max] < a[j]) {
max = j;
}
}
if (max != i) {
sswap(a[max], a[i]);
}
}
}
template<class T>
void pprintf(T a[], int len) {
for (int i = 0; i < len; i++)
{
cout << a[i] << " ";
}
cout << endl;
}
int main()
{
int a[] = { 1,2,3,4,5,6,7 };
int intlen = sizeof(a) / sizeof(int);//计算数组的长度
char b[] = "abcdef";
int csize = sizeof(b) / sizeof(char);
ssort(a, intlen);
ssort(b, csize);
pprintf(a, intlen);
pprintf(b, csize);
}
普通函数和函数模板的区别
1.普通函数调用可以发生隐式类型转换
2.函数模板 用自动类型推导,不可以发生隐式类型转换
3.函数模板 用显示指定类型,可以发生隐式类型转换 把传入的转为指定类型
普通函数和函数模板的调用规则
1.如果函数模板和普通模板都可以实现,优先普通模板
2.可以通过空模板参数列表来强制调用函数模板 (指定类型推导的方式,但不指定) sort< >(a,b)
3.函数模板可以重载
4.如果函数模板可以产生更好匹配,优先函数模板
模板的局限性
模板的通用性不是万能的,如数组的赋值和类内数据大小比较等…
C++为了解决这种情况提供了可具体化的实现
类内的比较:
#include<iostream>
#include<cstring>
using namespace std;
class p {
public:
p(string name, int age) {
this->name = name;
this->age = age;
}
string name;
int age;
};
template <class T>
bool bijiao(T& a, T& b)
{
if (a == b) return true;
else return false;
}
template <> bool bijiao(p& a, p& b)
{
if (a.age == b.age) return true;
else return false;
}
int main()
{
p p1("make", 18);
p p2("jett", 16);
if (bijiao(p1,p2))cout << "相等";
else cout << "不相等";
}
学习模板不是为了写模板,而是为了可以运用STL里提供的模板
类模板
语法:
template<class T>
类
举例
#include<iostream>
#include<cstring>
using namespace std;
template <class nameT,class ageT>
class p{
public:
p(nameT name, ageT age) {
this->name = name;
this->age = age;
}
void show() {
cout << this->name << ' ' << this->age;
}
nameT name;
ageT age;
};
int main()
{
p<string,int> p1("小溪",17);
p1.show();
}
模板和函数模板的区别
1.类模板没有自动类型推导使用方式
p<> p1("小溪",17);错误
p<string,int> p1("小溪",17);正确
2.类模板在模板参数列表中可以有默认参数
template <class nameT,class ageT = int>
p<string> p1("小溪",17);
类模板中成员函数创建时间
1.普通类中的成员函数一开始就可以创建
2.类模板中的成员函数在调用时才可以创建
类模板对象做函数参数
三种传入方式
1.指定传入的类型 2.参数模板化 3.整个类模板化
#include<iostream>
#include<cstring>
using namespace std;
template<class T1,class T2>
class p {
public:
p(T1 name, T2 age) {
this->name = name;
this->age = age;
}
string name;
int age;
void show() {
cout << name << ' ' << age;
}
};
//1.指定传入类型
void printfp1(p<string, int>& p1) {
p1.show();
}
//2.参数模板化
template<class T1,class T2>
void printfp2(p<T1,T2>& p1)
{
p1.show();
}
//3.整个模板化
template<class T>
void printfp3(T &p1)
{
p1.show();
cout << "T的数据类型" << typeid(T).name();//看看T的类型
}
int main()
{
p<string,int> p1("小溪",17);
printfp1(p1);
printfp2(p1);
printfp3(p1);
}
类模板与继承
如果父类是类模板,子类需要指定父类T的数据类型
template<class T>
class base {
public:
T a;
};
//class son:public base 错误,需要指定父类T的类型,才可以继承给子类
class son:public base<int>
{
};
template<class T>
class base {
public:
T a;
};
//想灵活指定父类中T的类型,子类也需要变成模板
template<class T1,class T2>
class son:public base<T2>
{
T1 ojb;
};
int main()
{
son<int, char> S1;
}
类模板的类外实现
#include<iostream>
#include<cstring>
using namespace std;
template<class T1, class T2>
class p {
public:
p(T1 name, T2 age);
string name;
int age;
void show();
};
template<class T1,class T2>
p<T1, T2>::p(T1 name, T2 age) {
this->name = name;
this->age = age;
}
template<class T1,class T2>
void p<T1, T2>::show() {
cout << this->name << ' ' << this->age;
}
int main() {
p<string,int> p1("小李", 17);
p1.show();
}
类模板的分文件编写的解决方法
1.将头文件改为.cpp直接读源代码(不常用)
2.申明和实现写入同一个文件 文件后缀.hpp(主流)
类模板和友元
类内实现-直接在类内申明友元即可
类外实现-需要让编译器提前知道全局变量的存在
#include<iostream>
#include<cstring>
using namespace std;
template<class T1, class T2>//类外实现要让编译器提前知道
class p;
template<class T1, class T2>
void showname(p<T1, T2> p) {
cout << p.name;
}
template<class T1, class T2>
class p {
//全局函数类内实现
friend void show(p<T1,T2> p) {
cout << p.name << ' ' << p.age;
}
//全局函数类外实现
friend void showname <>(p<T1, T2> p);//需要加空模板的参数列表,类外实现要让编译器提前知道
public:
p(T1 name, T2 age)
{
this->name = name;
this->age = age;
}
string name;
int age;
};
int main() {
p<string,int> p1("小李", 17);
show(p1);
showname(p1);
}
STL
初识STL
容器:vector
算法:for-each
迭代器:vector::iterator
vector容器存放内置数据类型:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
//vector容器存放内置数据类型
void myPrintf(int val) {
cout << val << endl;
}
void test01() {
vector<int>v;
//向容器中插入数据
v.push_back(10);
v.push_back(20);
v.push_back(30);
//通过迭代器访问容器中的数据
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(),myPrintf);
}
int main() {
test01();
}
vector容器存放自定义数据类型:
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
//vector容器存放自定义数据类型
class person {
public:
person(string a, int b) {
this->name = a;
this->age = b;
}
string name;
int age;
};
void test01() {
vector<person> v;
person p1("aa", 10);
person p2("bb", 20);
person p3("cc", 30);
person p4("dd", 40);
person p5("ee", 50);
person p6("ff", 60);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
v.push_back(p6);
for (vector<person>::iterator it = v.begin(); it < v.end(); it++) {
cout << (*it).name << ' ' << (*it).age << endl;
}
}
void test02() {
vector<person* > v;
person p1("aa", 10);
person p2("bb", 20);
person p3("cc", 30);
person p4("dd", 40);
person p5("ee", 50);
person p6("ff", 60);
v.push_back(&p1);
v.push_back(&p2);
v.push_back(&p3);
v.push_back(&p4);
v.push_back(&p5);
v.push_back(&p6);
for (vector<person *>::iterator it = v.begin(); it < v.end(); it++) {
cout << (*it)->name << ' ' << (*it)->age << endl;
}
}
int main() {
test02();
}
vector容器嵌套容器 形成二维数组
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
//vector容器嵌套容器
void test01() {
vector<vector<int>>v;
vector<int>v1;
vector<int>v2;
vector<int>v3;
vector<int>v4;
for (int i = 0; i < 4; i++) {
v1.push_back(i + 1);
v2.push_back(i + 2);
v3.push_back(i + 3);
v4.push_back(i + 4);
}
v.push_back(v1);
v.push_back(v2);
v.push_back(v3);
v.push_back(v4);
for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
//(*it) --- 容器vector<int>
for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++) {
cout << *vit << " ";
}
cout << endl;
}
}
int main() {
test01();
}
STL之string
本质
string时C++风格的字符串,而string本质上时一个类
string和char * 区别
char * 是一个指针
string是一个类,类内封装了char *,管理这个字符串,是一个char *的容器
特点
string类内封装了许多成员方法,如查找find,拷贝copy,删除delect,插入insert,替换replace
string管理char *所分配的内存,不用担心复制越界和取值越界等,由类内负责(真好)
析构函数
string(); //创建一个空的字符串 例如: string str;
string(const char* s); //使用字符串s初始化
string(const string& str); //使用一个string对象初始化另一个string对象
string(int n, char c); //使用n个字符c初始化
void test01() {
string s1;
const char * str= "c++好难";
string s2(str);
string s3(s2);
string s4(8, 'a');
}
赋值操作
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 test01() {
string s1;
s1 = "c++真不戳";
string s2;
s2 = s1;
s2 = 'a';
string s3;
s3.assign("早上好中国");
string s4;
s4.assign("c++真不戳", 3);
s4.assign(s3);
s4.assign(5, 'w');
}
string的赋值方式很多,operator=常用
字符串拼接
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 test01() {
string str1;
str1 = "C++";
str1.append("我是真的");
str1.append("烦死了", 2);//文字是两个字符 写2才可以加进去一个
string str2("真不错");
cout << str2;
str1.append(str2,0,4);
cout << str1;
}
查找和替换
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 从左往右查找 rfind 从右往左查找 但显示都是正常下标
替换
string str2 = "abcdef";
str2.replace(1, 3, "1111");//从1号位置起3个字符替换为1111
cout << str2 << endl;//输出结果为:a1111ef
比较
int compare(const string &s) const; //与字符串s比较
int compare(const char *s) const; //与字符串s比较
根据 ASCII码值进行比较
= 返回 0 > 返回 1 < 返回 -1
字符串存取
char& operator[](int n); //通过[]方式取字符
char& at(int n); //通过at方法获取字符
就是正常的读取… str[i] 这样
字符串插入和删除
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个字符
求子串,字符串截取
string substr(int pos = 0, int n = npos) const; //返回由pos开始的n个字符组成的字符串
string str1 = "yzb@151609";
int ant = str1.find('@');
string str2 = str1.substr(0, ant);
cout << str2;
vector
基本概念
vector数据结构和数组非常相似,也称为单端数组
vector与普通数组区别:不同之处在于数组是静态空间,而vector可以动态扩展
动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
构造函数
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector的构造函数
void test01() {
vector<int> v1;//默认构造
for (int i = 0; i < 10; i++) {
v1.push_back(i);
}
printfv(v1);
vector<int>v2(v1.begin(), v1.end());//区间方式构造
printfv(v2);
vector<int>v3(10, 100);//n个elem方式构造 10个100;
printfv(v3);
vector<int>v4(v3);//拷贝构造
printfv(v4);
}
int main()
{
test01();
}
赋值操作
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector的赋值
void test01() {
vector<int> v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
};
printfv(v1);
vector<int>v2 = v1;//等号 = 赋值
printfv(v2);
vector<int>v3;//assign 区间赋值
v3.assign(v1.begin(), v1.end());
printfv(v3);
vector<int>v4;//n个elem赋值
v4.assign(5, 100);
printfv(v4);
}
int main()
{
test01();
}
大小和容量
判断是否为空 — empty();
返回元素个数 — size();
返回容器容量 — capacity();
重新指定大小 — resize();
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector的大小和容量
void test01() {
vector<int> v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
};
printfv(v1);
int a = v1.empty();//是否为空 1=空 0=不为空
int b = v1.size();//大小
int c = v1.capacity();//容量
v1.resize(15);//重新指定比原大小长,默认0填充
v1.resize(15, 100);//重新指定比原大小长,指定100填充
v1.resize(6);//重新指定比原大小小,说出超出部分
printfv(v1);
}
int main()
{
test01();
}
插入和删除
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(); //删除容器中所有元素
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int> &v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector的插入和删除
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);
v1.push_back(60);
v1.push_back(70);
printfv(v1);
v1.pop_back();//尾删法
printfv(v1);
v1.insert(v1.begin(), 100);//利用迭代器进行插入
v1.insert(v1.begin(), 2,1000);//利用迭代器进行插入 重载版本可以插入多个数据
printfv(v1);
v1.erase(v1.begin());//利用迭代器进行删除
printfv(v1);
v1.erase(v1.begin(),v1.end());//利用迭代器进行一段区域删除
v1.clear();//全部清空
printfv(v1);
}
int main()
{
test01();
}
数据存取
at(int idx); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
//vector数据的存取
void test01() {
vector<int> v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
};
for (int i = 0; i < 10; i++) {
cout << v1[i] << ' ';//读取方式一 []
cout << v1.at(i) << ' ';//读取方式二 at()
};
cout << v1.front();//第一个元素的获取
cout << v1.back();//第一个元素的获取
}
互换容器
swap(a,b);
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector容器互换
void test01() {
vector<int> v1;
for (int i = 0; i < 10; i++) {
v1.push_back(i);
};
vector<int>v2;
for (int i = 10; i > 0; i--) {
v2.push_back(i);
}
//1.基本用法
v1.swap(v2);
printfv(v1);
printfv(v2);
}
//2.实际用途,巧用swap可以收缩内存空间
void test02() {
vector<int>v;
for (int i = 0; i < 10000; i++)
{
v.push_back(i);
}
cout << "v的容量:" << v.capacity() << endl;
v.resize(3);//重新指定大小
cout << "v的大小:" << v.size() << endl;
cout << "v的容量:" << v.capacity() << endl;//空间占用不变
vector<int>(v).swap(v);//巧用swap收缩内存
cout << "v的大小:" << v.size() << endl;
cout << "v的容量:" << v.capacity() << endl;
}
int main()
{
test02();
}
收缩内存空间解析
vector<int>(v).swap(v);//巧用swap收缩内存
vector<int>(v)//匿名对象
.swap(v)//交换
预留空间
数据量大 可以直接预留出大小 避免重新开辟
reserve(int len); //预留空间
#include<iostream>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
void printfv(vector<int>& v) {
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//vector容器互换
void test01() {
vector<int> v;
int num = 0;//计算开辟次数
int* p = NULL;
v.reserve(100000);//利用reserve预留空间
for (int i = 0; i < 100000; i++) {
v.push_back(i);
if (p != &v[0]) {//首地址改变
p = &v[0];
num++;
}
}
cout << num;
}
int main()
{
test01();
}
deque
功能:双端数组,可以从头端进行插入删除操作
deque和vector的区别
1.vector对于头部的插入删除效率低,数据量越大,效率越低
2.deque相对而言,对头部的插入删除速度回比vector快
3.vector访问元素时的速度会比deque快,这和两者内部实现有关
内部原理
中控器,维护每段缓冲区内容,缓冲区存放真实数据
中控器维护的是每个缓冲区的地址,使得使用deque时像连续的内存空间
访问效率低的原因
多段缓冲区,头尾的缓冲区留有插入的位置,但读取 中间的数据时,需要重新在中控器寻找该段的地址
构造函数
deque deqT; //默认构造形式
deque(beg, end); //构造函数将[beg, end)区间中的元素拷贝给本身。
deque(n, elem); //构造函数将n个elem拷贝给本身。
deque(const deque &deq); //拷贝构造函数
//deque的构造函数及遍历
void printff(const deque<int>& T) {//将数组设置为只读,不可以进行更改
for (deque<int>::const_iterator it = T.begin(); it != T.end(); it++) {//迭代器也要同样设置为只读
cout << *it << ' ';
}
cout << endl;
}
void test01() {
deque<int> d;
for (int i = 0; i < 10; i++) {
d.push_back(i);
}
deque<int>d2(d.begin(), d.end());
deque<int>d3(10, 100);//10个100;
deque<int>d4(d3);
}
赋值操作
deque& operator=(const deque &deq); //重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
void test01()
{
deque<int> d;
for (int i = 0;i < 10;i++)
{
d.push_front(i);
}
deque<int> d1 = d;
deque<int> d2;
d2.assign(d.begin(), d.end());
deque<int> d3;
d3.assign(10, 20);
}
大小操作
无容量的概念
deque.empty(); //判断容器是否为空
deque.size(); //返回容器中元素的个数
deque.resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
deque.resize(num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
void test03()//deque容器的大小操作
{
deque<int> d;
for (int i = 0;i < 10;i++)
{
d.push_front(i);
}
if (d.empty())
{
cout << "数组为空" << endl;
}
else
{
cout << "数组不为空" << endl;
}
cout << "数组的大小:" << d.size() << endl;
d.resize(15);//重新分配空间,超出的补零
d.resize(15,100);//超出的默认值改为自定义
d.resize(2);//重新分配的空间比原空间小,删除多余数据
}
注意: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位置的数据,返回下一个数据的位置。
两端插入案例
void test01() {
deque<int> d;
//尾插
d.push_back(10);
d.push_back(20);
//头插
d.push_front(5);
d.push_front(0);
//尾删
d.pop_back();
//头删
d.pop_front();
}
指定位置操作案例
void test02() {
deque<int> d;
d.push_back(10);
d.push_back(20);
d.push_front(5);
d.push_front(0);
d.insert(d.begin(), 1);//头部插入1
d.insert(d.begin(), 2,100);//头部插入两个100
deque<int>d2;
d2.push_back(1);
d2.push_back(2);
d2.push_back(3);
d.insert(d.begin(), d2.begin(), d2.end());//在d的头部插入d2的开始到结束
deque<int>::iterator it = d.begin();
it++; //可以改变迭代器指向的位置
d.erase(it);//删除指定迭代器位置的数据
d.erase(d.begin(), d.end());//删除数组区间的所有数据,即清空
d.clear();//清空
}
注意:插入和删除提供的位置是迭代器,而不是数字
数据存取
at(int idx); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
for (int i=0;i<d.size();i++)
{
cout << d[i] << " ";//[]访问
cout << d.at(i) << " ";//at 访问
}
cout << "deque容器的第一个元素:" << d.front() << endl;//返回开头
cout << "deque容器最后一个元素:" << d.back() << endl;//返回末尾
deque排序
sort(d.begin(),d.end());
对于支持随机访问的迭代器,都可以用sort进行访问
stack
基本概念
stack(栈)是一种先进后出的数据结构,他只有一个出口
push - 入栈 pop - 出栈
最里面 - 栈底 入口处 - 栈顶
在内存里 栈底高地址 栈顶低地址
栈中只有顶端的元素才可以被外界使用,因此栈中无遍历的行为
常用接口
构造函数:
stack<T> stk; //stack采用模板类实现, stack对象的默认构造形式
stack(const stack &stk); //拷贝构造函数
赋值操作:
stack& operator=(const stack &stk); //重载等号操作符
数据存取:
push(elem); //向栈顶添加元素
pop(); //从栈顶移除第一个元素
top(); //返回栈顶元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小
//栈stack容器
void test01() {
stack<int> s;
s.push(10);//入栈
s.push(20);
s.push(30);
s.push(40);
cout << "栈的大小:" << s.size() << endl;
//只要栈不为空,查看栈顶,并执行出栈操作
while (!s.empty())
{
cout << "栈顶元素为:" << s.top() << endl;
s.pop();
}
cout << "栈的大小:" << s.size() << endl;
}
queue
基本概念
Queue(队列)是一种先进先出的数据结构,他有两个出口
push - 入队 pop - 出队
front - 队头 back - 队尾
队列容器允许从一端新增元素,另一端移除元素
队列中只有队头和队尾可以被外界使用,因此队列不允许有遍历行为
常用接口
构造函数:
queue<T> que; //queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que); //拷贝构造函数
赋值操作:
queue& operator=(const queue &que); //重载等号操作符
数据存取:
push(elem); //往队尾添加元素
pop(); //从队头移除第一个元素
back(); //返回最后一个元素
front(); //返回第一个元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小
class player {
public:
player(string name, int age) {
this->name = name;
this->age = age;
}
string name;
int age;
};
void test01() {
queue<player>q;//创建队列
player p1("刘备", 38);
player p2("诸葛亮", 36);
player p3("张飞", 32);
player p4("关羽", 34);
//入队
q.push(p1);
q.push(p2);
q.push(p3);
q.push(p4);
cout << "队列大小 : " << q.size() << endl;
//队列不为空则查看队头和队尾,出队
while (!q.empty())
{
cout << "查看队头 姓名:" << q.front().name << " 年纪:" << q.front().age << endl;
cout << "查看队尾 姓名:" << q.back().name << " 年纪:" << q.back().age << endl;
q.pop();//出队
}
cout << "队列大小 : " << q.size() << endl;
}
list
基本概念及结构
功能:将数据进行链式存储
链表由一个个结点组成 结点由数据域和指针域组成 数据域存放当前位置的数据 指针域维护下一个结点数据域的地址
链表的优缺点
优点 :
采用动态储存分配,不会造成内存浪费和溢出
可以对任意位置进行快速插入或删除元素
插入时 将新结点的指针域指向下一结点的数据域地址,其前一节点的指针域指向新结点的数据域地址即可,不用移动其他位置上的数据
缺点:
容器遍历速度没有数组快,需要找指针域进行遍历,且占用的空间大于数组
list有一个重要的特性,插入操作和删除操作都不会造成原有list迭代器失效,这在vector是不成立的
STL中的链表结构
STL中的链表是一个双向循环链表 指针域存放的指针有两个,为前一结点的指针域和后一节点的指针域
图中并没有进行循环 第一个结点指针域中prev指针指向最后一个结点的数据域 而最后一个结点的next指针指向第一关结点的数据域
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器
默认构造
list<T> lst; //list采用采用模板类实现,对象的默认构造形式:
list(beg,end); //构造函数将[beg, end)区间中的元素拷贝给本身。
list(n,elem); //构造函数将n个elem拷贝给本身。
list(const list &lst); //拷贝构造函数。
示例
//list容器的构造函数
void test01() {
list<int>L1;//创建list容器
L1.push_back(10);//添加数据
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
list<int>L2(L1.begin(), L1.end());//区域方式构造
list<int>L3(L2);//拷贝构造
list<int>L4(4,100);//n个elem构造
}
赋值和交换操作
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
list& operator=(const list &lst); //重载等号操作符
swap(lst); //将lst与本身的元素互换。
示例
void test01() {
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
list<int>L2;
L2 = L1;
list<int>L3;
L3.assign(L2.begin(), L2.end());
list<int>L4;
L4.assign(10, 100);
list<int>L5;
L1.swap(L5);
}
大小操作
size(); //返回容器中元素的个数
empty(); //判断容器是否为空
resize(num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(num, elem);重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
如果容器变短,则末尾超出容器长度的元素被删除。
示例
void test01()
{
list<int>L1;
L1.push_back(10);
L1.push_back(20);
L1.push_back(30);
L1.push_back(40);
if (L1.empty())
{
cout << "L1为空" << endl;
}
else
{
cout << "L1不为空" << endl;
cout << "L1的大小为: " << L1.size() << endl;
}
//重新指定大小
L1.resize(10);
L1.resize(15,100);
}
插入和删除
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值匹配的元素。
示例
void test01()
{
list<int> L;
//尾插
L.push_back(10);
L.push_back(20);
L.push_back(30);
//头插
L.push_front(100);
L.push_front(200);
L.push_front(300);
//尾删
L.pop_back();
//头删
L.pop_front();
//插入
list<int>::iterator it = L.begin();
L.insert(++it, 1000);
//删除
it = L.begin();
L.erase(++it);
//移除
L.push_back(10000);
L.remove(10000);//会将链表里所有10000移除
//清空
L.clear();
printList(L);
}
尾插 — push_back 尾删 — pop_back 头插 — push_front 头删 — pop_front
插入 — insert 删除 — erase 移除 — remove 清空 — clear
数据存取
front(); //返回第一个元素
back(); //返回最后一个元素
list不支持[]和at的访问原因:
list本质是链表,不是连续的线性空间存储数据,迭代器也不支持随机访问
list<int>::iterator it = L.begin();
it = it + 2; //错误,不可以随机访问
it ++ ; //正确,支持双向
反转和排序
reverse();//反转
sort();//排序
bool myCompare(int val1 , int val2)
{
return val1 > val2;
}
//反转和排序
void test01()
{
list<int> L;
L.push_back(90);
L.push_back(30);
L.push_back(20);
L.push_back(70);
printList(L);
//反转容器的元素
L.reverse();//倒序输出
printList(L);
//排序
L.sort(); //默认的排序规则 从小到大
printList(L);
L.sort(myCompare); //指定规则,从大到小
printList(L);
}
排序的案例
#include<iostream>
#include<cstring>
#include<algorithm>
#include<list>
using namespace std;
class player {
public:
player(string name, int age, int high) {
this->name = name;
this->age = age;
this->high = high;
}
string name;
int age;
int high;
void printff() {
cout << "姓名:" << this->name << "年纪:" << this->age << "身高:" << this->high << endl;
}
};
void print(list<player>& L) {
for (list<player>::iterator it = L.begin(); it != L.end(); it++) {
it->printff();
}
}
//自己写的 麻烦了
//bool antt(const player& p1, const player& p2) {
// if (p1.age < p2.age) {
// return true;
// }
// else if (p1.age > p2.age) {
// return false;
// }
// else {
// if (p1.high < p2.high)
// return true;
// else
// return false;
// }
//}
bool antt(const player& p1, const player& p2) {
if (p1.age == p2.age) {
return p1.high < p2.high;
}
return p1.age < p2.age;
}
void test01() {
player p1("小赵", 18, 180);
player p2("大牛", 27, 170);
player p3("林五", 19, 181);
player p4("赵武", 18, 167);
player p5("马九", 16, 178);
list<player>L;
L.push_back(p1);
L.push_back(p2);
L.push_back(p3);
L.push_back(p4);
L.push_back(p5);
cout << "排序前:" << endl;
print(L);
L.sort(antt);
cout << "------------------------------------------------------" << endl;
cout << "排序后:" << endl;
print(L);
}
int main() {
test01();
}
set/multiset
基本概念
本质:set/multiset属于关联式容器,底层结构是用二叉树实现
所有元素都会在插入时自动被排序
set和multiset区别:
-
set不允许容器中有重复的元素
-
multiset允许容器中有重复的元素
构造和赋值
构造:
set<T> st; //默认构造函数:
set(const set &st); //拷贝构造函数
赋值:
set& operator=(const set &st); //重载等号操作符
void print(const set<int>& s) {
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//set容器的构造和赋值
void test01() {
set<int> s1;
s1.insert(10);//插入数据只有insert方式
s1.insert(60);
s1.insert(90);
s1.insert(20);
s1.insert(90);
print(s1);//所有元素插入时自动排序,且不允许出现重复的值
set<int>s2(s1);//拷贝构造
set<int>s3;
s3 = s2;//赋值
}
set的插入只可以用insert插入
大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
void print(const set<int>& s) {
for (set<int>::const_iterator it = s.begin(); it != s.end(); it++) {
cout << *it << ' ';
}
cout << endl;
}
//set大小和交换
void test01() {
set<int> s1;
s1.insert(10);
s1.insert(20);
s1.insert(30);
s1.insert(40);
if (!s1.empty()) {
cout << "s1的大小:" << s1.size() << endl;
}
else {
cout << "s1为空" << endl;
}
set<int> s2;
s2.insert(40);
s2.insert(50);
s2.insert(70);
cout << "交换前:";
print(s1);
print(s2);
s1.swap(s2);
cout << "交换后:";
print(s1);
print(s2);
}
插入和删除
insert(elem); //在容器中插入元素。
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem); //删除容器中值为elem的元素。
示例
//set插入和删除
void test01() {
set<int> s1;
s1.insert(10);//插入
s1.insert(20);
s1.insert(40);
s1.insert(30);
s1.erase(s1.begin());//删除排序后的位置上的数字
s1.erase(30);//删除set里的30
s1.erase(s1.begin(), s1.end());//清空
s1.clear();//清空
}
查找和统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数
//set查找和统计
void test01() {
set<int> s1;
s1.insert(10);
s1.insert(20);
s1.insert(40);
s1.insert(30);
set<int>::iterator pos = s1.find(20);
if (pos != s1.end()) {
cout << "找到元素:" << *pos << endl;
}
else {
cout << "未找到" << endl;
}
multiset<int> s2;
s2.insert(10);
s2.insert(20);
s2.insert(40);
s2.insert(30);
s2.insert(30);
s2.insert(30);
cout << "30的个数:" << s2.count(30);//查找 在set中无重复元素 意义不大.
}
set和multiset区别
set不可以插入重复数据,而multiset可以
set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
对于是否成功插入的检测
pair<set<int>::iterator, bool> ret = s.insert(10);//pair对组的使用
if (ret.second) {
cout << "插入成功!" << endl;
}
else {
cout << "插入失败!" << endl;
}
pair-对组
成对出现的数据,利用对组可以返回两个数据
创建方式
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 )
void test01(){
pair<string,int>p ("Tom",20);
cout << "姓名:" << p.first << "年龄:" << p.second << endl;
pair<string,int>p2 = make_pair("Joker",31);
}
排序规则
内置类型指定排序规则
利用仿函数,可以改变排序规则
需要在创建set时提前写排序规则
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
class Mypx {
public:
bool operator() (int a, int b) const
{
return a > b;
}
};
void test01() {
set<int> s1;
s1.insert(10);
s1.insert(20);
s1.insert(40);
s1.insert(30);
for (set<int>::iterator it = s1.begin(); it != s1.end(); it++) {
cout << *it << ' ';
}
cout << endl;
set<int,Mypx> s2;
s2.insert(10);
s2.insert(20);
s2.insert(40);
s2.insert(30);
for (set<int,Mypx>::iterator it = s2.begin(); it != s2.end(); it++) {
cout << *it << ' ';
}
}
int main() {
test01();
}
存放自定义数据类型的排序规则
对于仿函数的编写相对麻烦一点,其他的大差不差
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
using namespace std;
class player {
public:
player(string name, int age) {
this->name = name;
this->age = age;
}
string name;
int age;
};
class Mypx {
public:
bool operator ()(const player &p1,const player &p2) const{
return p1.age < p2.age;
}
};
void test01() {
player p1("刘备",32);
player p2("诸葛亮",31);
player p3("关羽",25);
player p4("张飞",23);
set<player,Mypx> s;
s.insert(p1);
s.insert(p2);
s.insert(p3);
s.insert(p4);
for (set<player>::iterator it = s.begin(); it != s.end(); it++) {
cout << "名字:" << it->name << " 年纪:" << it->age << endl;
}
}
map/multimap
基本概念
map中所有元素都是pair(对组)
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
可以根据key值快速找到value值
map和multimap区别:
map不允许容器中有重复key值元素
multimap允许容器中有重复key值元素
构造和赋值
构造:
map<T1, T2> mp; //map默认构造函数:
map(const map &mp);//拷贝构造函数
赋值:
map& operator=(const map &mp);//重载等号操作符
示例
void print(const map<int,int>&m) {
for (map<int, int>::const_iterator it = m.begin(); it != m.end(); it++) {
cout << "key: " << it->first << " value: " << it->second << endl;
}
}
void test01() {
map <int, int>m;//默认构造
m.insert(pair<int, int>(1, 10));//插入
m.insert(pair<int, int>(2, 15));
m.insert(pair<int, int>(3, 20));
m.insert(pair<int, int>(4, 25));
print(m);
map<int, int>m1(m);//拷贝构造
map<int, int>m2;
m2 = m;
}
map中所有元素都是成对出现,插入数据时候要使用对组
大小和交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
示例
void printMap(map<int,int>&m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key = " << it->first << " value = " << it->second << endl;
}
cout << endl;
}
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));
if (m.empty())
{
cout << "m为空" << endl;
}
else
{
cout << "m不为空" << endl;
cout << "m的大小为: " << m.size() << endl;
}
}
//交换
void test02()
{
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));
map<int, int>m2;
m2.insert(pair<int, int>(4, 100));
m2.insert(pair<int, int>(5, 200));
m2.insert(pair<int, int>(6, 300));
cout << "交换前" << endl;
printMap(m);
printMap(m2);
cout << "交换后" << endl;
m.swap(m2);
printMap(m);
printMap(m2);
}
插入和删除
insert(elem); //在容器中插入元素。
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(key); //删除容器中值为key的元素。
示例
void print(map<int, int>& m)
{
for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
{
cout << "key = " << it->first << " value = " << it->second << endl;
}
cout << endl;
}
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)); //第三种插入方式
m[4] = 40;//第四种插入方式 简单但不建议用于插入 可以用[key]去访问value
print(m);
m.erase(m.begin());//迭代器删除
m.erase(3);//key的位置删除
m.erase(m.begin(), m.end());//清空
m.clear();//清空
}
查找和统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数
示例
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));
map<int, int>::iterator pos = m.find(3);//查找,查到返回数值,未查到返回end()
if (pos != m.end()){
cout << "找到了元素 key = " << (*pos).first << " value = " << (*pos).second << endl;
}
else{
cout << "未找到元素" << endl;
}
//统计
int num = m.count(3);
cout << "num = " << num << endl;
}
查到 – find 查到返回迭代器,未查到返回end()
统计 – count 对于map 只有0或1
容器排序
可以利用仿函数改变其排序方式
class Mypx{
public:
bool operator()(int v1, int v2) {
return v1 > v2;
}
};
void test01()
{
//利用仿函数实现从大到小排序
map<int, int, Mypx> 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, Mypx>::iterator it = m.begin(); it != m.end(); it++) {
cout << "key:" << it->first << " value:" << it->second << endl;
}
函数对象
概念
重载函数调用操作符的类,其对象常称为函数对象
函数对象使用重载的()时,行为类似函数调用,也叫仿函数
本质
函数对象(仿函数)是一个类,不是一个函数
特点
- 函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
- 函数对象超出普通函数的概念,函数对象可以有自己的状态
- 函数对象可以作为参数传递
案例
#include<iostream>
#include<vector>
#include<map>
#include<ctime>
#include <map>
using namespace std;
//函数对象在使用时,可以像普通函数那样调用, 可以有参数,可以有返回值
class Myadd {
public:
int operator()(int a, int b) {
return a + b;
}
};
void test01() {
Myadd m;
cout << m(10, 20);
}
//函数对象超出普通函数的概念,函数对象可以有自己的状态
class Myprint {
public:
Myprint() :sum(0) {};//初始化列表来初始化 sum
void operator()(string a) {
cout << a << endl;
sum++;
}
int sum;
};
void test02() {
Myprint m;
m("C++是世界上最好的语言");
m("C++是世界上最好的语言");
m("C++是世界上最好的语言");
m("C++是世界上最好的语言");
}
void doprint(Myprint m, string a)
{
m(a);
}
void test03() {
Myprint m;
doprint(m, "早上好中国");
}
int main() {
//test01();
//test02();
test03();
}
谓词(pred)
概念:
-
bool类型的仿函数 称为 谓词
-
如果operator()接受一个参数,叫一元谓词,接受两个参数,叫二元谓词
示例:
#include<iostream>
#include<vector>
#include<algorithm>
using namespace std;
//一元谓词
class bijiao {
public:
bool operator()(int a) {
return a > 5;
}
};
class px {
public:
bool operator()(int a,int b) {
return a > b;
}
};
//二元谓词
void test01() {
vector<int> v;
for (int i = 1; i < 10; i++) {
v.push_back(i);
}
vector<int>::iterator it = find_if(v.begin(), v.end(), bijiao());
cout << *it << endl;
vector<int>v1;
v1.push_back(10);
v1.push_back(20);
v1.push_back(30);
v1.push_back(40);
sort(v1.begin(), v1.end(),px());
for (auto it = v1.begin(); it != v1.end(); it++) {
cout << *it << ' ';
}
}
int main() {
test01();
}
内建函数对象
意义
STL内建的仿函数
分类:算术仿函数 关系仿函数 逻辑仿函数
头文件
#include<functional>
算术仿函数
实现 四则运算
其中negate是一元运算,其他都是二元运算
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> //取反仿函数
示例
#include<iostream>
#include<functional>
using namespace std;
void test01() {
negate<int>n;//取反仿函数
cout << n(50) << endl;
plus<int>p;
cout << p(29, 11) << endl;
}
int main() {
test01();
}
关系仿函数
实现 对比操作
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> //小于等于
示例
#include<iostream>
#include<vector>
#include<functional>
#include<algorithm>
using namespace std;
void test01() {
vector<int> v;
v.push_back(10);
v.push_back(70);
v.push_back(20);
v.push_back(40);
v.push_back(30);
//实现从小到大排序 关系仿函数 大于 greater
// greater<int>()
sort(v.begin(), v.end(), greater<int>());
for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
}
int main() {
test01();
}
逻辑仿函数
实现 逻辑运算
template<class T> bool logical_and<T> //逻辑与
template<class T> bool logical_or<T> //逻辑或
template<class T> bool logical_not<T> //逻辑非
示例
void test01() {
vector<bool> v;
v.push_back(true);
v.push_back(false);
v.push_back(true);
v.push_back(false);
v.push_back(true);
for (vector<bool>::iterator it = v.begin(); it != v.end(); it++) {
cout << *it << ' ';
}
vector<bool> v2;
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());
for (vector<bool>::iterator its = v2.begin(); its != v2.end(); its++) {
cout << *its << ' ';
}
}
常用算法
算法主要是由头文件 组成。
-
是所有STL头文件中最大的一个,范围涉及到比较、 交换、查找、遍历操作、复制、修改等等
-
体积很小,只包括几个在序列上面进行简单数学运算的模板函数
-
定义了一些模板类,用以声明函数对象。
遍历算法
- for_each //遍历容器
- transform //搬运容器到另一个容器中
for_each
for_each(iterator beg, iterator end, _func);
void paddv(int a) {
cout << a + 1 << ' ';
}
class paddd {
public:
void operator()(int a) {
cout << a + 1 << ' ';
}
};
void test01() {
vector<int>v;
for (int i = 1; i < 10; i++) {
v.push_back(i);
}
for_each(v.begin(), v.end(), paddv);//函数指针的隐式转换
for_each(v.begin(), v.end(), paddd());//仿函数
}
transform
transform(iterator beg1, iterator end1, iterator beg2, _func);
int adp(int a) {
return a + 1;
}
void test01() {
vector<int>v;
for (int i = 1; i < 10; i++) {
v.push_back(i);
}
vector<int>v2;//空容量 需要开辟空间
v2.resize(v.size());
transform(v.begin(), v.end(), v2.begin(), adp);//仿函数和函数都可以
for (vector<int>:: iterator it = v2.begin(); it != v2.end(); it++) {
cout << *it << ' ';
}
}
总结: 搬运的目标容器必须要提前开辟空间,否则无法正常搬运
查找算法
查找指定元素,查找到返回指定元素迭代器,找到到返回结束迭代器end()
- find //查找元素
- find_if //按条件查找元素
- adjacent_find //查找相邻重复元素
- binary_search //二分查找法
- count //统计元素个数
- count_if //按条件统计元素个数
find
find(iterator beg,iterator end, value);
#include<iostream>
#include<vector>
#include<cstring>
#include<functional>
#include<algorithm>
using namespace std;
class player {
public:
player(string name, int age) {
this->name = name;
this->age = age;
}
bool operator == (const player&p) {//重载== 底层代码不知道如何比较
if (this->name == p.name && this->age == p.age)
return 1;
else return 0;
}
string name;
int age;
};
void test01() {//查找内置数据类型
vector<int>v;
for (int i = 1; i < 10; i++) {
v.push_back(i);
}
auto it = find(v.begin(), v.end(), 5);
cout << *it;
}
void test02() {//查找自定义数据类型
vector<player> v;
player p1("A", 10);
player p2("D", 20);
player p3("E", 23);
player p4("R", 16);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<player>::iterator it = find(v.begin(), v.end(), p2);//重载查找的==号
cout << it->name << ' ' << it->age;
}
int main() {
test02();
}
find_if
find(iterator beg,iterator end, _pred);
class pj {
public:
bool operator()(int val) {
return val > 5;
}
};
class player {
public:
player(string name, int age) {
this->name = name;
this->age = age;
}
string name;
int age;
};
class cz {
public:
bool operator () (const player& p) {
return p.age > 21;
}
};
void test01() {//查找内置数据类型
vector<int>v;
for (int i = 1; i < 10; i++) {
v.push_back(i);
}
vector<int>::iterator it = find_if(v.begin(), v.end(), pj());
cout << *it;
}
void test02() {//查找自定义数据类型
vector<player> v;
player p1("A", 10);
player p2("D", 20);
player p3("E", 23);
player p4("R", 16);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
vector<player>::iterator it = find_if(v.begin(), v.end(), cz());
cout << it->name << ' ' << it->age;
}
adjacent_find
查找相邻重复元素
adjacent_find(iterator beg,iteratir end);
void test01()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(5);
v.push_back(2);
v.push_back(4);
v.push_back(4);
v.push_back(3);
//查找相邻重复元素
vector<int>::iterator it = adjacent_find(v.begin(), v.end());
if (it == v.end()) {
cout << "找不到!" << endl;
}
else {
cout << "找到相邻重复元素为:" << *it << endl;
}
}
…面试遇到查询重复元素用这个算法
binary_search
查找元素是否存在 无序序列中不可用
bool binary_search(iterator beg, iterator end, value);
void test01()
{
vector<int>v;
for (int i = 0; i < 10; i++)
{
v.push_back(i);
}
//二分查找
bool ret = binary_search(v.begin(), v.end(), 2);
if (ret)
{
cout << "找到了" << endl;
}
else
{
cout << "未找到" << endl;
}
}
count
统计次数
count(iterator beg, iterator end, value);
//内置数据类型
void test01()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(3);
v.push_back(4);
v.push_back(4);
int num = count(v.begin(), v.end(), 4);
cout << "4的个数为: " << num << endl;
}
//自定义数据类型查找
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
bool operator==(const Person & p)
{
if (this->m_Age == p.m_Age)
{
return true;
}
else
{
return false;
}
}
string m_Name;
int m_Age;
};
void test02()
{
vector<Person> v;
Person p1("刘备", 35);
Person p2("关羽", 35);
Person p3("张飞", 35);
Person p4("赵云", 30);
Person p5("曹操", 25);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
Person p("诸葛亮",35);
int num = count(v.begin(), v.end(), p);重载==
cout << "num = " << num << endl;
}
count_if
按条件统计元素个数
count_if(iterator beg, iterator end, _Pred);
class Greater4
{
public:
bool operator()(int val)
{
return val >= 4;
}
};
//内置数据类型
void test01()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(4);
v.push_back(5);
v.push_back(3);
v.push_back(4);
v.push_back(4);
int num = count_if(v.begin(), v.end(), Greater4());
cout << "大于4的个数为: " << num << endl;
}
//自定义数据类型
class Person
{
public:
Person(string name, int age)
{
this->m_Name = name;
this->m_Age = age;
}
string m_Name;
int m_Age;
};
class AgeLess35
{
public:
bool operator()(const Person &p)
{
return p.m_Age < 35;
}
};
void test02()
{
vector<Person> v;
Person p1("刘备", 35);
Person p2("关羽", 35);
Person p3("张飞", 35);
Person p4("赵云", 30);
Person p5("曹操", 25);
v.push_back(p1);
v.push_back(p2);
v.push_back(p3);
v.push_back(p4);
v.push_back(p5);
int num = count_if(v.begin(), v.end(), AgeLess35());
cout << "小于35岁的个数:" << num << endl;
}
排序算法
- sort //对容器内元素进行排序
- random_shuffle //洗牌 指定范围内的元素随机调整次序
- merge // 容器元素合并,并存储到另一容器中
- reverse // 反转指定范围的元素
sort
对容器内元素进行排序
sort(iterator beg, iterator end, _Pred);
sort(v.begin(), v.end(), greater<int>());
random_shuffle
洗牌 指定范围内的元素随机调整次序
random_shuffle(iterator beg, iterator end);
random_shuffle洗牌算法比较实用,使用时记得加随机数种子
srand((unsigned int)time(NULL));
random_shuffle(v.begin(), v.end());
merge
两个容器元素合并,并存储到另一容器中
两个容器必须是有序的 放入另一个容器中需要提前开辟好空间
void test01()
{
vector<int> v1;
vector<int> v2;
for (int i = 0; i < 10 ; i++)
{
v1.push_back(i);
v2.push_back(i + 1);
}
vector<int> vtarget;
//目标容器需要提前开辟空间
vtarget.resize(v1.size() + v2.size());
//合并 需要两个有序序列
merge(v1.begin(), v1.end(), v2.begin(), v2.end(), vtarget.begin());
for_each(vtarget.begin(), vtarget.end(), myPrint());
cout << endl;
}
reverse
将容器内元素进行反转
reverse(iterator beg, iterator end);
reverse(v.begin(), v.end());
拷贝和替换算法
- copy // 容器内指定范围的元素拷贝到另一容器中
- replace // 将容器内指定范围的旧元素修改为新元素
- replace_if // 容器内指定范围满足条件的元素替换为新元素
- swap // 互换两个容器的元素
copy
容器内指定范围的元素拷贝到另一容器中
copy(iterator beg, iterator end, iterator dest);
copy(v1.begin(), v1.end(), v2.begin());
replace
将容器内指定范围的旧元素修改为新元素
replace(iterator beg, iterator end, oldvalue, newvalue);
void test01()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(20);
v.push_back(40);
v.push_back(50);
v.push_back(10);
v.push_back(20);
replace(v.begin(), v.end(), 20,2000);//将容器中的20替换成2000
}
replace会替换区间内满足条件的元素
replace_if
将区间内满足条件的元素,替换成指定元素
replace_if(iterator beg, iterator end, _pred, newvalue);
class ReplaceGreater30
{
public:
bool operator()(int val)
{
return val >= 30;
}
};
void test01()
{
vector<int> v;
v.push_back(20);
v.push_back(30);
v.push_back(20);
v.push_back(40);
v.push_back(50);
v.push_back(10);
v.push_back(20);
//将容器中大于等于的30 替换成 3000
replace_if(v.begin(), v.end(), ReplaceGreater30(), 3000);
}
swap
互换两个容器的元素
swap(container c1, container c2);
算术生成算法
包含的头文件为
#include <numeric>
- accumulate // 计算容器元素累计总和
- fill // 向容器中添加元素
accumulate
计算区间内 容器元素累计总和
accumulate(iterator beg, iterator end, value);
void test01()
{
vector<int> v;
for (int i = 0; i <= 100; i++) {
v.push_back(i);
}
int total = accumulate(v.begin(), v.end(), 0);//求和
cout << "total = " << total << endl;
}
fill
向容器中填充指定的元素
fill(iterator beg, iterator end, value);
void test01()
{
vector<int> v;
v.resize(10);
//填充
fill(v.begin(), v.end(), 100);
}
集合算法
- set_intersection // 求两个容器的交集
- set_union // 求两个容器的并集
- set_difference // 求两个容器的差集
set_intersection
求两个集合的交集
set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> a;
vector<int> b;
vector<int> c;
for (int i = 1; i < 10; i++) {
a.push_back(i);
b.push_back(i + 5);
}
c.resize(min(a.size(), b.size()));
vector<int>::iterator it = set_intersection(a.begin(), a.end(), b.begin(), b.end(), c.begin());
for_each(c.begin(), it, myPrint());
}
求交集的两个集合为有序序列
目标容器开辟空间需要从两个容器中取小值
set_intersection返回值是交集中最后一个元素的位置
set_union
求两个容器的并集
set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> a;
vector<int> b;
vector<int> c;
for (int i = 1; i < 10; i++) {
a.push_back(i);
b.push_back(i + 5);
}
c.resize(a.size()+b.size());//两容器大小的和
vector<int>::iterator it = set_union(a.begin(), a.end(), b.begin(), b.end(), c.begin());
for_each(c.begin(), it, myPrint());
}
求并集的两个集合为有序序列
目标容器开辟空间需要两个容器相加
set_union返回值既是并集中最后一个元素的位置
set_difference
求两个集合的差集
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
class myPrint
{
public:
void operator()(int val)
{
cout << val << " ";
}
};
void test01()
{
vector<int> a;
vector<int> b;
vector<int> c;
for (int i = 1; i < 10; i++) {
a.push_back(i);
b.push_back(i + 5);
}
c.resize(max(a.size(),b.size()));//两容器中较大值
vector<int>::iterator it = set_difference(a.begin(), a.end(), b.begin(), b.end(), c.begin());
for_each(c.begin(), it, myPrint());
}
求差集的两个集合为有序序列
目标容器开辟空间需要从两个容器取较大值
set_difference返回值既是差集中最后一个元素的位置