【c++】容器
1. 容器
2. 顺序容器
3. 向量
4. 双向链表
5. 关联容器
6. 映射
参考:《c++从入门到精通》 人民邮电出版社
标准模板库STL的c++最有特色、最实用的部分之一。标准模板库包含了容器类、迭代器和算法三部分。
容器:容器就是可以用于存放各种类型数据的数据结构。
迭代器:迭代器可依次存取容器中的元素,在C++中称迭代器为指针,它们提供了访问容器、序列中每个元素的方法。
算法:是用来操作容器中的元素的函数模板。C++的stl库中提供了能在各种容器中通用的算法,比如插入、删除、查找、排序等,大约有70种标准算法。这些算法的最重要的一个特性就是:统一性,并且可广泛应用于不同的对象和内置的数据类型。
1.容器
容器是伴随着面向对象语言的诞生而提出的,现在的几乎所有的面向对象的语言中,都伴随着一个容器集。在c++中的容器集就是STL(标准模板库)。
那什么是容器呢?
简单来讲,容器是用来装东西的。在C++中,容器就是可以用于存放各种类型数据的数据结构。为了方便使用,c++的STL库自带一些容器,如vector(向量)、list(列表)等就是根据不同的数据结构来定制的容器。
C++的stl库里提供3类容器:
顺序容器:vector、deque(双端队列)、list(列表)等。
关联容器:set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)等。
容器适配器:stack(栈)、queue(队列)、priority_queue(优先级队列)等。
2.顺序容器
顺序容器有:vector、deque(双端队列)、list(列表)等。
Vector(向量)类提供了具有连续内存地址的数据结构,通过下标运算符“[ ]”直接有效的访问向量的任何元素。其定义在头文件<vector>中。
向量的优点:
1. 向量可以用来实现队列、堆栈、列表和其他更复杂的结构。
2. Vector支持随机访问迭代器,vector的迭代器通常实现为vector元素的指针。
3. 与数组不同,vector的内存用尽时,将自动分配更大的连续内存区,将原先的元素复制到新的内存区,这也是vector的优点。
List(列表)是由双向链表组成的,列表的定义在头文件<list>中。所以有关链表的操作都适合列表操作,它有两个指针域,可以向前or向后进行访问,但不能随机访问即支持双向迭代器。
List的优点:
使用起来很方便,与双链表类模板相似,但通用性更好,使用更方便。
Deque(双端队列类)类是由双端队列组成,多以只允许在队列的两端进行操作。支持随机访问迭代器,也支持通过使用下标操作符“[ ]”进行访问。当要增加双端队列的存储空间时,可以在内存块中的deque两端进行分配,通常保存为这些快的指针数组。双端队列的定义在头文件<deque>中。
Deque(双端队列)的优点:
1. 双端队列利用不连续的内存空间,它的迭代器比vector的迭代器更加智能化。
2. 对双端队列分配存储块后,往往要等删除双端队列时才释放,它比重复分配(释放再分配)更有效,但也更浪费内存。
3.向量
向量属于顺序容器,用于容纳不定长线性序列,提供对序列的快速随机访问(也称直接访问)。这是因为,向量容器使用动态数组存储、管理对象。因为数组是一个随机访问数据结构,所以可以随机访问向量中的元素。
向量是一个动态数组,所以在尾部添加速度很快,在中间插入很慢。并且,向量是动态结构,它的大小不固定,可以在程序运行时增加或减少。
向量容器的对象的声明:
Vector<int> intVector; //int 型向量 Vector< string > stringVector; //string 型向量
Vector类包含了多个构造函数,其中包括默认构造函数,因此可以通过多种方式来声明和初始化向量容器。下表总结了常用的向量容器声明和初始化语句:
下表给出了vector类对象的与插入和删除元素的操作相关的成员函数,可以直接使用:
【向量应用实例】求n以内的素数
代码如下:
// vector操作.cpp //判断n以内的素数(质数)prime #include<iostream> #include<vector> using namespace std; int main(int argc,char *argv[]) { vector<int> A(10); //先定义一个int型向量,初始大小为 10 int n; // n以内的素数 int prime_count=0; cout<<"输入一个数:"<<endl; cin>>n; // n=1的情况,无素数 if(n==1) { cout<<"无符合条件的素数"<<endl; return 0; } // n=2 仅有2这一个素数(2是最小的质数) if(n==2) { cout<<"2"<<endl; return 0; } // n>2的情况 A[prime_count++]=2; //像访问数组一样,用下标[]访问vector for(int i=3;i<=n;i++) { if(prime_count == A.size() ) { A.resize(prime_count +10) ;//vector A的容量再加 10 } //求素数的核心代码 if(i%2==0) continue; // 大于2的偶数肯定不是质数 int j=3; while( j<=i/2 && i%j!=0 ) { j+=2; } //判断j跳出上述while原因: //若i能被j整除,i肯定不是质数; //若i还不能被j整数,且j已大于i/2,则i是质数。 if(j >i/2 ) { A[prime_count++] =i; } } for(int i=0;i<prime_count;i++) cout<<A[i]<<" "; cout<<endl; return 0; }
运行结果:
4.列表
列表(list)是由双向链表组成的。它的数据由若干个节点构成,每一个节点都包括一个信息块(即实际存储的数据)、一个前驱指针和一个后驱指针,可以向前也可以向后进行访问,但不能随机访问。
List的特点:前两条为优点,后两条为缺点
(1) 不使用连续的内存空间,这样可以随意的进行动态操作。(这是因为链表不使用连续内存)
(2)可以在内部的任何位置快速的插入和删除,比vector的中间插入和删除要灵活。(这也是链表比数组的插入和删除操作更灵活的优点)
(3)不能进行内部的随机访问,即不支持”[ ]”和vector.at( )。(链表要找到一个位置必须从头一个一个按顺序查找)
(4)相对于vector要占用更多的内存。
【list实例】求list对象中某个值的个数
【方法一:用count()函数】
count(list1.begin(),list1.end(), 100);
代码如下:
// list操作.cpp //统计list中的某个数 的个数 #include<iostream> #include<list> #include<algorithm> // 包含算法类库 例如count() using namespace std; int main(int argc,char *argv[]) { list<int> list1; //定义一个列表对象 list1.push_back(99); // 尾部插入 list1.push_back(99); list1.push_back(99); list1.push_back(100); list1.push_back(100); list1.push_back(100); int cnt=0; cnt=count(list1.begin(),list1.end(), 100); cout<<"100 的个数为:"<<cnt<<endl; return 0; }
运行结果:
【方法二:用迭代器】
核心:
list<int>::iterator list1_iterator ;//定义列表的迭代器,相当于指针 for(list1_iterator=list1.begin(); list1_iterator !=list1.end(); list1_iterator++ ) { if(*list1_iterator ==100 ) cnt++; }
代码如下:
// list操作.cpp //统计list中的某个数 的个数 #include<iostream> #include<list> #include<algorithm> // 包含算法类库 例如count() using namespace std; int main(int argc,char *argv[]) { list<int> list1; //定义一个列表对象 list<int>::iterator list1_iterator ;//定义列表的迭代器,相当于指针 list1.push_back(99); // 尾部插入 list1.push_back(99); list1.push_back(99); list1.push_back(100); list1.push_back(100); list1.push_back(100); int cnt=0; for(list1_iterator=list1.begin(); list1_iterator !=list1.end(); list1_iterator++ ) { if(*list1_iterator ==100 ) cnt++; } cout<<"100 的个数为:"<<cnt<<endl; return 0; }
运行结果:
5.关联容器
查找和排序总是对关键字进行的,函数模板和类模板中只介绍了通用类型,并没有涉及关键字。关联容器能通过关键字直接访问(存储和读取元素)。关联容器有set(集合)、multiset(多重集合)、map(映射)、multimap(多重映射)等。
集合和多重集合类,提供了控制数值集合的操作,其中数值是关键字,多重集合关联器用于快速存储和读取关键字。
映射和多重映射类提供了操作与关键字相关联的映射值的方法。多重映射和映射关联器用于快速存储和读取关键字与相关值(关键字/数值 对)
6.map
查找和排序总是对关键字进行的,关联容器能通过关键字直接访问(存储和读取元素)。
映射map就是STL中的一个关联容器,它提供一对一的数据处理能力。Map的元素是由key和value两个分量组成的对偶(key,value)。元素的键key是唯一的,给定一个key,就能唯一的确定另一个分量value。
例如:学生的学号和姓名就存在一一对应关系,给出学号就能找到相关联的学生姓名。假如学号和姓名都用string类型表示,可以用map构造一个名称为mapStudent的map对象。
Map<string, string> mapStudent;
为了实现类似数组的功能,类map重载了下标操作符“[ ]”。
T & operator[] (const Key &k)
其中,k是元素的键分量,类型为Key,返回值是元素的另一个分量value,类型为T.
例如:
map<string , int > m; m[“小红”]=100; int a=m[“小红”];
【map实例】
(1)map插入操作,并检查是否插入成功
map<string,string> mapStu ; //声明一个map对象 map<string,string>::iterator it;//声明一个map容器的迭代器 pair< map<string,string>::iterator, bool> ins_pair; //声明一个pair对象,检查插入是否成功 // 1.插入操作,并检查是否插入成功 ins_pair= mapStu.insert( pair<string,string>("001","zls")) ;//向容器插入元素,并返回值给pair对象 if(! ins_pair.second ) { cout<<"插入失败:001,zls"<<endl; } ins_pair= mapStu.insert( pair<string,string>("002","zas")) ;//向容器插入元素,并返回值给pair对象 if(! ins_pair.second ) { cout<<"插入失败:002,zas"<<endl; } ins_pair= mapStu.insert( pair<string,string>("003","zes")) ;//向容器插入元素 if(! ins_pair.second ) { cout<<"插入失败:003,zes"<<endl; } ins_pair= mapStu.insert( pair<string,string>("003","zjs")) ;//插入无效,因为关键字重复 if(! ins_pair.second ) { cout<<"插入失败:003,zjs"<<endl; } for(it=mapStu.begin();it!=mapStu.end(); it++) { cout<<"学号:"<<it->first <<" 姓名:"<<it->second <<endl; }
运行结果:
(2)修改map值
// 2.修改map存在值 cout<<endl<<"修改map存在值"<<endl; mapStu["002"]="hello"; mapStu["007"]="girl"; mapStu["009"]="wow"; for(it=mapStu.begin();it!=mapStu.end(); it++) { cout<<"学号:"<<it->first <<" 姓名:"<<it->second <<endl; }
运行结果:
(3)查找功能
// 3.查找map值 cout<<endl<<"查找map值"<<endl; it=mapStu.find("001"); if(it!=mapStu.end() ) { cout<<"找到,001的学生姓名是:"<<it->second <<endl; } else cout<<"没有找到!"<<endl;
运行结果:
------------------------------------------- END -------------------------------------