文章目录
- 一、标准模板库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入门编程,学习编程不再难