C++ STL之map、multimap、hash_map、unordered_map

一. 简介

  • map是STL 的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
  • map内部自建一颗红黑树(一种非严格意义上的平衡二叉树),这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
  • map 是一种有序无重复的关联容器。关联容器与顺序容器不同,他们的元素是按照关键字来保存和访问的,而顺序元素是按照它们在容器中的位置保存和访问的。map保存的是一种 key - value 的pair对象,其中 key 是关键字,value 是关键字对应的值。通过 key找到对应的 value。map中按照 key的大小升序排列pair对象。map会自动建立Key - value的对应。

二. 用法

1. 头文件

#include <map> 

2. 构造函数

//在定义map对象时,必须分别指明键和值的类型。Map建立key-value的一种映射。

//创建一个名为m的空map对象,其键和值的类型分别为key和value
map<key, value> m; 

//创建m2的副本m,m与m2必须有相同的键类型和值类型
map<key, value> m(m2);
 
//创建map类型的对象m,存储迭代器b和e标记的范围内所有元素的副本,元素的类型必须能转换为pair
map<key, value> m(b,e); 

//comp可选,键值对存放策略
map<key, value, comp> mp;

3. map定义的类型

// 在map容器中,用作索引的键的类型
map<key, value> ::key_type; 

// 在map容器中,键所关联的值的类型
map<key, value> ::mapped_type;  

// 一个pair类型,它的first元素具有const map<key, value> ::key_type类型,而second元素则xmap<key, value> :: mapped_type类型。
map<key, value> ::value_type;  

在学习map接口时,谨记其value_type是pair类型,它的值成员可以修改(second成员),但键成员不能修改。这个value_type相当于map的元素类型,而不是键所对应的值的类型。 
对map迭代器进行解引用将产生pair类型的对象,它的first成员存放键,为const,而second成员存放值。

4. 插入数据

①用insert函数插入pair数据

  m.insert(pair<int, string>(1, "student_one"));  
make_pair()//返回类型为对应的pair类型  
无需写出类别,就可以生成一个pair对象  
例:  
make_pair(1,'@')  
而不必费力的写成  
pair<int ,char>(1,'@')  

②用insert函数插入value_type数据

m.insert(map<int, string>::value_type (1, "student_one"));  

③数组插入方式

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()  
{  
       map<int, string> mapStudent;  
       mapStudent[1] =  "student_one";  
       mapStudent[2] =  "student_two";  
       mapStudent[3] =  "student_three";  
       map<int, string>::iterator  iter;  
       for(iter = mapStudent.begin(); iter != mapStudent.end(); iter++)  
       {  
          cout<<iter->first<<"   "<<iter->second<<endl;  
       }  
}  

5. map的大小

m.size()查看

6. 数据的遍历
①应用前向迭代器

例如4-③中的例子

②应用反向迭代器

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()  
{  
       map<int, string> mapStudent;  
       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>::reverse_iterator  iter;  
       for(iter = mapStudent.rbegin(); iter != mapStudent.rend(); iter++)  
       {  
          cout<<iter->first<<"   "<<iter->second<<endl;  
       }  
}  

③用数组方式

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()  
{  
       map<int, string> mapStudent;  
       mapStudent.insert(pair<int, string>(1, "student_one"));  
       mapStudent.insert(pair<int, string>(2, "student_two"));  
       mapStudent.insert(pair<int, string>(3, "student_three"));  
       int nSize = mapStudent.size()  
//此处有误,应该是 for(int nIndex = 1; nIndex <= nSize; nIndex++)   
//by rainfish  
       for(int nIndex = 0; nIndex < nSize; nIndex++)  
       {  
           cout<<mapStudent[nIndex]<<end;  
       }  
}  

7. 数据的查找

#include <map>  
#include <string>  
#include <iostream>  
using namespace std;  
int main()  
{  
       map<int, string> mapStudent;  
       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;  
       iter = mapStudent.find(1);  
       if(iter != mapStudent.end())  
      {  
           cout<<"Find, the value is "<<iter->second<<endl;  
       }  
       Else  
       {  
           cout<<"Do not Find"<<endl;  
       }  
}  

8. 排序

#include <map>  
#include <string>  
uing namespace std;  
Typedef struct tagStudentInfo  
{  
       int      nID;  
       String   strName;  
}StudentInfo, *PStudentInfo;  //学生信息  
int main()  
{  
       int nSize;  
       //用学生信息映射分数  
       map<StudentInfo, int>mapStudent;  
       map<StudentInfo, int>::iterator iter;  
       StudentInfo studentInfo;  
       studentInfo.nID = 1;  
       studentInfo.strName = "student_one"  
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 90));  
       studentInfo.nID = 2;  
       studentInfo.strName = "student_two";  
       mapStudent.insert(pair<StudentInfo, int>(studentInfo, 80));  
       for (iter=mapStudent.begin(); iter!=mapStudent.end(); iter++)  
         cout<<iter->first.nID<<endl<<iter->first.strName<<endl<<iter->second<<endl;  
}  

