数据结构: map与set的简单实现

目录

map与set的模拟实现

1.基本框架

2.模拟实现map与set所需要做的事

1.使用模板 , 达到泛性编程

2.比较问题

3.迭代器

RBTree中:

operator++

operator--

4.map [ ]  的实现

5.使用普通迭代器构造const迭代器

效果


map与set的模拟实现

1.基本框架

map

数据结构: map与set的简单实现_第1张图片

set

数据结构: map与set的简单实现_第2张图片

2.模拟实现map与set所需要做的事

  1. 调用模板, 使得用map与set复用同一棵树(解决map存pair, set存key的问题)
  2. 比较问题
  3. 迭代器中, ++,--的实现(遍历二叉树)
  4. map[ ] 的实现
  5. 维护底层搜索二叉树的性质, 要使用const迭代器, set可以都用const迭代器, 但是map需要达到key不能修改, 但允许value可以修改, 所以红黑树也需要实现普通迭代器,--使得key不能修改-->map再传递pair的时候,给K加上const就行                                                -->set的iterator 是红黑树中的const_iterator && const_iterator也是const_iterator            -->map的使用了iterator 与 const_iterator                                                                         set的iterator调用的时候, 会发生隐式类型转换,  普通迭代器-->const迭代器                     如果没提供普通迭代器到const迭代器的构造会报错  --> 需要提供

1.使用模板 , 达到泛性编程

   map: kv类型   set : k类型   

map:

数据结构: map与set的简单实现_第3张图片

set:

数据结构: map与set的简单实现_第4张图片


2.比较问题

由于使用了泛性编程, RBTreeNode里面的_data类型不确定, 可能是K, 也可能是pair

--如果是K(set)的比较,不会有问题

--如果是pair(map)的比较, 比较会出现问题

pair的比较: 如果first小就小, 如果first不小, 比较second, second小就小

但我们只期望按K去比较


解决: 使用仿函数

红黑树并不知道传过来的T是K类型还是pair类型, 但是上一层知道, 于是我们给它加一个仿函数,

这个仿函数在map/set里面实现 用来获取key,然后传递给红黑树, 让其根据key来比较

像这样:

数据结构: map与set的简单实现_第5张图片

map

数据结构: map与set的简单实现_第6张图片

set

数据结构: map与set的简单实现_第7张图片


用法:

data  为T类型, 可能是K类型, 也可能是pair类型,我们将其传递给仿函数,来获取key

1.插入

数据结构: map与set的简单实现_第8张图片

2.查找

   Node* cur ->_data  为T类型, 可能是K类型, 也可能是pair类型,我们将其传递给仿函数,来获取key

  

数据结构: map与set的简单实现_第9张图片

3.迭代器

map与set的迭代器是使用红黑树内部的迭代器

1.注意: 当我们去取一个类模板的内嵌类型的时候, 前面要加一个typename

2. 原因:  因为编译器无法区分, 你取得是类型还是静态变量 

加上typename告诉编译器所取得是类型, 等该类模板实例化后, 再去找这个类型

3.示例:

map

数据结构: map与set的简单实现_第10张图片

set

数据结构: map与set的简单实现_第11张图片

框架:

数据结构: map与set的简单实现_第12张图片

RBTree中:

数据结构: map与set的简单实现_第13张图片

begin

使用最左边的节点构造迭代器

end

1.使用空节点构造迭代器(一般是最右边的节点的下一个--->是nullptr)

2.实现的时候增加哨兵位

数据结构: map与set的简单实现_第14张图片

--此时如果it走到最后一个节点,再++, 就直接走到end

--特殊处理:

数据结构: map与set的简单实现_第15张图片

优点:end--的时候, 直接到最右边的节点

operator++

左子树,  根 ,  右子树  (中序,找完右子树后,  其树就被访问完了)
思路:
1.有右子树: 就往右子树走-->找右子树的最左节点

2.没有右子树: 看是不是parent的左子树
--是的话就把parent给it
--不是的话向上调整    (调整最后没有父亲节点了,跳空)

沿着根路径, 找孩子是父亲左孩子的那个祖先


数据结构: map与set的简单实现_第16张图片

对以下情况演示:

1.该节点有右子树

数据结构: map与set的简单实现_第17张图片

2.该节点没有右子树

数据结构: map与set的简单实现_第18张图片

3.走到最后一个节点

数据结构: map与set的简单实现_第19张图片

代码:

	Self& operator++() 
	{
		//1.右子树不为空,找右子树的最左节点
		if (_node->_right) 
		{
			Node* subRL = _node->_right;
			while (subRL->_left) 
			{
				subRL = subRL->_left;
			}
			_node = subRL;
		}
		//2.右子树为空, 找节点是父亲左孩子的祖先
		else 
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_right == cur)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}

operator--

和上面基本一样,只是换了个方向: 右子树,  根 ,  左子树(该顺序访问, 走完左子树,该树就走完)

思路:

1.有左子树, 就往左子树走--->找左子树里最右边节点

2.没有左子树, 就找节点是父亲的右孩子的祖先

--如果当前节点就是父亲的右孩子,  直接把parent给it

--如果当前节点不是父亲的右孩子, 沿根路径向上调整, 找到为止(或找到p为nullptr)

   找到了就把parent给it

代码:

	Self& operator--()
	{
		//1.如果有左子树,就往左子树走,找其最右边的节点
		if (_node->_left) 
		{
			Node* subLR = _node->_left;
			while (subLR->_right) 
			{
				subLR = subLR->_right;
			}
			//然后把这个节点给it
			_node = subLR;
		}
		//2.如果没有左子树, 就找节点是父亲右孩子的祖先
		else 
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && parent->_left == cur) 
			{
				cur = parent;
				parent = parent->_parent;
			}
			//然后把这个节点给it
			_node = parent;
		}

		return *this;
	}

4.map [ ]  的实现

1.修改insert的返回值为pair

--修改RBTree里Insert的返回值:

--空树插入成功

数据结构: map与set的简单实现_第20张图片

--非空树插入失败

--非空树插入成功

这里使用newnode记录以下cur节点,  下面需要对红黑树调整, cur 可能会发生改变

数据结构: map与set的简单实现_第21张图片

2.[ ]的实现

--调用RBTree里的Insert函数

--返回其second

		pair insert(const pair& kv)
		{
			return _t.Insert(kv);
		}

		//[]的实现
		V& operator[](const K& key) 
		{
			pair ret =  _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}

5.使用普通迭代器构造const迭代器

1.问题:如果使用普通迭代器, 那么key的值会被修改, 不满足二叉搜索树性质

示例:

数据结构: map与set的简单实现_第22张图片

2.我们使用const迭代器

出现报错

--原因:发生隐式类型转换, 但我们没有提供使用普通迭代器构造const迭代器的构造函数,

转换不了, 会报错

数据结构: map与set的简单实现_第23张图片

--解决: 提供一个支持普通迭代器到const迭代器的转换

--为什么RBTree不都返回const迭代器??

---因为map与set复用的是同一个红黑树来set全用const可以, 但map允许修改V

代码:

数据结构: map与set的简单实现_第24张图片

--当Ref是T&, Ptr是T*的时候, 调用这个构造函数就是拷贝构造

--当Ref是constT& ,Ptr是constT*的时候, 调用这个构造函数就是支持普通迭代器构造const迭代器的转换

效果

数据结构: map与set的简单实现_第25张图片

代码:Map and Set/Map and Set · 朱垚/数据结构练习 - 码云 - 开源中国 (gitee.com)

你可能感兴趣的:(数据结构,数据结构)