C++ Primer(第五版) 第九章:顺序容器

练习9.1:考察使用哪种顺序容器

(a)list,当需要在容器中任意位置执行插入/删除操作时,用list最好

(b)deque,当需要在头部插入/删除元素,不需要在容器中间任意位置插入/删除元素时,用deque最好

(c)vector,当不需要在头部/任意位置插入/删除元素的情况下,用vector最好

练习9.2:考察对容器可以保存的元素类型的限制

list<deque<int>>lst1;

list<deque<int> >lst2;     //在编译器较旧版本中的书写方式

练习9.3:考察迭代器范围的条件

它们指向同一个容器中的元素(包括尾元素的下一个位置)

end的位置不在begin之前(可以通过反复递增使得begin到达end)

练习9.4:考察通过迭代器+循环处理一个元素范围

 1 bool find(vector<int>::iterator begin,vector<int>::iterator end,int value)
 2 {
 3     //写法1:
 4     while ( begin!=end )
 5     {
 6         if ( *begin==value ) return true;
 7         ++begin;
 8     }
 9     //写法2:
10     for ( auto iter=begin;iter!=end;++iter )
11     {
12         if ( *iter==value ) return true;
13     }
14     return false;
15 }

练习9.4

练习9.5:考察同上(当未找到定值时返回尾元素之后的那个位置)

 1 vector<int>::iterator find(vector<int>::iterator begin,vector<int>::iterator end,int value)
 2 {
 3     //写法1:
 4     while ( begin!=end )
 5     {
 6         if ( *begin==value ) return begin;
 7         ++begin;
 8     }
 9     //写法2:
10     for ( auto iter=begin;iter!=end;++iter )
11     {
12         if ( *iter==value ) return iter;
13     }
14     return end;
15 }

练习9.5

练习9.6:考察迭代器支持的操作

两个迭代器只能判断是否相等,不能比较大小

1 list<int>lst1;
2 list<int>::iterator iter1=lst1.begin(),lter2=lst1.end();
3 while ( iter1!=lter2 ) /*……*/ 

练习9.6

练习9.7:考察容器类型成员中的类型别名的使用对象

size_type得到容器大小

练习9.8:考察同上

读取string的list中的元素,使用const_iterator //只读不写

写入list,使用iterator

练习9.9:考察相似迭代器的区别

begin返回容器的iterator类型

cbegin返回容器的const_iterator类型

练习9.10:考察迭代器的类型

在gcc4.8下这段代码是错误的,因为it1和it2的类型是不一样的

正确的写法应该是

1 auto it1 = v1.begin();
2 auto it2 = v2.begin(), it3 = v1.cbegin(), it4 = v2.cbegin();

即:只有it1是iterator,剩下对象的类型都是const_iterator

练习9.11:考察vector的创建和初始化

1 vector<int>vec1; //不包含值,容器为空
2 vector<int>vec2{1,2,3}; //包含三个值,分别为1,2,3
3 vector<int>vec3={1,2,3}; //包含三个值,分别为1,2,3
4 vector<int>vec4(vec3); //包含三个值,分别为1,2,3
5 vector<int>vec5(vec4.begin(),vec4.end()); //包含三个值,分别为1,2,3
6 vector<int>vec6(3); //包含三个值,均为0
7 vector<int>vec7(3,1); //包含三个值,均为1 

练习9.11

练习9.12:考察初始化时传容器和传两个迭代器(范围)的差别

传容器:要求两个容器的容器类型和元素类型必须相同,同时得到新容器中元素的范围和老容器中元素的范围相同(整个拷贝)

传两个迭代器(范围):不要求容器类型相同,同时只要能将拷贝的元素转换为要初始化的容器的类型就可以(即新容器和老容器中的元素类型也可以不同),同时可以拷贝容器的子序列(不一定是整个容器)

练习9.13:考察容器的拷贝初始化

1 list<int>lst1;
2 vector<double>vec1(lst1.begin(),lst1.end());
3 vector<int>vec2;
4 vector<double>vec3(vec2.begin(),vec2.end());

练习9.13

练习9.14:考察容器的赋值运算(assign)

1 list<const char*>lst;
2 vector<string>vec;
3 vec.assign(lst.cbegin(),lst.cend());

练习9.14

练习9.15:考察相同容器的大小比较

