C++ 中 map 和 set 的用法以及概念解析/含 map中operator[] 的用法

键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量<key,value>,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

STL中关于键值对的定义

template <class T1, class T2>
struct pair
{
    typedef T1 first_type;
    typedef T2 second_type;
    T1 first;
    T2 second;
    pair() : first(T1()), second(T2())
    {}

    pair(const T1& a, const T2& b) : first(a), second(b)
    {}
};

set

概念

C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。

关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。

特点

  1. set是按照一定次序存储元素的容器
  2. 在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
  3. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
  4. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直
    接迭代。
  5. set在底层是用二叉搜索树(红黑树)实现的。

注意点

  1. 与 map/multimap 不同,map/multimap 中存储的是真正的键值对 <key, value>,而 set 中只放 value,但在底层实际存放的是由<value, value>构成的键值对。
  2. set中插入元素时,只需要插入value即可,不需要构造键值对。
  3. set中的元素不可以重复(因此可以使用set进行去重)。
  4. 使用set的迭代器遍历set中的元素,可以得到有序序列(其实是对于底层红黑树的中序)
  5. set中的元素默认按照小于来比较(排成升序)
  6. set中查找某个元素,时间复杂度为:(LogN)
  7. set中的元素不允许修改。
  8. set中的底层使用红黑树

用法

#include<iostream>
#include <set>
using namespace std;

int main()
{
    //1.set 存储单个数据
    // 用数组array中的元素构造set
    int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 3, 7, 9, 2, 4, 6, 8, 3 };
    set<int> s(array, array + sizeof(array) / sizeof(array[0]));      //去重加排升序
   //multiset<int> s(array, array + sizeof(array) / sizeof(array[0]));  //不去重加排升序
    cout << s.size() << endl; 
     //正向打印set中的元素,从打印结果中可以看出:set可去重
    for (auto e : s)
        cout << e << " ";
    cout << endl;
     //使用反向迭代器逆向打印set中的元素
    for (auto it = s.rbegin(); it != s.rend(); ++it)
        cout << *it << " ";
    cout << endl;
     //set中判断s中3是否出现过,multiset中判断3出现的次数
    cout << s.count(3) << endl;
    
    //2. set 存储 pair 数据
    set<pair<string, string>> t;
    t.insert(make_pair("string", "字符串"));
    t.insert(make_pair("int", "整形"));
    t.insert(make_pair("float", "浮点"));
    auto e = t.find(make_pair("float", "浮点")); //pair 类型的数据返回的迭代器也是一个 pair
    cout << e->first << "->" << e->second << endl;
}

set 的四种形式

  1. set ; 去重 + 排升序 ; (底层是 红黑树 )
  2. multiset ; 不去重 + 排升序 ; (底层是 红黑树 )
  3. unordered_set (也就是 hash_set,但是 unordered_map 在C++11的时候被引入标准库了,而hash_map没有,所以现在都用前者,二者其实是同一个东西); 去重 + hash 排序 ; (底层是 hash )
  4. unordered_multiset ; 不去重 + hash 排序; (底层是 hash )

map

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素。
  2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起,为其取别名称为pair: typedef pair value_type;
  3. 在内部,map中的元素总是按照键值key进行比较排序的。
  4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行
    直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
  5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
  6. map通常被实现为平衡二叉搜索树 (红黑树)

使用

#include<iostream>
#include<map>
#include<set>
#include<string>
using namespace std;

int main()
{
	map<string, string> m;
	m.insert(make_pair("banana", "香蕉"));
	m.insert(make_pair("apple", "苹果"));
	m.insert(make_pair("apple", "苹果"));
	m.insert(make_pair("banana", "香蕉"));
	m.insert(make_pair("pear", "梨"));
	m.insert(make_pair("watermelon", "西瓜"));
	m.insert(make_pair("watermelon", "西瓜"));
	m.insert(make_pair("cherry", "樱桃"));
	m.insert(make_pair("Orange", "橙子"));
	m.insert(make_pair("Orange", "橙子"));
	m.insert(make_pair("Grape", "葡萄"));
	m.insert(make_pair("Grape", "葡萄"));
	m.insert(make_pair("Grape", "葡萄"));
	m.insert(make_pair("Grape", "葡萄"));
	
	cout << m.size() << endl;
	for (auto e : m)
	{
		cout << e.first << "-->" << e.second << " ";
	}
	cout << endl;
	m["Peach"] = "桃子";    //为什么这里的 [] 也能够进行插入呢?
	cout << m.size() << endl;
	for (auto e : m)
	{
		cout << e.first << "-->" << e.second << " ";
	}
	cout << endl;
}

map 的 operator[]

在这里插入图片描述

  • STL 中 operator[] 的实现
    mapped_type& operator[] (const key_type& k)
    {
    return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
    }

要想搞懂 operator [] 的实现,先得来看 insert 的返回值

single element (1)	
pair<iterator,bool> insert (const value_type& val);

with hint (2)	
iterator insert (iterator position, const value_type& val);
  • map对象中一个给定键只对应一个元素,也就是说 key_type(键) 和mapped_type(值) 是一一对应关系,如果试图插入的元素所对应的键已在容器中,则 insert 将不做任何操作;
  • 单元素版本(1)返回一对 (pair),其成员为 -> first 设置为指向新插入的元素或映射中具有等效键的元素的迭代器。如果插入了新元素,则将 bool 设置为true;如果已存在等效键,则将 bool 设置为 false,且不改变原来的 mapped_type;
pair<map<string,string>::iterator,bool> ret = m.insert(make_pair("Grape", "葡萄"));
mapped_type& operator[] (const key_type& k)
{
	return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
}
用我们自己的方式去写就是
mapped_type& operator[] (const key_type& k)
{
	pair<map<key_type,mapped_type>::iterator,bool> ret = m.insert(k,mapped_type());
	return ret.first->second;
}
  • pair 里面的两个元素的类型分别是:map<string,string >::iterator 这个指向新插入的元素或映射中具有等效键的元素的迭代器(这个迭代器其实也是一个 pair 对象的指针),和 bool 值。这个 pair 类型变量 ret 是insert 操作的返回值。
  • 带有提示(2)的版本返回一个迭代器,该迭代器指向新插入的元素或映射中已具有等效键的元素。元素可插入位置的提示 (position)。如果 position 指向插入元素之前的元素,函数将优化其插入时间。请注意,这只是一个提示,并不强制将新元素插入到映射容器中的该位置(映射中的元素始终遵循特定的顺序,具体取决于它们的键;
map<string,string> my_map;
map<string,string>::iterator it = my_map.begin();
my_map.insert(it,make_pair("string","字符串")); //这里不是强制插入,关键看 key 是否已经存在

在这里插入图片描述
map 的四种形式

  1. map; 去重 + 排升序 ; (底层是红黑树)
  2. multimap ; 不去重 + 排升序 ; (底层是红黑树)
  3. unordered_map ; 去重 + hash 排序 ; (底层是 hash )
  4. unordered_multimap ; 不去重 + hash 排序 ; (底层是 hash )
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值