C++提高编程

目录

1 模板

1.1 概念

1.2 函数模板

1.2.1语法 

1.2.2注意事项

1.2.3案例

1.2.4 普通函数与函数模板区别

1.2.5普通函数与函数模板调用规则

1.2.6 模板的局限性

1.3 类模板

1.3.1 类模板语法

1.3.2 类模板与函数模板的区别

1.3.3 类模板中成员函数创建时机

1.3.4 类模板对象做函数参数

1.3.5 类模板与继承

1.3.6 类模板成员函数类外实现

1.3.7 类模板分文件编写

1.3.8 类模板与友元

1.3.9 类模板案例

2.STL初识

2.1 STL的诞生

2.2 STL基本概念

2.3 STL六大组件

2.4 STL中容器、算法、迭代器

2.5 容器算法迭代器初识

2.5.1 vector存放内置数据类型

2.5.2 Vector存放自定义数据类型

2.5.3 Vector容器嵌套容器

3.STL-常用容器

3.1 string容器 

3.1.1 string基本概念

3.1.2 string构造函数

 3.1.3 string赋值操作

3.1.4 string字符串拼接

3.1.5 string查找和替换

3.1.6 string字符串比较

3.1.7 string字符存取

3.1.8 string插入和删除

3.1.9 string子串

3.2 vector容器

3.2.1 vector基本概念

3.2.2 vector构造函数

3.2.3 vector赋值操作

3.2.4 vector容量和大小

3.2.5 vector插入和删除

3.2.6 vector数据存取

3.2.7 vector互换容器

3.2.8 vector预留空间

3.3 deque容器

3.3.1 deque容器基本概念 

3.3.2 deque构造函数

3.3.3 deque赋值操作

3.3.4 deque大小操作

3.3.5 deque插入和删除

3.3.6 deque数据存取

3.3.7 deque排序

3.4 案例-评委打分

3.5 stack容器

3.5.1 stack基本概念

3.5.2 stack常用接口

3.6 queue容器

3.6.1 queue基本概念

3.6.2 queue常用接口

3.7 list容器

3.7.1 list基本概念 

3.7.2 list构造函数

3.7.3 list赋值和交换

3.7.4 list大小操作

3.7.5 list插入和删除

3.7.6 list数据存取

3.7.7 list反转和排序

3.7.8 排序案例

3.8 set / multiset 容器

3.8.1 set基本概念

3.8.2 set构造和赋值

3.8.3 set大小和交换

3.8.4 set插入和删除

3.8.5 set查找和统计

3.8.6 set和multiset区别

3.8.7 pair对组创建

3.8.8 set容器排序

3.9 map / multimap 容器

3.9.1 map基本概念

 3.9.2 map构造和赋值

3.9.3 map大小和交换

3.9.4 map插入和删除

3.9.5 map查找和统计

3.9.6 map容器排序

3.10 案例-员工分组

4.STL-函数对象

4.1 函数对象

4.1.1 函数对象概念

4.1.2 函数对象使用

4.2 谓词

4.2.1 谓词概念

4.2.2 一元谓词

4.2.3 二元谓词

4.3 内建函数对象

4.3.1 内建函数对象意义

4.3.2 算数仿函数

4.3.3 关系仿函数

4.3.4 逻辑仿函数

5.STL-常用算法

5.1 常用遍历算法

5.1.1 for_each(遍历)

5.1.2 transform(搬运容器到另一个容器中)

5.2 常用查找算法

5.2.1 find

5.2.2 find_if

5.2.3 adjacent_search

5.2.4 binary_search

5.2.5 count

5.2.6 count_if

5.3 常用排序算法

5.3.1 sort

5.3.2 random_shuffle 

5.3.3 merge

5.3.4 reverse

5.4 常用拷贝和替换算法

5.4.1 copy

5.4.2 replace

5.4.3 replace_if

5.4.4 swap

5.5 常用算数生成算法

5.5.1 accumulate

5.5.2 fill

5.6 常用集合算法

5.6.1 set_intersection  

5.6.2 set_union

5.6.3 set_difference


1 模板

模板的目的是为了提高复用性,将类型参数化 

1.1 概念

1.2 函数模板

1.2.1语法 

template<typename T>

函数声明 \ 定义

使用的两种方式

1. 自动类型推导:如 mySwap(a,b); 

2. 显示指定类型:如 mySwap<int>(a,b); 指定为int型

1.2.2注意事项

  • 自动类型推导,必须推导出一致的数据类型T,才可以使用
  • 模板必须要确定出T的数据类型,才可以使用。
    • 如函数模板中调用,func();错误    func<int>();正确

1.2.3案例

案例描述:

  • 利用函数模板封装一个排序的函数,可以对不同数据类型数组进行排序
  • 排序规则 从大到小,排序算法 选择排序
  • 分别利用char数组int数组进行测试
#include<iostream>
using namespace std;

template<typename T>
void mySwap(T &a, T &b)
{
	T temp = a;
	a = b;
	b = temp;
}

template<typename T>
void mySort(T arr[], int len)
{
	for (int i = 0; i < len; i++) {
		int max = i;
		for (int j = i + 1; j < len; j++) {
			if (arr[max] < arr[j]) {
				max = j;
			}
		}
		if (max != i) {
			mySwap(arr[max], arr[i]);
		}
	}
}

template<typename T>
void printArray(T arr[], int len)
{
	for (int i = 0; i < len; i++) {
		cout << arr[i] << " ";
	}
	cout << endl;

}

void test01() {
	char charArr[] = "badcfe";
	int num = sizeof(charArr) / sizeof(char);
	mySort(charArr, num);
	printArray(charArr, num);

}

void test02() {
	int intArr[] = { 2,4,5,3,1,9,6 };
	int num = sizeof(intArr) / sizeof(int);
	mySort(intArr, num);
	printArray(intArr, num);
}

int main()
{
	test01();
	test02();

	system("pause");
	return 0;
}

1.2.4 普通函数与函数模板区别

  • 普通函数调用时,可以发生自动类型转换(隐式类型转换)
  • 函数模板调用时,如果利用自动类型推导,不会发生隐式类型转换
  • 如果利用显示指定类型的方式,可以发生隐式类型转换
    • 如 int a = 2;  char b = 'b';
    • myAdd为函数模板;
    • myAdd(a,b);错误    myAdd<int>(a,b); 正确 (将字符b化为ASCII码)

1.2.5普通函数与函数模板调用规则

  • 如果函数模板和普通函数都可以实现,优先调用普通函数
  • 可以通过空模板参数列表来强调函数模板
    • 如myPrint<>(a,b);
  • 函数模板也可以发生重载
  • 如果函数模板可以产生更好的匹配,优先调用函数模板
    • 如普通函数为int 型
    • 调用时的实参ab都为char型 编译器会选择函数模板

1.2.6 模板的局限性

  • 模板的通用性不是万能的

1.3 类模板

1.3.1 类模板语法

 类模板作用:

  • 建立一个通用类,类中的成员 数据类型可以不具体制定,用一个虚拟的类型来代表

template<typename T>

template<class NameType,class AgeType>
class Person
{
public:
    NameType m_Name;
    AgeType m_Age;
};

1.3.2 类模板与函数模板的区别

  • 类模板没有自动类型推导的使用方式        
    • 如 Person p("张三",90);  错误,无法自动推导
    • 但 Person<string,int>p("张三",90);  正确,只能用显示指定类型
  • 类模板在模板参数列表中可以有默认参数
    • 如 template<class NameType,class AgeType = int>
    • 可以使用 Person<string>p("李四",80);  正确 年龄有默认参数类型,不需显示指定类型了

1.3.3 类模板中成员函数创建时机

  • 普通类 中的成员函数一开始就可以创建
  • 类模板 中的成员函数在调用时才创建

1.3.4 类模板对象做函数参数

三种传入方式:

  1. 指定传入的类型 —— 直接显示对象的数据类型(使用较为广泛)
  2. 参数模板化        —— 将对象中的参数变为模板进行传递
  3. 整个类模板化    —— 将这个对象类型 模板化进行传递
#include<iostream>
using namespace std;
#include<string>

template<class T1,class T2>
class Person
{
public:
	Person(T1 name, T2 age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson() {
		cout << "姓名:" << this->m_Name << "  年龄:" << this->m_Age << endl;
	}

	T1 m_Name;
	T2 m_Age;
};

//1.指定传入类型
void printPerson1(Person<string, int>& p) {
	p.showPerson();
}
void test01() {
	Person<string, int>p("张三", 8);
	printPerson1(p);
}

//2.参数模板化
template<class T1,class T2>
void printPerson2(Person<T1, T2>&p) {
	p.showPerson();
}
void test02() {
	Person<string, int>p("李四", 18);
	printPerson2(p);
}

//3.整个类模板化
template<class T>
void printPerson3( T &p) {
	p.showPerson();
}
void test03() {
	Person<string, int>p("王五", 28);
	printPerson3(p);
}


int main()
{
	test03();
	system("pause");
	return 0;
}

1.3.5 类模板与继承

类模板碰到继承,需注意:

