【C++】std::map、std::unordered_map详解

  • 容器是存放数据的地方,常见的容器有:序列式容器和关联式容器。序列式容器,即其中的元素不一定有序,但可以被排序,比如:vector、list、queue、stack、heap、priority_queue;而关联式容器内部结构基本上是一个平衡二叉树。所谓关联,指每个元素都有一个键值和一个实值,元素按照一定的规则存放,比如:RB-tree、set、map、unordered_map、hashtable、hash_map、hash_set。

一、map / multimap

  1. map是STL里重要容器之一,它的特性总结来讲就是:所有元素都会根据元素的键值key自动排序(也可根据自定义的仿函数进行自定义排序),其中的每个元素都是的键值对。map中不允许有键值相同的元素,因此map中元素的键值key不能修改,但是可以通过key修改与其对应的value。如果一定要修改与value对应的键值key,可将已存在的key删除掉,然后重新插入。
  2. map以模板(泛型)方式实现,可以存储任意类型的数据,包括使用者自定义的数据类型。Map主要用于一对一映射(one-to-one)的情況,map內部的实现自建一颗红黑树,这颗树具有对数据自动排序的功能。在map内部所有的数据都是有序的。
    【C++】std::map、std::unordered_map详解_第1张图片
  3. 定义原型:
template < class Key,                                     // map::key_type
           class T,                                       // map::mapped_type
           class Compare = less<Key>,                     // map::key_compare
           class Alloc = allocator<pair<const Key,T> >    // map::allocator_type
           > class map;
  1. 使用map得包含map类所在的头文件:
#include   //注意,STL头文件没有扩展名.h

map对象是模板类,需要关键字和存储对象两个模板参数:

std:map<int, string> personnel;

这样就定义了一个用int作为索引,并拥有相关联的指向string的指针。
为了使用方便,可以对模板类进行一下类型定义:

typedef map<int,CString> UDT_MAP_INT_CSTRING;
UDT_MAP_INT_CSTRING enumMap;
  1. 插入元素:
// 定义一个map对象
map<int, string> mapStudent;
// 第一种 用insert函數插入pair
mapStudent.insert(pair<int, string>(000, "student_zero"));
// 第二种 用insert函数插入value_type数据
mapStudent.insert(map<int, string>::value_type(001, "student_one"));
// 第三种 用"array"方式插入
mapStudent[123] = "student_first";
mapStudent[456] = "student_second";

以上三种用法,虽然都可以实现数据的插入,但是它们是有区别的,第一种和第二种在效果上是完成一样的。用insert函数插入数据,在数据的插入上涉及到集合的唯一性这个概念,即当map中有这个关键字时,insert操作是不能在插入数据的;但是用数组方式就不同了,它可以覆盖以前该关键字对应的值,用程序说明如下:

mapStudent.insert(map<int, string>::value_type (001, "student_one"));
mapStudent.insert(map<int, string>::value_type (001, "student_two"));

上面这两条语句执行后,map中001这个关键字对应的值是“student_one”,第二条语句并没有生效,那么这就涉及到我们怎么知道insert语句是否插入成功的问题了,可以用pair来获得是否插入成功,程序如下:

// 构造定义,返回一个pair对象
pair<iterator,bool> insert (const value_type& val);
pair<map<int, string>::iterator, bool> Insert_Pair;
Insert_Pair = mapStudent.insert(map<int, string>::value_type (001, "student_one"));
if(!Insert_Pair.second)
    cout << ""Error insert new element" << endl;

我们通过pair的第二个变量来知道是否插入成功,它的第一个变量返回的是一个map的迭代器,如果插入成功的话Insert_Pair.second应该是true的,否则为false。

  1. 查找元素:
    当所查找的关键key出现时,则返回一个指向该元素的迭代器,否则返回一个指向map::end的迭代器。
