从0到1入门C++编程——09 STL、string容器、vector容器、deque容器

news2024/10/5 12:44:43

文章目录

  • 一、标准模板库STL
  • 二、容器算法迭代器应用
    • 1、遍历容器中整型数据
    • 2、遍历容器中自定义数据类型
    • 3、容器中嵌套容器
  • 三、string容器
    • 1、构造函数
    • 2、赋值操作
    • 3、字符串拼接
    • 4、查找和替换
    • 5、字符串比较
    • 6、字符访问与存取
    • 7、插入和删除
    • 8、子串
  • 四、vector容器
    • 1、构造函数
    • 2、赋值操作
    • 3、容量和大小
    • 4、插入和删除
    • 5、数据存取
    • 6、容器互换
    • 7、预留空间
  • 五、deque容器
    • 1、构造函数
    • 2、赋值操作
    • 3、大小操作
    • 4、插入和删除
    • 5、数据存取
    • 6、排序
  • 六、评委打分案例

一、标准模板库STL

C++面向对象(封装、继承、多态)和泛型编程(模板)的思想,就是提升代码的复用性。大多数情况下,数据结构和算法都未能有一套标准,为了建立这样的标准,出现了STL。
STL全称是Standard Template Library,即标准模板库。
STL从广义上分为容器、算法、迭代器,容器和算法之间通过迭代器无缝衔接。STL几乎所有的代码都采用了模板类或者模板函数。
STL的六大组件:容器、算法、迭代器、仿函数、适配器(配接器)、空间配置器。
容器:各种数据结构,如string、vector、list、deque、set、map等,用来存放数据。
算法:各种常用的算法,如sort、find、copy、for_each等。
迭代器:扮演了容器与算法之间的胶合剂。
仿函数:行为类似函数,可作为算法的某种策略。
适配器:一种用来修饰容器或者仿函数或迭代器接口的东西。
空间配置器:负责空间的配置与管理。
容器就是将运用最广泛的一些数据结构实现出来。常用的数据结构:数组,链表,树,栈,队列,集合,映射表等,这些容器分为序列式容器和关联式容器。序列式容器强调值的排序,序列式容器中的每个元素均有固定的位置。关联式容器基于二叉树结构,各元素之间没有严格的物理上的顺序关系。
算法用于解决逻辑或数学上的问题,其分为质变算法和非质变算法。质变算法是指运算过程中会更改区间内的元素的内容,例如拷贝、替换、删除等。非质变算法是指运算过程中不会更改区间内的元素内容,例如查找、计数、遍历、寻找极值等。
迭代器是容器和算法之间粘合剂,其提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。每个容器都有专属的迭代器,迭代器使用非常类似于指针。

迭代器的种类功能支持运算
输入迭代器对数据的只读访问++、==、!=
输出迭代器对数据的只写访问++
前向迭代器读写操作,能向前推进迭代器++、==、!=
双向迭代器读写操作,能向前向后推进迭代器++、- -
随机访问迭代器读写操作,可以以跳跃的方式访问任意数据++、- -、[n]、-n、<、<=、>、>=

双向迭代器和随机访问迭代器是较为常用的两种。


二、容器算法迭代器应用

1、遍历容器中整型数据

STL中最常用的容器是vector,可以将其理解为数组,下面通过代码向容器中插入数据,并遍历这个容器。
涉及到的容器:vector,算法:for_each,迭代器:vector::iterator。
容器、算法、迭代器的简单应用示例如下。

#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
using namespace std;

void print(int value)  //for_each的回调函数
{
	cout<<value<<endl;
}

void fun()
{
	//1.创建vector容器
	vector<int> v;
	//2.向容器中插入数据
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	//3.通过迭代器遍历容器
	//方法一遍历——while循环
	//vector<int>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<int>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<*it_begin<<endl;  //*it_begin解引用得到的数据类型与容器定义的数据类型一致
	//	it_begin++;
	//}
	//方法二遍历——for循环
	//for(vector<int>::iterator i = v.begin();i!=v.end();i++)
	//{
	//	cout<<*i<<endl;
	//}
	//方法三遍历——需要包含算法头文件和回调函数
	for_each(v.begin(),v.end(),print);
}

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

上面代码中,push_back()函数用于向容器中插入数据,采用的是尾插法,begin()函数用于返回容器中第一个数据的位置,end()函数用于返回容器中最后一个数据的下一个位置。
上面程序的运行结果如下图所示。
在这里插入图片描述

2、遍历容器中自定义数据类型

遍历容器中自定义数据类型的示例如下。

#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
#include <string>
using namespace std;

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

void print(Person value)
{
	cout<<"姓名:"<<value.name<<" 年龄:"<<value.age<<endl;
}