  • 当子类继承的父类是一个类模板时,子类在声明的时候,要指出父类中T的类型
    • class Son : public Base<int>
  • 如果不指定,编译器无法给子类分配内存
  • 如果想灵活指定出父类中的T的类型,子类也需变为类模板
    • template<class T1,class T2>
    • class Son2 : public Base<T2>
    • {    T1 obj;    };
    • ——————————————————————————
    • 创建对象时 Son2<int,char>S2;      int给子类,char给父类

1.3.6 类模板成员函数类外实现

类内实现:

	Person(T1 name, T2 age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	void showPerson()
	{
		cout << "姓名:" << this->m_Name << "  年龄:" << this->m_Age << endl;
	}

类外实现:​​​​

//构造函数的类外实现
template<class T1,class T2>
Person<T1, T2>::Person(T1 name, T2 age)
{
	this->m_Name = name;
	this->m_Age = age;
}

//成员函数的类外实现
template<class T1,class T2>
void Person<T1,T2>::showPerson()
{
	cout << "姓名:" << this->m_Name << "  年龄:" << this->m_Age << endl;
}

1.3.7 类模板分文件编写

问题:

  • 类模板中成员函数创建时机是在调用阶段,导致份文件编写时链接不到

解决:

  1. 直接包含.cpp源文件
  2. 将声明和实现写到同一个文件中,并改后缀名为.hpp(hpp是约定的名称,并不是强制

1.3.8 类模板与友元

  • 全局函数类内(建议)实现  直接在类内声明友元即可
    • 如类内:friend void printPerson(Person<T1,T2> p){…}
  • 全局函数类外实现 需要提前让编译器知道全局函数的存在(加空模板的参数列表)
    • 提前让编译器知道:
    • template<class T1,class T2>
    • class Person;                
    • 如类内:friend void printPerson<>(Person<T1,T2> p);
    •     类外:template<class T1,class T2>
    •                void printPerson(Person<T1,T2> p){…}

1.3.9 类模板案例

实现一个通用的数组类

  • 可以对内置数据类型以及自定义数据类型的数据进行存储
  • 将数组中的数据存储到堆区
  • 构造函数中可以传入数组的容量
  • 提供对应的拷贝构造函数以及operator=防止浅拷贝问题
  • 提供尾插法和尾删法对数组中的数据进行增加和删除
  • 可以通过下标的方式访问数组中的元素
  • 可以获取数组中当前元素个数和数组的容量

cpp中:

#include<iostream>
using namespace std;
#include<string>
#include"MyArray.hpp"

void printIntArray(MyArray<int>&arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << arr[i] << endl;
	}
}

void test01() {
	MyArray <int>arr1(5);

	//利用尾插法 向数组中插入数据
	for (int i = 0; i < 5; i++) {
		arr1.Push_Back(i);
	}
	cout << "arr1的打印输出为:" << endl;
	printIntArray(arr1);

	cout << "arr1的容量:" << arr1.getCapacity() << endl;
	cout << "arr1的大小:" << arr1.getSize() << endl;


	MyArray<int>arr2(arr1);
	cout << "arr2的打印输出为:" << endl;
	printIntArray(arr2);

	//利用尾删法
	arr2.Pop_Back();
	cout << "尾删后 arr2的容量:" << arr2.getCapacity() << endl;
	cout << "尾删后 arr2的大小:" << arr2.getSize() << endl;

	//MyArray<int>arr3(100);
	//arr3 = arr1;
}



//测试自定义数据类型
class Person
{
public:
	Person() {};
	Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;

};

void printPersonArray(MyArray<Person>& arr) {
	for (int i = 0; i < arr.getSize(); i++) {
		cout << "姓名:" << arr[i].m_Name << "  年龄:" << arr[i].m_Age << endl;
	}
}

void test02() {
	MyArray<Person>arr(10);
	
	Person p1("张1", 14);
	Person p2("张2", 15);
	Person p3("张3", 16);
	Person p4("张4", 17);
	Person p5("张5", 18);

	//将数据插入到数组中
	arr.Push_Back(p1);
	arr.Push_Back(p2);
	arr.Push_Back(p3);
	arr.Push_Back(p4);
	arr.Push_Back(p5);

	//打印数组
	printPersonArray(arr);



}
int main()
{
	test02();

	system("pause");
	return 0;
}

hpp中:

//自己通用的数组类
#pragma once
#include<iostream>
using namespace std;

template<class T>
class MyArray
{
public:
	//有参构造
	MyArray(int capacity) {
		//cout << "调用有参构造" << endl;
		this->m_Capacity = capacity;
		this->m_Size = 0;
		this->pAddress = new T[this->m_Capacity];
	}

	//拷贝构造
	MyArray(const MyArray& arr) {

		//cout << "调用拷贝构造" << endl;
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		//this->pAddress = arr.pAddress; //指针不能这么赋值  浅拷贝的问题 会导致堆区内存重复释放
		
		//深拷贝
		this->pAddress = new T[arr.m_Capacity];

		//将arr中的数据都拷贝过来
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}

	}
	
	//operator= 防止浅拷贝问题
	MyArray& operator=(const MyArray& arr)
	{
		//cout << "调用operator=" << endl;

		//先判断原来堆区是否有数据,如果有先释放
		if (this->pAddress != NULL)
		{
			delete[] this->pAddress;
			this->pAddress = NULL;
			this->m_Capacity = 0;
			this->m_Size = 0;
		}

		//深拷贝
		this->m_Capacity = arr.m_Capacity;
		this->m_Size = arr.m_Size;
		this->pAddress = new T[arr.m_Capacity];
		for (int i = 0; i < this->m_Size; i++) {
			this->pAddress[i] = arr.pAddress[i];
		}
		return *this;
	}

	//尾插法
	void Push_Back(const T& val) {
		//判断是否满了
		if (this->m_Capacity == this->m_Size) {
			return;
		}
		this->pAddress[this->m_Size] = val; //数组末尾插入数据
		this->m_Size++; //更新数组大小
	}

	//尾删法
	void Pop_Back() {
		//让用户访问不到最后一个元素,即为尾删(逻辑删除)
		if (this->m_Size == 0) {
			return;
		}
		this->m_Size--;
	}

	//通过下标的方式访问数组中的元素-重载[]
	T operator[](int index)
	{
		return this->pAddress[index];
	}

	//返回数组容量
	int getCapacity()
	{
		return this->m_Capacity;
	}

	//返回数组大小
	int getSize()
	{
		return this->m_Size;
	}

	~MyArray() {
		if (this->pAddress != NULL) {
			//cout << "调用析构函数" << endl;
			delete[]this->pAddress;
			this->pAddress = NULL;
		}
	}
private: 
	T* pAddress;      //指针指向堆区开辟的真实数组
	int m_Capacity;		 //数组容量
	int m_Size;          //数组大小
};

2.STL初识

2.1 STL的诞生

  •  长久以来,软件界一直希望建立一种可重复利用的东西
  • C++的面对对象泛型编程思想,目的就是复用性的提升
  • 大多数情况下,数据结构和算法都未能有一套标准,导致被迫从事大量重复工作
  • 为了建立数据结构和算法的一套标准,诞生了STL

2.2 STL基本概念

  •  STL(Standard Template Library,标准模板库)
  • STL从广义上分为:容器(container)算法(algorithm)迭代器(iterator)
  • 容器算法之间通过迭代器进行无缝连接
  • STL几乎所有的代码都采用了模板类或者模板函数

2.3 STL六大组件

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

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

2.4 STL中容器、算法、迭代器

  •  STL容器就是将运用最广泛的一些数据结构实现出来
  • 常用的数据结构:数组、链表、树、栈、队列、集合、映射表 等
  • 这些容器分为序列式容器关联式容器两种:
    • 序列式容器:强调值的排序,序列式容器中的每个元素均有固定的位置
    • 关联式容器:二叉树结构,各元素之间没有严格的物理上的顺序关系

  • 算法(Algorithms),即有限的步骤,解决逻辑或数学上的问题的一门学科
  • 算法分为:质变算法非质变算法
    • 质变算法:是指运算过程中会更改区间内的元素内容。例如拷贝、替换、删除等
    • 非质变算法:是指运算过程中不会更改区间内的元素内容。例如查找、计数、遍历、寻找极值等。

  • 迭代器:容器和算法之间粘合剂
  • 提供一种方法,使之更改依序访问某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。
  • 每个容器又自己专属的迭代器
  • 迭代器的使用非常类似于指针,初学阶段我们可以先理解迭代器为指针

迭代器种类:

种类功能支持运算
输入迭代器对数据的只读访问只读,支持++、==、!=
输出迭代器对数据的只写访问

只写,支持++

前向迭代器读写操作,并能向前推进迭代器读写,支持++、==、!=
双向迭代器读写操作,并能向前和向后操作

读写,支持++、--

随机访问迭代器读写操作,可以以跳跃的方式访问任何数据,功能最强的迭代器读写,支持++、--、[n]、-n、<、<=、>、>=

常用的容器迭代器种类为:双向迭代器、随机访问迭代器 

2.5 容器算法迭代器初识

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

2.5.1 vector存放内置数据类型

容器:vector

算法:for_each

迭代器:vector<int>::iterator

#include<iostream>
using namespace std;
#include<vector>
#include<algorithm>

void myPrint(int val)
{
	cout << val << endl;
}

void test01() 
{
	//创建了一个vector容器、数组
	vector<int>v;

	//向容器中插入数据
	v.push_back(10);   //尾插
	v.push_back(20);  
	v.push_back(30);
	v.push_back(40);
	v.push_back(50);

	//通过迭代器访问容器中的数据
	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;
	}