// find 返回迭代器指向当前查找元素的位置否则返回map::end()
iter = mapStudent.find("123");
if(iter != mapStudent.end())
   cout<<"Find, the value is"<<iter->second<<endl;
else
   cout<<"Do not Find"<<endl;
  1. 刪除与清空元素:
//迭代器刪除
iter = mapStudent.find("123");
mapStudent.erase(iter);
//用关键字刪除
int n = mapStudent.erase("123"); //如果刪除了會返回1,否則返回0
//用迭代器范围刪除 : 把整个map清空
mapStudent.erase(mapStudent.begin(), mapStudent.end());
//等同于mapStudent.clear()
  1. map的大小:
    在往map里面插入了数据,我们怎么知道当前已经插入了多少数据呢,可以用size函数,用法如下:
int nSize = mapStudent.size();
  1. map的基本操作函数:
    C++ maps是一种关联式容器,包含“关键字/值”对:
    begin() 返回指向map头部的迭代器
    clear() 删除所有元素
    count() 返回指定元素出现的次数
    empty() 如果map为空则返回true
    end() 返回指向map末尾的迭代器
    equal_range() 返回特殊条目的迭代器对
    erase() 删除一个元素
    find() 查找一个元素
    get_allocator() 返回map的配置器
    insert() 插入元素
    key_comp() 返回比较元素key的函数
    lower_bound() 返回键值>=给定元素的第一个位置
    max_size() 返回可以容纳的最大元素个数
    rbegin() 返回一个指向map尾部的逆向迭代器
    rend() 返回一个指向map头部的逆向迭代器
    size() 返回map中元素的个数
    swap() 交换两个map
    upper_bound() 返回键值>给定元素的第一个位置
    value_comp() 返回比较元素value的函数
  2. multimap和map的唯一差别是map中key必须是唯一的,而multimap中的key是可以重复的。由于不用再判断是否插入了相同key的元素,所以multimap的单个元素版本的insert的返回值不再是一个pair,而是一个iterator

二、unordered_map / unordered_multimap

  1. 在C++ 11中有新出4个关联式容器:unordered_map/unordered_set/unordered_multimap/unordered_multiset。
    这4个关联式容器与map/multimap/set/multiset功能基本类似,最主要就是底层结构不同,使用场景不容。如果需要得到一个有序序列,使用红黑树系列的关联式容器;如果需要更高的查询效率,使用以哈希表为底层的关联式容器
  2. C++ STL中,哈希表对应的容器是 unordered_map(since C++ 11)。根据 C++ 11 标准的推荐,用 unordered_map 代替 hash_map。哈希表是根据关键码值(key value)而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度,这个映射函数叫做散列函数。哈希表的一个重要问题就是如何解决映射冲突的问题。常用的有两种:开放地址法 和 链地址法。
  3. unordered_map底层实现是用哈希桶实现的:
    【C++】std::map、std::unordered_map详解_第2张图片

关联性:std::unorederd_map 是一个关联容器,其中的元素根据键来引用,而不是根据索引来引用。
无序性:在内部,std::unordered_map中的元素不会根据其键值或映射值按任何特定顺序排序,而是根据其哈希值组织到桶中,以允许通过键值直接快速访问各个元素(常量的平均时间复杂度)。
唯一性:std::unorederd_map中的元素的键是唯一的。

  1. 定义原型:
template < class Key,                                    // unordered_map::key_type
           class T,                                      // unordered_map::mapped_type
           class Hash = hash<Key>,                       // unordered_map::hasher
           class Pred = equal_to<Key>,                   // unordered_map::key_equal
           class Alloc = allocator< pair<const Key,T> >  // unordered_map::allocator_type
           > class unordered_map;
  1. 引入头文件:
#include 
  1. 成员函数:

迭代器:
begin   返回指向容器起始位置的迭代器(iterator)
end    返回指向容器末尾位置的迭代器
cbegin  返回指向容器起始位置的常迭代器(const_iterator)
cend    返回指向容器末尾位置的常迭代器

