C++提高编程总结

C++提高编程

1. 模板

1.1 模板的概念
  • 建立通用的模板,大大提高复用性
1.2 函数模板
  • C++的另一种编程思想称为泛型编程,主要利用的技术就是模板
  • C++提供两种模板机制:函数模板和类模板
1.2.1 函数模板语法

函数模板的作用:建立一个通用函数,其函数返回值类型形参类型可以不具体制定,用一个虚拟的类型来代表
语法:template<typename T>
template:声明创建模板
typename:表明其后面的符号是一种数据类型,可用class代替
T:通用的数据类型,名称可以替换,通常为大写字母

1.2.2 函数模板注意事项
  • 自动类型推导,必须推导出一致的数据类型T才可使用
  • 模板必须要确定出T的数据类型,才可以使用
//函数模板
template<typename T>//声明一个模板,告诉编译器后面代码的T是一个通用数据类型,不要报错
void my_swap(T& a, T& b)
{
	T temp = a;
	a = b;
	b = temp;
}

void test2()
{
	int a = 10, b = 20;
	double c = 1.2, d = 2.3;
	char e = 'e';
	my_swap(a, b);//自动类型推导
	my_swap<double>(c, d);//显示指定类型
	//my_swap(d, e);//错误,推导不出一致的数据类型
	cout << "a=" << a << " b=" << b << " c=" << c << " d=" << d << endl;
}
1.2.3 普通函数和函数模板的区别
  • 普通函数调用时可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
#include<iostream>
using namespace std;

//普通函数
int my_add1(int a, int b)
{
	return a + b;
}

//函数模板
template<class T>
T my_add2(T a, T b)
{
	return a + b;
}

void test1()
{
	int a = 10;
	int b = 20;
	char c = 'c';
	cout << my_add1(a, b) << endl;
	cout << my_add1(a, c) << endl;//c的ASCII码是99,调用该函数的时候隐式类型转换了
	cout << my_add2(a, b) << endl;//自动类型推导
	//cout << my_add2(a, c) << endl;//自动类型推导无法隐式类型转换
	cout << my_add2<int>(a, c) << endl;//显示指定类型即可发生隐式类型转换
}

int main()
{
	test1();
	system("pause");
	return 0;
}
1.2.4 普通函数与模板函数的调用规则
  1. 如果函数模板和普通函数都可以实现,优先调用普通函数
  2. 可以通过空模板参数列表来强制调用函数模板
  3. 函数模板也可以发生重载
  4. 如果函数模板可以产生更好的匹配,优先调用函数模板
#include<iostream>
using namespace std;

void my_print(int a, int b)
{
	cout << "调用的普通函数" << endl;
}

template<class T>
void my_print(T a, T b)
{
	cout << "调用的函数模板" << endl;
}

template<class T>
void my_print(T a, T b, T c)
{
	cout << "调用的重载函数模板" << endl;
}

void test1()
{
	int a = 10;
	int b = 20;
	my_print(a, b);//若都可调用,则优先调用普通函数
	my_print<>(a, b);//通过空模板参数列表强制调用函数模板
	my_print(a, b, 100);
	char c1 = 'a', c2 = 'b';
	my_print(c1, c2);//如果函数模板可以产生更好的匹配(不用隐式类型转换),则调用函数模板
}

int main()
{
	test1();
	system("pause");
	return 0;
}
  • 总结:既然提供了函数模板,就最好不要使用普通函数,否则容易出现二义性
1.2.5 模板的局限性
  • 模板并不是万能的,有些具体数据类型,要用具体化的方式做特殊实现
  • 总结:利用具体化的模板,可以解决自定义类型的通用化,学习模板并不是为了写模板,而是在STL能够运用系统提供的模板
1.3 类模板
1.3.1 类模板语法

建立一个通用类,类中的成员数据类型可以不具体制定,用一个虚拟的类型来代表
语法:template<typename T>
template:声明创建模板
typename:表明其后面的符号是一种数据类型,可用class代替
T:通用的数据类型,名称可以替换,通常为大写字母

1.3.2 类模板和函数模板的区别
  • 类模板没有自动类型推导的使用方式
  • 类模板在模板参数列表中可以有默认参数
//类模板
template<class NameType,class AgeType = int>//可以有默认参数列表(默认age是int型)
class person
{
public:
	person(NameType name, AgeType age)
	{
		m_name = name;
		m_age = age;
	}
	void show_person()
	{
		cout << "name=" << m_name << " age=" << m_age << endl;
	}
	NameType m_name;
	AgeType m_age;
};

void test1()
{
	//person p0("fuck", 17);//错误,类模板不能自动类型推导
	person<string, int> p1("fuck", 14);//实例化对象的时候要指定模板参数列表的数据类型
	person<string> p2("cufk", 15);//可以省略默认参数
	p1.show_person();
}
1.3.3 类模板中成员函数创建时机

类模板中成员函数和普通类中成员函数创建时机是有区别的:

  • 普通类中的成员函数一开始就可以创建
  • 类模板中的成员函数在调用时才创建
//普通类
class person1
{
public:
	void show_person1()
	{
		cout << "person1 show" << endl;
	}
};

class person2
{
public:
	void show_person2()
	{
		cout << "person2 show" << endl;
	}
};

//类模板
template<class T>
class my_class
{
public:
	T obj;
	//类模板中的成员函数只有在调用的时候才会创建
	void func1()
	{
		obj.show_person1();
	}
	void func2()
	{
		obj.show_person2();
	}
};

void test1()
{
	my_class<person1> m;//确定了obj是person1类型,则m可以调用fun1
	m.func1();
	//m.func2();//错误,函数调用时才去创建成员函数,而m不能调用fun2
	my_class<person2> n;
	n.func2();
}
1.3.4 类模板对象做函数参数