	//第三种遍历方式
	for_each(v.begin(), v.end(), myPrint);
}


int main()
{
	test01();

	system("pause");
	return 0;
}

2.5.2 Vector存放自定义数据类型

#include<iostream>
using namespace std;
#include<string>
#include<vector>

class Person {
public:
	Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;

};

void test01()
{
	vector<Person>v;

	Person p1("张1", 10);
	Person p2("张2", 20);
	Person p3("张3", 30);
	Person p4("张4", 40);
	Person p5("张5", 50);

	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name << "  年龄:" << (*it).m_Age << endl;
		//cout << "姓名:" << it->m_Name << "  年龄:" << it->m_Age << endl;  //可以运行
	}
}


void test02()
{
	vector<Person*>v;

	Person p1("张1", 10);
	Person p2("张2", 20);
	Person p3("张3", 30);
	Person p4("张4", 40);
	Person p5("张5", 50);

	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	v.push_back(&p5);

	for (vector<Person*>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it)->m_Name << "  年龄:" << (*it)->m_Age << endl;
	}
}

int main()
{
	
	test02();

	system("pause");
	return 0;
}

2.5.3 Vector容器嵌套容器

#include<iostream>
using namespace std;
#include<vector>


void test01()
{
	vector<vector<int>>v;

	//创建小容器
	vector<int> v1;
	vector<int> v2;
	vector<int> v3;
	vector<int> v4;
	vector<int> v5;

	//向小容器中添加数据
	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);
		v5.push_back(i + 5);
	}

	//小容器放入大容器
	v.push_back(v1);
	v.push_back(v2);
	v.push_back(v3);
	v.push_back(v4);
	v.push_back(v5);


	for (vector<vector<int>>::iterator it = v.begin(); it != v.end(); it++) {
		for (vector<int>::iterator vit = (*it).begin(); vit != (*it).end(); vit++)
		{
			cout << *vit << " ";
		}
		cout << endl;
	}
}




int main()
{
	
	test01();

	system("pause");
	return 0;
}

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();                                           //创建一个空的字符串,例如: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 = "hello world";
	string s2(str);

	cout << "s2 = " << s2 << endl;

	string s3(s2);
	cout << "s3 = " << s3 << endl;

	string s4(10, 'c');
	cout << "s4 = " << s4 << endl;

}

 3.1.3 string赋值操作

  • 给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 test01()
{
	string str1;
	str1 = "hello world";
	cout << "str1 = " << str1 << endl;

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

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

	string str4;
	str4.assign("hello C++");
	cout << "str4 = " << str4 << endl;

	string str5;
	str5.assign("hello", 3);
	cout << "str5 = " << str5 << endl;

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

	string str7;
	str7.assign(6, 'c');
	cout << "str7 = " << str7 << endl;

	/*
	str1 = hello world
	str2 = hello world
	str3 = a
	str4 = hello C++
	str5 = hel
	str6 = hello C++
	str7 = cccccc
	*/

}

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);                                                                                                                //字符串s中从pos开始的n个字符连接到字符串结尾
void test01()
{
	string str1 = "我";
	str1 += "爱玩";
	cout << "str1 = " << str1 << endl;

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

	string str2 = "游戏";
	str1 += str2;
	cout << "str1 = " << str1 << endl;



	string str3 = "I";
	str3.append(" Love ");
	cout << "str3 = " << str3 << endl;

	str3.append("UFO", 1);
	cout << "str3 = " << str3 << endl;

	string str4 = " Every ";
	str3.append(str4);
	cout << "str3 = " << str3 << endl;

	string str5 = "WWWDay";
	str3.append(str5, 3, 3);
	cout << "str3 = " << str3 << endl;

	/*
	str1 = 我爱玩
	str1 = 我爱玩:
	str1 = 我爱玩:游戏
	str3 = I Love
	str3 = I Love U
	str3 = I Love U Every
	str3 = I Love U Every Day
	*/

}

3.1.5 string查找和替换

  • 查找:查找指定字符串是否存在
  • 替换:在指定的位置替换字符串

赋值的函数原型:

  • 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
//查找
void test01()
{
	string str1 = "abcdefgde";

	int pos=str1.find("de");
	cout << "pos = " << pos << endl;         //3
	int pos1 = str1.find("df");
	cout << "pos1 = " << pos1 << endl;       //-1 (查不到)


	//rfind 和 find 区别
	//rfind从右往左  find从左往右
	int pos2 = str1.rfind("de");
	cout << "pos2 = " << pos2 << endl;       //7

}

//替换
void test02() {
	string str1 = "abcdefg";
	str1.replace(1, 3, "1111");
	cout << "str1 = " << str1 << endl;       //a1111efg

}

3.1.6 string字符串比较

  •  字符串之间的比较
  • 字符串比较是按字符的ASCII码进行对比
  • = 返回0          >返回1          <返回-1

函数原型:

  • int compare(const string &s)const;         //与字符串s比较
  • int compare(const char *s)const;            //与字符串s比较
void test01()
{
	string str1 = "xello";
	string str2 = "yello";
	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.7 string字符存取

  • char& operator[ ](int n);                            //通过[]方式取字符
  • char& at(int n);                                         //通过at方法获取字符
void test01()
{
	string str = "hello";
	cout << "str = " << str << endl;

	//通过[]访问单个字符
	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[4] = '0';
	cout << "str = " << str << endl;         //str = hell0

	str.at(0) = '1';
	cout << "str = " << str << endl;     //str = 1ell0

}

3.1.8 string插入和删除

  • 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个字符
void test01()
{
	string str = "hello";
	cout << "str = " << str << endl;
	string str1 = "2";

	str.insert(1, "111");
	cout << "str = " << str << endl;       //str = h111ello

	str.insert(0, str1);
	cout << "str = " << str << endl;         //str = 2h111ello

	str.erase(2, 3);
	cout << "str = " << str << endl;         //str = 2hello

}

3.1.9 string子串

  • string substr(int pos = 0, int n = npos)const;     //返回由pos开始的n个字符组成的字符串
void test01()
{
	string str = "hello";
	cout << "str = " << str << endl;
	
	cout << "子串 = " << str.substr(2, 3) << endl;  //子串 = llo
}

3.2 vector容器

3.2.1 vector基本概念

  • vector数据结构和数组非常相似,也称为单端数组
  • 不同之处在于数组是静态空间,而vector可以动态扩展
  • 动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将元数据拷贝新空间,释放原空间。

  •  vector容器的迭代器是支持随机访问的迭代器

3.2.2 vector构造函数

  • 创建vector容器
  • vector<T> v;                           //采用模板实现类实现,默认构造函数
  • vector(v.begin(),v.end());                                                                                                                                             //将v[begin(),end())区间(前闭后开)中的元素拷贝给本身
  • vector(n,elem);                       //构造函数将n个elem拷贝到本身
  • vector(const vector &vec);   //拷贝构造函数
void printVector(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);
	}
	printVector(v1);

	//通过区间方式进行构造
	vector<int>v2(v1.begin(), v1.end());
	printVector(v2);

	//n个elem方式构造
	vector<int>v3(10, 0);
	printVector(v3);

	//拷贝构造
	vector<int>v4(v3);
	printVector(v4);

	/*
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	0 0 0 0 0 0 0 0 0 0
	0 0 0 0 0 0 0 0 0 0
	*/
}

3.2.3 vector赋值操作

  • vector& operator = (const vector &vec);  //重载等号操作符
  • assign(beg, end);                                     //将[ beg, end )区间中的数据拷贝赋值给本身
  • assign(n, elem);                                       //将n个elem拷贝赋值给本身
void printVector(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);
	}
	printVector(v1);
	
	//赋值 operator=
	vector<int>v2;
	v2 = v1;
	printVector(v2);

	//assign
	vector<int>v3;
	v3.assign(v1.begin(), v1.end());
	printVector(v3);

	vector<int>v4;
	v4.assign(10, 2);
	printVector(v4);

	/*
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	2 2 2 2 2 2 2 2 2 2
	*/
}

3.2.4 vector容量和大小

  • empty();                //判断容器是否为空
  • capacity();            //容器的容量
  • size();                   //返回容器中元素的个数
  • resize(int num);                                                                                                                                            //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。                              //如果容器变短,则末尾超出容器长度的元素被删除
  • resize(int num,elem);                                                                                                                                   //重新指定容器的长度为num,若容器变长,则以elem填充新位置。                               //如果容器变短,则末尾超出容器长度的元素被删除
//vector容器容量和大小操作
void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	printVector(v1);

	if (v1.empty())           //真,容器为空
	{
		cout << "v1 空" << endl;
	}
	else
	{
		cout << "v1 不为空" << endl;
		cout << "v1容量为:" << v1.capacity() << endl;
		cout << "v1大小为:" << v1.size() << endl;
	}

	/*
	0 1 2 3 4 5 6 7 8 9
	v1 不为空
	v1容量为:13
	v1大小为:10
	*/

	//重新指定大小
	v1.resize(15);
	printVector(v1);     //0 1 2 3 4 5 6 7 8 9 0 0 0 0 0

	v1.resize(5);
	printVector(v1);     //0 1 2 3 4

}

