【c++】容器:vector、list、map

【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      -------------------------------------

猜你喜欢

转载自blog.csdn.net/u012679707/article/details/80273738
今日推荐