类模板实例化出的对象,向函数传参的方式:

  • 指定传入的类型:直接显示对象的数据类型
  • 参数模板化:将对象中的参数变为模板进行传递
  • 整个类模板化:将这个对象类型模板化进行传递
#include<iostream>
#include<string>
using namespace std;

template<class T1,class T2>
class person
{
public:
	person(T1 name, T2 age)
	{
		m_name = name;
		m_age = age;
	}
	void show_person()
	{
		cout << "name=" << m_name << " age=" << m_age << endl;
	}
	T1 m_name;
	T2 m_age;
};

//指定传入类型
void print_person1(person<string, int>& p)
{
	p.show_person();
}
void test1()
{
	person<string, int> p("fuck", 15);
	print_person1(p);
}

//参数模板化
template<class T1,class T2>
void print_person2(person<T1,T2>& p)
{
	p.show_person();
	cout << "T1的类型为:" << typeid(T1).name() << endl;
	cout << "T2的类型为:" << typeid(T2).name() << endl;
}
void test2()
{
	person<string, int> p("trae", 15);
	print_person2(p);
}

//整个类模板化
template<class T>
void print_person3(T& p)
{
	p.show_person();
	cout << "T的类型为:" << typeid(T).name() << endl;
}

void test3()
{
	person<string, int> p("young", 21);
	print_person3(p);
}

int main()
{
	test1();
	test2();
	test3();
	system("pause");
	return 0;
}
1.3.5 类模板与继承
  • 当子类继承的父类是一个类模板时,子类在声明的时候要指定出父类中T的类型
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中T的类型,子类也要变为类模板
1.3.6 类模板成员函数类外实现
  • 需要加上模板参数列表
template<class T1,class T2>
class person
{
public:
	person(T1 name, T2 age);
	/*{
		m_name = name;
		m_age = age;
	}*/
	void show_person();
	/*{
		cout << "name=" << m_name << " age=" << m_age << endl;
	}*/
	T1 m_name;
	T2 m_age;
};

//构造函数类外实现
template<class T1,class T2>
person<T1,T2>::person(T1 name, T2 age)//类模板的构造函数实现,要加模板参数列表
{
	m_name = name;
	m_age = age;
}

//成员函数类外实现
template<class T1,class T2>
void person<T1, T2>::show_person()
{
	cout << "name=" << m_name << " age=" << m_age << endl;
}

void test1()
{
	person<string, int> p("fuck", 22);
	p.show_person();
}
1.3.7 类模板分文件编写

问题:类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决方法:

  1. 直接包含.cpp源文件
  2. 将声明和实现写到同一个文件中,并更改后缀名为.hpp
1.3.8 类模板与友元

全局函数类内实现:直接在类内声明友元即可
全局函数类外实现:需要提前让编译器知道全局函数的存在

2. STL初识

2.1 STL的诞生
  • C++的面向对象和泛型编程思想,目的就是复用性的提升
  • 为建立数据结构和算法的一套标准,诞生了STL
2.2 STL的基本概念
  • STL(Standard Template Library)标准模板库
  • STL从广义上分:容器、算法、迭代器
  • 容器和算法之间通过迭代器无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数
2.3 STL六大组件

STL六大组件为:容器、算法、迭代器、仿函数、适配器、空间配置器

  1. 容器:各种数据结构,如vector,list,deque,set,map等,用来存放数据
  2. 算法:各种常用的算法,如sort,find,copy,for_each等
  3. 迭代器:容器与算法之间的胶合剂
  4. 仿函数:行为类似函数,可作为算法的某种策略
  5. 适配器:一种用来修饰容器或者仿函数或者迭代器接口的东西
  6. 空间配置器:负责空间的配置与管理
2.4 STL中的容器、算法、迭代器

容器
STL容器就是将运用最广泛的一些数据结构实现出来
序列式容器:强调值的排序,每个元素均有固定的位置

  • vector、list、deque、stack、queue、heap、priority_queue、slist

关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

  • RB-tree、set、map、multiset、multimap、hashtable、hash_set、hash_map、hash_multiset、hash_multimap

算法

  • 质变算法:运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等
  • 非质变算法:运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等

迭代器
容器和算法之间的胶合剂
提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式,每个容器都有自己专属的迭代器,迭代器的使用非常类似指针

2.5 容器算法迭代器初识

STL中最常用的容器为vector,可以理解为数组

  • 通过迭代器访问容器中的元素
vector<int>::iterator it_begin() = v.begin(); //起始迭代器,指向容器中第一个元素
vector<int>::iterator it_end = v.end();//结束迭代器,指向容器中最后一个元素的下一个位置
//第一种遍历方式
while (it_begin != it_end)
{
	cout << *it_begin << " ";
	it_begin++;
}
	cout << endl;

//第二种遍历方式
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
	cout << *it << " ";
}
	cout << endl;

void my_print(int val)
{
	cout << val<<" ";
}
//第三种遍历方式
for_each(v.begin(), v.end(), my_print); // 使用STL提供的for_each标准遍历算法

3. STL常用容器

3.1 string容器
3.1.1 string基本概念

本质:

  • string是C++风格的字符串,而string本质上是一个类

string和char*的区别:

  • char*是一个指针
  • string是一个类,类内部封装了char*,管理这个字符串,是一个char*的容器

特点:

  • string类内部封装了很多成员方法
  • 查找find,拷贝copy,删除delete,替换replace,插入insert
  • string管理char*所分配的内存,不用担心复制越界和取值越界等,由类内部进行负责
3.1.2 string构造函数和析构函数
  • string();:生成一个空字符串s,例如string str;
  • string(const char* s);:使字符串s初始化
  • string(const string& str);:拷贝构造
  • string(int n,char c);:使用n个字符c初始化