1 vector<int>vec1(3,1);
2 vector<int>vec2(3,2);
3 if ( vec1==vec2 ) cout<<"vec1==vec2"<<endl;
4 else cout<<"vec1!=vec2"<<endl;

练习9.15

练习9.16:考察不同容器的大小比较(先统一容器再进行比较)

1 vector<int>vec1(3,1);
2 list<int>lst1(3,2);
3 vector<int>vec2(lst1.begin(),lst1.end());
4 if ( vec1==vec2 ) cout<<"vec1==lst1"<<endl;
5 else cout<<"vec1!=lst1"<<endl;

练习9.16

练习9.17:考察容器比较的限制

c1和c2必须是相同类型的容器,且必须保存相同类型的元素

同时且保存的元素必须支持条件运算符的比较

练习9.18:考察deque的push_back操作

1 deque<string>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;

练习9.18

练习9.19:考察list的push_back操作及其和dequepush_back操作的不同

list、vector和deque的push_back操作类似,只需要改变一下类型名即可

1 list<string>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout<<*iter<<endl;

练习9.19

练习9.20:考察访问list和向deque中添加元素

1 list<int>lst{1,2,3,4};
2 deque<int>odd;
3 deque<int>even;
4 for ( auto i:lst )
5 {
6     if ( i&1 ) odd.push_back(i);
7     else even.push_back(i);
8 }

练习9.20

练习9.21:考察为什么通过insert的返回值插入这个循环和push_front()等价

1 vector<string>vec;
2 auto iter=vec.begin();
3 string word;
4 while ( cin>>word ) iter=vec.insert(iter,word);

练习9.21

刚开始string被添加到begin的位置,因为insert的返回值指向第一个新加入的元素的迭代器(而新加入的元素加在原来容器中begin所在的元素的前一位置,即新加入的元素变成了当前容器的begin),所以insert的返回值是当前容器的begin,每次都将重复这样的操作,即每次都往容器头部加入元素。

练习9.22:考察容器添加元素对迭代器的影响

错误:(a)循环将一直进行下次,因为iter永远无法等于mid

(b)添加元素会使mid这个迭代器失效

修改:在添加元素的过程中不断更新中间迭代器mid、当前迭代器iter和容器大小

 1 vector<int>iv{1,1,1,2,3,4};
 2 int cursor=iv.size()/2;
 3 int some_value=1;
 4 auto iter=iv.begin();
 5 auto mid=iv.begin()+cursor;
 6 while ( iter!=mid )
 7 {
 8     if ( *iter==some_value )
 9     {
10         iv.insert(iter,2*some_value);
11         ++iter;
12         ++cursor;
13         mid=iv.begin()+cursor;
14     }
15 }

练习9.22

练习9.23:考察当容器中只有一个元素时,访问首元素和尾元素的结果

val==val2==val3==val4

练习9.24:考察使用不同的方式访问vector中的第一个元素,以及当容器为空时的访问结果

1 vector<int>vec;
2 vec.at(0);
3 vec[0];
4 vec.front();
5 *(vec.begin());

练习9.24

容器为空时,访问容器中的元素会产生未定义行为

练习9.25:考察删除多个元素

当elem1==elem2(==尾后迭代器)时,没有事情发生

当elem1!=尾后迭代器&&elem2==尾后迭代器时,删除容器中从elem1所在位置的元素往后所有的元素(包括elem1所在位置的元素)

练习9.26:考察从容器内部删除元素

 1 int ia[]={0,1,1,2,3,5,8,13,21,55,89};
 2 vector<int>vec(ia,end(ia));
 3 list<int>lst(vec.begin(),vec.end());
 4 auto iter1=vec.begin();
 5 while ( iter1!=vec.end() )
 6 {
 7     if ( (*iter1)&1 ) ++iter1;
 8     else iter1=vec.erase(iter1);
 9 }
10 auto iter2=lst.begin();
11 while ( iter2!=lst.end() )
12 {
13     if ( (*iter2)&1 ) iter2=lst.erase(iter2);
14     else ++iter2;
15 }
16 for ( auto i:vec ) cout<<i<<endl;
17 for ( auto i:lst ) cout<<i<<endl;

练习9.26

