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

2021-07-12 01:07

阅读:591

标签:头部   names   space   c++ prime   只读   r++   deque   子序列   拷贝   

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

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

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

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

 

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

list>lst1;

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

 

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

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

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

 

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

技术分享图片技术分享图片
 1 bool find(vectorint>::iterator begin,vectorint>::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 vectorint>::iterator find(vectorint>::iterator begin,vectorint>::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 listint>lst1;
2 listint>::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();
View Code

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

 

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

技术分享图片技术分享图片
1 vectorint>vec1; //不包含值,容器为空
2 vectorint>vec2{1,2,3}; //包含三个值,分别为1,2,3
3 vectorint>vec3={1,2,3}; //包含三个值,分别为1,2,3
4 vectorint>vec4(vec3); //包含三个值,分别为1,2,3
5 vectorint>vec5(vec4.begin(),vec4.end()); //包含三个值,分别为1,2,3
6 vectorint>vec6(3); //包含三个值,均为0 
7 vectorint>vec7(3,1); //包含三个值,均为1 
练习9.11

 

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

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

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

 

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

技术分享图片技术分享图片
1 listint>lst1;
2 vectordouble>vec1(lst1.begin(),lst1.end());
3 vectorint>vec2;
4 vectordouble>vec3(vec2.begin(),vec2.end());
练习9.13

 

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

技术分享图片技术分享图片
1 listconst char*>lst;
2 vectorstring>vec;    
3 vec.assign(lst.cbegin(),lst.cend());
练习9.14

 

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

技术分享图片技术分享图片
1 vectorint>vec1(3,1);
2 vectorint>vec2(3,2);
3 if ( vec1==vec2 ) cout"vec1==vec2"endl;
4 else cout"vec1!=vec2"
练习9.15

 

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

技术分享图片技术分享图片
1 vectorint>vec1(3,1);
2 listint>lst1(3,2);
3 vectorint>vec2(lst1.begin(),lst1.end());
4 if ( vec1==vec2 ) cout"vec1==lst1"endl;
5 else cout"vec1!=lst1"
练习9.16

 

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

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

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

 

练习9.18:考察deque的push_back操作

技术分享图片技术分享图片
1 dequestring>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout
练习9.18

 

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

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

技术分享图片技术分享图片
1 liststring>container;
2 string word;
3 while ( cin>>word ) container.push_back(word);
4 for ( auto iter=container.cbegin();iter!=container.cend();++iter ) cout
练习9.19

 

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

技术分享图片技术分享图片
1 listint>lst{1,2,3,4};
2 dequeint>odd;
3 dequeint>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 vectorstring>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 vectorint>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 vectorint>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 vectorint>vec(ia,end(ia));
 3 listint>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 ) coutendl;
17 for ( auto i:lst ) cout
练习9.26

 

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

技术分享图片技术分享图片
 1 forward_listint>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
练习9.27

 

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

技术分享图片技术分享图片
 1 void find_and_insert(forward_liststring>& 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 listint>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
list

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

技术分享图片技术分享图片
 1 forward_listint>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
forward_list

 

练习9.32:考察程序规范

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

 

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

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

 

练习9.34:考察insert的返回值

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

技术分享图片技术分享图片
 1 #include 2 #include 3 using namespace std;
 4 
 5 int main()
 6 {
 7     vectorint>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 ) coutendl;
19 }
练习9.34

 

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

标签:头部   names   space   c++ prime   只读   r++   deque   子序列   拷贝   

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

上一篇:js数组

下一篇:python assert


评论


亲,登录后才可以留言!