void test1()
{
	string s1;//默认构造
	const char* str = "fuck trae young";
	string s2(str);
	cout << "s2=" << s2 << endl;
	string s3(s2);
	cout << "s3=" << s3 << endl;
	string s4(10, 'k');
	cout << "s4=" << s4 << endl;
}
3.1.3 string赋值操作

赋值的函数原型:

  • 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 test1()
{
	string str1;
	str1 = "fuck trae young";
	cout << "str1=" << str1 << endl;

	string str2;
	str2 = str1;
	cout << "str2=" << str2 << endl;

	string str3;
	str3 = 'a';
	cout << "str3=" << str3 << endl;

	string str4;
	str4.assign("fuck you");
	cout << "str4=" << str4 << endl;

	string str5;
	str5.assign("fuck you", 4);
	cout << "str5=" << str5 << endl;

	string str6;
	str6.assign(str4);
	cout << "str6=" << str6 << endl;

	string str7;
	str7.assign(10, 'f');
	cout << "str7=" << str7 << endl;
}
3.1.4 string字符串拼接

拼接函数原型:

  • 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);:字符串中从pos开始的n个字符连接到字符串结尾
void test1()
{
	string str1 = "我";
	str1 += "朝那面打";
	cout << "str1=" << str1 << endl;

	str1 += ';';
	cout << "str1=" << str1 << endl;

	string str2 = "打呗";
	str1 += str2;
	cout << "str1=" << str1 << endl;

	string str3 = "芜湖";
	str3.append("~");
	cout << "str3=" << str3 << endl;

	str3.append("game avd", 4);
	cout << "str3=" << str3 << endl;

	//str3.append(str1);
	//cout << "str3=" << str3 << endl;

	str3.append(str1, 0, 3);
	cout << "str3=" << str3 << endl;

}
3.1.5 string查找和替换

总结:

  • find是从左往右找,rfind是从右往左找
  • find找到字符串后返回查找的第一个字符串位置,找不到则返回-1
  • replace在替换时,要指定从哪个位置起,多少个字符替换为什么样的字符串
//查找
void test1()
{
	string str1 = "fuck trae young trae";
	int pos = str1.find("tra");//找到tra在str1中第一次出现位置的索引,未找到则pos=-1
	cout << "pos=" << pos << endl;
	//rfind是从右往左找第一次出现的位置,find是从左往右
	pos = str1.rfind("tra");
	cout << "pos=" << pos << endl;
}

//替换
void test2()
{
	string str1 = "abcdefg";
	str1.replace(1, 3, "1111");//从第一个位置开始后面三个字符替换为1111
	cout << "str1=" << str1 << endl;
}
3.1.6 string字符串比较

比较方式:

  • 按字符的ASCII码进行比较

= = =返回0
> > >返回1
< < <返回-1
函数原型:

  • int compare(const string &s) const;:与字符串s比较
  • int compare(const char* s) const;:与字符串s比较
//比较
void test3()
{
	string str1 = "fuck";
	string str2 = "guck";
	if (str1.compare(str2) == 0)
	{
		cout << "str1等于str2" << endl;
	}
	else if (str1.compare(str2) > 0)
	{
		cout << "str1大于str2" << endl;
	}
	else
	{
		cout << "str1小于str2" << endl;
	}
}
3.1.7string字符存取
  • char& operator[](int n);:通过[]取字符
  • char& at(int n);:通过at取字符
//存取
void test4()
{
	string str = "fuck trae young";
	//通过[]访问单个字符
	//str.size()返回字符串长度(或str.length())
	for (int i = 0; i < str.size(); i++)
	{
		cout << str[i] << " ";
	}
	cout << endl;
	//通过at访问单个字符
	for (int i = 0; i < str.size(); i++)
	{
		cout << str.at(i) << " ";
	}
	cout << endl;
	//修改单个字符
	str[0] = 'g';
	str.at(1) = 'i';
	cout << "str=" << str << endl;
}
3.1.8 string插入和删除

函数原型:

  • string& insert(int pos, const char* s);:在pos位置之后插入字符串
  • string& insert(int pos, const string& str);:在pos位置之后插入字符串
  • string& insert(int pos, int n, char c);:在pos位置插入n个字符c
  • string& erase(int pos, int n);:删除pos开始的n个字符
//插入和删除
void test5()
{
	string str1 = "hello";
	str1.insert(1, "111");//在第一个位置插入111
	cout << "str1=" << str1 << endl;
	str1.erase(1, 3);//在第一个位置往后删3个字符
	cout << "str1=" << str1 << endl;
}
3.1.9 string子串

函数原型:

  • string substr(int pos=0,int n=npos) const;:返回从pos开始的n个字符组成的字符串
//求子串
void test6()
{
	string str = "abcdef";
	string sub = str.substr(1, 3);//从第一个位置开始往后数3个位置的子串
	cout << "sub=" << sub << endl;
	string email = "1328861924@qq.com";
	int pos = email.find("@");
	cout << "qq=" << email.substr(0, pos) << endl;
}
3.2 vector容器
3.2.1 vector基本概念

功能:

  • vector数据结构跟数组非常类似,也称为单端数组

vector与普通数组的区别:

  • 不同之处在于数组是静态空间,而vector可以动态扩展

动态扩展:

  • 并不是在原有空间之后接续新空间,而是找更大的空间,然后将原数据拷贝到新空间,释放原空间
  • vector的迭代器是支持随机访问的迭代器