练习9.27:考察从forward_list中删除元素

 1 forward_list<int>flst={0,1,2,3,4,5,6,7,8,9};
 2 auto prev=flst.before_begin();
 3 auto curr=flst.begin();
 4 while ( curr!=flst.end() )
 5 {
 6     if ( (*curr)&1 ) curr=flst.erase_after(prev);
 7     else
 8     {
 9         prev=curr;
10         ++curr;
11     }
12 }
13 for ( auto i:flst ) cout<<i<<endl;

练习9.27

练习9.28:考察往forward_list中添加元素

 1 void find_and_insert(forward_list<string>& list,const string& to_find,const string& to_add)
 2 {
 3     auto prev=list.before_begin();
 4     bool change=false;
 5     for ( auto curr=list.begin();curr!=list.end();prev=curr++ )
 6     {
 7         if ( (*curr)==to_find )
 8         {
 9             curr=list.insert_after(curr,to_add);
10             change=true;
11         }
12     }
13     if ( !change ) list.insert_after(prev,to_add);
14 }

练习9.28

练习9.29:考察resize的用法

vec.resize(100)会往rec中再添加75个元素,添加元素的值均为0

vec.resize(10)会使得rec中删除90个元素只保留下前10个元素

练习9.30:考察resize对容器初始化的影响

当元素的类型是类时,同时类的构造函数没有提供初始值时不能接受单个参数的resize

练习9.31:考察vector、list和forward_list迭代器操作的不同

list的迭代器没有+=2这样的操作,改成advance(iter,2),和+=2等价

 1 list<int>vi={0,1,2,3,4,5,6,7,8,9};
 2 auto iter=vi.begin();
 3 while ( iter!=vi.end() )
 4 {
 5     if ( (*iter)&1 )
 6     {
 7         iter=vi.insert(iter,*iter);
 8         advance(iter,2);
 9     }
10     else iter=vi.erase(iter);
11 }
12 for ( auto i:vi ) cout<<i<<endl;

list

forword_list中重点注意两个迭代器位置的变化

 1 forward_list<int>vi={0,1,2,3,4,5,6,7,8,9};
 2 auto prev=vi.before_begin();
 3 auto iter=vi.begin();
 4 while ( iter!=vi.end() )
 5 {
 6     if ( (*iter)&1 )
 7     {
 8         iter=vi.insert_after(prev,*iter);
 9         advance(iter,2);
10         advance(prev,2);
11     }
12     else iter=vi.erase_after(prev);
13 }
14 for ( auto i:vi ) cout<<i<<endl;

forward_list

练习9.32:考察程序规范

不合法,当执行完insert后,iter进行的操作取决于编译器是不确定的。更详细的讨论请看:https://github.com/Mooophy/Cpp-Primer/issues/125

练习9.33:考察insert的返回值(返回插入的第一个新添加的元素)

insert使得vector的存储空间被重新分配,导致迭代器失效,会造成程序崩溃

练习9.34:考察insert的返回值

会造成无限循环,当访问的一个奇数时,会无限访问该奇数,因为程序遇到奇数会在奇数前添加一个元素,同时迭代器指向该奇数的前一个元素,当迭代器++时又是该奇数

 1 #include<iostream>
 2 #include<vector>
 3 using namespace std;
 4
 5 int main()
 6 {
 7     vector<int>vi={0,1,2,3,4,5,6,7,8,9};
 8     auto iter=vi.begin();
 9     while ( iter!=vi.end() )
10     {
11         if ( (*iter)&1 )
12         {
13             iter=vi.insert(iter,*iter);
14             ++iter;
15         }
16         ++iter;
17     }
18     for ( auto i:vi ) cout<<i<<endl;
19 }

练习9.34

原文地址:https://www.cnblogs.com/HDUjackyan/p/9547263.html

时间: 2024-10-15 12:24:45

C++ Primer(第五版) 第九章:顺序容器的相关文章

C++primer(第五版)第九章 顺序容器(容器的运用及其部分习题解答,C++11特性总结,重点章节内容较多)

顺序容器:为程序员提供了控制元素存储和访问顺序的能力.(无序容器)           1.顺序容器的概述           A.顺序容器的类型           vector:可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能很慢.          deque:双端队列.支持快速随机访问.在头尾位置插入/删除速度很快.           list:双向链表.只支持双向顺序访问.在list中任何位置进行插入/删除操作速度都很快.          forword_list

c++ primer 第五版第九章

