![](https://i-blog.csdnimg.cn/blog_migrate/bf55ff906400b216a7dd5a8fc896e179.jpeg)
"湖人总冠军"
一、Map\Set的介绍
Set是C++标准库中的一种关联容器。所谓关联容器就是通过键(key)来读取和修改元素。与map关联容器不同,它只是单纯键的集合。
取自这里
Map是STL 的一个关联容器,它提供一对一(其中第一个可以称为关键字,每个关键字只能在map中出现一次,第二个可能称为该关键字的值)的数据处理能力,由于这个特性,它完成有可能在我们处理一对一数据的时候,在编程上提供快速通道。
取自这里
说下map内部数据的组织, map内部自建一颗红黑树(一种非严格意义上的平衡二叉树), 这颗树具有对数据自动排序的功能,所以在map内部所有的数据都是有序的,后边我们会见识到有序的好处。
取自这里
在map、set没出来之前呢,我们存储数据常用的都是顺序表、链表、栈队列等这些都被称为"序列式容器"。如果我们想要存储一些关联式键值对,例如"名字:身份证号","商品名称:商品数量"等等以这样数值建立起来的<Key,Value>模型,在数据检索时比序列式容器效率更高。
二、封装Map、Set
(1)红黑树
我们先来看看STL源码中,是如何处理结点的值。
![](https://i-blog.csdnimg.cn/blog_migrate/94b264d324ef164dde68e0b419533909.png)
template<class Value>
struct RBTreeNode
{
RBTreeNode<Value*> _left;
RBTreeNode<Value*> _right;
RBTreeNode<Value*> _parent;
Value _data;
Color _color;
RBTreeNode(const Value& data)
:_data(data),
_left(nullptr),
_right(nullptr),
_parent(nullptr),
_color(RED)
{}
};
我们之前实现的Node结点都是<K,V>模型,为什么源码中会这样声明了呢?在之后封装的时候会解答。
(2)红黑树类
同样,我们先拿源码来看看~
![](https://i-blog.csdnimg.cn/blog_migrate/40b6e571d4553c379716acedb0e0082a.png)
Key:键值
Value:值
KeyOfValue:取键值里的值
这里有几个疑问:
为什么 还会需要传Key这个参数?
为什么难道我们在Value里找不到Key值嘛?
为什么需KeyOfValue?
假设我们使用map,传入的一定是一个"pair值",那么map的数值传入到底层红黑树这一层,是传递给Key,还是Value呢?当然是Value!(之后会解答) 而我们比较的是键值(key)还是值(value)?是键值(key),所以我们传入键值与这个传入Value有必然的关联吗?肯定是没有的。
上述问题也就能够简略地回答这么设计的两个疑问。
那为什么需要KeyOfValue呢?因为C++中pair内置的"比较重载"很恶心,它不仅仅只会比较kv.first,还会比较kv.second。但是,我们只需要注意pair中的first。
![](https://i-blog.csdnimg.cn/blog_migrate/424a890d0dace041113f334027a21b96.png)
template<class K,class Value,class KeyOfValue>
class RBTree
{
typedef RBTree<Value> Node;
KeyOfValue kot;
//.....
}
(3)红黑树迭代器
![](https://i-blog.csdnimg.cn/blog_migrate/2cece903fe04285506c97994e380fc2b.png)
template<class Value,class Ref,class Ptr>
struct __RBTreeIterator__
{
typedef __RBTreeIterator__<Value, Ptr, Ref> Self;
typedef __RBTreeIterator__<Value, const Value&, const Value*> const_iterator;
typedef __RBTreeIterator__<Value, Value&, Value*> iterator;
typedef RBTreeNode<Value> Node;
Node* _node;
__RBTreeIterator__(const Node& node)
:_node(node)
{}
Ref operator*()
{
return _node->_data;
}
Ptr operator->()
{
return &_node->_data;
}
bool operator!=(const Self& s)
{
return _node != s._node;
}
bool operator==(const Self& s)
{
return _node == s._node;
}
};
那么如何进行++、--呢?
![](https://i-blog.csdnimg.cn/blog_migrate/c333b759364f219886fd1ee4c8919260.png)
![](https://i-blog.csdnimg.cn/blog_migrate/65a67a1a8e8bd11bf716b516ff04a7f6.png)
Self& operator++()
{
if (_node->_right)
{
//1.找到 右子树的最左节点
Node* left = _node->_right;
while (left->_left)
{
left = left->_left;
}
_node = left;
}
else
{
//2.向上调整
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_right)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
return *this;
}
Self& operator--()
{
if (_node->_left)
{
//找左子树的最右节点
Node* right = _node->_left;
while (right->_right)
{
right = right->_right;
}
_node = right;
}
else
{
Node* cur = _node;
Node* parent = cur->_parent;
while (parent && cur == parent->_left)
{
cur = cur->_parent;
parent = parent->_parent;
}
_node = parent;
}
}
const迭代器与非const迭代器的转换:
![](https://i-blog.csdnimg.cn/blog_migrate/d0a5995145d2e47d591b1d26ba7ced28.png)
在STl中,存在这样的转换。但是我们模拟实现的这一部分却不支持这样。那么如何理解源码那一份拷贝构造的代码呢?
![](https://i-blog.csdnimg.cn/blog_migrate/66a4f4a7e7538f24bc9f16fddcd936b3.png)
__RBTreeIterator__(const iterator& it)
:_node(it._node)
{}
(4)Map\Set封装
不过在这之前,我们已经实现了一份红黑树的迭代器,我们正好可以继续完善原RBTree的代码,
![](https://i-blog.csdnimg.cn/blog_migrate/25da825ccf185ab22447f9fd2c9cb856.png)
![](https://i-blog.csdnimg.cn/blog_migrate/525cf549a72c12b31c5a1f59dac4569b.png)
template<class K,class Value,class KeyOfValue>
class RBTree
{
public:
typedef RBTreeNode<Value> Node;
//普通迭代器 const迭代器
typedef __RBTreeIterator__<Value, Value&, Value*> iterator;
typedef __RBTreeIterator__<Value, const Value&, const Value*> const_iterator;
protected:
KeyOfValue kot; //比较函数
public:
iterator begin()
{
//找左节点
Node* left = _root;
while (left->_left)
{
left = left->_left;
}
return iterator(left);
}
iterator end()
{
return iterator(nullptr);
}
const_iterator begin()const
{
Node* left = _root;
while (left->_left)
{
left = left->_left;
}
return const_iterator(left);
}
const_iterator end()const
{
return const_iterator(nullptr);
}
Set:
![](https://i-blog.csdnimg.cn/blog_migrate/d31fbbc0734b363acf886327348e4a57.png)
template<class K>
class Set
{
public:
struct SetKeyOfValue
{
bool operator()(const K& key)
{
//如果是set的Key 直接返回就行了
return key;
}
};
//typename是向编译器声明 把类部类里的模板 当成一个对象
typedef typename RBTree<K, K, SetKeyOfValue>::const_iterator iterator;
typedef typename RBTree<K, K, SetKeyOfValue>::const_iterator const_iterator;
iterator begin()const
{
return _set.begin();
}
iterator end()const
{
return _set.end();
}
std::pair<iterator, bool> insert(const K& key)
{
//std::pair<typename RBTree<K, K, SetKeyOfValue>::iterator, bool> ret = _set.Insert(key);
auto ret = _set.Insert(key);
return std::make_pair(ret.first, ret.second);
}
private:
RBTree<const K, K, SetKeyOfValue> _set;
};
map;
![](https://i-blog.csdnimg.cn/blog_migrate/1799f69c3148ea0b06dcda05d7f6dca8.png)
template<class K,class V>
class Map
{
public:
struct MapKeyOfValue
{
const K& operator()(const std::pair<K,V>& kv)
{
return kv.first;
}
};
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfValue>::iterator iterator;
typedef typename RBTree<K, std::pair<const K, V>, MapKeyOfValue>::const_iterator const_iterator;
std::pair<iterator, bool> insert(const std::pair<K, V>& kv)
{
return _map.Insert(kv);
}
V& operator[](const K& key)
{
//auto ret = _map.Insert(make_pair(key, V()));
std::pair<typename RBTree<K,std::pair<K,V>,MapKeyOfValue>::iterator,bool> ret = _map.Insert(make_pair(key, V()));
return ret.first->second;
}
private:
RBTree<const K, std::pair<const K, V>, MapKeyOfValue> _map;
};
(5)map、set调用过程
![](https://i-blog.csdnimg.cn/blog_migrate/eaedcca95491c4c4995c19a3cb5a1459.png)
虽然画起来错综、杂糅,但是如果真的理解到了其实也还好。
三、测试
我们分别对set、map进行测试。
void TestSet()
{
std::cout << "TestSet" << std::endl;
int a[] = { 4, 2, 6, 1, 3, 5, 15, 7, 16, 14 };
Set<int> s;
for (auto e : a)
{
s.insert(e);
}
Set<int>::iterator it = s.begin();
while (it != s.end())
{
std::cout << *it << " ";
++it;
}
std::cout << std::endl;
}
void TestMap()
{
std::cout << "Map For Test" << std::endl;
std::string arr[] = { "苹果","苹果", "苹果", "梨儿","梨儿","西瓜","西瓜" };
Map<std::string, int> CountMap;
for (auto e : arr)
{
CountMap[e]++;
}
for (auto& kv : CountMap)
{
std::cout << kv.first << ":" << kv.second << std::endl;
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/ae905b4fc65b1f75cb69737c84106b84.png)
总结:
我们并非是要造轮子,阅读源码,汲取前辈们的智慧设计。
本篇到此为止,感谢你的阅读。
祝你好运,向阳而生~
![](https://i-blog.csdnimg.cn/blog_migrate/4f82f269f79f8965c58b570b0b4098d4.jpeg)