3.2.2 vector构造函数
void print_vector(vector<int>& v)
{
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
vector<int> v1;//默认构造
//通过区间方式进行构造
vector<int> v2(v1.begin(), v1.end());
//n个elem方式构造
vector<int> v3(10, 100);//10个100
vector<vector<int>> v4(2,vector<int>(2,0));//2个长度为2值为0的一维数组
vector<int> v5(v3);
3.2.3 赋值操作
//赋值
void test2()
{
	vector<int> v1;//默认构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	print_vector(v1);

	//赋值
	vector<int> v2;
	v2 = v1;//重载operator=
	print_vector(v2);
	
	vector<int> v3;
	v3.assign(v1.begin(), v1.end());//assign区间赋值
	print_vector(v3);

	vector<int> v4;
	v4.assign(10, 100);//assign10个100
	print_vector(v4);
}
3.2.4 vector容量和大小

capacity():容量
size():大小
容量 ≥ \ge 大小

void test3()
{
	vector<int> v1;//默认构造
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
	}
	print_vector(v1);

	if (v1.empty())
	{
		cout << "v1为空" << endl;
	}
	else
	{
		cout << "v1不为空" << endl;
		cout << "v1的容量为:" << v1.capacity() << endl;
		cout << "v1的大小为:" << v1.size() << endl;
	}
	//重新指定大小
	v1.resize(15);
	print_vector(v1);//如果重新指定得比原来长了,多的默认添0
	v1.resize(20, 20);//可用指定值取代默认值
	print_vector(v1);
	v1.resize(10);//比原来短了,超出的部分直接删
	print_vector(v1);
}
3.2.5 vector插入和删除
//插入和删除
void test4()
{
	vector<int> v1;//默认构造
	v1.push_back(10);//尾插法
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(50);
	print_vector(v1);
	v1.pop_back();//尾删法
	print_vector(v1);
	v1.insert(v1.begin(), 0);//在头部插入0,第一个参数是迭代器
	print_vector(v1);
	v1.insert(v1.begin(), 2, 100);//在头部插两个100
	print_vector(v1);
	v1.erase(v1.begin());//删除头部元素
	print_vector(v1);
	//v1.erase(v1.begin(), v1.end());//从头删到尾
	v1.clear();//直接清空容器
	print_vector(v1);
}
3.2.6 vector数据存取

v[i]:重载[]访问元素
v.at(i):at访问元素
v.front():第一个元素
v.back():最后一个元素

3.2.7 vector互换容器

实现两个容器内元素进行互换
函数原型:v.swap(vec);将vec与v的元素互换

3.2.8 vector预留空间

减少vector在动态扩展容量时的扩展次数
函数原型:reserve(int len);容器预留len个元素长度,预留位置不初始化,元素不可访问

//预留空间
void test8()
{
	vector<int> v;
	v.reserve(10000);//利用reserve预留空间,则num=1
	int num = 0;//统计内存开辟次数
	int* p = NULL;
	for (int i = 0; i < 10000; i++)
	{
		v.push_back(i);
		//让p指向v的首地址,每开辟一次内存,首地址会变化,num统计开辟次数
		if (p != &v[0])
		{
			p = &v[0];
			num++;
		}
	}
	cout << "num=" << num << endl;
}
3.3 deque容器
3.3.1 deque容器基本概念

功能:

  • 双端数组,可以对头端进行插入删除操作

deque和vector的区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低
  • deque相对而言,对头部的插入删除速度比vector快
  • vector访问元素的速度比deque快

deque迭代器也支持随机访问

3.3.2 deque构造、赋值、大小、数据存取

与vector类似

3.3.3 deque插入删除
void print_deque(const deque<int>& d)//只可读不可写,注意要用const_iterator
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
//
插入删除
void test4()
{
	deque<int> d1;
	//尾插
	d1.push_back(10);
	d1.push_back(20);
	//头插
	d1.push_front(30);
	d1.push_front(40);
	print_deque(d1);
	d1.pop_back();//尾删
	print_deque(d1);
	d1.pop_front();//头删
	print_deque(d1);
	d1.insert(d1.begin(), 50);//insert插入
	print_deque(d1);
	d1.insert(d1.begin(), 2, 60);//插2个60
	print_deque(d1);
	deque<int> d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);
	d1.insert(d1.begin(), d2.begin(), d2.end());//在d1的begin插入d2的begin到d2的end的元素
	print_deque(d1);
	deque<int>::iterator it = d1.begin();
	it++;
	d1.erase(it);//删除第二个元素
	print_deque(d1);
	d1.erase(d1.begin(), d1.end());//按区间方式删除
	d1.clear();//清空
	print_deque(d1);
}
3.3.4 deque排序

sort(d.begin(),d.end());:默认升序

3.4 stack容器
3.4.1 stack基本概念

stack是一种先进后出的数据结构,只有一个出口,栈中只有栈顶的元素可以被外界使用,因此栈不能有遍历行为

3.4.2 stack常用接口
void test1()
{
	stack<int> s;
	//入栈
	s.push(14);
	s.push(15);
	s.push(16);
	s.push(17);
	cout << "栈的大小为:" << s.size() << endl;
	//只要栈不为空,查看栈顶并出栈
	while (!s.empty())
	{
		cout << "栈顶元素为:" << s.top() << endl;
		s.pop();//出栈
	}
	cout << "栈的大小为:" << s.size() << endl;
}
3.5 queue容器
3.5.1 queue基本概念

queue是一种先进先出的数据结构,有两个出口,队列容器允许一端新增元素,从另一端移除元素,只有队头和队尾的元素允许被外界使用,因此队列没有遍历行为

3.5.2 queue常用接口
push(elem); //往队尾添加元素
pop();//移除队头元素
back();//返回最后一个元素
front();//返回第一个元素

empty();//判断队列是否为空
size();//队列大小
3.6 list容器
3.6.1 list基本概念

功能:将数据链式存储
链表是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表中的迭代器只支持前移和后移,属于双向迭代器

3.6.2 list构造、赋值和大小

与vector类似