void fun()
{
	Person p1("a",10);
	Person p2("b",20);
	Person p3("c",30);
	Person p4("d",40);
	vector<Person> v;
	v.push_back(p1);
	v.push_back(p2);
	v.push_back(p3);
	v.push_back(p4);
	//方法一遍历——while循环
	//vector<Person>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<Person>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<"姓名:"<<(*it_begin).name<<" 年龄:"<<(*it_begin).age<<endl;  //*it_begin解引用得到的数据类型是Person
	//  cout<<"姓名:"<<it_begin->name<<" 年龄:"<<it_begin->age<<endl;  //也可以通过指针的方式访问成员属性,与上面代码的作用一样
	//	it_begin++;
	//}
	//方法二遍历——for循环
	//for(vector<Person>::iterator i = v.begin();i!=v.end();i++)
	//{
	//	cout<<"姓名:"<<(*i).name<<" 年龄:"<<(*i).age<<endl;
	//}
	//方法三遍历——需要包含算法头文件和回调函数
	for_each(v.begin(),v.end(),print);
}

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

使用自定义数据类型时需要注意的是,在定义容器的时候也要使用自定义的数据类型,通过解引用得到的是自定义的数据类型,但是需要加上括号再访问成员变量,因为.的优先级高于解引用*的优先级。
上面程序的运行结果如下图所示。
在这里插入图片描述
容器中存放自定义数据类型地址的代码示例如下。

#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
#include <string>
using namespace std;

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

void print(Person* value)
{
	cout<<"姓名:"<<value->name<<" 年龄:"<<value->age<<endl;
}

