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

文章目录

  • 一、标准模板库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");   //字符串不存在就返回-1cout<<"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.mzph.cn/news/723977.shtml

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

相关文章

游戏力:竞技游戏设计实战教程

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 游戏力&#xff1a;竞技游戏设计实战教程 引言…

GBU3010-ASEMI火牛适配器专用整流桥GBU3010

编辑&#xff1a;ll GBU3010-ASEMI火牛适配器专用整流桥GBU3010 型号&#xff1a;GBU3010 品牌&#xff1a;ASEMI 封装&#xff1a;GBU-4 最大重复峰值反向电压&#xff1a;1000V 最大正向平均整流电流(Vdss)&#xff1a;30A 功率(Pd)&#xff1a;大功率 芯片个数&…

前端运算符比较与计算中的类型转换,运算规则

题目&#xff1a; 下面表达式的值分别都是什么&#xff08;类型转换&#xff09; 0 0 0 2 true 2 false false false false 0 false undefined false null null undefined\t\r\n 0JS中的原始类型有哪些 原始值类型就是 存储的都是值&#xff0c;没有函数可以调用的。…

【C++】二叉树进阶面试题(下)

目录 6. 根据一棵树的前序遍历与中序遍历构造二叉树 题目 分析 代码 7. 根据一棵树的中序遍历与后序遍历构造二叉树 题目 分析 代码 8. 二叉树的前序遍历&#xff0c;非递归迭代实现 题目 分析 代码 9. 二叉树中序遍历 &#xff0c;非递归迭代实现 题目 分析 …

RLNNA-DBSCAN聚类

RLNNA-DBSCAN聚类 RLNNA算法&#xff08;基于强化学习的神经网络优化算法&#xff09;是一种性能较佳的优化算法。DBSCAN聚类算法&#xff08;密度聚类算法&#xff09;是一种基于密度的聚类算法&#xff0c;其主要思想是通过寻找样本点周围的密度可达关系来聚类数据。 使用RL…

自动化专家来谈谈Selenium中的时间等待

在 Selenium 中&#xff0c;时间等待指在测试用例中等待某个操作完成或某个事件发生的时间。Selenium 中提供了多种方式来进行时间等待&#xff0c;包括使用 ExpectedConditions 中的 presence_of_element_located 和 visibility_of_element_located 方法等待元素可见或不可见&…

小白如何选择阿里云服务器配置?小白不怕

阿里云服务器配置怎么选择&#xff1f;CPU内存、公网带宽和系统盘怎么选择&#xff1f;个人开发者或中小企业选择轻量应用服务器、ECS经济型e实例&#xff0c;企业用户选择ECS通用算力型u1云服务器、ECS计算型c7、通用型g7云服务器&#xff0c;阿里云服务器网aliyunfuwuqi.com整…

事件流、事件捕获、事件冒泡、事件委托

一、事件流 事件流指的是事件完整执行过程中的流动路径&#xff0c;分为捕获阶段、冒泡阶段。如上图 二、事件捕获 当一个元素的事件被触发时&#xff0c;会从DOM的根元素开始&#xff0c;依次调用同名事件&#xff08;从外到里&#xff0c;从父到子&#xff09;。 DOM.addEve…

基于炬芯ATS283X头戴式蓝牙耳机开发

基于炬芯ATS283X头戴式蓝牙耳机开发 是否需要申请加入数字音频系统研究开发交流答疑群(课题组)?可加他微信hezkz17, 本群提供音频技术答疑服务,+群赠送蓝牙音频,DSP音频项目核心开发资料, 1 电路设计 2 SDK 开发

CryoEM - CryoAI: Amortized Inference of Poses 工程源码复现

欢迎关注我的CSDN:https://spike.blog.csdn.net/ 本文地址:https://blog.csdn.net/caroline_wendy/article/details/136384544 Paper: CryoAI: Amortized Inference of Poses for Ab Initio Reconstruction of 3D Molecular Volumes from Real Cryo-EM Images CryoAI: 基于摊…