3.6.3 list数据存取
void test5()
{
	list<int> L;
	//尾插
	L.push_back(10);
	L.push_back(20);
	L.push_back(30);
	L.push_back(40);
	//L[0]、L.at(0)都不行,list不支持随机访问
	cout << "第一个元素为:" << L.front() << endl;
	cout << "最后一个元素为:" << L.back() << endl;
	//验证迭代器支不支持随机访问
	list<int>::iterator it = L.begin();
	it++;
	it--;//支持双向
	//it = it + 1;//不支持随机访问
}
3.6.4 list反转和排序

L.reverse();:反转list

//sort(L.begin(), L.end());
//所有不支持随机访问迭代器的容器,都不能用标准算法
//不支持随机访问迭代器的容器,内部提供了一些算法
L.sort();//默认升序
3.7 set/multiset容器
3.7.1 set基本概念

简介:

  • 所有的元素在插入时自动被排序

本质:

  • set/multiset属于关联式容器,底层结构是二叉树

区别:

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素
3.7.2 set构造和赋值
//构造和赋值
void test1()
{
	set<int> s1;
	//插入数据只有insert方式
	s1.insert(1);
	s1.insert(4);
	s1.insert(2);
	s1.insert(3);
	s1.insert(4);
	s1.insert(3);
	//set容器特点:所有元素在插入时自动排序且无重复值
	print_set(s1);
	set<int> s2(s1);//拷贝构造
	print_set(s2);
	set<int> s3 = s1;//赋值
	print_set(s3);
}
3.7.3 set大小和交换

与vector类似

3.7.4 set插入和删除
//插入和删除
void test3()
{
	set<int> s1;
	s1.insert(4);
	s1.insert(5);
	s1.insert(2);
	s1.insert(4);
	s1.insert(1);
	print_set(s1);
	//删除
	s1.erase(s1.begin());
	print_set(s1);
	s1.insert(6);
	print_set(s1);
	s1.erase(6);//把值为6的删了
	print_set(s1);
	//s1.erase(s1.begin(), s1.end());
	s1.clear();//清空
	print_set(s1);
}
3.7.5 set查找和统计
//查找和统计
void test4()
{
	set<int> s1;
	s1.insert(4);
	s1.insert(5);
	s1.insert(2);
	s1.insert(4);
	s1.insert(1);
	print_set(s1);
	set<int>::iterator pos=s1.find(2);//返回2的位置
	if (pos != s1.end())
	{
		cout << "找到元素:" << *pos << endl;
	}
	else
	{
		cout << "未找到元素" << endl;
	}
	int num = s1.count(4);//统计4的个数
	cout << "num=" << num << endl;//对于set而言,统计的结果0/1
}
3.7.6 set和multiset的区别
  • set不可以插入重复数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会检测数据,因此可以插入重复数据
3.7.7 pair对组创建

功能:

  • 成对出现的数据,利用对组可以返回两个数据

两种创建方式:

  • pair<type,type> p(value1,value2);
  • pair<type,type> p=make_pair(value1,value2);
3.7.8 set容器排序
//set容器排序
class my_compare
{
public:
	//重载operator()
	//成员函数名后const表明这是一个常量成员函数,不能修改成员变量的值
	bool operator()(int v1,int v2) const
	{
		return v1 > v2;
	}
};
void test7()
{
	set<int> s1;
	s1.insert(1);
	s1.insert(4);
	s1.insert(2);
	s1.insert(5);
	s1.insert(3);
	print_set(s1);
	//指定排序规则为从大到小
	set<int,my_compare> s2;
	s2.insert(1);
	s2.insert(4);
	s2.insert(2);
	s2.insert(5);
	s2.insert(3);
	//不同的容器迭代器不同
	for (set<int, my_compare>::iterator it = s2.begin(); it != s2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

//set容器排序(自定义数据类型)
class person
{
public:
	person(string name, int age)
	{
		m_name = name;
		m_age = age;
	}
	string m_name;
	int m_age;
};
class compare_person
{
public:
	bool operator()(const person& p1,const person& p2) const
	{
		//按年龄降序排列
		return p1.m_age > p2.m_age;
	}
};
void test8()
{
	//对于自定义数据类型,要指定排序规则
	set<person,compare_person> s;
	person p1("刘备", 35);
	person p2("曹操", 38);
	person p3("孙策", 30);
	person p4("关羽", 34);
	person p5("张飞", 31);
	person p6("赵云", 28);
	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);
	s.insert(p5);
	s.insert(p6);
	for (set<person,compare_person>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名:" << it->m_name << "\t年龄:" << it->m_age << endl;
	}
}
3.8 map/multimap容器
3.8.1 map基本概念

简介:

  • map中所有元素都是pair
  • pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
  • 所有元素会根据键值自动排序

本质:

  • map/multimap属于关联式容器,底层结构是二叉树

优点:

  • 可以根据key值快速找到value值

区别:

  • map中不允许有重复key值元素
  • multimap中允许有重复key值元素
3.8.1 map构造和赋值
void print_map(map<int, int>& m)
{
	for (map<int, int>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key=" << (*it).first << "\tvalue=" << it->second << endl;
	}
	cout << endl;
}
//构造和赋值
void test1()
{
	map<int, int> m;//默认构造,模板参数列表有两个参数
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(3, 30));
	m.insert(pair<int, int>(5, 50));
	print_map(m);//默认按key值升序
	map<int, int> m2(m);//拷贝构造
	print_map(m2);
	map<int, int> m3 = m;//operator=
	print_map(m3);
}
3.8.2 map大小和交换

与vector类似