void fun()
{
	Person p1("a",10);
	Person p2("b",20);
	Person p3("c",30);
	Person p4("d",40);
	vector<Person *> v;
	v.push_back(&p1);
	v.push_back(&p2);
	v.push_back(&p3);
	v.push_back(&p4);
	//方法一遍历——while循环
	//vector<Person *>::iterator it_begin = v.begin();  //起始迭代器,指向容器中的第一个元素
	//vector<Person *>::iterator it_end = v.end();  //结束迭代器,指向容器中最后一个元素的下一个位置
	//while(it_begin != it_end)
	//{
	//	cout<<"地址:"<<&(*it_begin)<<endl;
	//	cout<<"姓名:"<<(*it_begin)->name<<" 年龄:"<<(*it_begin)->age<<endl;
	//	it_begin++;
	//}
	//方法二遍历——for循环
	for(vector<Person *>::iterator i = v.begin();i!=v.end();i++)
	{
		cout<<"地址:"<<&(*i)<<endl;
		cout<<"姓名:"<<(*i)->name<<" 年龄:"<<(*i)->age<<endl;
	}
	//方法三遍历——需要包含算法头文件和回调函数
	//for_each(v.begin(),v.end(),print);
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

3、容器中嵌套容器

前面已经说过,容器相当于数组,在一个容器中再嵌套一个容器就相当于一个二维数组。
容器中嵌套容器的代码示例如下。

#include <iostream>
#include <vector>   //包含vector头文件
#include <algorithm>   //包含标准算法头文件
using namespace std;

void fun()
{
	vector<vector<int>> v;  //容器中嵌套容器
	vector<int> v1;  //创建小容器
	vector<int> v2;
	vector<int> v3;
	for(int i=0;i<3;i++)   //给每个小容器插值
	{
		v1.push_back(i+1);
		v2.push_back(i+4);
		v3.push_back(i+7);
	}
	v.push_back(v1);  //给大容器中插入小容器
	v.push_back(v2);
	v.push_back(v3);
	
	for(vector<vector<int>>::iterator i = v.begin();i!=v.end();i++)  //遍历大容器
	{
		//*i此时是<vector<int>类型,还是一个容器,<>中是什么类型,解引用得到的就是什么类型
		for(vector<int>::iterator j = (*i).begin();j!=(*i).end();j++) //遍历小容器
		{
			cout<<*j<<" ";
		}
		cout<<endl;
	}
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述


三、string容器

string是C++风格的字符串,string本质上是一个类,类的内部封装了一个char类型的指针来管理这个字符串,是一个char类型的容器。
string类的内部封装了很多成员方法,比如查找find、拷贝copy、删除delete、替换replace、插入insert等,string管理char*所分配的内存,由类内部负责,因此不用担心复制越界和取值越界的问题。

1、构造函数

string的构造函数类型包括:

string()——创建一个空的字符串,默认的空实现;
string(const char *str)——使用字符串str初始化;
string(const string &str)——使用一个string对象初始化另一个string对象;
string(int n, char c)——使用n个字符c初始化字符串。

代码中分别使用上面的四种方式创建字符串的代码示例如下。

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

void fun()
{
	string str1;
	cout<<"str1="<<str1<<endl;
	const char* str = "hello";
	string str2(str);
	cout<<"str2="<<str2<<endl;
	string str3(str2);
	cout<<"str3="<<str3<<endl;
	string str4(5,'a');
	cout<<"str4="<<str4<<endl;
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

2、赋值操作

string赋值操作包括以下几种。

string& operator=(const char* s);
string& operator=(const string &s);
string& operator=(char c);    //把单个字符赋值给字符串
string& assign(const char* s);   //通过调用assign()函数赋值
string& assign(const char* s, int n);   //把字符串s的前n个字符赋值给当前字符串,注意第一个参数的类型不能是string
string& assign(const string &s);
string& assign(int n, char c);

string的几种赋值操作代码示例如下。

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

void fun()
{
	string str1;
	str1 = "helloworld1";
	cout<<"str1="<<str1<<endl;
	string str2;
	str2 = str1;
	cout<<"str2="<<str2<<endl;
	string str3;
	str3 = 'a';
	cout<<"str3="<<str3<<endl;

	string str4;
	str4.assign("helloworld4");
	cout<<"str4="<<str4<<endl;
	string str5;
	str5.assign("helloworld4",5);
	cout<<"str5="<<str5<<endl;
	string str6;
	str6.assign(str5);
	cout<<"str6="<<str6<<endl;
	string str7;
	str7.assign(5,'a');
	cout<<"str7="<<str7<<endl;
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

3、字符串拼接

string字符串拼接操作主要有以下几种。

string& operator+=(const char* s);
string& operator+=(char c);
string& operator+=(const string &s);
string& append(const char* s);
string& append(const char* s, int n);  
string& append(const string &s);
string& append(const string &s, int pos, int n);  //字符串s中从pos开始的n个字符连接到字符串结尾

string字符串拼接的几种操作代码示例如下。

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

void fun()
{
	string str = "spo";
	str += "rts";
	cout<<"str="<<str<<endl;
	str += ':';
	cout<<"str="<<str<<endl;
	string str1 = "basketball";
	str += str1;
	cout<<"str="<<str<<endl;
	str.append(" football");
	cout<<"str="<<str<<endl;
	str.append(" baseballabcdef",9);
	cout<<"str="<<str<<endl;
	string str2 = " volleyball";
	str.append(str2);
	cout<<"str="<<str<<endl;
	str.append(" ");
	str.append(str2,7,4);
	cout<<"str="<<str<<endl;
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

4、查找和替换

string查找和替换的函数原型如下。

int find(const string& s, int pos = 0) const;   //查找s第一次出现位置,从pos开始查找,默认是下标为0的位置
int find(const char* s, int pos = 0) const;
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& s, int pos = npos)const;   //查找str最后一次位置,从pos开始查找,默认是字符串最后一个字符的下标
int rfind(const char* s, int pos =npos) const;
int rfind(const char* s, int pos, int n) const;  //从pos查找s的前n个字符最后一次位置
int rfind(const char c, int pos = 0) const;  //查找字符c最后一次出现位置
//rfind从后往前找,find从前往后找
string& replace(int pos, int n, const string& s);   //替换从pos开始的n个字符为字符串s,两者的数量不一定相等
string& replace(int pos, int n,const char* s);

查找和替换的例子如下。

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

void fun1()
{
	int pos;
	string str = "hellowordhelloword";
	pos = str.find("llo");
	cout<<"pos="<<pos<<endl;
	pos = str.rfind("llo");
	cout<<"pos="<<pos<<endl;
	pos = str.find("loo");   //字符串不存在就返回-1
	cout<<"pos="<<pos<<endl;
}

void fun2()
{
	string str = "hood";
	str.replace(1,2,"ellowor");  //这里的替换在数量上不是一一对应关系
	cout<<"str="<<str<<endl; 
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

5、字符串比较

字符串比较是按照字符的ASCII进行对比的。字符串相等返回0,大于返回1,小于返回-1。
字符串比较的函数原型如下。

int compare(const string& s) const;  
int compare(const char* s) const;  

函数的实现也很简单,如下。

void fun()
{
	string str1 = "hello";
	string str2 = "hello";
	if(str1.compare(str2) == 0)
	{
		cout<<"str1 = str2"<<endl;
	}
	else if(str1.compare(str2) > 0)
	{
		cout<<"str1 > str2"<<endl;
	}
	else
	{
		cout<<"str1 < str2"<<endl;
	}
}

6、字符访问与存取

对string字符串中的单个字符访问有两种方式,一种是通过[]下标的方式,另一种是通过函数at()进行访问。
关于字符访问的两种方式,其代码如下。

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

void fun()
{
	string str = "hello";
	//方式一,数组下标
	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[2] = 'a';
	cout<<"str="<<str<<endl;
	str.at(3) = 'b';
	cout<<"str="<<str<<endl;
}


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

7、插入和删除

插入和删除函数的原型如下。

string& insert(int pos,const char* s);
string& insert(int pos,const string& s);
string& insert(int pos,int n,char c);   //在指定位置插入n个字符c
string& erase(int pos,int n=npos);   //删除从pos开始的n个字符,不给定第二个参数就删除到字符串结束

插入和删除的具体实现代码如下。

void fun()
{
	string str = "hello";
	str.insert(2,"abcd");
	cout<<"str="<<str<<endl;
	str.erase(2,4);
	cout<<"str="<<str<<endl;
}

8、子串

string子串获取的函数原型如下。

string substr(int pos=0,int n=npos)const;  //返回由pos开始的n个字符组成的字符串

string子串截取的简单应用例子如下。

void fun()
{
	string str = "zhangsan@qq.com";
	int pos = str.find('@');  //找到截取停止的位置
	string user = str.substr(0,pos);
	cout<<"user="<<user<<endl;
}

四、vector容器

vector数据结构和数组非常相似,也称为单端数组,其与数组的不同之处在于数组是静态空间,而vector可以动态扩展。
动态扩展并不是在原有空间之后续接新空间,而是找一块更大的内存空间,将原数据拷贝到新的空间并释放原有空间。
vector容器的简单示意图如下。
在这里插入图片描述
vector容器是单端开口进行相关操作的,该容器的迭代器是支持随机访问的迭代器。

1、构造函数

vector容器有四种构造函数,在下面的代码中都有体现。

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

void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	vector<int> v1;  //1.默认构造
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
	}
	cout<<"默认构造"<<endl;
	printVector(v1);

	vector<int> v2(v1.begin(),v1.begin()+3);  //2.通过区间方式进行构造
	cout<<"通过区间方式进行构造"<<endl;
	printVector(v2);

	vector<int> v3(5,1);  //3.通过n个相同的数据构造
	cout<<"通过n个相同的数据构造"<<endl;
	printVector(v3);

	vector<int> v4(v1);  //4.拷贝构造
	cout<<"拷贝构造"<<endl;
	printVector(v4);
}


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

上面程序的运行结果如下图所示。
在这里插入图片描述

2、赋值操作

容器赋值操作有两种方式,=赋值和assign指定区间赋值,具体的代码示例如下。

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

void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	vector<int> v1;
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
	}
	printVector(v1);

	vector<int> v2; 
	v2 = v1;    // = 赋值
	printVector(v2);

	vector<int> v3;
	v3.assign(v1.begin(),v1.end());  //左开右闭区间
	printVector(v3);

	vector<int> v4;
	v4.assign(3,6);
	printVector(v4);
}


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

上面程序的运行结果如下图所示。
在这里插入图片描述

3、容量和大小

对vector容器容量和大小操作的相关函数如下。

empty();  //判断容器是否为空
capacity();  //获取容器容量
size();  //获取容器中元素的个数
resize(int num);  //重新指定容器长度num,容器变长以默认值填充,容器变短超出部分被删除
resize(int num,elem);  //重新指定容器长度num,容器变长以elem填充

关于容器容量和大小操作的应用示例如下。

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

void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

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

	if(!v.empty())
	{
		cout<<"容器不为空!"<<endl;
		cout<<"容器的容量为:"<<v.capacity()<<endl;
		cout<<"容器的大小为:"<<v.size()<<endl;
	}
	else
	{
		cout<<"容器为空!"<<endl;
	}

	v.resize(5);   //容器变短,截取前5个元素
	printVector(v);
	v.resize(10,1);   //容器变长,自定义数据填充后面的元素
	printVector(v);
	v.resize(15);   //容器变长,以0填充后面的元素
	printVector(v);
}


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

