7.1概述
7.1.1容器分类
(复习的重点在于不同之处)
(1)序列性容器:按照线性排列来存储某类型值的集合,每个元素都有自己特定的位置,顺序容器主要有 vector、deque 和 list。
vector:就是动态数组。它是在堆中分配内存,元素连续存放,有保留内存,如果减少大小后内存也不会释放。新值大于当前大小时才会再分配内存。对最后元素操作最快(在后面添加删除最快),此时一般不需要移动内存。只有保留内存不够时才需要对中间和开始处进行添加删除元素操作,这时需要移动内存,如果你的元素是结构或是类,那么移动的同时还会进行构造和析构操作。vector 的一大特点是可直接访问任何元素。
deque: 与 vector类似,支持随机访问和快速插人删除-支持加减操作,它在容器中某一位置上的操作所花费的是线性时间。与 vector 不同的是,deque 还支持从开始端插人、删除数据。由于它主要对前端、后端进行操作,因此也叫做双端队列。
list:又叫链表,是一种双线性列表,只能顺序访问(从前向后或者从后向前),与前面两种容器类有一个明显的区别就是它不支持随机访问。要访问表中某个下标处的项需要从表头或表尾处(接近该下标的一端)开始循环。
(2)关联式容器:【底层结构一红黑树】与前面讲到的顺序容器相比,关联式容器更注重快速和高效地检索数据的能力。这些容器是根据键值(key)来检索数据的,键可以是值也可以是容器中的某一成员。这一类中的成员在初始化后都是按一定顺序排好序的。关联式容器主要有 set、multiset(关联规则(是否重复),排序方式 ;map 和multimap。
set:快速查找,不允许重复值。
multiset:快速查找,允许重复值。
map:一对一映射,基于关键字快速查找,不允许重复值。map (用pair组成,有first和second)
multimap:一对多映射,基于关键字快速查找,允许重复值。
(3)容器适配器对已有的容器进行某些特性的再封装,不是一个真正的新容器。要有 stack、queue。
队列:一般队列和优先队列; 栈 ;操作方式不同,应用范围不同
stack:堆栈类,特点是后进先出 queue: 队列类,特点是先进先出
(4)二进制容器:从右往左
7.1.2容器共性
容器一般来说都有下列函数。
默认构造函数:提供容器默认初始化的构造函数。
复制构造函数:将容器初始化为现有同类容器副本的构造函数。
析构函数:不再需要容器时进行内存整理的析构函数。
empty:容器中没有元素时返回true,否则返回false。
max_size:返回容器中最大元素个数
size:返回容器中当前元素个数。
operator=:将一个容器赋给另一个容器。
operator<:如果第一个容器小于第二个容器,返回 true,否则返回 false。
operator<=:如果第一个容器小于或等于第二个容器,返回 true,否则返回 falseeoperator>:如果第一个容器大于第二个容器返回 true,否则返回 false。
operator>=:如果第一个容器大于或等于第二个容器,返回 true,否则返回 false。operator==:如果第一个容器等于第二个容器,返回 true,否则返回 false。。operator!-:如果第一个容器不等于第二个容器,返回 true,否则返回 false。
swap:交换两个容器的元素。
顺序容器和关联容器共有函数如下。
begin:该函数有两个版本,返回 iterator 或 const_iterator,返回容器第一个元素迭代器指针。
end:该函数有两个版本,返回iterator 或 const_iterator,返回容器最后一个元素后面一位的迭代器指针。
rbegin:该函数有两个版本,返回 reverse_iterator 或 const_reverse_iterator,返回容器最后一个元素的迭代器指针。
Rend:该函数有两个版本,返回reverse_iterator 或 const_reverse_iterator,返回容器首个元素前面一位的迭代器指针。
erase:从容器中清除一个或几个元素。
clear:清除容器中所有元素。
7.1.3容器比较
vector(连续的空间存储,可以使用[ ]操作符)快速地访问随机的元素,快速地在末尾插人元素,但是在序列中随机插人、删除元素比较慢。而且如果一开始分配的空间不够的话,有一个重新分配更大空间的过程。
deque(小片的连续,小片间用链表相连,实际上内部有一个map 的指针,因为知道类型所以还是可以使用[ ],只是速度没有 vector 快)快速地访问随机的元素,快速地在开始和末尾插人元素,随机地插入、删除元素要慢,空间的重新分配要比 vector 快,重新分配空间后原有的元素不需要备份。对 deque 的排序操作,可将 deque 先复制到 vector,排序后再复制回deque。
list(每个元素间用链表相连)访问随机元素不如vector 快,随机地插人元素比vector快,对每个元素分配空间,所以不存在空间不够,重新分配的情况。
set 内部元素唯一,用一棵平衡树结构来存储,因此遍历的时候就排序了,查找也比较快。
map一对一地映射结合,key 不能重复
7.2 vector 容器
7.2.1 概述
vector类称作向量类,它实现了动态的数组,用于元素数量变化的对象数组。像数组一样,vector类也用从0开始的下标表示元素的位置;但和数组不同的是当vector 对象创建后,数组的元素个数会随着 vector 对象元素个数的增大和缩小而自动变化。(从尾部增加是最快的)
vector类常用的函数如下所示。
(1)构造函数。
vector():创建一个空 vector。
vector(int nSize):创建一个 vector,元素个数为 nSize。
vector(int nSize,const T&t):创建一个 vector,元素个数为 nSize,且值均为t。
vector(const vector&):复制构造函数。
(2)增加函数。
void push_back(const T& x):向量尾部增加一个元素 x。
iterator insert(iterator it,const T& x):向量中某一元素前增加一个元素 x。
void insert(iterator it,int n,const T& x);向量中某一元素前增加n个相同元素x。
void insert(iterator it,const_iterator first,const_iterator last):向量中某一元素插人另一个相同类型向量的[first,last)间的数据。
(3)删除函数。
iterator erase(iterator it):删除向量中某一个元素。
iterator erase(iterator first, iterator last): 删除向中[first,last)中元素。
void pop_back():删除向量中最后一个元素。(尾部)
void clear():删除向量中所有元素。
(4)遍历函数。
reference at(int pos):返回 pos 位置元的引用
reference front():返回首元素的引用。
reference back():返回尾元素的引用。
iterator begin():返回向量头指针,指向第一个元素。
iterator end():返回向量尾指针,不包括最后一个元素,在其下面。
reverse_iterator rbegin():反向迭代器,最后一个元素迭代指针。
reverse_iterator rend():反向选代器,第一个元素之前的迭代指针
(5)判断函数。
bool empty() const:向量是否为空,若 true,则向量中无元素。
(6)大小函数。
int size()const:返回向量中元素的个数。
int capacity() const:返回当前向量所能容纳的最大元素值
int max_size()const:返回最大可允许的 vector 元素数量值
(7)其他函数。
void swap(vector&):交换两个同类型向量的数据。
void assign(int n,const T& x):设置容器大小为n个元每个元素值为x。
void assign(const_iterator first,const_iterator last):容器中[first,last)中元素设置成当前向量元素。
7.2.2初始化示例(次重点)
#include<vector>
#include<iostream>
class A
{
};
int main(int argc,char *argv[])
{
std::vector<int>int_ect;
std::vector<float>flo_vect;
std::vector<A>cA_vect;
std::vector<A*>cpA_vect;
cout<<"init success!"<<endl;
return 0;
}
#include<vector>
#include<iostream>
using namespace std;
class A{
};
int main(int argc, char * argv[])
vector<int>int ect;
vector<float>flo_vect;
vector<A>cA_vect;
vector<A>cpA_vect;
cout<<"init success!"<<endl;
return 0;}
通过比较,可以得出如下结论。
(1)向量类可以定义基本变量、类、类的指针等。
(2)若程序中用到标准库,必须包含相应的头文件,本例包含的头文件是<vector>及<iostream>
(3)若用了命名空间 using namespace std,则在序中可直接定义,如 vectot<int>int_ect;;否则必须用前缀 std,如std::vector<int> int_ect;。
(4)如果用typedef 对 vector进行了重新定义它的好处是:一方面主程序简洁,与普通类的用法一致;另一方面程序容易维护。
例若现在 vector<int>要改成 vector<short>,必须把主程序中所有相关行都改;若把 typedef vector<int> INT_VECT改为 typedef vector<short> INT_VECT 则主程序可不动。
7.2.3 增加及获得元素示例
例:针对基本变量的向量增加及获得元素示例。定义一个整型元素的向量类先增加两个元素,之后在屏幕上重新显示这两个元素。
#include <vector>
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
vector<int> int_vec; // 创建一个名为int_vec的整数向量(vector)
int_vec.push_back(1); // 向向量末尾添加元素1
int_vec.push_back(2); // 向向量末尾添加元素2
int nSize = int_vec.size(); // 获取向量的大小,即元素个数
cout << "通过数组方式输出:";
for (int i = 0; i < nSize; i++) {
cout << int_vec[i] << "\t"; // 通过数组方式访问向量元素并输出
}
cout << endl; // 换行
cout << "通过获得引用输出:";
for (int i = 0; i < nSize; i++) {
int& nValue = int_vec.at(i); // 通过at()函数获取指定位置的元素的引用
cout << nValue << "\t"; // 输出引用所指向的元素值
}
cout << endl; // 换行
cout << "通过迭代器输出:";
vector<int>::iterator int_vec_iter = int_vec.begin(); // 创建一个迭代器并指向向量的起始位置
while (int_vec_iter != int_vec.end()) { // 循环迭代器直到到达向量的末尾位置
cout << *int_vec_iter << "\t"; // 通过迭代器获取元素值并输出
int_vec_iter++; // 迭代器向后移动一位
}
cout << endl; // 换行
return 0;
}
注意:
(1)获得向量中的元素值可通过三种常用方式:直接用向量数组,获得元素引用,获得元素的指针。
(2)选代器变量int_vec_iter 表示向量元素的指针,所以*int_vec_iter 才表示元素真正的值。
7.2.4 修改元素示例
例某整型向量先存人三个数,值为 1,2,3。先后修改第二个数为 5,10,20,并分别在屏幕上显示修改后的全部向量元素。
#include <vector>
#include <iostream>
using namespace std;
int main(int argc, char* argv[]) {
vector<int> int_vec; // 创建一个名为int_vec的整数向量(vector)
int_vec.push_back(1); // 向向量末尾添加元素1
int_vec.push_back(2); // 向向量末尾添加元素2
int_vec.push_back(3); // 向向量末尾添加元素3
// 通过数组修改
cout << "通过数组修改,第2元素为5:";
int_vec[1] = 5; // 使用数组方式访问并修改第2个元素(下标为1)
for(int i = 0; i < int_vec.size(); i++) {
cout << int_vec[i] << "\t";
}
cout << endl;
// 通过引用修改
cout << "通过引用修改,第2元素为10:";
int& m = int_vec.at(1); // 使用at()函数获取第2个元素的引用
m = 10; // 通过引用修改第2个元素的值
for(int i = 0; i < int_vec.size(); i++) {
cout << int_vec[i] << "\t";
}
cout << endl;
// 通过迭代器修改
cout << "通过迭代器修改,第2元素为20:";
vector<int>::iterator int_vec_iter = int_vec.begin() + 1; // 创建迭代器并指向第2个元素
*int_vec_iter = 20; // 通过迭代器修改第2个元素的值
for(int i = 0; i < int_vec.size(); i++) {
cout << int_vec[i] << "\t";
}
return 0;
}
解释:每个循环的作用是遍历向量 int_vec 中的每个元素,并将它们依次输出到控制台。 for 循环,它用于迭代遍历向量中的元素。
int i = 0:在循环开始之前,我们初始化一个整数变量 i 并将其设置为0。这个变量将用作向量索引。
i < int_vec.size():这是循环的终止条件。只要 i 的值小于向量 int_vec 的大小(也就是元素的数量),循环将继续执行。
i++:在每次循环迭代之后,我们将 i 的值增加1,以便在下一次迭代中访问下一个元素。
cout << int_vec[i]:这行代码输出向量 int_vec 中索引为 i 的元素的值。
7.2.5 删除元素示例
删除元素主要是掌握erase函数的用法
例:一个整型向量,初始化元素为1~10,删除第 5 个元素,屏幕显示向量第值;删除第2~5个元素,屏幕显示向量元素值:删除2~5个元素,屏幕显示向量元素的值。
#include <vector>
#include <iostream>
using namespace std;
int main(int argc, char* argv[])
{
vector<int> int_vec;//定义一个名为 int_vec 的整型向量
// 添加元素 1 到 10 到向量中
for (int i = 1; i <= 10; i++) {
int_vec.push_back(i);//将变量 i 的值添加到向量 int_vec 的末尾
}
// 删除第 5 个元素
int_vec.erase(int_vec.begin() + 4);//
cout << "删除第 5 个元素后向量为: ";
for (int i = 0; i < int_vec.size(); i++) //遍历向量中的元素
{
cout << int_vec[i] << "\t";//输出向量中索引为 i 的元素
}
cout << endl;
// 删除第 2~5 个元素
int_vec.erase(int_vec.begin() + 1, int_vec.begin() + 5);
cout << "再删除第 2~5 个元素后向量为: ";
for (int i = 0; i < int_vec.size(); i++) {
cout << int_vec[i] << "\t";
}
cout << endl;
return 0;
}
int_vec.erase(int_vec.begin() + 4);删除向量中索引为 4 的元素删除第 5 个元素
int_vec.erase(int_vec.begin() + 1, int_vec.begin() + 5); 删除第 2~5 个元素从向量中删除从第2个元素到第6个元素(不包括第六个元素)的所有元素。
7.2.6 进一步理解vector(次重点)vector容量问题
增加新元素时,如果超过当前的容量,则容量会自动扩充 2 倍,如果两倍容量仍不足,就扩大至足够大的容量。对 vector 容器而言,当增加新的元素时,有可能很快完成(直接存在预留空间中),有可能稍慢(扩容后再放新元素);对修改元素值而言是较快的;对删除元素来说,若删除尾部元素较快,非尾部元素则稍慢,因为牵涉到删除后的元素移动。
7.2.7 综合操作示例
例利用 vector 编一个学生信息[学号(它是关键字)、姓名、性别、出生日期]管理类,有添加函数、查询函数(依据学号查询)、显示函数(对查询结果完成显示)。并编制函数测试。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
class Student {
public:
string m_strNO;
string m_strName;
string m_strSex;
string m_strDate;
public://构造函数 Student 接受这些参数并初始化成员变量
Student(string strNO, string strName, string strSex, string strDate) :
m_strNO(strNO), m_strName(strName), m_strSex(strSex), m_strDate(strDate) {}
void Display() {//Display 函数用于在控制台输出学生的信息
cout << m_strNO << "\t" << m_strName << "\t" << m_strSex << "\t" << m_strDate << endl;
}
};
class StudCollect {
vector<Student> m_vStud;//是私有的一个存储 Student 对象的向量
public:
void Add(Student& s) {//接受一个 Student 对象的引用作为参数
m_vStud.push_back(s);//并使用 push_back 函数将该对象添加到向量的末尾
}
Student* Find(string strNO) {//Find函数用于根据学生编号查找学生对象。
//它接受一个字符串参数 strNO,代表要查找的学生编号
bool bFind = false;//定义了一个布尔变量 bFind,用于标记是否找到匹配的学生对象
int i = 0;//循环的计数器
for (i = 0; i < m_vStud.size(); i++) {//遍历 m_vStud 向量中的每个学生对象
Student& s = m_vStud.at(i);//m_vStud.at(i)获取当前迭代的学生对象的引用,并将其赋值给 s
//通过比较学生对象的学生编号 s.m_strNO 和传入的参数 strNO 是否相等来判断是否找到匹配的学生对象
if (s.m_strNO == strNO) {
bFind = true;
break;
}
}
Student* s = NULL;//定义了一个指向 Student 对象的指针 s并初始化
if (bFind)
s = &m_vStud.at(i);//如果找到了匹配的学生对象,则指针指向该对象
return s;
}
};
int main() {//创建了三个 Student 对象,分别为 s1、s2 和 s3,并传入相应的参数进行初始化
Student s1("1001", "zhangsan", "boy", "1985-10-10");
Student s2("1002", "lisi", "boy", "1984-6-10");
Student s3("1003", "wangwu", "boy", "1985-11-15");
StudCollect s;
s.Add(s1), s.Add(s2), s.Add(s3);//调用 s.Add 函数将 s1、s2 和 s3 添加到 s 对象中的学生集合中
Student* ps = s.Find("1002");//调用 s.Find 函数,并传入参数 "1002" 进行查找。
if (ps)
ps->Display();//显示找到的学生对象的信息
return 0;
}
(1)基本思想是要有一个基本信息类及该基本信息的集合类。对本题而言,基本信息类是学生类 Student,集合类是 StudCollect。
(2)Student类中定义了4 个基本的成员变量及幕显示函数 Display()。StudColleci定义了一个成员变量 vector<Student>v,可看出本类是对学生集合对象的一个管理类,是实现集合管理类的根本所在。该类主要定义了 Add(Student& s)函数及 Student * Find(string strNO)函数。Add 函数非常好理解,就是向集合 中添加学生对象。对于根据学号查询函数 Find,可能有的学生会按如下代码写:
void Find(string strNO){
for(int i=0; i<m_vStud.size(); i++){
Student& s=m_vStud.at(i);//s 是一个引用类型的 Student 对象,它引用了 m_vStud 容器中索引为 i 的元素
if(s.m_strNO==strNO)//比较 s 对象的成员变量 m_strNO 和传入的 strNO 参数是否相等。
{
cout<<s.m_strNO<<"\t"<<s.m_strName<<"\t"<<s.m_strSex<<"\t"<<s.m_strDate;
break;
}}}
例:已知作者的基本信息有作者编号、姓名,书籍信息有书号、书名、出版社,一位作者可以出版许多书,如何用集合类体现出上述关系?并编制简单的测试类
#include<iostream>
#include<vector>
#include<string>
using namespace std;
class Book {
string m_strBookNO;
string m_strBookName; // 出版社
string m_strPublic;
public://构造函数用于初始化这些成员变量
Book(string strNo, string strName, string strpublic) : m_strBookNO(strNo), m_strBookName(strName), m_strPublic(strpublic) {}
};
class Writer {
string m_strWriterNO;
string m_strWriterName;
vector<Book> m_vecBook;//存储作者的图书
public:
Writer(string strNO, string strName) : m_strWriterNO(strNO), m_strWriterName(strName) {}
void AddBook(Book& book) {//AddBook 函数用于向作者的图书容器中添加图书对象
m_vecBook.push_back(book);
}
};
class WriterCollect {
vector<Writer> m_vecWriter;//存储多个作者对象
public:
void AddWriter(Writer& writer) {//AddWriter 函数用于向作者集合中添加作者对象
m_vecWriter.push_back(writer);
}
};
int main() {
Writer w1("1001", "zhangsan");
Book b1("b001", "aaa", "public");
Book b2("002", "bbb", "public");
w1.AddBook(b1);
w1.AddBook(b2);
Writer w2("1002", "lisi");
Book b3("b003", "ccc", "public");
w2.AddBook(b3);
//collect.AddWriter 将 w1 和 w2 添加到作者集合中
WriterCollect collect;
collect.AddWriter(w1);
collect.AddWriter(w2);
return 0;
}
vector<Book> m_vecBook;声明了一个名为 m_vecBook 的成员变量,它是一个向量容器(vector),用于存储 Book 类型的对象
(1)Writer 作者类是一个基本信息类,但它包含了作者书籍的集合对象 vector<Book>m_vecBook,也就是说,基本信息类中也经常用到集合类。
(2)集合类多层嵌套是常用的编程方法,若数据关系能划分成树型结构,且均满足一个根结点对应多个子结点,那么编程时就要考虑到集合类嵌套方法。
7.3 习题
1. 以下哪个容器可以支持随机访问
A. map B.deque C.list D. stack
2.以下哪个容器不可以随机访问任何元素()
A.vector B.deque C.list D. bitset
list 不支持随机访问任何元素。list 是双向链表的实现,它的特点是插入和删除操作的效率高,但不支持常数时间内访问任意位置的元素
vector 和 deque 都是序列容器,可以通过索引直接访问元素,而 bitset 是一个固定大小的位数组,也可以通过索引来访问各个位元素。
3. 哪个容器内元素不允许出现重复 ( )
A. set B. list C. queue D. multimap
4. 那个容器( )不属于序列化容器?
A.vector B. set C. list D. deque
序列化容器是指能够维护元素的线性顺序的容器,其中元素的插入和访问顺序与其在容器中的位置有关
vector 是序列化容器,它以连续的内存块存储元素,支持快速随机访问。
list 也是序列化容器,它使用双向链表存储元素,支持快速插入和删除操作。
deque(双端队列)也是序列化容器,它以多个连续的内存块存储元素,支持在两端进行快速插入和删除操作。
set 是关联容器,它以平衡二叉树(通常是红黑树)实现,存储元素的顺序是根据元素的键值自动进行排序。由于关联容器是按照键值排序的,因此它不符合序列化容器的定义。
5. 想要仿真医院急诊等候的现象,哪种( B)结构更合理些?利用优先队列
A. queue B. priority_queue C. set D. map
6.想要操作系统中cache缓存机制的现象,哪种(B)结构更合理些?
A. queue B. priority_queue C. set D. map
7、我校目前有3万多名学生,如果想通过查询学号定位到具体的学生信息,使用哪种容器存放学生数据会更便于访问查询?
a)vector b)priority_queue c)set d)map
map是一种关联容器,它提供了一对一的键值对存储和访问方式。在 map 中,每个元素都由一个键(学号)和一个值(学生信息)组成,这种结构非常适合用于根据键进行快速查找和访问。
8.把电话通讯录存入容器,该选哪一个容器
vector: 是一个动态数组,适用于需要频繁随机访问元素的情况。如果电话通讯录需要支持按照索引或随机访问元素
list: 是一个双向链表,适用于需要频繁插入和删除元素的情况。如果电话通讯录需要频繁地插入和删除联系人
map是关联容器,适用于需要按照键值对存储联系人信息的情况。如果您需要通过联系人的名称或其他唯一键来快速查找联系人,set 或 unordered_set: 和 是关联容器,适用于需要存储唯一联系人信息的情况。如果电话通讯录中的联系人信息唯一且无重复
9、仿真后进先出的线性表,哪种结构更好些?
a)queue b)stack c)vector d)list
因为stack栈满足后进先出(LIFO)的原则。在栈中,最后插入的元素位于栈顶,也是最先被访问和删除的元素。
其他选项的数据结构不完全满足后进先出的要求:
队列(queue)是先进先出(FIFO)的线性表,与题目要求的后进先出不符。
向量(vector)和链表(list)都是支持随机访问的线性表,它们没有明确的后进先出的特性。
10.以下哪一项不属于序列性容器C
A.vector B.deque C.stack D.llist
A. vector 是一种动态数组,支持随机访问,可以通过索引快速访问元素。
B. deque(双端队列)是一种双向开口的序列容器,可以在两端进行插入和删除操作。
C. stack 是一种适配器容器,不属于序列性容器。它提供了一种后进先出(LIFO)的操作方式,只允许在顶部进行插入和删除操作。
D. list 是一种双向链表,可以在任意位置进行插入和删除操作,支持双向遍历。
11、向vector<int> v(10)容器存放一定数据,下面哪种做法是不合理的?A
a)v.push_front(10)//vector中没有这个函数,这个是list容器的成员函数
b) v[5] = 10//将值 10 存放到 vector 容器的第 6 个位置(索引从 0 开始)
c) v.at(5) = 10
d) v.push_back(10)//将值 10 添加到 vector 容器的末尾
12、那个容器不属于关联容器?
a)stack b)set c)map d)multiset
13.那个容器是关系型容器(B)
A.vector B. set C. list D. deque
关联容器是 C++ STL 中的一类容器,其中的元素被组织成一些特定的数据结构,可以通过关键字(key)来访问和查找元素。常见的关联容器包括 set、multiset、map、multimap 等。
而 stack 是一种顺序容器适配器,它提供了一种 LIFO(Last In First Out)的数据存储方式,可以通过 push() 和 pop() 操作在栈顶插入和删除元素。
14.哪种容器不允许有重复的元素?B
a) vector b)set c)list d)deque
vector、list 和 deque 都是顺序容器,允许有重复的元素存在
15. vector<int> v不可以通过(D)来访问元素本身(i是索引,iter是迭代器)。
A. v.at(i) B. v[i] C. *iter D. iter->this
迭代器的成员函数应该使用 -> 操作符而不是 . 操作符。此外,this 关键字是指向当前对象的指针,并不适用于访问容器元素。
16.对于set<Student>,Student是自建类,下面哪个说法是错误的?
A. set中元素不可以重复 B. set中元素缺省是升序
C. (集合中元素是类对象的,必须)必须重载操作符<
D. 必须重载操作==
set中默认情况下,元素是按照升序排序的。
因为set中的元素要进行排序,所以必须定义<运算符来进行比较
在set中,元素的唯一性不是通过==运算符来判断的,而是通过<运算符来判断的。因此,不需要重载==运算符。
17. 在main()中,有如下代码:
vector<int> v(2);//大小为2
v.push_back(3);//向容器中添加一个元素 3
cout<<v.size()<<endl;
此时屏幕输出值为:()
A.2 B. 3 C. 4 D. 5
18. set<int> s;
for(int i=0;i<5;i++)
s.insert(i);
pair<set<int>::iterator, bool> pr;
pr=s.insert(2);//将元素 2 插入到集合中
pr.first++;//向后移动一个位置,指向了集合中的元素 3
cout<<*pr.first<<endl;
执行完上述代码后,屏幕输出结果为:()
A. 1 B. 2 C. 3 D. 4
19、以下说法正确的是()。
A.list链表是一种双线性列表,只能顺序访问。
B.vector,list,queue都是序列性容器。
C.stack的特点是先进先出。
D.set是关联性容器,实现快速查找,允许重复值。
A. list 链表是一种双向链表,而不是双线性列表。它允许在任意位置进行插入和删除操作,并且可以双向遍历,不仅限于顺序访问。
B. vector 是序列容器,支持随机访问;list 是双向链表,支持双向遍历和插入删除;queue 是适配器容器,用于实现先进先出(FIFO)的操作。
C. stack 的特点是后进先出(LIFO)。它是一种适配器容器,用于在底层容器(如 vector,deque 或 list)上实现栈的行为
20.vector<int>v(6);
v.pop_front(3)<<endl; //换为其他函数也行
结果输出为()
A.3 B.4 C.5 D.2
C++ 中,vector 类型没有 pop_front() 方法。pop_front() 是 deque 类型的成员函数,用于从容器的前端移除元素。因此,给定的代码在语法上是错误的。
如果我们将代码中的 v.pop_front(3) 替换为 v.push_back(3),表示向末尾插入元素 3,那么输出结果将是选项 D. 2。
21、补充完整代码,要求利用copy函数将v中的元素全部显示到屏幕上,每一个元素中间用“v”间隔。
int main()
{
vector<int> v;
for(int i=0;i<10;i++)
v.insert(i);
copy(_v.begin()___,_v.end()__,_ostream_iterator<int> (cout, “v”));
return 0;
以下哪一个方法无法向Vector容器中插入数据?
A. insert() B.push_front() C.push_back() D.选项ABC均可插入数据
push_front() 方法用于在 deque、list 或 forward_list 这样的容器中,在容器的开头插入元素。而对于 vector,元素的插入操作通常使用 push_back() 在末尾插入,或使用 insert() 在指定位置插入。
22.一下那种容器存储在内存的地址是完全连续的?
A. vector B. set C.deque D. list
vector是C++标准库中的一个动态数组容器,它的元素在内存中是连续存储的,因此地址是完全连续的。set是一个关联容器,它通常使用红黑树实现,元素在内存中的存储位置是根据元素值的比较结果来确定的,因此地址不一定是连续的。deque是双端队列容器,它通常由多个连续的存储块组成,每个存储块中的元素是连续存储的,但不同的存储块之间的地址不一定是连续的。list是双向链表容器,它的元素在内存中是通过指针相连的节点形式存储的,因此地址不是连续的。
23. 三种顺序容器中,____vector___不太适合作为queue的基本容器类。
24. 根据数组int a[] = {1, 2, 3, 4, 5};构造vector容器实例v补全代码
vector<int> v(a, ___a+5__);
25.对于vector<int> v{1, 3, 4, 5, 8, 9},distance(v.begin() + 2, v.end())返回的值是____4____。
distance 是一个算法函数,用于计算两个迭代器之间的距离。在这个例子中,我们使用 v.begin() + 2 作为起始迭代器,它指向向量 v 中的第三个元素(值为 4)。v.end() 是结束迭代器,它指向向量 v 的尾后位置这个位置也算作一个点
25.5 int a[] = {1, 2, 3, 4, 5, 6};
list<int>::iterator mid = find(l.begin(), l.end(), 3);查找元素为的位置
cout << "元素 3 位置:" << distance(l.begin(), mid) << endl;
1-2;2-3;所以输出为2
26、请写出stl中的其中3种容器vector,deque,list,set,multiset,map,multimap,stack,queue等
27、容器有3类,分别是 序列性容器,关联式容器,容器适配器
28、如何向一个vector容器添加新元素?
答:1>.void push_back(const T& x); //向量尾部增加一个元素x
2>.Iterator insert(iterator it, const T& x);//向量中某一元素前增加一个元x
3>.Void insert(iterator it,int n,const T& x);//向量中某一元素前增加n个相同的x
4>.Void insert(iterator it,const_iterator first,const_iterator last);
29. 下列哪个是错误的?
int ia[ 7 ] = { 0, 1, 1, 2, 3, 5, 8 };
(a) vector< vector< int > > ivec;//定义了一个存储着整数向量的向量对象
(b) vector< int > ivec = { 0, 1, 1, 2, 3, 5, 8 };//定义并初始化了一个存储着整数的向量对象。
(c) vector< int > ivec( ia, ia+7 );//定义并使用数组 ia 初始化了一个存储着整数的向量对象。
(d) vector< string > svec = ivec;
//将一个存储着整数的向量 ivec 赋值给一个存储着字符串的向量 svec,类型不匹配
(e) vector< string > svec( 10, string( "null" ));
//定义并初始化了一个存储着字符串的向量对象,其中包含了10个字符串 "null"。
30、容器可分为:序列性容器:vector,deque,list
关联式容器:set(默认升序,快速查找,不允许有重复值)
Multiset(快速查找,允许有重复值)
map(一对一映射,基于关键字快速查找,不允许重复值) multimap(一对多映 射,基于关键字快速查找,允许有重复值)
31.STL都有哪两类容器?有什么区别与联系?
有序列性容器和关联式容器。序列容器按照线性排列来存储某类型值的集合,每个元素都有自己特定的位置。关联性容器与序列性容器比更注重快速和高效的检索数据的能力。
都有共同的函数:begin;end;rbegin;rend;erase、;clear
32.不同容器会有怎样的应用场景?
Vector:简单,允许随机存储,数据的存取十分灵活,若已经知道需要存储元素的数目在缺省的情况下使用;
Deque:经常在头部和尾部安插和移除元素,并且存储的容量也比vector大得多,只有需要在首端进行插入/删除操作的时候,还要兼顾随机访问效率,才选择deque。若既需要随机插入/删除,又需要随机访问
List:若需要随机插入/删除(不仅仅在两端)如果经常在容器的中段执行安插,移除,和移动元素,用到list
Set和multiset:经常以某个准则寻找元素,可以使用“以这个准则为排序准则的”set和multiset
Map和multimap:使用字典,使用关联式数组。
当要存储的是大型负责类对象时,list要优于vector;当然这时候也可以用vector来存储指向对象的指针,同样会取得较高的效率,但是指针的维护非常容易出错,因此不推荐使用。
加上map和set:set使用红黑树;map使用红黑树,一对一映射结合;key不能重复;容器适配器主要有stack堆栈类,特点是后进先出,queue 是队列类,特点是先进先出,priority_queue是优先队列
33. vector插入删除和list有什么区别?
vector插入和删除数据,需要对现有数据进行复制移动,如果vector存储的对象很大或者构造函数很复杂,则开销较大,如果是简单的小数据,效率优于list。
34.vector、deque、list容器有何区别和相同?
相同:都是容器;都有默认析构函数,复制构造函数,析构函数
内存分配:
Vector:连续的空间存储,可以使用【】操作符
Deque:小片的连续,小片间用链表相连,实际内部有一个map指针,因为知道类型,所以、还是可以使用 【】,只是速度没有vector快。
List:每个元素间用链表相连。
内存不够时:
Vector:如果一开始分配额内存不够的话,有一个重新分配更大空间的过程。
Deque:空间的重新分配要比vector快,重新分配空间后,原有的元素不需要重新备份
List:对每个元素分配空间,所以不存在空间不够,重新分配的情况。
35. 下面代码中需要补充完整,下划线部分该选择()
void PrintQueue(queue<int> obj) {
while (!obj.empty()) {
cout <<_________ << endl;
obj.pop();
}
}
A.obj.pop() B. obj.front() C. obj.push() D. obj.top()
obj.front() 返回队列中的第一个元素,因此可以用来输出当前队列的头部元素。然后使用 obj.pop() 将队列中的头部元素移除,以便继续处理队列中的下一个元素。
36. 执行下述代码后,v的内容_______335________。
vector<int> v(2, 3);//创建了一个包含两个元素,且每个元素的值均为 3 的向量
v.push_back(5);//添加到 v 的末尾
37、执行下述代码后,v的内容____4 4 4 2___________。
vector<int> v(3, 4); //3个“4”
v.push_back(2);
38.成员函数rbegin()得到容器_最后一个元素_的位置,而rend得到容器_第一个元素前__的位置
39.成员函数end()得到容器(1)最后一个元素的后继位置的位置,而rend得到容器(2)逆向该容器前后反转之后end()的位置。算法通常返回(3)指向某个位置或元素迭代子
40.为何每次insert之后,以前保存的iterator不会失效?
答:iterator这里就相当于指向节点的指针,内存没有变,指向内存的指针怎么会失效呢(当然被删除的那个元素本身已经失效了)。相对于vector来说,每一次删除和插入,指针都有可能失效,调用push_back在尾部插入也是如此。因为为了保证内部数据的连续存放,iterator指向的那块内存在删除和插入过程中可能已经被其他内存覆盖或者内存已经被释放了。即使时push_back的时候,容器内部空间可能不够,需要一块新的更大的内存,只有把以前的内存释放,申请新的更大的内存,复制已有的数据元素到新的内存,最后把需要插入的元素放到最后,那么以前的内存指针自然就不可用了。特别时在和find等算法在一起使用的时候,牢记这个原则:不要使用过期的iterator。
41.如何遍历一个容器vector/deque/list
vector<int>::iterator it=v.begin();
while(it!=v.end())
{
cout<<*it<<endl;
it++;
}
cout<<endl;
vector<int>::reverse_iterator v_rita=v.rbegin();
while(v_rita!=v.rend())
{
cout<<*v_rita<<"\t";
v_rita++;
}
cout<<endl;
42.序列性容器和关联使容器的区别和联系
序列性容器按照线性排列来存储某类型值的集合,每个元素都有自己特定的位置;
关联式容器与顺序容器相比,关联式容器更注重快速与高效的检索数据的能力,这些容器是根据键值(key)来检索数据的,键可以是值,也可以是容器中的某一成员,这一类中的成员在初始化后,都是按照一定顺序排好序的。