以上程序是无法编译通过的,只要重载小于号,就OK了,如下:

Typedef struct tagStudentInfo  
{  
       int      nID;  
       String   strName;  
       Bool operator < (tagStudentInfo const& _A) const  
       {  
              //这个函数指定排序策略,按nID排序,如果nID相等的话,按strName排序  
              If(nID < _A.nID)  return true;  
              If(nID == _A.nID) return strName.compare(_A.strName) < 0;  
              Return false;  
       }  
}StudentInfo, *PStudentInfo;  //学生信息  

 

三. multimap

  • multimap 与 map 一样,都是使用红黑树对记录型的元素数据,按元素键值的比较关系,进行快速的插入、删除和检索操作。
  • 所不同的是 multimap 允许将具有重复键值的元素插入容器。在 multimap 容器中,元素的键值与元素的映照数据的映照关系,是多对多的,因此,multimap 称为多重映照容器。
  • multimap 与 map 之间的多重特性差异,类似于 multiset 与 set 的多重特性差异。
  • multimap 多重映照容器实现了 Sorted Associative Container 、Pair Associative Container 和 Multimap Associative Container 概念的接口规范。
  • 一些说明:
/*
 *
 ********************************************
 *  multimap多重映照容器的基础说明:
 ********************************************
 *
 * multimap多重映照容器:容器的数据结构采用红黑树进行管理
 * multimap的所有元素都是pair:第一元素为键值(key),不能修改;第二元素为实值(value),可被修改
 *
 * multimap特性以及用法与map完全相同,唯一的差别在于:
 * 允许重复键值的元素插入容器(使用了RB-Tree的insert_equal函数) 
 * 因此:
 * 键值key与元素value的映照关系是多对多的关系
 * 没有定义[]操作运算 
 * 
 * Sorted Associative Container  Pair Associative Container   Unique Associative Container
 *
 * 使用multimap必须使用宏语句#include <map>          
 *
*/

 /*************创建multimap对象**************/

multimap<char,int,greater<char> > a; 
//元素键值类型为char,映照数据类型为int,键值的比较函数对象为greater<char>

multimap(const key_compare& comp);
//指定一个比较函数对象comp来创建map对象

multimap(const multisetr&);      
//multimap<int,char*> b(a); //此时使用默认的键值比较函数less<int>

multimap(first,last);         
multimap(first,last,const key_compare& comp);  
 
//Example:
 pair<const int ,char> p1(1,'a');
 pair<const int ,char> p2(2,'b');
 pair<const int ,char> p3(3,'c');
 pair<const int ,char> p4(4,'d');
 pair<const int ,char> pairArray[]={p1,p2,p3,p4};
 multimap<const int,char> m4(pairArray,pairArray+5);
 multimap<const int,char> m3(m4);
 multimap<const int,char,greater<const int> > m5(pairArray,pairArray+5,greater<const int>());

 
 /**************元素的插入****************/
 typedef pair<const key,T> value_type;

 pair<iterator,bool> insert(const value_type& v);    

 iterator insert(iterator pos,const value_type& v);

 void insert(first,last);


 /*********元素的删除*************/
 void erase(iterator pos);
 
size_type erase(const key_type& k);
//删除等于键值k的元素

void erase(first,last);
//删除[first,last)区间的元素

void clear();


/*********访问与搜索**************/
 iterator begin();iterator end();
//企图通过迭代器改变元素是不被允许的

reverse_iterator rbegin();
reverse_iterator rend();

iterator find(const key_type& k) const;
pair<iterator,iterator> equal_range(const key_type& k) const;//返回的pair对象,
     //first为lower_bound(k);大于等于k的第一个元素位置
     //second为upper_bound();大于k的第一个元素位置


/*********其它常用函数*************/
bool empty() const;
size_type size() const;
size_type count(const key_type& k) const;   //返回键值等于k的元素个数
void swap();

 

  • 运用例子:
#include <map>
#include <string>
#include <iostream>
 
// 基本操作与set类型,牢记map中所有元素都是pair
// 对于自定义类,初学者会觉得比较函数如何构造很麻烦,这个可以参照前面的书写示例
// 但若设置键值为int或char类型,无须构造比较函数
 
struct student{
 char* name;
 int age;
 char* city;
 char* phone;
};
 