3.2.5 vector插入和删除

  • push_back(ele);                                    //尾部插入元素ele 
  • pop_back();                                           //删除最后一个元素
  • insert(const_iterator pos, ele);            //迭代器指向pos位置插入元素ele
  • insert(const_iterator pos, int count, ele);     //pos位置插入count个ele元素
  • erase(const_iterator pos);                    //删除迭代器指向的元素
  • erase(const_iterator start, const_iterator end); //删除从start到end之间的元素
  • clear();                                                     //删除容器中所有元素
//vector插入和删除
void test01()
{
	vector<int>v1;
	
	//尾插
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);
	v1.push_back(5);

	printVector(v1);      //1 2 3 4 5

	//尾删
	v1.pop_back();
	printVector(v1);      //1 2 3 4

	//插入
	v1.insert(v1.begin(), 100);    // 100 1 2 3 4
	printVector(v1);
	//v1.insert(0, 100);     //位置 要用迭代器表示

	v1.insert(v1.end(), 10, 0);
	printVector(v1);        //100 1 2 3 4 0 0 0 0 0 0 0 0 0 0 

	//删除
	v1.erase(v1.begin());
	printVector(v1);           // 1 2 3 4 0 0 0 0 0 0 0 0 0 0
}

3.2.6 vector数据存取

  • at(int idx);           //返回所有idx所指的数据
  • operator[ ];         //返回索引idx所指的数据
  • front();                //返回容器中第一个数据元素
  • back();                //返回容器中最后一个数据元素
void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	
	//[]访问
	for (int i = 0; i < v1.size(); i++) {
		cout << v1[i] << " ";
	}
	cout << endl;

	//at访问
	for (int i = 0; i < v1.size(); i++) {
		cout << v1.at(i) << " ";
	}
	cout << endl;

	cout << v1.front() << endl;    //0
	cout << v1.back() << endl;     //9

}

3.2.7 vector互换容器

  • 实现两个容器内元素进行互换
  • swap(vec);          //将vec与本身的元素互换
void printVector(vector<int>& v) {
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

//1.基本使用
void test01()
{
	vector<int>v1;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	cout << "交换前:" << endl;
	printVector(v1);

	vector<int>v2;
	for (int i = 10; i > 0; i--) {
		v2.push_back(i);
	}
	printVector(v2);


	cout << "交换后:" << endl;
	v1.swap(v2);
	printVector(v1);
	printVector(v2);

	/*
	交换前:
	0 1 2 3 4 5 6 7 8 9
	10 9 8 7 6 5 4 3 2 1
	交换后:
	10 9 8 7 6 5 4 3 2 1
	0 1 2 3 4 5 6 7 8 9
	*/

}

//2.实际用途
//巧用swap可以收缩内存空间
void test02() {
	vector<int>v;
	for (int i = 0; i < 100000; i++) {
		v.push_back(i);
	}
	cout << "v的容量:" << v.capacity() << endl;
	cout << "v的大小:" << v.size() << endl;              //v的容量:138255 	v的大小:100000

	v.resize(3);
	cout << "v的容量:" << v.capacity() << endl;
	cout << "v的大小:" << v.size() << endl;              //v的容量:138255   	v的大小:3

	//巧用swap收缩内存
	vector<int>(v).swap(v);
	cout << "v的容量:" << v.capacity() << endl;
	cout << "v的大小:" << v.size() << endl;              //v的容量:3   	v的大小:3

}

3.2.8 vector预留空间

  • 减少vector在动态扩展容量时的扩展次数
  • reserve(int len);         //容量预留len个元素长度,预留位置不初始化,元素不可访问
void test01()
{
	vector<int>v1;

	//利用reserve预留空间,就不必多次开辟了
	v1.reserve(100000);        //num=1

	int num = 0;      //统计开辟次数
	int* p = NULL;
	for (int i = 0; i < 100000; i++)
	{
		v1.push_back(i);

		if (p != &v1[0]) {
			p = &v1[0];
			num++;       //num=30
		}
	}
	cout << "num = " << num << endl;
}

3.3 deque容器

3.3.1 deque容器基本概念 

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

deque和vector区别:

  • vector对于头部的插入删除效率低,数据量越大,效率越低(因为头插,要移动后面数据)
  • deque相对而言,对头部的插入删除速度会比vector快
  • vector访问元素时的速度会比deque快,这和两者内部实现有关

deque内部工作原理:

  • deque内部有个中控器 ,维护每段缓冲区中的内容,缓冲区中存放真实数据
  • 中控器维护的是每个缓冲区的地址,使得使用deque时像一片连续的内存空间

  •  deque容器的迭代器也是支持随机访问

3.3.2 deque构造函数

  • deque<T>deqT;
  • deque(beg,end);
  • deque(n,elem);
  • deque(const deque &deq);
void printDeque(deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int>d2(d1.begin(), d1.end());
	printDeque(d2);

	deque<int>d3(3, 6);
	printDeque(d3);

	deque<int>d4(d3);
	printDeque(d4);

	/*
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	6 6 6
	6 6 6
	*/

3.3.3 deque赋值操作

  • deque& operator=(const deque &deq);
  • assign(beg,end);
  • assign(n,elem);
void printDeque(deque<int>& d) 
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	deque<int>d2;
	d2 = d1;
	printDeque(d2);

	deque<int>d3;
	d3.assign(d2.begin(), d2.end());
	printDeque(d3);

	deque<int>d4;
	d4.assign(3, 6);
	printDeque(d4);

	/*
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	0 1 2 3 4 5 6 7 8 9
	6 6 6
	*/
}

3.3.4 deque大小操作

  • deque.empty();
  • deque.size();
  • deque.resize(num);
  • deque.resize(num,elem);
void test01()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);

	if (d1.empty()) {
		cout << "空" << endl;
	}
	else
	{
		cout << "不为空" << endl;
		cout << "d1大小:" << d1.size() << endl;
	}

/*	d1.resize(15);
	printDeque(d1);  */   //0 1 2 3 4 5 6 7 8 9 0 0 0 0 0

	d1.resize(15,6);   //0 1 2 3 4 5 6 7 8 9 6 6 6 6 6
	printDeque(d1);
}

3.3.5 deque插入和删除

  • push_back(elem);        //尾插
  • push_front(elem);         //头插
  • pop_back();                  //尾删
  • pop_front();                   //头删

 

  • insert(pos,elem);                //会返回新数据位置
  • insert(pos,n,elem);              //无返回值
  • insert(pos,beg,end);            //无返回值
  • clear();
  • erase(beg,end);                  //会返回下一个数据位置
  • erase(pos);                         //会返回下一个数据位置