9.01 对于下面的程序任务,vector, deque和list哪种容器最为合适?解释你选择的理由.如果没有哪一种容器优于其它容器,也请解释理由. 读取固定数量的单词,将它们按字典序插入到容器中.我们将在下一章看到,关联容器更适合这个问题. 读取未知数量的单词,总是将新单词插入到末尾.删除操作在头部进行. 从一个文件中读取未知数量的整数.将这些整数排序,然后打印到标准输出. 使用list,需要在中间插入,用list效率更高. 使用deque.只在头尾进行操作,deque效率更高. 使用vect

C++ Primer学习总结 第9章 顺序容器

第9章 顺序容器 1.    顺序容器如果有一个只需要容器大小参数的默认构造函数,该函数使用的是元素的默认构造函数来构造每个元素对象,如果该容器的元素没有默认构造函数,那么就不能使用这个容器的该构造函数P294: 2.    容器进行拷贝初始化时,两个容器的元素必须同类型. 但是如果列表初始化,或迭代器范围初始化容器,那么只要求列表中元素或迭代器所指元素可以转化为容器的元素即可. 3.    容器类型可以直接通过=号赋值(包括array容器数组类型): 注意:assign仅适用于顺序容器(但不适

C++ Primer 第九章顺序容器

一.综述 <vector>:可变大小数组.支持快速随机访问.在尾部之外的位置插入或删除元素可能很慢. <deque>:双端队列.支持快速随机访问.在头尾位置插入/删除速度很快. <list>:双向链表.只支持双向顺序访问.在list中任何位置进行插入/删除操作速度都很快. <forward_list>:单向链表.只支持单向顺序访问.在链表任何位置进行插入/删除操作速度都很快. <array>:固定大小数组.支持快速随机访问.不能添加或删除元素.

C++ primer笔记——第九章 顺序容器

顺序容器的元素按照其位置存储和访问.除了顺序容器之外,标准库还提供了几种关联容器,其元素按照键(key)排序.每组容器都提供一组不同的时间和功能的折中方案.顺序容器根据位置来存储和访问元素,元素的排列次序与元素值无关,而是由元素添加到容器的顺序决定.标准库定义了三种顺序容器:vector.list.dequeue.他们的差别在于元素访问的方式以及添加和删除元素相关操作的运行代价.标准库还提供了三种适配器.适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口来适应基础的容器类型.顺序容器适

C++ Primer 第九章 顺序容器

由于书籍上写的已经很经典了,故大部分用图片的形式来阐述概念,代码纯手打进行验证. 1.顺序容器类型:vector.deque.list.forword_list.array.string. 2.顺序容器概述: 3.小结 4. 验证代码如下: 1 #include <iostream> 2 #include <vector> 3 #include <array> 4 #include <string> 5 #include <list> 6 usi

C++ Primer(第五版) 第二章 基本内置类型

容易忘记的部分: 2.1:C++提供的几种字符串类型有哪些及其用途? 基本的字符类型char,一个char的类型和一个机器字节一样 其他字符类型用于拓展字符集,如wchar_t.char16_t.char32_t wchar_t类型确保可以存放机器最大拓展字符集中的任意一个字符 char16_t和char32_t则为Unicode字符集服务 2.2:如何选择所使用的类型 当数值不为负数时,使用无符号类型(unsigned) 一般常用int和long long执行整数的运算 算术表达式中不使用ch

《C++primer》v5 第9章 顺序容器 读书笔记 习题答案

9.1 (a)list.可以快速插入. (b)deque.支持尾部快速插入和头部快速删除. (c)vector或者deque. 9.2 list<deque<int> > l; 9.3 它的范围是该容器的第一个元素和尾元素之后.区间左闭右开. 9.4 #include<iostream> #include<algorithm> #include<cstdio> #include<list> #include<deque>

第九章 顺序容器

一 容器概览 1.容器定义和初始化 C c{a, b, c....} c初始化为初始列表中元素的拷贝. C c = {a, b, c....} 列表中的元素类型必须与C的元素类型相容. 对于array类型,列表中元素数目必须等于或小于array的大小,任何遗漏的元素都进行值初始化 C c1(c2) c1初始化为c2的拷贝 C c1= c2 c1和c2必须是相同类型(即:它们必须是相同的容器类型,且保存的是相同的元素类型,对于array类型,两者还必须具有相同大小) C c(b,e) c初始化为迭