3.8.3 map插入和删除
//插入和删除
void test3()
{
	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;//第四种(不建议)
	print_map(m);
	//不建议使用[]插入,可以用key访问到value
	cout << m[4] << endl;
	m.erase(m.begin());//删除第一个元素
	print_map(m);
	m.erase(3);//删除key为3的元素(只能按key删)
	print_map(m);
	//m.erase(m.begin(), m.end());
	m.clear();//清空
	print_map(m);
}
3.8.4 map查找和统计
//查找和统计
void test4()
{
	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));
	m.insert(pair<int, int>(3, 40));
	map<int, int>::iterator pos = m.find(3);
	if (pos != m.end())
	{
		cout << "查到了元素key=" << pos->first << "\tvalue=" << pos->second << endl;
	}
	else
	{
		cout << "未找到元素" << endl;
	}
	int num = m.count(3);
	cout << "num=" << num << endl;//num只能是0/1
}
3.8.5 map容器排序
//排序
class my_compare
{
public:
	bool operator()(int v1, int v2) const
	{
		//降序
		return v1 > v2;
	}
};
void test5()
{
	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));
	m.insert(pair<int, int>(4, 40));
	m.insert(pair<int, int>(5, 50));
	print_map(m);//默认按key从小到大排序
	map<int, int, my_compare> m2;
	m2.insert(pair<int, int>(2, 40));
	m2.insert(pair<int, int>(3, 20));
	m2.insert(pair<int, int>(4, 40));
	m2.insert(pair<int, int>(1, 10));
	m2.insert(pair<int, int>(5, 90));
	for (map<int, int,my_compare>::iterator it = m2.begin(); it != m2.end(); it++)
	{
		cout << "key=" << (*it).first << "\tvalue=" << it->second << endl;
	}
	cout << endl;
	
}

4. STL函数对象

4.1 函数对象
4.1.1 函数对象概念

概念:

  • 重载函数调用操作符的类,其对象称为函数对象
  • 函数对象使用重载的()时,行为类似函数调用,也叫仿函数

本质:

  • 函数对象(仿函数)是一个类,不是一个函数
4.1.2 函数对象使用

特点:

  • 函数对象在使用时,可以像普通函数那样调用,可以有参数,可以有返回值
  • 函数对象超出普通函数的概念,函数对象可以有自己的状态
  • 函数对象可以作为参数传递
class my_add
{
public:
	int operator()(int v1, int v2)
	{
		return v1 + v2;
	}
};

//函数对象在使用时,可以像普通函数那样调用,可以有参数也可以有返回值
void test1()
{
	my_add myadd;
	cout << myadd(10, 20) << endl;
}

class my_print
{
public:
	my_print()
	{
		this->count = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		count++;//每调用一次该函数,count++
	}
	int count = 0;//记录内部状态
};

//函数对象超出普通函数的概念,函数对象可以有自己的状态
void test2()
{
	my_print myprint;
	myprint("fuck trae young");
	myprint("fuck trae young");
	myprint("fuck trae young");
	cout << "fuck trae young的次数为:" << myprint.count << endl;
}

//函数对象可以作为参数传递
void do_print(my_print& mp,string test)
{
	mp(test);
}
void test3()
{
	my_print myprint;
	do_print(myprint, "fuck trae young");
}
4.2 谓词
4.2.1 谓词概念
  • 返回bool类型的仿函数称为谓词
  • 如果operator()接受一个参数,那么叫做一元谓词
  • 如果operator()接受两个参数,那么叫做二元谓词
4.2.2 一元谓词
class greater_than_five
{
public:
	//一元谓词
	bool operator()(int val)
	{
		return val > 5;
	}
};
void test1()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//查找容器中有无大于5的数字
	vector<int>::iterator it=find_if(v.begin(), v.end(), greater_than_five());//最后一个参数是匿名函数对象 
	if (it == v.end())
	{
		cout << "未找到" << endl;
	}
	else
	{
		cout << "找到大于5的数字为:" << *it << endl;
	}
}
4.2.3 二元谓词
//使用函数对象改变排序规则
class my_compare
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};
void test2()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(4);
	v.push_back(3);
	v.push_back(2);
	v.push_back(5);
	sort(v.begin(), v.end());//默认升序
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	sort(v. begin(), v.end(), my_compare());//改为降序
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
4.3 内建函数对象
4.3.1 内建函数对象

概念:

  • STL内建了一些函数对象

分类:

  • 算术仿函数
  • 关系仿函数
  • 逻辑仿函数

用法:

  • 这些仿函数所产生的对象,用法和一般函数完全相同
  • 使用内建函数对象,要引入头文件include<functional>;
4.3.2 算术仿函数
//算术仿函数
void test1()
{
	negate<int> n;//取反仿函数
	cout << n(10) << endl;
	plus<int> p;//加法仿函数
	cout << p(1, 2) << endl;
}
4.3.3 关系仿函数
//关系仿函数
void test2()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(4);
	v.push_back(3);
	v.push_back(5);
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	sort(v.begin(), v.end(), greater<int>());//大于仿函数(内建函数对象)
	//默认 less<int>()
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}
4.3.4 逻辑仿函数
//逻辑仿函数
void test3()
{
	vector<bool> v;
	v.push_back(true);
	v.push_back(true);
	v.push_back(true);
	v.push_back(false);
	for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
	//利用逻辑非,将容器v搬运到v2中,并取反
	vector<bool> v2;
	v2.resize(v.size());
	transform(v.begin(), v.end(), v2.begin(), logical_not<bool>());//逻辑仿函数取反
	for (vector<bool>::iterator it = v2.begin(); it != v2.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;
}

5. 常用算法

5.1 常用遍历算法
  • for_each //遍历容器
  • transform //搬运容器到另一个容器中
//普通函数
void print1(int val)
{
	cout << val << " ";
}

//仿函数
class myprint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test1()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	for_each(v.begin(), v.end(), print1);//起始迭代器、终止迭代器、普通函数
	cout << endl;
	for_each(v.begin(), v.end(), myprint()); //起始迭代器、终止迭代器、匿名函数对象
	cout << endl;
}