void printDeque(deque<int>& d)
{
	for (deque<int>::const_iterator it = d.begin(); it != d.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01()
{
	deque<int>d1;
	for (int i = 0; i < 10; i++) {
		d1.push_back(i);
	}
	printDeque(d1);               //0 1 2 3 4 5 6 7 8 9 

	d1.push_back(10);
	printDeque(d1);              //0 1 2 3 4 5 6 7 8 9 10

	d1.push_front(-1);
	printDeque(d1);             //-1 0 1 2 3 4 5 6 7 8 9 10

	d1.pop_back();
	printDeque(d1);             //-1 0 1 2 3 4 5 6 7 8 9

	d1.pop_front();
	printDeque(d1);            //0 1 2 3 4 5 6 7 8 9

}

void test02() {

	deque<int>d1;
	for (int i = 0; i < 5; i++) {
		d1.push_back(i);
	}
	printDeque(d1);           //0 1 2 3 4 

	d1.insert(d1.begin(), -1);
	printDeque(d1);               //-1 0 1 2 3 4 

	d1.insert(d1.end(), 3, 6);
	printDeque(d1);                 //-1 0 1 2 3 4 6 6 6

	deque<int>d2;
	d2.push_back(1);
	d2.push_back(2);
	d2.push_back(3);
	d1.insert(d1.begin(), d2.begin(), d2.end());
	printDeque(d1);              //1 2 3 -1 0 1 2 3 4 6 6 6

}

void test03() {

	deque<int>d1;
	for (int i = 0; i < 5; i++) {
		d1.push_back(i);
	}
	printDeque(d1);           //0 1 2 3 4 

	deque<int>::iterator it = d1.begin();
	it+=2;
	d1.erase(it);
	printDeque(d1);          //0 1 3 4

	d1.erase(it, d1.end());
	printDeque(d1);          //0 1

}

3.3.6 deque数据存取

  • at(int idx);
  • operator[ ];
  • front();
  • back();
void test01()
{
	deque<int>d1;
	for (int i = 0; i < 5; i++) {
		d1.push_back(i);
	}
	printDeque(d1);               //0 1 2 3 4 

	cout << d1.at(3) << endl;     //3
	cout << d1[2] << endl;       //2
	cout << d1.front() << endl;   //0
	cout << d1.back() << endl;   //4
}

3.3.7 deque排序

  • sort(iterator beg, iterator end);      //对beg和end区间内元素进行排序
  • 要包含头文件algorithm
  • 默认排序规则:从小到大(升序)
  • 对于支持随机访问的迭代器的容器,都可以利用sort算法直接对其进行排序
  • 如vector容器
void test01()
{
	deque<int>d1;
	d1.push_back(3);
	d1.push_back(4);
	d1.push_back(1);
	d1.push_front(200);
	d1.push_front(100);
	d1.push_front(300);
	printDeque(d1);        //300 100 200 3 4 1         

	sort(d1.begin(), d1.end());
	printDeque(d1);        //1 3 4 100 200 300

}

3.4 案例-评委打分

  • 案例描述:
  • 有五名选手:选手ABCDE,10个评委分别对每一名选手打分,去除最高分,去除最低分,取平均分。
  • 实现步骤:
  • 1.创建五名选手,放到vector中
  • 2.遍历vector容器,取出来每一个选手,执行for循环,可以把10个评分打分到deque容器中
  • sort算法对deque容器中分数排序,去除最高和最低分
  • deque容器遍历一遍,累加总分
  • 获取平均分
#include<iostream>
using namespace std;
#include<string>
#include<vector>
#include<deque>
#include<algorithm>
#include<ctime>

class Person
{
public:
	Person(string name, int store) {
		this->m_Name = name;
		this->m_Store = store;
	}

	string m_Name;
	int m_Store;
};


void createPerson(vector<Person>&v) 
{
	string nameSeed = "ABCDE";
	for (int i = 0; i < 5; i++) {
		string name = "选手";
		name += nameSeed[i];

		int score = 0;

		Person p(name, score);

		v.push_back(p);
	}
}

void setScore(vector<Person>& v)
{
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		//将评委分数放入到deque容器中
		deque<int>d;
		for (int i = 0; i < 10; i++)
		{
			int score = rand() % 41 + 60;       //60~100
			d.push_back(score);
		}

		//去除最高低分,先排序
		sort(d.begin(), d.end());
		d.pop_back();
		d.pop_front();

		//取平均分
		int sum = 0;
		for (deque<int>::iterator dit=d.begin();dit!=d.end();dit++)
		{
			sum += *dit;
		}
		int avg = sum / d.size();

		//将平均分赋值给选手
		it->m_Store = avg;
	}
}

void showScore(vector<Person>&v) {
	for (vector<Person>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name 
			<< " 分数:" << it->m_Store << endl;
	}
}

int main()
{
	//随机数种子
	srand((unsigned int)time(NULL));

	//1.创建5名选手
	vector<Person>v;       //存放选手容器
	createPerson(v);

	//测试
	//for (vector<Person>::iterator it = v.begin(); it != v.end(); it++) {

	//	cout << "姓名:" << (*it).m_Name << " 分数:" << (*it).m_Store << endl;

	//}

	//2.给5名选手打分
	setScore(v);


	//3.显示最后得分
	showScore(v);


	system("pause");
	return 0;
}
/*
姓名:选手A 分数:78
姓名:选手B 分数:82
姓名:选手C 分数:74
姓名:选手D 分数:80
姓名:选手E 分数:77
*/

3.5 stack容器

3.5.1 stack基本概念

  • stack是一种先进后出的数据结构,它只有一个出口
  • 不允许有遍历的行为,只有栈顶元素可以被外界访问到

3.5.2 stack常用接口

  • 构造函数
  • stack<T> stk;
  • stack(const stack &stk);
  • 赋值操作
  • stack& operator=(const stack &stk);
  • 数据存取
  • push(elem);
  • pop();
  • top();
  • 大小操作
  • empty();
  • size();
void test01()
{
	stack<int>s;
	s.push(1);
	s.push(2);
	s.push(3);
	s.push(4);

	//只要栈不为空,查看栈顶,并执行出栈操作
	while (!s.empty()) {
		cout << "栈顶元素:" << s.top() << endl;

		s.pop();
	}
	cout << "栈大小:" << s.size() << endl;

}
/*
栈顶元素:4
栈顶元素:3
栈顶元素:2
栈顶元素:1
栈大小:0
*/

3.6 queue容器

3.6.1 queue基本概念

  • Queue是一种先进先出的数据接口,它有两个出口 

3.6.2 queue常用接口

  • 构造函数
  • queue<T>que;
  • queue(const queue &que);
  • 赋值操作
  • queue& operator=(const queue &que);
  • 数据存取
  • push(elem);
  • pop();
  • back();
  • front();
  • 大小操作
  • empty();
  • size();
class Person
{
public:
	Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};
void test01()
{
	queue<Person>q;
	
	Person p1("一", 1);
	Person p2("二", 2);
	Person p3("三", 3);
	Person p4("四", 4);

	q.push(p1);
	q.push(p2);
	q.push(p3);
	q.push(p4);

	//判断只要队列不为空,查看队头,查看队尾,出队
	while (!q.empty()) {
		cout << "队头-姓名:" << q.front().m_Name
			<< "  队头-年龄:" << q.front().m_Age
			<< "  队尾-姓名:" << q.back().m_Name 
			<<"  队尾-年龄:"<<q.back().m_Age << endl;
		q.pop();
	}
}

/*
队头-姓名:一  队头-年龄:1  队尾-姓名:四  队尾-年龄:4
队头-姓名:二  队头-年龄:2  队尾-姓名:四  队尾-年龄:4
队头-姓名:三  队头-年龄:3  队尾-姓名:四  队尾-年龄:4
队头-姓名:四  队头-年龄:4  队尾-姓名:四  队尾-年龄:4
*/

3.7 list容器

3.7.1 list基本概念 

  • 将数据进行链式存储
  • 链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
  • 链表的组成:链表由一系列结点组成
  • 结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
  • STL中的链表是一个双向循环链表

  • 由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器 
  • list优点:
  1. 采用动态存储分配,不会造成内存浪费和溢出
  2. 链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
  • list缺点
  1. 链表灵活,但是空间(指针域)和时间(遍历)额外消耗较大

list有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。

3.7.2 list构造函数

  • list<T>lst;
  • list(beg,end);
  • list(n,elem);
  • list(const list &lst);
void printList(list<int>& l) {
	for (list<int>::const_iterator it = l.begin(); it != l.end(); it++) {
		cout << *it << " ";
	}
	cout << endl;
}

void test01() {
	list<int>l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);

	printList(l1);

	list<int>l2(l1.begin(), l1.end());
	printList(l2);

	list<int>l3(3, 6);
	printList(l3);

	list<int>l4(l3);
	printList(l4);

}

3.7.3 list赋值和交换

  • assign(beg,end);
  • assign(n,elem);
  • list& operator=(const list &list);
  • swap(lst);
//赋值
void test01() {
	list<int>l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);
	printList(l1);

	list<int>l2;
	l2.assign(l1.begin(), l1.end());
	printList(l2);

	list<int>l3;
	l3.assign(3, 6);
	printList(l3);

	list<int>l4;
	l4 = l3;
	printList(l4);

}

//交换
void test02()
{
	list<int>l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
	l1.push_back(4);
	printList(l1);

	list<int>l2;
	l2.assign(3, 8);
	cout << "交换前:" << endl;
	cout << "l1 = " << endl;
	printList(l1);
	cout << "l2 = " << endl;
	printList(l2);

	l2.swap(l1);
	cout << "交换后:" << endl;
	cout << "l1 = " << endl;
	printList(l1);
	cout << "l2 = " << endl;
	printList(l2);
}

3.7.4 list大小操作

  • size();
  • empty();
  • resize(num);
  • resize(num,elem);

3.7.5 list插入和删除

  • push_back(elem);        //尾插
  • pop_back();          //尾删
  • push_front(elem);        //头插
  • pop_front();             //头删

 

  • insert(pos,elem);
  • insert(pos,n,elem);
  • insert(pos,beg,end);

 

  • clear();
  • erase(beg,end);
  • erase(pos);
  • remove(elem);            //删除匹配elem的所有值

3.7.6 list数据存取

  • front();
  • back();
  • 不可以用 [ ] / at 访问list容器中的元素
  • 原因:list本质是链表,不是用连续线性空间存储数据,迭代器也是不支持随机访问的
  • 如  it+1  /  it+2   …  (属于跳跃式的,不支持随机访问) ,  但可以  it++  (支持双向)

3.7.7 list反转和排序

  • reverse();
  • sort();       //默认升序
  • 所有不支持随机访问迭代器的容器,不可以用标准算法
  • 如 sort( l1.begin( ) , l1.end( ) )  错误        l1.sort()     正确
void test01() {
	list<int>l1;
	l1.push_back(10);
	l1.push_back(30);
	l1.push_back(20);
	l1.push_back(0);
	printList(l1);            //10 30 20 0

	l1.reverse();
	cout << "反转后 :";
	printList(l1);           //反转后 :0 20 30 10

	l1.sort();
	printList(l1);          //0 10 20 30
}
bool myCompare(int v1, int v2)
{
	return v1 > v2;
}
l1.sort(myCompare);        //降序

3.7.8 排序案例

  • 案例描述:将Person自定义数据类型进行排序,Person中属性有姓名、年龄、身高
  • 排序规则:按照年龄进行升序,如果年龄相同按照身高进行降序
class Person
{
public:
	Person(string name, int age, int height)
	{
		this->m_Name = name;
		this->m_Age = age;
		this->m_Height = height;
	}