上面程序的运行结果如下图所示。
在这里插入图片描述

4、插入和删除

对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();  //删除容器中所有元素

不同于string容器的插入和删除,其可以提供索引值在指定位置进行插入和删除,vector容器在插入和删除的时候需要提供迭代器,使用索引值是不行的。
插入和删除操作的应用示例如下。

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

void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	vector<int> v;
	for(int i=0;i<3;i++)
	{
		v.push_back(i+1);
	}
	printVector(v);

	v.pop_back();  //删除最后一个元素
	printVector(v);
	v.insert(v.begin()+1,5);  //在迭代器指向的位置处插入元素
	printVector(v);
	v.insert(v.begin()+2,3,6);  //在迭代器指向的位置处插入n个元素
	printVector(v);
	v.erase(v.begin());  //删除迭代器指向的元素
	printVector(v);
	v.erase(v.begin(),v.begin()+2);  //删除迭代器指向区间的元素
	printVector(v);
	v.clear();  //清空容器中所有元素
	//v.erase(v.begin(),v.end());   //与v.clear()的作用相同
	printVector(v);
}


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

上面程序的运行结果如下图所示。
在这里插入图片描述

5、数据存取

和vector容器中数据存取操作有关的函数如下。

at(int index);  //返回索引index指向的元素
operator[int index];  //返回索引index指向的元素
front();  //返回容器中的第一个元素
back();  //返回容器中的最后一个元素

数据存取的简单示例如下。

void fun()
{
	vector<int> v;
	for(int i=0;i<5;i++)
	{
		v.push_back(i+1);
	}

	cout<<v.at(3)<<endl;  //下标为3的元素
	cout<<v[3]<<endl;
	cout<<v.front()<<endl;  //第一个元素
	cout<<v.back()<<endl;   //最后一个元素
}