int main()
{
	 using namespace std;
 
	 student s[]={
	  {"童进",23,"武汉","XXX"},
	  {"老大",23,"武汉","XXX"},
	  {"饺子",23,"武汉","XXX"},
	  {"王老虎",23,"武汉","XXX"},
	  {"周润发",23,"武汉","XXX"},
	  {"周星星",23,"武汉","XXX"}
	 };
	  pair<int,student> p1(4,s[0]);
	  pair<int,student> p2(2,s[1]);
	  pair<int,student> p3(3,s[2]);
	  pair<int,student> p4(4,s[3]);  //键值key与p1相同
	  pair<int,student> p5(5,s[4]);
	  pair<int,student> p6(6,s[5]);
	 multimap<int,student> a;
	 a.insert(p1);
	 a.insert(p2);
	 a.insert(p3);
	 a.insert(p4);
	 a.insert(p5);
	 a.insert(p6);
	 typedef multimap<int,student>::iterator int_multimap;
	 pair<int_multimap,int_multimap> p = a.equal_range(4);
	 int_multimap i = a.find(4);
	 cout<<"班上key值为"<< i->first<<"的学生有:"<<a.count(4)<<"名,"<<"   他们是:"<<endl;
	 for(int_multimap k = p.first; k != p.second; k++)
	 {
		cout<<k->second.name<<endl;
	 }
	 cout<<"删除重复键值的同学"<<endl;
	 a.erase(i);
	 cout<<"现在班上总人数为:"<<a.size()<<".   人员如下:"<<endl;
	 for(multimap<int,student>::iterator j=a.begin(); j != a.end(); j++)
	 {      
		  cout<<"The name: "<<j->second.name<<"      "<<"age: "<<j->second.age<<"   "
		   <<"city: "<<j->second.city<<"      "<<"phone: "<<j->second.phone<<endl;
	 }
 
	 return 0;
}
 

 

四. hash_map

  • hash_map的用法和map是一样的,提供了 insert,size,count等操作,并且里面的元素也是以pair类型来存贮的。
  • 虽然对外部提供的函数和数据类型是一致的,但是其底层实现是完全不同的,map底层的数据结构是红黑树,hansh_map却是哈希表来实现的。
  • 总体来说,hash_map 查找速度会比map快,而且查找速度基本和数据量大小无关,属于常数级别;而map的查找速度是log(n)级别。hash还有hash函数的耗时。当有100w条记录的时候,map也只需要20次的比较,200w也只需要21次的比较!所以并不一定常数就比log(n) 小!
  • hash_map对空间的要求要比map高很多,所以是以空间换时间的方法,而且,hash_map如果hash函数和hash因子选择不好的话,也许不会达到你要的效果。所以用map,还是hash_map,从3个方面来权衡: 查找速度、数据量、内存使用。
  • 另外可以通过重写 hash_compair仿函数,更改里面关于桶数量的定义,如果取值合适,也可以得到更优的性能。而且如果你的数据是自定义的类型,必须要重写这个仿函数。可以模仿原来的写法,所有的成员函数,成员变量一个不能少!
  • 另外再有两篇参考文章:
    hash_map (STL/CLR)【微软STL文档】
    [Z]C++ STL中哈希表 hash_map介绍

 

五. unordered_map

  • hash_map并非标准STL中的,VS直接#include<unordered_map>会提醒你这个库可能变化比较大,建议你换用其他的。所以现在比较常用的是类似的unordered_map。
  • hash_map内部是一个hash_table一般是由一个大vector,vector元素节点可挂接链表来解决冲突,来实现。

  • unordered_map需要定义hash_value函数并且重载operator==。对于内置类型,如string,这些都不用操心。对于自定义的类型做key,就需要自己重载operator< 或者hash_value()了。 

 

  • 一个使用例子:
#include<iostream> 
#include<unordered_map>
#include<string>
using namespace std;

/*
一个应用哈希表的例子:
查找一个字符串中第一个只出现一次的数
*/

int main() {
	string in = "abcdefabcdf";
	unordered_map<char,int> hashtable;
	for (int i = 0; i < in.length(); i++) {
		//key是每一个字符,value是出现次数
		unordered_map<char, int>::iterator one;
		one = hashtable.find(in[i]);
		//如果没有,直接插入,并且标记为1
		if (one == hashtable.end()) {
			hashtable.insert(make_pair(in[i], 1));
		}
		//如果有,该位置加一
		else {
			one->second = one->second + 1;
		}
	}
	//查找并输出
	for (unordered_map<char, int>::iterator i = hashtable.begin(); i != hashtable.end(); i++) {
		if (i->second == 1) {
			cout << i->first << endl;
			break;
		}
	}
	getchar();
	return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值