	string m_Name;
	int m_Age;
	int m_Height;
};

//指定排序规则
bool comparePerson(Person& p1, Person& p2) 
{
	//按照年龄升序
	if (p1.m_Age == p2.m_Age) {
		return p1.m_Height > p2.m_Height;
	}
    else
	    return p1.m_Age < p2.m_Age;

}
void test01()
{

	list<Person>L;
	Person p1("一", 35, 175);
	Person p2("二", 45, 180);
	Person p3("三", 40, 170);
	Person p4("四", 25, 190);
	Person p5("五", 35, 160);
	Person p6("六", 35, 200);

	L.push_back(p1);
	L.push_back(p2);
	L.push_back(p3);
	L.push_back(p4);
	L.push_back(p5);
	L.push_back(p6);

	for (list<Person>::iterator it = L.begin(); it != L.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name << "  年龄:" << (*it).m_Age << "  身高:" << (*it).m_Height << endl;
	}

	cout << "-------------------------------------------" << endl;
	cout << "排序后:" << endl;

	L.sort(comparePerson);
	for (list<Person>::iterator it = L.begin(); it != L.end(); it++)
	{
		cout << "姓名:" << (*it).m_Name << "  年龄:" << (*it).m_Age << "  身高:" << (*it).m_Height << endl;
	}
}

3.8 set / multiset 容器

3.8.1 set基本概念

  • 所有元素都会在插入时自动被排序
  • set/multiset属于关联式容器,底层结构是用二叉树实现

set和multiset的区别:

  • set不允许容器中有重复的元素
  • multiset允许容器中有重复的元素

3.8.2 set构造和赋值

  • set<T>st;
  • set(const set &st);
  • set& operator=(const set &st);

3.8.3 set大小和交换

  • size();
  • empty();
  • swap(st);

3.8.4 set插入和删除

  • insert(elem);
  • clear();
  • erase(pos);        //删除的是排序后 该位置的结果
  • erase(beg,end);
  • erase(elem);

3.8.5 set查找和统计

  • find(key);       //查找key是否存在,存在返回该元素的迭代器,不存在返回set.end()
  • count(key);    //统计key元素个数
  • 对set而言,统计的结果要么是0要么是1
void test01() 
{

	set<int>s1;
	s1.insert(1);
	s1.insert(5);
	s1.insert(4);
	s1.insert(2);
	s1.insert(3);
	s1.insert(4);


	set<int>::iterator pos=s1.find(2);
	if (pos != s1.end())
	{
		cout << "找到了,元素为:" << *pos << endl;           //找到了,元素为:2
	}
	else
		cout << "未找到" << endl;

	int num = s1.count(4);
	cout << "num = " << num << endl;        //num = 1

}

3.8.6 set和multiset区别

  • set不可以插入重复数据,而multiset可以
  • set插入数据的同时会返回插入结果,表示插入是否成功
  • multiset不会监测数据,因此可以插入重复数据

3.8.7 pair对组创建

  • 成对出现的数据,利用队组可以返回两个数据
  • pair<type, type> p ( value1, value2 );
  • pair<type, type> p = make_pair ( value1, value2 );
	pair<string, int>p("Tom", 15);
	cout << "姓名:" << p.first << "  年龄:" << p.second << endl;      //姓名:Tom  年龄:15

	pair<string, int>p2 = make_pair("Jerry", 40);
	cout << "姓名:" << p2.first << "  年龄:" << p2.second << endl;      //姓名:Jerry  年龄:40

3.8.8 set容器排序

  • 利用仿函数,改变排序规则
  • 1.set存放内置数据类型
//set容器排序
class myCompare
{
public:
	bool operator()(int v1, int v2)const   //重载()
	{
		return v1 > v2;
	}
};

void test01() 
{
	set<int>s1;
	s1.insert(10);
	s1.insert(40);
	s1.insert(20);
	s1.insert(50);
	s1.insert(30);

	for (set<int>::iterator it = s1.begin(); it != s1.end(); it++)
	{
		cout << *it << " ";         //10 20 30 40 50
	}
	cout << endl;

	//指定排序规则为从大到小
	set<int,myCompare>s2;
	s2.insert(10);
	s2.insert(40);
	s2.insert(20);
	s2.insert(50);
	s2.insert(30);

	for (set<int,myCompare>::iterator it = s2.begin(); it != s2.end(); it++)
	{
		cout << *it << " ";         //50 40 30 20 10
	}
	cout << endl;

}

2.set存放自定义数据类型

class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	
	string m_Name;
	int m_Age;
};

class myCompare
{
public:
	bool operator()(const Person& p1, const Person& p2)const //重载()  //此处const不加会报错
	{
		return p1.m_Age > p2.m_Age;
	}
};

void test01() 
{
	set<Person,myCompare>s;
	Person p1("一", 24);
	Person p2("一", 28);
	Person p3("一", 25);
	Person p4("一", 21);

	s.insert(p1);
	s.insert(p2);
	s.insert(p3);
	s.insert(p4);

	for (set<Person,myCompare>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << "姓名:" << it->m_Name << "  年龄:" << it->m_Age << endl;
	}
}

/*
姓名:一  年龄:28
姓名:一  年龄:25
姓名:一  年龄:24
姓名:一  年龄:21
*/

3.9 map / multimap 容器

3.9.1 map基本概念

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

本质:

  • map/multipair属于关联式容器,底层结构是用二叉树实现

优点:

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

map和multimap的区别:

  • map不允许容器中有重复key值元素
  • multimap允许容器中有重复key值元素

 3.9.2 map构造和赋值

  • map<T1,T2>mp;
  • map(const map &mp);
  • map& operator=(const map &mp);
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>(3, 30));
	m.insert(pair<int, int>(2, 20));
	m.insert(pair<int, int>(4, 40));

	printMap(m);      
	/* 自动排序了
	key = 1 value = 10
	key = 2 value = 20
	key = 3 value = 30
	key = 4 value = 40
	*/

	map<int, int>m2(m);
	printMap(m2);

	map<int, int>m3;
	m3 = m2;
	printMap(m3);

}

3.9.3 map大小和交换

  • size();
  • empty();
  • swap(mp);

3.9.4 map插入和删除

  • insert(elem);
  • clear();
  • erase(pos);
  • erase(beg,end);
  • erase(key);

3.9.5 map查找和统计

  • find();
  • count(key);

3.9.6 map容器排序

class myCompare
{
public:
	bool operator()(int v1, int v2)const
	{
		return v1 > v2;
	}

};

void test01()
{
	map<int, int,myCompare>m;
	m.insert(pair<int, int>(1, 10));
	m.insert(pair<int, int>(4, 40));
	m.insert(make_pair(3, 30));
	m.insert(make_pair(2, 20));
	m.insert(make_pair(5, 50));

	for (map<int, int,myCompare>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "key = " << it->first << "  value = " << it->second << endl;
	}
	cout << endl;

}
//自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}
	string m_Name;
	int m_Age;
};

class myCompare
{
public:
	bool operator()(const Person& p1, const Person& p2)const
	{
		return p1.m_Age > p2.m_Age;
	}

};


void test01()
{
	map<Person,int,myCompare>m;

	Person p1("一",10);
	Person p2("二", 20);
	Person p3("三", 30);

	m.insert(make_pair(p1, 10));
	m.insert(make_pair(p3, 30));
	m.insert(make_pair(p2, 20));

	for (map<Person,int,myCompare>::iterator it = m.begin(); it != m.end(); it++)
	{
		cout << "  name = " << it->first.m_Name << "  age = " << it->first.m_Age << "  value = " << it->second << endl;
	}
	cout << endl;

}

/*
  name = 三  age = 30  value = 30
  name = 二  age = 20  value = 20
  name = 一  age = 10  value = 10
*/

3.10 案例-员工分组

  • 公司招聘10个员工(A~J),需指派他们的部门
  • 员工信息有:姓名 工资组成  部门:策划、美术、研发
  • 随机给10名员工分配部门和工资
  • 通过multimap进行信息的插入 key(部门编号)value(员工)
  • 分部门显示员工信息
#include<iostream>
using namespace std;
#include<map>
#include<ctime>
#include<vector>
#include<string>

#define CEHUA 0
#define MEISHU 1
#define YANFA 2

class Worker
{
public:
	string m_Name;
	int m_Salary;
};

void createWorker(vector<Worker>& v)
{
	string nameSeed = "ABCDEFGHIJ";
	for (int i = 0; i < 10; i++)
	{
		Worker worker;
		worker.m_Name = "员工";
		worker.m_Name += nameSeed[i];

		worker.m_Salary = rand() % 10000 + 10000;       //10000~19999

		v.push_back(worker);
	}
}

void setGroup(vector<Worker>& v, multimap<int, Worker>& m)
{
	for (vector<Worker>::iterator it = v.begin(); it != v.end(); it++)
	{
		int deptId = rand() % 3;    //0 1 2
		m.insert(make_pair(deptId, *it));
	}
}