Capacity
size    返回有效元素个数
max_size 返回 unordered_map 支持的最大元素个数
empty 判断是否为空

元素访问
operator[]    访问元素
at        访问元素

元素修改
insert   插入元素
erase   删除元素
swap    交换内容
clear   清空内容
emplace  构造及插入一个元素
emplace_hint 按提示构造及插入一个元素

操作
find       通过给定主键查找元素,没找到:返回unordered_map::end
count      返回匹配给定主键的元素的个数
equal_range   返回值匹配给定搜索值的元素组成的范围

Buckets
bucket_count    返回槽(Bucket)数
max_bucket_count 返回最大槽数
bucket_size     返回槽大小
bucket       返回元素所在槽的序号
load_factor     返回载入因子,即一个元素槽(Bucket)的最大元素数
max_load_factor   返回或设置最大载入因子
rehash       设置槽数
reserve       请求改变容器容量

/*
题目:leetcode 1 Two Sum 两数之和
      从给定的一组数中找到两个数的和为target.返回两个数的序号,假设每次输入只有一组解
时间:2018年9月14日 12:16:34
思路:1.暴力搜索,对于每个数去找后面和它的和为target的数,如果找到了就返回,时间复杂度O(n^2)
      2.hash:先进行hash映射,,对于每一个数,可以在O(1)的时间找到
  即:用空间来弥补时间,建立使用一个HashMap,来建立数字和其下标位置之间的映射,也就是我求得一个数就直接能知道它的下标。
      我们都知道HashMap是常数级的查找效率,这样,我们在遍历数组的时候,用target减去遍历到的数字,就是另一个需要的数字了,
      直接在HashMap中查找其是否存在即可,注意要判断查找到的数字不是第一个数字,比如target是4,遍历到了一个2,那么另外一个2不能是之前那个2,
      整个实现步骤为:先遍历一遍数组,建立HashMap映射,然后再遍历一遍,开始查找,找到则记录index。
*/
 
 
#include 
#include 
#include 
 
using namespace std;
 
//法一:暴力搜索
class Solution {
public:
    vector<int> twoSum(vector<int> &num, int target) {
        vector<int> v;
        for(int i=0;i<num.size();++i)
        {
            for(int j=i+1;j<num.size();++j)
            {
                if(num[i]+num[j]==target)
                {
                    v.push_back(i);
                    v.push_back(j);
                    break;
                }
            }
        }
        return v;
    }
};
 
//法二:hash表
class Solution {
public:
    vector<int> twoSum(vector<int> &num, int target) {
        vector<int> v;
        unordered_map<int,int> m;//声明hash图
        for(int i=0;i<num.size();++i)//建立hash图
        {
            m[num[i]]=i;
        }
        
        for(auto &it:m)//遍历hash图;
            cout<<it.first<<" "<<it.second<<endl;
        for(int i=0;i<num.size();++i)
        {
            int t=target-num[i];
            if(m.count(t)&& m[t]!=i)//if(m.find(t)!=m.end()&&m[t]!=i)
            {
                v.push_back(i);
                v.push_back(m[t]);
                break;
            }
        }
        return v;
    }
};
 
int main()
{
    vector<int> a={1,6,4,5,7};
    Solution so;
    vector<int> res;
    res=so.twoSum(a,9);
    for(auto i:res)//遍历容器的简单方法
        cout<<i<<" ";
    return 0;
}

参考:

  1. https://www.cnblogs.com/tp-16b/p/9156810.html#_label0
  2. http://www.cplusplus.com/reference/map/map/
  3. http://www.cplusplus.com/reference/unordered_map/unordered_map/
  4. https://blog.csdn.net/weixin_42905141/article/details/93974191
  5. https://blog.csdn.net/sevenjoin/article/details/81943864
  6. https://blog.csdn.net/fcku_88/article/details/88353431

你可能感兴趣的:(c++)