map要点


  map是以平衡二叉树完成,其元素是 key/value pair;
  map的是定义在std内的class templates:

template <class Key,class T,class Compare = less<key>,
          class Allocator = allocator<pair<cosnt key,T>>> 
class map;

  其中:

  • 第一个参数为key
  • 第二个参数为value
  • 第三个参数为排序准则
  • 第四个参数为内存模型

  map根据key自动进行排序,因此,根据某个已知key来搜寻某个元素,就有很好的性能;而根据已知value搜寻元素,性能就很糟糕。但是这也使得:不能直接改变元素key,因为这会改变元素的正确次序;所以,正如前面代码片段,key为const类型,而value则是常数可改;

  与vector、list不同的是,我们可以定义map的排序准则,一般默认为less<>;相同的是,key/value也必须可赋值、可复制,对于排序准则而言,必须可比较;

定义排序规则

  我们可以template参数定义排序规则:

std::map<folat,std::string,std::gerater<float> > coll;

  但map也支持运行时修改排序规则,如下:

map c(op)   //以op为排序准则,产生一个空的map/multimap

  其中,op是一个函数对象,需要我们定义排序规则,在c++标准程序库一书中,提供了下面这个示例,以作参考:

#include <iostream>
#include <iomanip>
#include <map>
#include <string>
#include <algorithm>
using namespace std;

class RuntimeStringCmp{
public:
  enum cmp_mode{normal,nocase};
  
private:
  const cmp_mode mode;
  
  static bool nocase_compare(char c1,char c2)
  {
    return toupper(c1) < toupper(c2);
  }
  
public:
  RuntimeStringCmp (cmp_mode m = normal) : mode(m){
    
  }
  
  bool operator()(const string& s1,const string& s2) const
  {
    if(mode == normal)
      return s1 < s2;
    else
      return lexicographical_compare(s1.begin(),s1.end(),s2.begin(),s2.end(),nocase_compare);
  }
};

typedef map<string,string,RuntimeStringCmp> StringStringMap;

void fillTheMap(StringStringMap coll);

int main()
{
  StringStringMap coll;
  fillTheMap(coll);
  RuntimeStringCmp ignorecase(RuntimeStringCmp::nocase);
  StringStringMap coll2(ignorecase);
}

  代码并不难懂,唯一可能需要注意的可能是函数对象这个概念,不太理解的同学可以补补;

  排序规则不同,但是pair类型相同,可以视作同一类型的map,可进行赋值,拷贝,互换,互换时排序规则也会互换;但是,不能用于比较;例如:

std::map<float,std::string> c1;
std::map<float,std::string,std::greater<float> > c2;
if(c1 == c2) { // ERROR :differenent types
...
}

元素查找

根据key值查找

  我们可以使用STL提供的find()函数来进行根据key值得查找,但是为了更好的性能,map提供了成员函数find(),如下表所示:
在这里插入图片描述

根据value查找

  正如前文所述,根据vale查找并没有根据value查找的良好性能,但是也无法避免存在这种使用的特殊场景,这种情况下,主要有两种方式:

  • 遍历map,根据value进行符合条件的判断;
  • 使用find_if,提供一个函数对象,根据value进行符合条件的判断。

元素排序

  和list相同,map是双向迭代器,所以无法使用STL中用于随机存取迭代器(vector随机存取迭代器)中的算法;

元素修改

  我们可以很轻易的修改value,无论是通过迭代器或者下标(返回引用)都可以,但是key值是const类型,该如何修改呢?
  我们可以像下面这样修改key值:

std::map<std::string,float> coll;
coll['new _key"] = coll["old_key'];
coll.erase("olg_key");

  上述过程可表述为:

创建新key
新key赋值
移除旧key

元素移除安插

  前文已知,map的key为const,这是因为map自动排序,而修改key值会打破这种排序,也因此,不能针对map调用任何变动性算法,如STL中的remove(),因为其实际是以一个参数覆盖被移除的元素;但我们可使用它们所提供的成员函数,例如erase()函数;
在这里插入图片描述
  在这里,我们需要注意erase使用方式与list、vector的不同,这里的erase并不会返回下一个元素的迭代器位置,这是因为,这个过程对于map而言将会耗时,所以STL设计时就否决了这种设计,那么我们该如何使用迭代器移除元素呢?正确做法如下:

for(pos = coll.begin() ; pos != coll.end();)
{
if(pos->second == value )
	coll.erase(pos++);
else
	+pos;
}

  pos++会移向下一元素,但返回其原始值的一个副本,因为erase被调用,但是pos已经不再指向那个即将被移除的元素了;

  至于安插,我们可以使用insert函数,也可以使用map[key] = value直接进行赋值,但是需要注意的是,后者效率较低,因为其实际完成两个过程:新元素先用default构造函数将实值初始化,然后这个初值又马上被真正的value给覆盖了;

元素获取

  map支持使用[]重载来获取元素,但需要注意的是,不是通过索引,而是key值;
  其次,这种方式获取元素也存在一定风险,很多时候,我们在该key值已经存在的情况下可以直接获取,但是如果不存在呢?则会安插新元素,这在有时候可能会导致一些我们意象之外的问题;例如:

std::cout << coll[”otto“] ;

  不知不觉间,我们已经为map新安插了一个键值为"otto",而value为0的元素;

map与multimap

  map与multimap的唯一区别在于,multimap允许元素重复,map不允许。

map与unordered_map

  • map内部实现了一个红黑树,红黑树具有自动排序的功能,因此map是有序的。unordered_map内部实现是哈希表,其元素排列是无序的。
  • map内部实现红黑树,所以黑多操作在lgn的时间复杂度下就可以实现,因此效率非常高。
  • unordered_map内部实现哈希表,因此其查找速度非常快,可达到O(1)。
  • map空间占用率高,因为其每一个节点都需要额外保存父节点、子节点和红黑性质,使得每一个节点都占用大量空间。
  • unordered_map中的哈希表建立比较费时。

  对于那些对顺序有要求的问题,使用map更高效;而如果不关注有序,对查找效率要求比较高,那么应该使用unordered_map。至于其他操作,二者基本相同,这里不再赘述。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值