void showWorkerByGroup(multimap<int, Worker>& m)
{
	cout << "策划部门:" << endl;
	multimap<int, Worker>::iterator pos = m.find(CEHUA);
	int count = m.count(CEHUA);
	int index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名:" << pos->second.m_Name << "  薪资:" << pos->second.m_Salary << endl;
	}

	cout << "美术部门:" << endl;
	pos = m.find(MEISHU);
	count = m.count(MEISHU);
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名:" << pos->second.m_Name << "  薪资:" << pos->second.m_Salary << endl;
	}

	cout << "研发部门:" << endl;
	pos = m.find(YANFA);
	count = m.count(YANFA);
	index = 0;
	for (; pos != m.end() && index < count; pos++, index++)
	{
		cout << "姓名:" << pos->second.m_Name << "  薪资:" << pos->second.m_Salary << endl;
	}
}

/*
策划部门:
姓名:员工D  薪资:16500
姓名:员工I  薪资:16962
姓名:员工J  薪资:14464
美术部门:
姓名:员工C  薪资:16334
姓名:员工E  薪资:19169
姓名:员工G  薪资:11478
研发部门:
姓名:员工A  薪资:10041
姓名:员工B  薪资:18467
姓名:员工F  薪资:15724
姓名:员工H  薪资:19358
*/

int main()
{
	srand((unsigned int)time(NULL));

	vector<Worker>vWorker;
	createWorker(vWorker);

	multimap<int, Worker>mWorker;
	setGroup(vWorker, mWorker);
	//for (vector<Worker>::iterator it = vWorker.begin(); it != vWorker.end(); it++)
	//{
	//	cout << "姓名:" << it->m_Name << "  薪资:" << it->m_Salary << endl;
	//}
	showWorkerByGroup(mWorker);

	system("pause");
	return 0;
}

4.STL-函数对象

4.1 函数对象

4.1.1 函数对象概念

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

4.1.2 函数对象使用

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

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

class MyPrint
{
public:
	MyPrint()
	{
		this->count = 0;
	}
	void operator()(string test)
	{
		cout << test << endl;
		this->count++;
	}
	int count;    //内部自己状态  //函数对象超出普通函数的概念,函数对象可以有自己状态
};

void test02()
{
	MyPrint myprint;
	myprint("哈哈哈哈哈哈哈哈哈");     //哈哈哈哈哈哈哈哈哈
	myprint("哈哈哈哈哈哈哈哈哈");     //哈哈哈哈哈哈哈哈哈
	myprint("哈哈哈哈哈哈哈哈哈");     //哈哈哈哈哈哈哈哈哈
	cout << myprint.count << endl;     //3
}


void doPrint(MyPrint& mp, string test)
{
	mp(test);    //函数对象可以作为参数传递
}
void test03()
{
	MyPrint myPrint;
	doPrint(myPrint, "哈哈哈");
}

4.2 谓词

4.2.1 谓词概念

  • 返回bool类型的仿函数称为谓词
  • 如果operator()接受一个参数,即一元谓词
  • 如果operator()接受两个参数,即二元谓词 

4.2.2 一元谓词

//一元谓词
class GreaterFive
{
public:
	bool operator()(int val)
	{
		return val > 5;
	}
};
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i);
	}
	//查找容器中有没有>5的数字
	//GreaterFive()匿名的函数对象
	vector<int>::iterator it=find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end())
	{
		cout << "没有找到" << endl;
	}
	else
		cout << "找到了" << *it << endl;  //找到了6
}

4.2.3 二元谓词

//二元谓词

class MyCompare
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void test01()
{
	vector<int>v;
	v.push_back(10);
	v.push_back(40);
	v.push_back(20);
	v.push_back(30);
	v.push_back(50);

	sort(v.begin(),v.end());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";     //10 20 30 40 50
	}
	cout << endl;

	//使用函数对象 改变算法策略 改为从大到小
	sort(v.begin(), v.end(),MyCompare());
	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";     //50 40 30 20 10
	}
	cout << endl;
}

4.3 内建函数对象

4.3.1 内建函数对象意义

  • STL内建了一些函数对象
  • 这些仿函数所产生的对象,用法和一般函数完全相同
  • 使用内建函数对象,需要引入头文件 #include<functional>

4.3.2 算数仿函数

  • 实现四则运算
  • 其中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>        //取反
void test01()
{
	//negate 一元仿函数 取反仿函数
	negate<int>n;
	cout << n(50) << endl;    //-50

	//plus 二元仿函数 加法
	plus<int>p;
	cout << p(10, 20) << endl;    //30

}

4.3.3 关系仿函数

  • template<class T> bool equal_to<T>                  //等于
  • template<class T> bool not_equal_to<T>           //不等于
  • template<class T> bool greater<T>                    //大于
  • template<class T> bool greater_equal<T>         //大于等于
  • template<class T> bool less<T>                         //小于
  • template<class T> bool less_equal<T>              //小于等于
class myCompare
{
public:
	bool operator()(int v1, int v2)
	{
		return v1 > v2;
	}
};

void test01()
{
	greater<int>n;
	cout << n(10,20) << endl;    //0


	vector<int>v;
	v.push_back(10);
	v.push_back(30);
	v.push_back(60);
	v.push_back(40);
	v.push_back(20);
	v.push_back(50);

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;          //10 30 60 40 20 50

	//sort(v.begin(), v.end(),myCompare());
	sort(v.begin(), v.end(),greater<int>());

	for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";
	}
	cout << endl;                   //60 50 40 30 20 10
}

4.3.4 逻辑仿函数

  • 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);


	for (vector<bool>::iterator it = v.begin(); it != v.end(); it++)
	{
		cout << *it << " ";        //1 0 1 0
	}
	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 << " ";        //0 1 0 1
	}
	cout << endl;


}

5.STL-常用算法

  • 算法主要是由头文件<algorithm><functional><numeric>组成
  • <algorithm>是所有STL头文件中最大的一个,范围涉及到比较、交换、查找、遍历操作、复制、修改等等
  • <functional>定义了一些模板类,用以声明函数对象
  • <numeric>体积很小,只包括几个在序列上面进行简单数学运算的模板函数

5.1 常用遍历算法

5.1.1 for_each(遍历)

  • for_each(iterator beg, iterator end, _func);
//普通函数
void print01(int val)
{
	cout << val << " ";
}


//仿函数
class print02
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i);
	}
	for_each(v.begin(), v.end(), print01);     //0 1 2 3 4 5 6 7 8 9
	cout << endl;

	for_each(v.begin(), v.end(), print02());   //0 1 2 3 4 5 6 7 8 9

}

5.1.2 transform(搬运容器到另一个容器中)

  • transform(iterator beg1, iterator end1, iterator beg2, _func);
  • beg1 源容器开始迭代器       end1 源容器结束迭代器
  • beg2  目标容器开始迭代器   _func  函数或者函数对象
class Transform
{
public:
	int operator()(int v)
	{
		return v + 100;      
	}
};

class MyPrint
{
public:
	void operator()(int val)
	{
		cout << val << " ";
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; i < 10 ; i++) {
		v.push_back(i);
	}

	vector<int>vTarget;
	vTarget.resize(v.size());
	transform(v.begin(), v.end(), vTarget.begin(), Transform());

	for_each(vTarget.begin(), vTarget.end(), MyPrint());
	cout << endl;  //100 101 102 103 104 105 106 107 108 109
}

5.2 常用查找算法

  • find
  • find_if                                //按条件查找元素
  • adjacent_find                    //查找相邻重复元素
  • binary_search                   //二分查找法
  • count                                 //统计元素个数
  • count_if                             //按条件统计元素个数

5.2.1 find

  • find(iterator beg, iterator end, value);
//查找内置数据类型
void test01()
{
	vector<int>v;
	for (int i = 0; i < 20; i++) {
		v.push_back(i);
	}
	vector<int>::iterator it = find(v.begin(), v.end(), 5);
	if (it == v.end()) {
		cout << "未找到" << endl;
	}
	else {
		cout << "找到了:" << *it << endl;        //找到了:5
	}
}

class Person
{
public:
	Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	bool operator==(const Person& p) {
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		else false;
	}

	string m_Name;
	int m_Age;
};

//查找自定义数据类型
void test02()
{
	vector<Person>v;
	Person p1("一", 10);
	Person p2("二", 20);
	Person p3("三", 30);
	Person p4("四", 40);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	vector<Person>::iterator it = find(v.begin(), v.end(), p2);
	if (it == v.end()) {
		cout << "没找到" << endl;
	}
	else {
		cout << "找到了:" << it->m_Name << it->m_Age << endl;        //找到了:二20
	}

}

5.2.2 find_if

  • find_if(iterator beg, iterator end, _Pred);  //Pred 函数或者谓词(返回bool的仿函数
//查找内置数据类型
class GreaterFive
{
public:
	bool operator()(int val) {
		return val > 5;
	}
};

void test01()
{
	vector<int>v;
	for (int i = 0; i < 20; i++) {
		v.push_back(i);
	}
	vector<int>::iterator it = find_if(v.begin(), v.end(), GreaterFive());
	if (it == v.end()) {
		cout << "未找到" << endl;
	}
	else {
		cout << "找到了:" << *it << endl;        //找到了:6
	}
}



//查找自定义数据类型
class Person
{
public:
	Person(string name, int age) {
		this->m_Name = name;
		this->m_Age = age;
	}

	bool operator==(const Person& p) {
		if (this->m_Name == p.m_Name && this->m_Age == p.m_Age)
		{
			return true;
		}
		else false;
	}