国产嵌入式DSP教学实验箱_操作教程:22-AD采集DA输出实验(采集输出正弦波)

一、实验目的 掌握EMIFA、SPI的使用&#xff0c;了解AD7606、AD5724的芯片特性和使用&#xff0c;并实现基于AD7606采集、AD5724输出正弦波。 二、实验原理 StarterWare StarterWare是一个免费的软件开发包&#xff0c;它包含了示例应用程序。StarterWare提供了一套完整的G…

代码随想录刷题笔记-Day30

1. 解数独 37. 解数独https://leetcode.cn/problems/sudoku-solver/ 编写一个程序&#xff0c;通过填充空格来解决数独问题。 数独的解法需 遵循如下规则&#xff1a; 数字 1-9 在每一行只能出现一次。数字 1-9 在每一列只能出现一次。数字 1-9 在每一个以粗实线分隔的 3x…

打动HR的秘密武器:四款在线简历制作网站,让你脱颖而出!

如果你想找到一份好工作&#xff0c;你必须先写一份优秀的工作。在这个人力资源部承认一天要看数百份简历的时代&#xff0c;求职者需要一份优秀的简历才能脱颖而出&#xff0c;获得面试机会。 现实中&#xff0c;很多人往往无法设计出满意的简历&#xff0c;所以他们甚至没有…

人工智能-飞桨

文章目录 概要安装零基础教程基础知识小结 概要 集核心框架、基础模型库、端到端开发套件、丰富的工具组件于一体的深度学习平台 官方入口 安装 python安装 python官方下载 PaddlePaddle安装 python -m pip install paddlepaddle2.6.0 -i https://mirror.baidu.com/pypi/s…

上位机图像处理和嵌入式模块部署(qmacvisual三个特色)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 了解了qmacvisual的配置之后&#xff0c;正常来说&#xff0c;我们需要了解下不同插件的功能是什么。不过我们不用着急&#xff0c;可以继续学习下…

MySQL数据库技术(二)

MySQL数据库技术&#xff08;1X&#xff09; 定义表的时候&#xff0c;需要确定每个字段的数据类型。MySQL 支持多种数据类型&#xff0c; 大致可以分为三类&#xff1a;数值类型、日期/时间类型和字符串类型。 数值类型 数值类型包括 5 种整数、2 种浮点数和 1 种定点数&a…

Java 实现国网698协议,帧头、数据帧校验算法

最近在做电表测试相关软件&#xff0c;开发过程需要学习698协议&#xff0c;网上关于698协议的资料很少。此文记录一下协议中关于帧头和整帧校验码算法&#xff0c;Java代码如下&#xff1a; public class Test {public static void main(String[] args) {//定义一个字节数据用…

跨境电商选品API商品采集API接入指南

选品是每个电商卖家的必经之路&#xff0c;产品的好坏将直接决定店铺的盈利、发展方向。选择合适的产品可以让卖家事半功倍&#xff0c;快速爆单。 用API实现代购系统和1688淘宝等平台的商品信息对接&#xff0c;可以免去很多选品工作。 item_get 获得淘宝商品详情item_get_p…

在vue3中使用el-tree-select做一个树形下拉选择器

el-tree-select是一个含有下拉菜单的树形选择器&#xff0c;结合了 el-tree 和 el-select 两个组件的功能。 因为包含了el-tree的功能&#xff0c;我们可以自定义tree的节点&#xff0c;创造出想要的组件 使用default插槽可以自定义节点内容&#xff0c;它的default插槽相当于…

Day19:信息打点-红蓝队自动化项目资产侦察武器库部署企查产权网络空间

目录 各类红蓝队优秀工具项目集合 自动化-武器库部署-F8x 自动化-网络空间-AsamF 自动化-企查信息-ENScan 自动化-综合架构-ARL&Nemo 思维导图 章节知识点 Web&#xff1a;语言/CMS/中间件/数据库/系统/WAF等 系统&#xff1a;操作系统/端口服务/网络环境/防火墙等 应…