C++Primer新笔记之----容器和算法

来源:互联网 发布:linux jdk版本降级 编辑:程序博客网 时间:2024/05/16 16:18

9.1、顺序容器概述

包括 vector、deque、list、forward_list、array、string  //其中forward_list、array为c++11的新特性

另外还有3种适配器,stack、queue、priority_queue;

9.2、迭代器

#include "stdafx.h"#include <iostream>#include<forward_list>#include<vector>#include<array>using namespace std;int _tmain(int argc, _TCHAR* argv[]){<pre class="cpp" name="code">array<int,5> a ={0,1,2,3};//后面自动补0vector<int>v(a.begin(),a.end());//vector<int>v={0,1,2};//errorv.push_back(0);v.push_back(1);v.push_back(2);for(auto iter=v.begin();iter<v.end();iter++)cout<<*iter;cout<<endl;vector<int>v1(v);for(auto iter=v1.rbegin();iter<v1.rend();iter++)cout<<*iter;return 0;

}

输出:

01230012 21003210请按任意键继续. . .

思想:

这种反向迭代思想不错,利用别的容器来进行复制构造,但是还是不能像array那样进行初始化。

1、assign和swap

assign适用于初始化之后,再用别的容器或者常量来对其赋值

如:v.assigh(c.begin(),c.end())

或者(10,8)//10个8

v1.assign(v.begin(),v.begin()+3);//这里为从begin开始数3个字符,这是为了跟end()配合

 

swap

swap(v1,v);//还是很好用的

 

9.3、顺序容器操作

1、向顺序容器添加元素

(1)array都不支持

(2)vector,string支持push_back(),不建议使用insert

(3)deque支持头尾插

(4)insert(pos,v.begin(),v.end());//在插入位置插入数,返回插入位置

或者insert(插入位置,{...})//返回刚刚指向

(5)现在还没有找到emplace和push_back等的区别,都调用的是复制构造函数

2、访问元素

*iter           front()           at()     []

3、删除元素

pop_back()   erase ()   (begin,end)  clear

#include "stdafx.h"#include <iostream>#include<string>#include<vector>using namespace std;int _tmain(int argc, _TCHAR* argv[]){vector<int> v(3,2);v[1]=3;v[2]=4;for(auto iter=v.begin();iter<v.end();iter++)if ( *iter == 3){iter=v.erase(iter);//这里erase函数改变了iter的值,因此需要返回iter,否则会出错<span style="font-family: Arial, Helvetica, sans-serif;"></span>
<span style="white-space:pre"></span><pre class="cpp" name="code"><span style="white-space:pre"></span>--iter;
cout<<*iter<<endl; //返回的位置还是原来擦除的位置}for(auto iter=v.begin();iter<v.end();iter++)cout<<*iter<<endl;return 0;}

这个是面试官问我的一个题,其实看起来简单,但实际上要操作起来,还是有问题的

输出:

424请按任意键继续. . .

思想:

(1)注意擦除之后记得要返回位置

(2)擦除函数的用法,中间用的是迭代器

4、forward_list

before_begin   cbefore_begin   insert_after  emplace_after   erase_after

、forward_list和array类型list(双向链表)和forward_list(单项链表)可以使得在任何位置插入删除都很快,但是不支持随机访问。list<int> c(ar.begin(),ar.end());c.insert(c.begin(),3); //这样3就插在了最前面,在迭代器前面插入,同样支持push_back push_frontfc.insert_after(c.begin(),3);//单项链表只支持在迭代器后插入,且支持push_front,前插法array是一种更安全更容易使用的数组类型,长度是固定的。用法为:array<int,10> ar={1,2}; 列表初始化,都是可以的,但是要vs2012及以上的编译器

5、resize

多余的会被删掉,但是不会改变容器的大小

6、管理容量的成员函数

capacity()   reserve() shrink_to_fit()       size()

 

9.4  string 的一些操作

 

#include "stdafx.h"#include <iostream>#include<string>using namespace std;int _tmain(int argc, _TCHAR* argv[]){char a[]="fdsljklcvtfdsfsCVTfdklkjfcVtd888";string b="";for(int i=0;i<sizeof(a);i++)b+=tolower(a[i]);string c(a);size_t pos=0;int count=0;while(pos<b.size()){pos=b.find("cvt",pos);if(pos<b.size()){c.replace(pos+count,3,"cvte");//每替换掉一个c就多出一个字符,需要进行调整count++;}elsebreak;pos=pos+count+3;//调整}cout<<c<<endl;return 0;}


 这个是cvte的一个题,当时不知道string有这么强大的功能,这里拿来用,很轻松的就解决了问题。

思想:

(1)利用char的转化操作tolower转化小写

(2)string有find("fs",pos)在pos个字符之后找字符的功能,用while循环

(3)每替换掉一个,cvte就比以前多了个字符,所以需要向前移动一下

输出:

fdsljklcvtefdsfscvtefdklkjfcvted888
请按任意键继续. . .

