【C++ STL】模拟实现 map 和 set(对一颗红黑树进行封装)

一、STL - map 和 set

阅读STL源码就可以发现,其实 set 和 map 自己没有实现啥东西,整体就是对红黑树进行了一个封装,set 和 map 的 insert / erase 接口底层就是调用红黑树的 insert / erase 接口,并且 set 和 map 的迭代器也都是取的红黑树里面的迭代器

那 set 和 map 的核心区别在哪里呢?

set 底层封装了一颗红黑树,是 KV 结构,值得注意的是 key_type 和 value_type 都是 key,说明传了两个 key 给红黑树。

// stl_set.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class set {
   
public:
  typedef Key key_type;
  typedef Key value_type; // set的value
  //......
private:
  typedef rb_tree<key_type, value_type, 
                  identity<value_type>, key_compare, Alloc> rep_type; // 红黑树类型
  rep_type t;  // red-black tree representing set
  //......
};

map 底层封装了一颗红黑树,是 KV 结构,但 key_type 是 key,value_type 是 pair<const Key, T>,说明传了一个 key 和一个键值对(pair)给红黑树。

但这里和我自己理解的有些不一样,不应该只传一个 pair 就行了吗?为啥还要多传一个 key 呢?因为实现查找接口(find)时需要 key 作为形参。

// stl_map.h
template <class Key, class T, class Compare = less<Key>, class Alloc = alloc>
class map {
   
public:
  typedef Key key_type;
  typedef pair<const Key, T> value_type; // map的value
  //......
private:
  typedef rb_tree<key_type, value_type, 
                  select1st<value_type>, key_compare, Alloc> rep_type; // 红黑树类型
  rep_type t;  // red-black tree representing map  
  //......
};

我们发现 stl_map.h 和 stl_set.h 中并没有包含红黑树,那怎么用红黑树的呢?

在库文件和中可以找到原因,在之前包了 stl_tree.h 头文件,而这个就是红黑树的实现:

// map
#include <stl_tree.h>
#include <stl_map.h>
#include <stl_multimap.h>

// set
#include <stl_tree.h>
#include <stl_set.h>
#include <stl_multiset.h>

STL源码中红黑树的实现,只截取了关键部分:

红黑树节点结构是一个泛型设计,根据传进来 value 的类型决定是 set 还是 map

// 红黑树节点结构
// value: 节点数据的类型,key或者pair
template <class Value>
struct __rb_tree_node : public __rb_tree_node_base
{
   
  typedef __rb_tree_node<Value>* link_type;
  Value value_field;
};

// 红黑树结构
template <class Key, class Value, class KeyOfValue, class Compare,
          class Alloc = alloc>
class rb_tree {
   
protected:
  typedef __rb_tree_node<Value> rb_tree_node; // 红黑树节点
  //......
public:
  typedef rb_tree_node* link_type;
  //......
protected:
  link_type header;  
  //......
};

图解:

image-20220330153037721


二、模拟实现 map 和 set

2.1 改造红黑树的结构

这里直接拿我们自己写的红黑树代码(原先是KV模型的)改造下,使其既可兼容K模型,也可兼容KV模型:

我的红黑树里面具体存的是什么类型的元素,是由模板参数 T 来决定

  • 如果 T 是 Key 那么就是 set
  • 如果 T 是 pair<const Key, V> 那么就是 map

1、定义红黑树的节点结构

// 定义红黑颜色
enum Colour
{
   
	BLACK = 0,
	RED
};

// 定义红黑树节点结构
// T: 数据的类型,如果是map,则为pair<const K, V>; 如果是set,则为K
template<class T>
struct RBTreeNode
{
   
	T _data;       // 数据域

	Colour _col;   // 用来标记节点颜色
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	RBTreeNode(const T& data) // 构造函数
		: _data(data), _col(RED)
		, _left(nullptr), _right(nullptr), _parent(nullptr)
	{
   }
};

2、定义红黑树结构

改造前

// 定义红黑树结构(KV)
// K: 键值key的类型
// T: 数据的类型,如果是map,则为pair<const K, V>; 如果是set,则为K
template<class K, class T>
class RBTree
{
   
	typedef RBTreeNode<T> Node; // 红黑树节点

private:
	Node* _root;

public:
	RBTree() :_root(nullptr) {
   }  // 构造函数
    // ......
    bool Insert(const T& data);  // 插入节点
    // ......
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值