	string m_Name;
	int m_Age;
};

class GreaterTwenty
{
public:
	bool operator()(Person& p) {
		return p.m_Age > 20;
	}
};
void test02()
{
	vector<Person>v;
	Person p1("一", 10);
	Person p2("二", 20);
	Person p3("三", 30);
	Person p4("四", 40);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	vector<Person>::iterator it = find_if(v.begin(), v.end(), GreaterTwenty());
	if (it == v.end()) {
		cout << "没找到" << endl;
	}
	else {
		cout << "找到了:" << it->m_Name << it->m_Age << endl;        //找到了:三30
	}
}
  • adjacent_find(iterator beg, iterator end);        //查找相邻重复元素
void test01()
{
	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 << "未找到" << endl;
	}
	else {
		cout << "找到了:" << *pos << endl;        //找到了:3
	}
}
  • bool binary_search(iterator beg, iterator end, value);    //查找指定的元素
  • 注意:在无序序列中不可用
void test01()
{
	vector<int>v;
	for (int i = 0; i < 10; i++)
	{
		v.push_back(i);
	}
	//查找容器中是否有9
	bool ret = binary_search(v.begin(), v.end(), 9);
	if (ret)
	{
		cout << "找到" << endl;     //找到
	}
	else
		cout << "mei找到" << endl;
}

5.2.5 count

  • count ( iterator beg, iterator end, value );         //统计出现的次数
//内置数据类型
void test01()
{
	vector<int>v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(1);
	v.push_back(3);
	v.push_back(1);
	v.push_back(1);
	cout << count(v.begin(), v.end(), 1) << endl;      //4

}

//自定义数据类型
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("一", 1);
	Person p2("二", 3);
	Person p3("三", 2);
	Person p4("四", 1);
	Person p5("五", 1);
	Person p6("七", 7);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);
	v.push_back(p6);
	cout << count(v.begin(), v.end(), p5) << endl;   //3
}

5.2.6 count_if

  • count_if ( iteratorbeg, iterator end, _Pred ) ;
//内置数据类型
class Greater20
{
public:
	bool operator()(int val) {
		return val > 20;
	}
};

void test01()
{
	vector<int>v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(40);
	v.push_back(30);
	v.push_back(50);

	cout << count_if(v.begin(), v.end(), Greater20()) << endl;     //3
}

//自定义数据类型
class Person
{
public:
	Person(string name, int age)
	{
		this->m_Name = name;
		this->m_Age = age;
	}

	string m_Name;
	int m_Age;
};

class GreaterTwenty
{
public:
	bool operator()(const Person& p)
	{
		return p.m_Age > 20;
	}
};
void test02()
{
	vector<Person>v;
	Person p1("aaa", 10);
	Person p2("bbb", 20);
	Person p3("ccc", 30);
	Person p4("ddd", 40);
	Person p5("eee", 50);
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	v.push_back(p5);

	cout << count_if(v.begin(), v.end(), GreaterTwenty());      //3

}

5.3 常用排序算法

5.3.1 sort

  • sort( iterator begin, iterator end, _Pred);
void myPrint(int val) {

	cout << val << " ";

}

class myCompare
{
public:
	bool operator()(int v1,int v2) {
		return v1 > v2;
	}
};
void test01()
{
	vector<int>v;
	v.push_back(10);
	v.push_back(20);
	v.push_back(40);
	v.push_back(30);
	v.push_back(50);
	sort(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);  //10 20 30 40 50
	cout << endl;

	//改变为降序
	//sort(v.begin(), v.end(), myCompare());
	sort(v.begin(), v.end(), greater<int>());
	for_each(v.begin(), v.end(), myPrint);  //50 40 30 20 10
}

5.3.2 random_shuffle 

  • 洗牌 指定范围内的元素随机调整次序
  • random_shuffle ( iterator beg, iterator end );
void myPrint(int val) {

	cout << val << " ";

}

void test01()
{
	srand((unsigned int)time(NULL));
	vector<int>v;
	for (int i = 0; i < 10; i++) {
		v.push_back(i);
	}
	for_each(v.begin(), v.end(), myPrint);  //0 1 2 3 4 5 6 7 8 9
	cout << endl;
	random_shuffle(v.begin(), v.end());
	for_each(v.begin(), v.end(), myPrint);  //8 1 9 2 0 5 7 3 4 
}

5.3.3 merge

  • 容器元素合并 并存储到另一容器中
  • merge ( iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest );
  • dest:目标容器开始迭代器 
  • 注意v1,v2顺序一致,才能做合并
void myPrint(int val) {

	cout << val << " ";
}

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);  //0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 10
}

5.3.4 reverse

  • reverse ( iterator beg, iterator end ) ;

5.4 常用拷贝和替换算法

5.4.1 copy

  • 容器内指定范围的元素拷贝到另一容器中
  • copy ( iterator beg, iterator end, iterator dest ) ;
void myPrint(int val) {

	cout << val << " ";
}

void test01()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
	}
	v2.resize(v1.size());
	copy(v1.begin(), v1.end(), v2.begin());
	for_each(v2.begin(), v2.end(), myPrint);
}

5.4.2 replace

  • 将容器内指定范围的旧元素修改为新元素
  • replace ( iterator beg, iterator end, oldvalue, newvalue ) ;
void myPrint(int val) {

	cout << val << " ";
}

void test01()
{
	vector<int>v1;
	v1.push_back(10);
	v1.push_back(20);
	v1.push_back(20);
	v1.push_back(30);
	v1.push_back(40);
	v1.push_back(10);
	for_each(v1.begin(), v1.end(), myPrint);
	cout << endl;         //10 20 20 30 40 10

	replace(v1.begin(), v1.end(), 20, 0);
	for_each(v1.begin(), v1.end(), myPrint);      //10 0 0 30 40 10
}

5.4.3 replace_if

  • 容器内指定范围满足条件的元素替换为新元素
  • replace_if ( iterator beg, iterator end, _pred, newplace ) ;
void myPrint(int val) {

	cout << val << " ";
}

class Greater30
{
public:
	bool operator()(int val) {
		return val > 30;
	}
};

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);
	for_each(v1.begin(), v1.end(), myPrint);       //10 20 30 40 50 60
	cout << endl;       

	replace_if(v1.begin(), v1.end(), Greater30(), 0);
	for_each(v1.begin(), v1.end(), myPrint);      //10 20 30 0 0 0
}

5.4.4 swap

  • 互换两个容器的元素
  • swap ( container c1, container c2 ) ;
class MyPrint
{
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
		v2.push_back(i + 2);
	}

	cout << "v1:" << endl;
	for_each(v1.begin(), v1.end(), MyPrint());
	cout << endl;
	cout << "v2:" << endl;
	for_each(v2.begin(), v2.end(), MyPrint());

	cout << endl;
	cout << "互换后 ---------------------- " << endl;
	swap(v1, v2);

	cout << "v1:" << endl;
	for_each(v1.begin(), v1.end(), MyPrint());
	cout << endl;
	cout << "v2:" << endl;
	for_each(v2.begin(), v2.end(), MyPrint());

}

/*
v1:
0 1 2 3 4 5 6 7 8 9
v2:
2 3 4 5 6 7 8 9 10 11
互换后 ----------------------
v1:
2 3 4 5 6 7 8 9 10 11
v2:
0 1 2 3 4 5 6 7 8 9
*/

5.5 常用算数生成算法

  • 包含头文件#include<numeric> 

5.5.1 accumulate

  • accumulate ( iterator beg, iterator end, value); 
    •                                                        //value 起始值:value+(beg,end)内和

5.5.2 fill

  • fill ( iterator beg, iterator end, value);     //将指定区间内填充为value

5.6 常用集合算法

5.6.1 set_intersection  

  • 求交集
class MyPrint
{
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
		v2.push_back(i + 2);
	}
	vector<int>vTarget;
	vTarget.resize(min(v1.size(),v2.size()));
	vector<int>::iterator itEnd=set_intersection(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, MyPrint());      //2 3 4 5 6 7 8 9
}

5.6.2 set_union

  • 求并集
class MyPrint
{
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
		v2.push_back(i + 5);
	}
	vector<int>vTarget;
	vTarget.resize(v1.size()+v2.size());
	vector<int>::iterator itEnd=set_union(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, MyPrint());      //0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
}

5.6.3 set_difference

  • 求差集,且两个集合必须是有序序列
class MyPrint
{
public:
	void operator()(int val) {
		cout << val << " ";
	}
};
void test01()
{
	vector<int>v1;
	vector<int>v2;
	for (int i = 0; i < 10; i++) {
		v1.push_back(i);
		v2.push_back(i + 5);
	}
	vector<int>vTarget;
	vTarget.resize(max(v1.size(), v2.size()));
	vector<int>::iterator itEnd=set_difference(v1.begin(), v1.end(), v2.begin(), v2.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd, MyPrint());      //0 1 2 3 4 

	cout << endl;

	vector<int>::iterator itEnd2 = set_difference(v2.begin(), v2.end(), v1.begin(), v1.end(), vTarget.begin());
	for_each(vTarget.begin(), itEnd2, MyPrint());      //10 11 12 13 14
}

2022.12.15 大三上 完结撒花 跨考顺利

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

biank trrrry

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值