1、reserve和resize的区别

reserve只是改变了capacity的大小,但是没有改变size。只有一个参数。

resize改变了capacity,也改变了size,如原来有3个元素,那么v.resize(5,2),会在原来元素的后面添加两个元素。如果只有一个参数的话,那么会调用默认的构造函数。

2、除了erase还有clear函数可以用。用来删除元素。

3、区分string中find(“”,pos)以及find_first_of(“”)的区别

find_first_of为里面任何一个字符第一次出现的位置。

find_last_of任意一个字符最后一次出现的位置

find_first_not_of第一个不在里面的字符

find_last_not_of最后一个不再里面的字符

4、emplace操作

emplace_front、emplace、emplace_back

分别对应push_front、insert、push_back区别是,emplace是构造而不是拷贝对象。

如一个对象为salse,共有三个元素。那么用法为:

c.emplace("aaa",1,2);

c.insert(salse("aaa",1,2)); 

10.1 一些算法

#include "stdafx.h"#include <iostream>#include<string>#include<vector>#include<list>#include<iterator>#include<algorithm>#include<array>using namespace std;int _tmain(int argc, _TCHAR* argv[]){vector<int> v;fill_n(back_inserter(v),10,1);//作为初始化之后要填充还是有用处的fill(v.begin(),v.end(),3);//当v为空时会出现问题replace(v.begin(),v.end(),3,2);//替换array<int,4> a={1,2,3};//sort和unique操作v.clear();v.push_back(0);v.push_back(2);v.push_back(3);v.push_back(2);v.push_back(3);sort(v.begin(),v.end());auto end_iter=unique(v.begin(),v.end());//返回排序消除重复的元素的最后一个位置,且会覆盖掉重复的元素,那么整个容器变得不可信for(auto iter=v.begin();iter<end_iter;iter++)cout<<*iter<<endl;   //输出0 2 3return 0;}

思想:

(1)注意fill操作不能允许为空,但是用back_inserter 还是可以的

(2)注意sort作用还是蛮大的,另外unique是返回一个迭代器的。

 

10.3  定制操作

1、lambda表达式

1.1  绑定,形参,可修改捕获值、修改返回类型等

适用于只在一两个地方使用的简单操作

绑定

int sz=4;auto f=[&sz](int a,int b){return (a+b)>sz?a+b:sz;};//放在主函数内,感觉这个东西还是蛮有用的sz=5;cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的

输出:5

因为对捕获值进行绑定 &sz

反之没有绑定的话,输出4

可修改

int sz=4;auto f=[sz](int a,int b)mutable{return (a+b)>sz?a+b:++sz;};//放在主函数内,感觉这个东西还是蛮有用的sz=5;cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的

输出:5

在没有绑定的情况下,要修改sz的值,需要加上mutable,然而若为绑定,则不需要

修改返回类型

int sz=4;auto f=[sz](int a,int b)mutable -> int{if((a+b)>sz)return a+b;else return ++sz;};//放在主函数内,感觉这个东西还是蛮有用的sz=5;cout<<f(1,2)<<endl;//其中sz必须是函数体内的一个局部变量,这样用这种lambda还是不错的

输出:5

lambda一般是不允许返回两种类型的,会默认为void,如果非要那么做,必须加上-> int 其中int为返回类型。

 

一些泛型算法

#include <iostream>#include<vector>#include<string>#include<algorithm>#include<iterator>using namespace std;int main(){int a[5]={1,3,2,3,4};vector<int> v(a,a+5);vector<int> vtemp;replace(v.begin(),v.end(),2,5);//替换操作非常强大,但是这种方法不适用于整个文本中子串的替换吧sort(v.begin(),v.end());//排序也挺方便for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});//以后就用这种输出方式,显的很好用cout<<endl;auto iter=unique(v.begin(),v.end());//去掉重复,返回最后不重复元素的下一个for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});cout<<endl;v.erase(iter,v.end());//擦除重复的for_each(v.begin(),v.end(),[](const int &a){cout<<a<<" ";});cout<<endl;iter=find_if(v.begin(),v.end(),[](const int &a)-> bool{if(a>=4)return true;else return false;});//最后一个参数是判断//可以简写为[](const int &a){return a>=4 ;}for_each(iter,v.end(),[](const int &a){cout<<a<<" ";});system("pause");return 0;}


 1、find(v.begin().v.end(),val)若找到则指向它,否则指向end.

find_if(v.begin().v.end(),[](const int &a){return true;}) //根据后面的lambda表达式来判断。

2、accumulate(v.begin().v.end(),val),是由第三个参数决定初始值,以及做什么操作,以及返回什么类型,可以是string,int等,但是如果类型是double,但是初值为0,那么最后返回的是整形。

3、equal(v.begin(),v.end(),v1.begin())比较两个容器中的元素,判断是否相等。第二个容器中的元素至少和第一个容器中的元素一样多。

4、fill(v.begin(),v.end(),val)是覆盖操作