6、容器互换

容器互换指的是两个容器内元素进行互换,使用的函数是swap(v)。
容器互换的应用示例如下。

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

void printVector(vector<int> &v)
{
	for(vector<int>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun1()
{
	vector<int> v1;
	vector<int> v2;
	for(int i=0;i<5;i++)
	{
		v1.push_back(i+1);
		v2.push_back(i+6);
	}
	cout<<"v1容器内元素:";
	printVector(v1);
	cout<<"v2容器内元素:";
	printVector(v2);
	
	v1.swap(v2);  //互换两容器
	cout<<"v1容器和v2容器互换后:"<<endl;
	cout<<"v1容器内元素:";
	printVector(v1);
	cout<<"v2容器内元素:";
	printVector(v2);
}

void fun2()   //实际的应用
{
	vector<int> v;
	for(int i=0;i<1000;i++)
	{
		v.push_back(i+1);
	}
	cout<<"1.v的容量:"<<v.capacity()<<endl;
	cout<<"1.v的大小:"<<v.size()<<endl;
	v.resize(3);  //重新指定容器大小
	cout<<"2.v的容量:"<<v.capacity()<<endl;
	cout<<"2.v的大小:"<<v.size()<<endl;
	vector<int>(v).swap(v);  //创建匿名对象和当前的容器进行互换,匿名对象在本行执行完后就会被回收
	//相当于将指向容器的指针和指向匿名对象的指针进行了交换,大的内存在本行执行完后被系统回收
	cout<<"3.v的容量:"<<v.capacity()<<endl;
	cout<<"3.v的大小:"<<v.size()<<endl;
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述
可以看到,通过创建匿名对象和本容器进行交换,就可以收缩容器的容量,减小其占用的内存。

7、预留空间

vector的预留空间可以减少vector在动态扩展容量时的扩展次数。

reserve(int len);  //容器预留len个元素长度,预留的位置不初始化,元素不可访问

关于容器预留空间的示例如下。

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

void fun1()  //不预留空间
{
	vector<int> v;
	int count = 0;
	int *p = NULL;
	for(int i=0;i<10000;i++)
	{
		v.push_back(i+1);
		if(p!=&v[0])
		{
			p = &v[0];
			count++;
		}
	}
	cout<<"开辟内存的次数:"<<count<<endl;
}

void fun2()  //预留空间
{
	vector<int> v;
	int count = 0;
	int *p = NULL;
	v.reserve(10000);   //一开始就预留空间
	for(int i=0;i<10000;i++)
	{
		v.push_back(i+1);
		if(p!=&v[0])
		{
			p = &v[0];
			count++;
		}
	}
	cout<<"预留空间后开辟内存的次数:"<<count<<endl;
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述


五、deque容器

vector容器是单端数组,deque容器是双端数组,对于头部和尾部都可以进行插入和删除操作。
vector容器和deque容器的区别在于:vector对于头部的插入和删除效率太低,数据量越大,其效率就越低;deque容器在头部的插入删除操作速度比vector快;vector访问元素的速度比deque快。
deque容器的简单示意图如下图所示。
在这里插入图片描述
deque容器内部的工作原理:内部有一个中控器,其维护着每段缓冲区的内容,缓冲区中存放着真实的数据,中控器维护的是每个缓冲区的地址,这就使得使用deque时像一片连续的内存空间。
在这里插入图片描述
deque容器的迭代器也是支持随机访问的,即跳跃性的访问容器里面的元素。

1、构造函数

deque容器的构造函数和vector容器的构造函数类似,其应用示例如下。

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

void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	deque<int> d1;  //默认构造
	for(int i=0;i<5;i++)
	{
		d1.push_back(i+1);
	}
	printDeque(d1);
	deque<int> d2(d1);  //拷贝构造
	printDeque(d2);
	deque<int> d3(d1.begin(),d1.begin()+3);  //区间构造
	printDeque(d3);
	deque<int> d4(5,1);  //自定义数值构造
	printDeque(d4);
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

2、赋值操作

与vector容器一样,deque容器进行赋值时有=赋值和assign赋值两种方式。
赋值操作的简单示例如下。

void fun()
{
	deque<int> d1;
	for(int i=0;i<5;i++)
	{
		d1.push_back(i+1);
	}
	printDeque(d1);
	deque<int> d2;
	d2 = d1;  // =赋值
	printDeque(d2);
	deque<int> d3;
	d3.assign(d1.begin(),d1.end());  //assign指定区间赋值
	printDeque(d3);
	deque<int> d4;
	d4.assign(5,6);  //assign指定数值进行赋值
	printDeque(d4);
}

3、大小操作

不同于vector容器,对于deque容器的操作中没有获取其容量的函数,即没有capacity()。

empty();  //判断容器是否为空
size();  //获取容器中元素的个数
resize(int num);  //重新指定容器长度num,容器变长以默认值填充,容器变短超出部分被删除
resize(int num,elem);  //重新指定容器长度num,容器变长以elem填充

关于deque容器大小操作的示例如下。

void fun()
{
	deque<int> d;
	for(int i=0;i<5;i++)
	{
		d.push_back(i+1);
	}
	printDeque(d);
	if(!d.empty())
	{
		cout<<"容器不为空!"<<endl;
		cout<<"容器的大小为:"<<d.size()<<endl;
	}
	d.resize(7);
	printDeque(d);
	d.resize(10,1);
	printDeque(d);
	d.resize(3);
	printDeque(d);
}

4、插入和删除

相比于vector容器的插入和删除,deque容器的插入和删除稍微复杂一点,因为其不仅涉及尾部的操作,还涉及头部的操作。

push_front(elem);  //在容器头部插入一个数据
push_back(elem);  //在容器尾部插入一个数据
pop_front();  //从容器头部删除一个数据
pop_back();  //从容器尾部删除一个数据

insert(pos,elem);  //在指定位置插入指定元素
insert(pos,n,elem);  //在指定位置插入n个指定元素
insert(pos,start_pos,end_pos); //从指定位置开始插入区间数据
clear(); //清空数据
erase(pos); //删除指定位置的数据
erase(start_pos,end_pos);  //删除指定区间的数据

关于deque容器插入和删除的简单操作示例如下。

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

void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_front(3);
	d.push_front(4);
	printDeque(d);
	d.pop_back();
	d.pop_front();
	printDeque(d);

	d.insert(d.begin(),6);   //使用插入函数的时候需要提供迭代器
	d.insert(d.end(),3,6);
	printDeque(d);
	deque<int> d1;
	d1.push_back(10);
	d1.push_back(20);
	d.insert(d.begin(),d1.begin(),d1.end());  //在容器的指定位置处从插入另一个容器的某一段区间
	printDeque(d);
	d.erase(d.begin());
	printDeque(d);
	d.clear();
	printDeque(d);
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述

5、数据存取

deque容器的数据存取有以下几个函数可使用。

at(int index);
operator[];
front();  //返回容器中第一个数据
back();   //返回容器中最后一个数据

关于数据存取的简单示例如下。

void fun()
{
	deque<int> d;
	d.push_back(1);
	d.push_back(2);
	d.push_back(3);
	for(int i=0;i<d.size();i++)
	{
		//cout<<d[i]<<" ";
		cout<<d.at(i)<<" ";
	}
	cout<<endl;
	cout<<"第一个元素是:"<<d.front()<<endl;
	cout<<"最后一个元素是:"<<d.back()<<endl;
}

6、排序

通过sort()就可以对deque容器进行排序,函数原型如下。

sort(iterator begin,iterator end);   //对区间内的数据进行排序

在使用sort()函数之前,需要先包含头文件 algorithm。关于deque容器排序的简单应用示例如下。

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

void printDeque(const deque<int> &d) //限制为只读,不能修改
{
	for(deque<int>::const_iterator i=d.begin();i!=d.end();i++)
	{
		cout<<*i<<" ";
	}
	cout<<endl;
}

void fun()
{
	deque<int> d;
	d.push_back(2);
	d.push_back(1);
	d.push_back(6);
	d.push_back(4);
	d.push_back(5);
	d.push_back(3);
	cout<<"排序前:"<<endl;
	printDeque(d);
	sort(d.begin(),d.end());  //默认是升序排列
	cout<<"排序后:"<<endl;
	printDeque(d);
}

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

上面程序的运行结果如下图所示。
在这里插入图片描述
需要说明的是,支持随机访问的迭代器的容器,都可以利用sort算法直接对容器中的元素进行排序,比如vector容器也是可以的。


六、评委打分案例

有五位选手ABCDE,10个评委分别对每一名选手打分,去除一个最高分和一个最低分,将平均分输出。
实现步骤:
1.创建选手类,添加属性,实例化五个对象,依次将其放到vector容器中;
2.遍历vector容器,把每一个选手的得分情况存到deque容器中;
3.对于每个选手的得分,使用sort算法对deque容器中的元素进行排序,去掉首尾元素;
4.遍历deque容器,将剔除最高分和最低分后的分数相加并求平均值。
该案例的代码如下。

#include <iostream>
#include <string>
#include <vector>
#include <deque>
#include <algorithm>
#include <ctime>
using namespace std;

class Person
{
public:
	Person(string name,double score)
	{
		this->name = name;
		this->score = score;
	}
	string name;
	double score;
};

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

void setScore(vector<Person> &v)
{
	for(vector<Person>::iterator i=v.begin();i!=v.end();i++)
	{
		deque<int> d;
		for(int j=0;j<10;j++)
		{
			int num = rand()%41 + 60;  //60-100之间的随机数
			d.push_back(num);
		}
		
		cout<<i->name<<"的得分情况:"<<endl;
		for(deque<int>::iterator j=d.begin();j!=d.end();j++)
		{
			cout<<*j<<" ";
		}
		cout<<endl;

		sort(d.begin(),d.end());  //对deque容器中的元素进行升序排序
		d.pop_front();  //去除最低分
		d.pop_back();  //去除最高分
		int sum = 0;
		for(deque<int>::iterator j=d.begin();j!=d.end();j++)
		{
			sum += *j;  //求分数总和
		}
		double aver_score = sum / d.size();   //获取平均分
		i->score = aver_score;   //将求得的平均分存放在成员属性中
	}
}

void printScore(vector<Person> &v)
{
	for(vector<Person>::iterator i=v.begin();i!=v.end();i++)
	{
		cout<<(*i).name<<"的平均分是:"<<(*i).score<<endl;
	}
}

int main()
{
	srand((unsigned int)time(NULL));  //加入随机数种子,保证生成数的随机性
	//将5位选手存入vector容器中
	vector<Person> v;
	createPerson(v);
	//评委打分
	setScore(v);
	//打印平均分
	printScore(v);
	system("pause");
	return 0;
}

上面程序的运行结果如下图所示。
在这里插入图片描述
本案例是比较综合的一个例子,包括了string容器、vector容器和deque容器。


本文参考视频:
黑马程序员匠心之作|C++教程从0到1入门编程,学习编程不再难

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1490929.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

灯丝灯双通道低过温高压线性恒流芯片SM2082ED的应用及特性解析

双通道低过温高压线性恒流芯片是一种电子芯片&#xff0c;它具有双通道设计&#xff0c;可以在高电压条件下工作&#xff0c;并具有低过温特性。这种芯片通常用于需要高电流和高电压的应用&#xff0c;如LED照明、激光器、电机驱动等。 双通道设计意味着该芯片可以同时处理两个…

高级软件开发知识点

流程 算法题简历上项目用到技术、流程、遇到问题HR 准备 常考的题型和回答思路刷100算法题&#xff0c;理解其思想&#xff0c;不要死记最近一家公司所负责的业务和项目&#xff1a; 项目背景、演进之路&#xff0c;有哪个阶段&#xff0c;每个阶段主要做什么项目中技术选型…

【Sql Server】C#通过拼接代码的方式组合添加sql语句,会出现那些情况,参数化的作用

欢迎来到《小5讲堂》&#xff0c;大家好&#xff0c;我是全栈小5。 这是《Sql Server》系列文章&#xff0c;每篇文章将以博主理解的角度展开讲解&#xff0c; 特别是针对知识点的概念进行叙说&#xff0c;大部分文章将会对这些概念进行实际例子验证&#xff0c;以此达到加深对…

想要高薪还想要低要求?想转行做Python自动化测试,我该怎么做?

前言 最近小编连续收到好几个粉丝的私信询问&#xff1a;我年纪上来了&#xff0c;原来的行业做不下去了&#xff0c;想转行还能行吗&#xff1f;我是女生&#xff0c;计算机专业快毕业了&#xff0c;但是不喜欢做开发怎么办&#xff1f;我对编程行业感兴趣&#xff0c;想学编…

社交媒体的未来图景:探索Facebook的数字化之旅

社交媒体已经成为现代社会不可或缺的一部分&#xff0c;其影响力已经深入到人们生活的方方面面。而在众多社交媒体平台中&#xff0c;Facebook无疑是其中的巨头&#xff0c;其数字化之旅更是引领着整个社交媒体行业的发展方向。本文将深入探讨社交媒体的未来图景&#xff0c;以…

Linux中服务端开发

1 创建socket,返回一个文件描述符lfd---socket(); 2 将lfd和IP&#xff0c;PROT进行绑定---bind(); 3 将lfd由主动变成被动监听---listen(); 4 接收一个新的连接&#xff0c;得到一个的文件描述符cfd--accept() --该文件描述符用于与客户端通信 5 while(1) { 接受数据&a…

证明高维度神经网络模型是低纬度神经网络模型的加和

神经网络中矩阵乘法的分解与应用 启发标题&#xff1a;神经网络中矩阵乘法的分解与应用摘要&#xff1a;引言&#xff1a;方法&#xff1a;实验&#xff1a;结论&#xff1a;参考文献&#xff1a;附录1附录2实验数据 启发 理论上 更具矩阵乘法 A[p,mn]B[mn,q]C[p,q] Acat(A[:,…

ChatGPT 4.0 升级指南

1.ChatGPT 是什么&#xff1f; ChatGPT 是由 OpenAI 开发的一种基于人工智能的聊天机器人&#xff0c;它基于强大的语言处理模型 GPT&#xff08;Generative Pre-trained Transformer&#xff09;构建。它能够理解人类语言&#xff0c;可以为我们解决实际的问题。 1.模型规模…

K8S实现零宕机实践

越来越多的大厂都在上云、上容器、上K8S编排&#xff0c;K8S和容器云确实帮助我们解决了很多问题。但是&#xff0c;带来方便的同时&#xff0c;也让我们的架构变得更复杂了&#xff0c;更难于依靠“老经验”来解决问题了。虽然我们不用再费力考虑一层的问题&#xff0c;怎么实…

《低代码平台开发实践:基于React》读书心得与实战体验

低代码平台开发实践标题 &#x1f3ac; 江城开朗的豌豆&#xff1a;个人主页 &#x1f525; 个人专栏 :《 VUE 》 《 javaScript 》 &#x1f4dd; 个人网站 :《 江城开朗的豌豆&#x1fadb; 》 ⛺️ 生活的理想&#xff0c;就是为了理想的生活 ! 目录 &#x1f4d8; 一、引…

【EI会议征稿通知】第七届交通运输与土木建筑国际学术论坛(ISTTCA 2024)

第七届交通运输与土木建筑国际学术论坛&#xff08;ISTTCA 2024&#xff09; 2024 7th International Symposium on Traffic Transportation and Civil Architecture 交通运输是经济发展的先行官&#xff0c;而岩土是发展交通运输网络无法避开的话题。将传统的土木工程技术与先…

Linux 设置快捷命令

以ll命令为例&#xff1a; 在 Linux 系统上&#xff0c;ll 命令通常不是一个独立的程序&#xff0c;而是 ls 命令的一个别名。 这个别名通常在用户的 shell 配置文件中定义&#xff0c;比如 .bashrc 或 .bash_aliases 文件中。 要在 Debian 上启用 ll 命令&#xff0c;你可以按…

Hello World!第一个labview程序

软件版本&#xff1a; labview myrio 2021英文版 因为没有找到中文版的&#xff0c;据说是myrio没有中文版本 实验内容&#xff1a; 文本显示&#xff0c;程序界面输入任意文本&#xff0c;然后运行程序 在前面板显示出输入的文本 以下为具体步骤&#xff1a; 第一步&…

Linux常用命令(超详细)

一、基本命令 1.1 关机和重启 关机 shutdown -h now 立刻关机 shutdown -h 5 5分钟后关机 poweroff 立刻关机 重启 shutdown -r now 立刻重启 shutdown -r 5 5分钟后重启 reboot 立刻重启 1.2 帮助命令 –help命令 shutdown --help&#xff1a; ifconfig --help&#xff1a;查看…

【软件使用】Markdown编辑器第一次使用介绍

【软件使用】Markdown编辑器第一次使用介绍 markdown格式支持的软件有&#xff1a;VS Code 和 Typora&#xff0c;CSDN写网页博文也是用的.md&#xff0c;CSDN能支持导入的文件也是以.md格式结尾的文件名。 欢迎使用Markdown编辑器 你好&#xff01; 这是你第一次使用 Markd…

opencv官网 Blob检测

参考&#xff1a;Blob Detection Using OpenCV ( Python, C ) Bolob检测 Blob 是图像中一组连接的像素&#xff0c;它们共享一些共同属性&#xff08;例如&#xff0c;灰度值&#xff09;。在上图中&#xff0c;深色连接区域是 Blob&#xff0c;Blob 检测旨在识别和标记这些区…

基于51单片机风速仪风速测量台风预警数码管显示

基于51单片机风速仪风速测量报警数码管显示 1. 主要功能&#xff1a;2. 讲解视频&#xff1a;3. 仿真4. 程序代码5. 设计报告6. 设计资料内容清单&&下载链接资料下载链接&#xff1a; 基于51单片机风速仪风速测量报警数码管显示( proteus仿真程序设计报告讲解视频&…

CRM客户体验建设三剑客:构建旅程的必备策略

在企业越来越重视客户体验的今天&#xff0c;客户体验建设包含客户认知、客户旅程设置、NPS客户满意度调查三大版块&#xff0c;在工具上分别对应Zoho CRM的路径探查器、旅程构建器和NPS。上期介绍了路径探查器的作用和价值&#xff0c;本文将围绕客户旅程构建展开&#xff0c;…

Vue 3的Composition API和vue2的不同之处

Vue 3的Composition API是Vue.js框架的一个重要更新&#xff0c;它提供了一种新的组件逻辑组织和复用方式。在Vue 2中&#xff0c;我们通常使用Options API&#xff08;data、methods、computed等&#xff09;来组织组件的逻辑&#xff0c;但这种组织方式在处理复杂组件时可能会…

前端面试题 ===> 【JavaScript - 高级】

公众号&#xff1a;需要以下pdf&#xff0c;关注下方 2023已经过完了&#xff0c;让我们来把今年的面试题统计号&#xff0c;来备战今年的金三银四&#xff01;所以&#xff0c;不管你是社招还是校招&#xff0c;下面这份前端面试工程师高频面试题&#xff0c;请收好。 JavaScr…