//仿函数
class Transform
{
public:
	int operator()(int val)
	{
		return val;
	}
};
void test2()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<int> v_target;//目标容器
	v_target.resize(v.size());//目标容器要开辟空间
	transform(v.begin(), v.end(), v_target.begin(), Transform());
	for_each(v_target.begin(), v_target.end(), myprint());
	cout << endl;
}
5.2 常用查找算法
  • find //查找元素
  • find_if //按条件查找元素
  • adjacent_find //查找相邻重复元素
  • binary_search //二分查找法
  • count //统计元素个数
  • count_if //按条件统计元素个数
//find查找内置数据类型
void test1()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator pos = find(v.begin(), v.end(), 5);//起始迭代器、结束迭代器、值
	if (pos != v.end())
	{
		cout << "找到元素:" << *pos << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

//find查找自定义数据类型
class person
{
public:
	person(string name, int age)
	{
		this->m_name = name;
		this->m_age = age;
	}
	//重载==号,让find知道如何对比peron数据类型
	/*bool operator==(const person& p)
	{
		if (this->m_name == p.m_name && this->m_age == p.m_age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}*/
	//重载==号,让count知道如何对比peron数据类型
	bool operator==(const person& p)
	{
		if (this->m_age == p.m_age)
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	string m_name;
	int m_age;
};
void test2()
{
	vector<person> v;
	person p1("aaa", 20);
	person p2("bbb", 30);
	person p3("ccc", 40);
	person p4("ddd", 50);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	vector<person>::iterator pos = find(v.begin(), v.end(), p2);
	if (pos != v.end())
	{
		cout << "找到元素 姓名:" << pos->m_name << "\t年龄:" << pos->m_age << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

//find_if查找内置数据类型
class greater_than_five
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};
void test3()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<int>::iterator pos;
	pos=find_if(v.begin(), v.end(), greater_than_five());//起始迭代器、结束迭代器、仿函数(谓词)
	if (pos != v.end())
	{
		cout << "找到大于5的数:" << *pos << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

//find_if查找自定义数据类型
class greater_than_20
{
public:
	bool operator()(person &p)
	{
		return p.m_age > 20;
	}
};
void test4()
{
	vector<person> v;
	person p1("aaa", 20);
	person p2("bbb", 30);
	person p3("ccc", 40);
	person p4("ddd", 50);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	vector<person>::iterator pos;
	pos=find_if(v.begin(), v.end(), greater_than_20());
	if (pos != v.end())
	{
		cout << "找到年龄大于20的人 姓名:" << pos->m_name << "\t年龄:" << pos->m_age << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

//adjacent_find查找相邻重复元素
void test5()
{
	vector<int> v;
	v.push_back(0);
	v.push_back(2);
	v.push_back(0);
	v.push_back(3);
	v.push_back(1);
	v.push_back(4);
	v.push_back(3);
	v.push_back(3);
	vector<int>::iterator pos = adjacent_find(v.begin(), v.end());
	if (pos != v.end())
	{
		cout << "找到相邻重复元素:" << *pos << endl;
	}
	else
	{
		cout << "未找到相邻重复元素" << endl;
	}
}

//binary_search在有序序列中查找指定元素
void test6()
{
	set<int> s;
	s.insert(4);
	s.insert(3);
	s.insert(2);
	s.insert(6);
	s.insert(5);
	s.insert(8);
	bool ret = binary_search(s.begin(), s.end(), 3);//二分查找是否有3
	if (ret)
	{
		cout << "找到了" << endl;
	}
	else
	{
		cout << "未找到" << endl;
	}
}

//count统计元素个数(内置数据类型)
void test7()
{
	vector<int> v;
	v.push_back(2);
	v.push_back(4);
	v.push_back(3);
	v.push_back(3);
	v.push_back(3);
	v.push_back(4);
	v.push_back(1);
	int num = count(v.begin(), v.end(), 3);
	cout << "3的个数为:" << num << endl;
}

//count统计元素个数(自定义数据类型)
void test8()
{
	vector<person> v;
	person p1("刘备", 34);
	person p2("关羽", 34);
	person p3("张飞", 34);
	person p4("曹操", 40);
	person p5("赵云", 32);
	person p("诸葛亮", 34);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	int num = count(v.begin(), v.end(), p);
	cout << "和诸葛亮年龄一样的人的个数为:" << num << endl;
}

//count_if按条件统计元素个数(内置数据类型)
class less_than_3
{
public:
	bool operator()(int val)
	{
		return val < 3;
	}
};
void test9()
{
	vector<int> v;
	v.push_back(2);
	v.push_back(4);
	v.push_back(3);
	v.push_back(3);
	v.push_back(3);
	v.push_back(4);
	v.push_back(1);
	int num = count_if(v.begin(), v.end(), less_than_3());//起始迭代器、结束迭代器、仿函数(谓词)
	cout << "小于3的元素个数为:" << num << endl;
}

//count_if统计自定义数据类型(自定义数据类型)
class age_greater_than_32
{
public:
	bool operator()(const person& p)
	{
		return p.m_age > 32;
	}
};
void test10()
{
	vector<person> v;
	person p1("刘备", 34);
	person p2("关羽", 34);
	person p3("张飞", 34);
	person p4("曹操", 40);
	person p5("赵云", 32);
	person p("诸葛亮", 34);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	//统计大于32的人数
	int num = count_if(v.begin(), v.end(), age_greater_than_32());
	cout << "年龄大于32的人数为:" << num << endl;
}
5.3 常用排序算法
  • sort //对容器内元素进行排序
  • random_shuffle //洗牌 指定范围内元素随机调整次序
  • merge //容器元素合并,并存储到另一容器中
  • reverse //反转指定范围的元素
//sort排序算法
void my_print(int val)
{
	cout << val << " ";
}
void test1()
{
	vector<int> v;
	v.push_back(4);
	v.push_back(3);
	v.push_back(2);
	v.push_back(1);
	v.push_back(5);
	sort(v.begin(), v.end());//默认升序
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
	sort(v.begin(), v.end(), greater<int>());//改为从大到小,最后一个参数是内建仿函数(谓词)
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
}

//random_shuffle洗牌
void test2()
{
	srand((unsigned int)time(NULL));
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	random_shuffle(v.begin(), v.end());//打乱v中元素的顺序
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
}

//merge合并两个容器并放到另一个容器中
//v1、v2必须是有序的且顺序相同,合并之后的v也是有序的
void test3()
{
	vector<int> v1;
	vector<int> v2;
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 1);
	}
	v.resize(v1.size() + v2.size());//给v分配空间
	merge(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
}

/reverse将容器内元素与反转
void test4()
{
	vector<int> v;
	v.push_back(6);
	v.push_back(4);
	v.push_back(2);
	v.push_back(3);
	v.push_back(1);
	cout << "反转前:" << endl;
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
	reverse(v.begin(),v.end());
	cout << "反转后:" << endl;
	for_each(v.begin(), v.end(), my_print);
	cout << endl;
}
5.4 常用拷贝和替换算法
  • copy //容器内指定范围的元素拷贝到另一容器中
  • replace //容器内指定范围的旧元素修改为新元素
  • replace_if //容器内指定范围满足条件的元素替换为新元素
  • swap //互换两个容器的元素
//copy将指定范围的元素复制到另一容器
void my_print(int val)
{
	cout << val << " ";
}
void test1()
{
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	vector<int> v2;
	v2.resize(v.size());//目标容器要开辟空间
	copy(v.begin(), v.end(), v2.begin());
	for_each(v2.begin(), v2.end(), my_print);
	cout << endl;
}

//replace将指定范围内的元素改为新元素
class myprint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test2()
{
	vector<int> v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(10);
	v.push_back(80);
	v.push_back(20);
	v.push_back(20);
	v.push_back(50);
	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
	cout << endl;
	replace(v.begin(), v.end(), 20, 200);//将v中的20替换为200
	cout << "替换后:" << endl;
	for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
	cout << endl;
}

//replace_if将指定范围内满足条件的元素改为新元素
class greater_than_30
{
public:
	bool operator()(int val)
	{
		return val > 30;
	}
};
void test3()
{
	vector<int> v;
	v.push_back(20);
	v.push_back(30);
	v.push_back(10);
	v.push_back(80);
	v.push_back(20);
	v.push_back(20);
	v.push_back(50);
	//将大于30的数替换为300
	cout << "替换前:" << endl;
	for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
	cout << endl;
	replace_if(v.begin(), v.end(), greater_than_30(), 300);//谓词(函数对象)
	cout << "替换后:" << endl;
	for_each(v.begin(), v.end(), myprint());//仿函数(函数对象)
	cout << endl;
}

//swap互换两个同类型的容器
void test4()
{
	vector<int> v1;
	vector<int> v2;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);
		v2.push_back(i + 2);
	}
	cout << "交换前:" << endl;
	for_each(v1.begin(), v1.end(), myprint());
	cout << endl;
	for_each(v2.begin(), v2.end(), myprint());
	cout << endl;
	swap(v1, v2);
	cout << "交换后:" << endl;
	for_each(v1.begin(), v1.end(), myprint());
	cout << endl;
	for_each(v2.begin(), v2.end(), myprint());
	cout << endl;
}
5.5 常用算术生成算法
  • accumulate //计算容器元素累计总和
    accumulate(v.begin(), v.end(), 0);//0是起始累加值
  • fill //向容器中添加元素
    fill(v.begin(), v.end(), 100);//用100填充
5.6 常用集合算法
  • set_intersection //求两个容器的交集
    • 求交集的两个容器必须是有序序列
    • 目标容器开辟空间取两个容器最小值
    • 返回值是交集中最后一个元素的迭代器
  • set_union //求两个容器的并集
  • set_difference //求两个容器的差集(独有的元素)
//set_intersection求交集
//两个集合必须是有序序列
class myprint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test1()
{
	vector<int> v1;
	vector<int> v2;
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);//0~9
		v2.push_back(i + 2);//2~11
	}
	//目标容器要开辟空间,最特殊的就是一个大容器包含一个小容器
	v.resize(min(v1.size(), v2.size()));
	vector<int>::iterator pos_end;
	//返回目标容器最后一个元素的迭代器位置
	pos_end = set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), pos_end, myprint());
	cout << endl;
}

//set_union求并集
void test2()
{
	vector<int> v1;
	vector<int> v2;
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);//0~9
		v2.push_back(i + 2);//2~11
	}
	//目标容器要开辟空间,最特殊的就是没有交集
	v.resize(v1.size()+v2.size());
	vector<int>::iterator pos_end;
	//返回目标容器最后一个元素的迭代器位置
	pos_end = set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), pos_end, myprint());
	cout << endl;
}

//set_difference求差集(独有的元素)
void test3()
{
	vector<int> v1;
	vector<int> v2;
	vector<int> v;
	for (int i = 0; i < 10; i++)
	{
		v1.push_back(i);//0~9
		v2.push_back(i + 2);//2~11
	}
	//目标容器要开辟空间,最特殊的是两个容器没有交集
	v.resize(max(v1.size(), v2.size()));
	vector<int>::iterator pos_end;
	//返回目标容器最后一个元素的迭代器位置
	cout << "v1和v2的差集:" << endl;
	pos_end = set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), v.begin());
	for_each(v.begin(), pos_end, myprint());
	cout << endl;
	cout << "v2和v1的差集:" << endl;
	pos_end = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), v.begin());
	for_each(v.begin(), pos_end, myprint());
	cout << endl;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值