fill_n(v.begin(),n,val) 从这个位置开始,后面的n个元素覆盖为val,但是会出问题,因为原来本来就没有这么多元素。

因此用到auto it=back_inserter(v);其返回一个迭代器it,当执行*it=val的时候,会调用push_back将元素插入。

用法为fill_n(back_inserter(v),n,val) ,会默认调用push_back。

5、copy

数组拷贝如:copy(begin(a1),end(a1),a2),其中a1和a2都是数组。

6、replace()

7、删除重复单词

sort(v.begin(),v.end());//可以重载其排序方法

auto iter=unique(v.begin(),v.end());

v.erase(iter,v.end());

8、字符串排序

elimDups(words);//按照字典序排序,消除重复

stable_sort(words.begin().words.end(),[](const string &s1,const string &s2){return s1.size()<s2.size();})//按照长度排序,但是长度相同的话按照字典序排序。

注意后面用的是lambda表达式。

9、lambda表达式的应用场合

在函数比较短而且只在一两个场合使用的地方。

10、bind在旧版本中是用bind1st和bind2st分别绑定第一个和第二个参数的,但是现在被启用,用bind非常方便。

int f(int, char, double);
// 绑定f()函数调用的第二个和第三个参数,
// 返回一个新的函数对象为ff,它只带有一个int类型的参数
auto ff = bind(f, _1, ‘c’, 1.2); 其中_1为指定传进来参数的位置。
int x = ff(7); 

也就是说可以更改位置:如bind(f,_3,_2,_1)等。

绑定引用参数

bind是做拷贝的,因此对于大的对象如ostream,需要做引用处理,因此需要用ref函数,其作用是返回一个对象的引用

添加头文件<functional>

using namespace std::placeholders//因为_1等都在std::placeholders命名空间中。

其实可以用下面这种方法,就不用ostream了。

void print(const int &a)
{
cout<<a<<" "<<endl;
}

for_each(v.begin(),v.end(),bind(print,_1));

如果非要用的话

ostream &print(ostream &out,const int &a)
{
return out<<a<<" "<<endl;
}

for_each(v.begin(),v,end(),bind(print,ref(cout),_1))

也是可以的。

11、插入迭代器 back_inserter、front_inserter、inserter??

12、iostream迭代器??

13、反向迭代器

14、特定容器算法?? 

 


11.1 使用关联容器

1、set

#include "stdafx.h"#include <iostream>#include <set>using namespace std;int main(){    int a[] = {1,2,3};    set<int> s(a,a+3);    set<int>::iterator iter;//不能用autofor(auto iter=s.begin();iter!=s.end();iter++)        cout<<*iter<<" ";cout<<endl;if((iter=s.find(1))!=s.end());//无法find stringcout<<*++iter;int b;cin>>b;while(cin.get()!='\n')//不为为回车,get()函数直接转化成ASCII码,但是会删去最后一个元素,不好用{s.insert(b);cin>>b;}    return 0;}

思想:

(1)在find函数中 iter不能用auto,遍历的时候可以

(2)对于string类型的set无法用find函数

(3)利用cin.get()来判断是否输入回车,但是这种办法的缺点是,最后一个字符不能插入,这个问题有待解决

 

2、map

#include "stdafx.h"#include <map>#include <string>#include <iostream>using namespace std;int main(){       map<int, string> mapStudent;//pair<int,string>p;p=make_pair(v1,v2);<SPAN style="BACKGROUND-COLOR: rgb(240,248,255); FONT-FAMILY: Arial; COLOR: rgb(255,0,0); FONT-SIZE: 13px"> </SPAN>       mapStudent.insert(pair<int, string>(1, "student_one"));       mapStudent.insert(pair<int, string>(2, "student_two"));       mapStudent.insert(pair<int, string>(3, "student_three"));       map<int, string>::iterator  iter;       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)       {          cout<<iter->first<<"  "<<iter->second<<endl;       }}

输出:

1  student_one
2  student_two
3  student_three
请按任意键继续. . .

思想:

(1)发现一个问题,就是无法用string作为第一个,这样会出现问题,无法输出。如果两者不能都用整形,那么这个实际上有什么用处。

赋值和遍历操作

#include "stdafx.h"#include <map>#include <string>#include <iostream>using namespace std;int main(){  map<string,size_t> m;  m["aa"]=1;  cout<<m["aa"]<<" "<<m["bb"]<<endl;  for(auto iter=m.begin();iter!=m.end();iter++)  cout<<iter->first<<iter->second;}

 

思想:

(1)第一个参数为关键字,第二个为值key-value

(2)遍历为利用iter指向来读取,还是比较方便的

(3)进行查询的时候若找不到关键字,则将关键字加入,默认初始化。

 1、介绍

map 关键数组,保存关键字-值对

set 关键字即值,即只保存关键字的容器

multimap 关键字可重复出现

multiset 同样

无序

unordered_map//以哈希函数组织的map

unordered_set

unordered_multimap

unordered_set



 

0 0
原创粉丝点击