C++提高编程笔记

文章目录

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返回值既是差集中最后一个元素的位置

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值