C++数据结构重要知识点(4)(map和set封装)

前面我们已经实现了红黑树,接下来我们需要将这个数据结构封装成map和set两个容器,其中很多地方的处理都有一定难度,下面会按照我的思路讲解map的改造

map成员变量如下,如果是第一次看到它,那么一定会很陌生,下面我会从问到答的形式分析:

1.为什么使用pair<K, V>?

一颗红黑树要实例化出set和map,其中set和map都有key,但set存储数据的对象还是key,map存储数据的对象是pair,这里为了统一map和set,统一将第一个参数用于接收key,第二个参数用于接收存储数据的类型。第三个参数后面紧接着会讲,它也是为了统一map和set所需的参数。

2.为什么不用更简单的K, V来直接实例化红黑树?

在红黑树的实现中,我们直接将树存储的数据类型写死了,但在这里我们要实例化的是两个容器,两个容器存储数据的方式不同,所以我们要抓住它们的共同点:都有key,都有一个存储数据的类型。如果直接以K, V来存储数据,我们可以想象在RBtree实现中没有办法来区分map和set。V传map的value,set的key,那如何判断这个V到底是map的value还是set的key呢?

理解了这个,set的成员变量的实例化就能很快写出来了。

3.为什么要使用const K和pair<const K, V>呢?那个const有什么讲究吗?

上面我讲了,K是接收key,而T是接收存储类型。T接收存储类型后会用这个类型来实例化结点

在之前红黑树的实现里,我们默认创建的node都是pair,但在这里我们要区分set和map,用模板参数T来作为_val的类型。当set和map实例化时,会分别传const K和pair<const K, V>作为_val的类型用于区分。同时,当利用迭代器对数据进行访问时,得到的_val也是const K和pair<const K, V>类型,均不能对key进行修改,符合了关联式容器的需求。

3.改变了模板参数会对原来红黑树的实现产生怎样的影响?

首先我们需要想清楚,结点指针始终不变,都有_left、_right、_parent,但是_val类型发生改变,这意味着只有涉及_val比较的代码才需要修改。像旋转、红黑树的判断的函数都只涉及结点指针或颜色的识别,跟值没关系,所以它们不需要改变。

(1)_val比较

对于map的pair,比较的是pair的key,而对于set,比较的也是key。但是如何区分呢?

很多人会想到第一个模板参数传的就是key,但注意在RBTree中,第一个模板参数并没有被实例化,结点中也没有key,没办法利用,所以这个时候我们使用了第三个模板参数——key_of_T。

这其实就是个仿函数,是在map和set的类中实现,在实例化时将对应的函数名作为第三个参数传过去,当调用的时候就针对不同的函数得到相对应的key。

注意map_key_of_T不是模板函数,它是在map这个模板函数实例化后自动实例化的类,传参时直接将名字传过去即可。

4.iterator的实现

我们先暂时认为Node的指针就能完成迭代器,如下图,注意当声明模板类的成员变量类型时,最前面要加typename防止被认为是静态成员变量导致编译不通过。

实例化的时候要根据迭代器的成员变量实例化所需的类型来确定迭代器的模板参数,要会反推,明白每个参数出现的意义。

常规的迭代器操作这里不再详述

下面重点讲讲operator++

当我们传begin()时得到的是最左边结点的迭代器,之后每一次++都要按照左中右的规则来遍历,如何控制呢?很明显不能再使用以前的递归方法了,递归方法是遍历,即从头到尾走一遍,递归没有办法从中间某个结点开始向后走,也没办法在中途停下来。

我们需要根据中序的路线再找一种新的规律

我们发现,it所在的位置都可以看作所在子树的根节点,根据左中右的规则,每次it++都是访问这个右树的最左结点,如2->3,4->5。但是这里还有个疑问,1->2,3->4遵循什么规律呢?5之后怎么处理呢?

我们发现刚刚的规律是it所在的位置都可以看作所在子树的根节点,在这种思想下默认左和中都已经遍历完了,只需要去找右的最左结点即可,如果像1和3那样没有右,就意味着当前整棵树都访问完了。关键的点在于,这颗被访问完的子树如果是上一个父节点的左子树,我们就需要找到这个父节点然后返回;如果这颗被访问完的子树如果是上一个父节点的右子树,根据左中右,父节点所在的树也访问完了。所以,1和3这种情况就是要去找一个父节点,使得对于这个父节点而言,上个节点是在它的左子树,如1->2,3->4。如果找不到呢?如5,我们发现当它通过_root->parent走到nullptr也没有找到符合规则的父节点时,就退出,表示整棵树都走完了。

只要理解了上面的思想方法,代码就很简单了,总体思路同样是nodeP向上探路,nodeC在后面跟着,nodeC如果是nodeP的左,就意味找到了父节点

operator--的方法一样,但是有个细节需要处理

当我们传end()时,是尾节点的下一个,即nullptr。那么如何让end()--后能找到尾节点呢?

注意分析为什么存在这个问题,my_RBTree::map<string, int>::iterator it = m.end(),it是一个类对象,其成员变量只有一个Node* _cur == nullptr,此外迭代器类里只有m对应的实例化类型,但没有m这个对象相关的任何指针或引用,这个时候--什么也做不了。因此我们需要在迭代器里专门再加一个成员变量。

有且仅有在这种处理下,我们才能进行特殊处理

库里面专门设置了个哨兵位header,和root互为父子(header红色,和root区分),header左边指向leftmost(),右边指向rightmost(),可以end()--,这是一种处理的思路,两者掌握其一即可

rightmost和leftmost就是寻找最右和最左结点,很简单,但调用非静态成员函数需要实例化对象,这也是为什么要传一个指针过去的原因之一。

iterator实现后,insert也需要修改,我们要在两个层面进行修改,红黑树的insert要返回pair,其key为插入结点的指针或找到的结点指针,其value是插入是否成功。在map里,就利用接收到的pair的key来构造iterator,再返回一个pair<iterator, bool>即可。

至此,map和set封装所有难点都讲解完了,有很多小细节需要多上手练习解决。

5.所有代码

map.hpp

#pragma once


#include "RBTree.hpp"
using namespace my_RBTree;


namespace my_RBTree
{
	template<class K, class T, class Key_Of_T>
	struct map_iterator
	{
		map_iterator(my_RBTree::RBTree<K, T, Key_Of_T>* t, typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* cur = nullptr)
			:_t(t),
			_cur(cur)
		{}

		map_iterator& operator++()
		{
			typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			nodeC = nodeC->_right;
			if (nodeC)
			{
				while (nodeC->_left)
					nodeC = nodeC->_left;
				_cur = nodeC;
				return *this;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_left == nodeC)
				{
					_cur = nodeP;
					return *this;
				}
			}

			return *this;
		}


		map_iterator operator++(int)
		{
			typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			map_iterator ret = *this;

			nodeC = nodeC->_right;
			if (nodeC)
			{
				while (nodeC->_left)
					nodeC = nodeC->_left;
				_cur = nodeC;
				return ret;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_left == nodeC)
				{
					_cur = nodeP;
					return ret;
				}
			}

			return ret;
		}


		map_iterator& operator--()
		{
			typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			if (_cur == nullptr)
			{
				_cur = _t->rightmost();
				return *this;
			}

			nodeC = nodeC->_left;
			if (nodeC)
			{
				while (nodeC->_right)
					nodeC = nodeC->_right;
				_cur = nodeC;
				return *this;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_right == nodeC)
				{
					_cur = nodeP;
					return *this;
				}
			}

			return *this;
		}

		map_iterator operator--(int)
		{
			typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			map_iterator ret = *this;

			if (_cur == nullptr)
			{
				_cur = _t->rightmost();
				return ret;
			}

			nodeC = nodeC->_left;
			if (nodeC)
			{
				while (nodeC->_right)
					nodeC = nodeC->_right;
				_cur = nodeC;
				return ret;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_right == nodeC)
				{
					_cur = nodeP;
					return ret;
				}
			}

			return ret;
		}

		T& operator*()
		{
			return _cur->_val;
		}

		T* operator->()
		{
			return &(_cur->_val);
		}


		bool operator==(const map_iterator& it)
		{
			return it._cur == _cur;
		}

		bool operator!=(const map_iterator& it)
		{
			return it._cur != _cur;
		}

		typename my_RBTree::RBTree<K, T, Key_Of_T>::Node* _cur;

		my_RBTree::RBTree<K, T, Key_Of_T>* _t;
	};

	template<class K, class V>
	class map
	{
	public:
		struct map_key_of_T;
		typedef map_iterator<K, pair<const K, V>, map_key_of_T> iterator;
		typedef map_iterator<K, pair<const K, const V>, map_key_of_T> const_iterator;

		iterator begin()
		{
			return iterator(&_tree, _tree.leftmost());
		}

		iterator end()
		{
			return iterator(&_tree);
		}

		pair<iterator, bool> insert(const K& key, const V& val)
		{
			pair<typename my_RBTree::RBTree<K, pair<const K, V>, map_key_of_T>::Node*, bool> p = _tree.insert({ key, val });
			return { iterator(&_tree, p.first), p.second };
		}

		bool isRBTree()
		{
			return _tree.isRBTree();
		}

	private:

		struct map_key_of_T
		{
			const K& operator()(typename my_RBTree::RBTree<K, pair<const K, V>, map_key_of_T>::Node* cur)
			{
				return cur->_val.first;
			}
		};

	private:
		RBTree<K, pair<const K, V>, map_key_of_T> _tree;
	};
}

set.hpp

#pragma once


#include "RBTree.hpp"


namespace my_RBTree
{

	template<class K, class Key_Of_T>
	struct set_iterator
	{
		set_iterator(my_RBTree::RBTree<K, const K, Key_Of_T>* t, typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* cur = nullptr)
			:_t(t),
			_cur(cur)
		{}

		set_iterator& operator++()
		{
			typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			nodeC = nodeC->_right;
			if (nodeC)
			{
				while (nodeC->_left)
					nodeC = nodeC->_left;
				_cur = nodeC;
				return *this;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_left == nodeC)
				{
					_cur = nodeP;
					return *this;
				}				
			}

			return *this;
		}

		set_iterator operator++(int)
		{
			typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			set_iterator ret = *this;

			nodeC = nodeC->_right;
			if (nodeC)
			{
				while (nodeC->_left)
					nodeC = nodeC->_left;
				_cur = nodeC;
				return ret;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_left == nodeC)
				{
					_cur = nodeP;
					return ret;
				}
			}

			return ret;
		}

		set_iterator& operator--()
		{
			typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			if (_cur == nullptr)
			{
				_cur = _t->rightmost();
				return *this;
			}

			nodeC = nodeC->_left;
			if (nodeC)
			{
				while (nodeC->_right)
					nodeC = nodeC->_right;
				_cur = nodeC;
				return *this;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_right == nodeC)
				{
					_cur = nodeP;
					return *this;
				}
			}

			return *this;
		}

		set_iterator operator--(int)
		{
			typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* nodeC = _cur, * nodeP = _cur;

			set_iterator ret = *this;

			if (_cur == nullptr)
			{
				_cur = _t->rightmost();
				return ret;
			}

			nodeC = nodeC->_left;
			if (nodeC)
			{
				while (nodeC->_right)
					nodeC = nodeC->_right;
				_cur = nodeC;
				return ret;
			}

			while (nodeP)
			{
				nodeC = nodeP, nodeP = nodeP->_parent;
				if (nodeP == nullptr || nodeP->_right == nodeC)
				{
					_cur = nodeP;
					return ret;
				}
			}

			return ret;
		}


		const K& operator*()
		{
			return _cur->_val;
		}

		const K* operator->()
		{
			return &(_cur->_val);
		}

		bool operator==(const set_iterator& it)
		{
			return it._cur == _cur;
		}		
		
		bool operator!=(const set_iterator& it)
		{
			return it._cur != _cur;
		}

		typename my_RBTree::RBTree<K, const K, Key_Of_T>::Node* _cur;
		my_RBTree::RBTree<K, const K, Key_Of_T>* _t;
	};


	template<class K>
	class set
	{
	public:
		struct set_key_of_T;
		typedef set_iterator<K, set_key_of_T> iterator;
		typedef set_iterator<K, set_key_of_T> const_iterator;

		iterator begin()
		{
			return iterator(&_tree, _tree.leftmost());
		}		
		
		iterator end()
		{
			return iterator(&_tree);
		}

		pair<iterator, bool> insert(const K& key)
		{
			pair<typename my_RBTree::RBTree<K, const K, set_key_of_T>::Node*, bool> p = _tree.insert(key);
			return { iterator(&_tree, p.first), p.second };
		}

		bool isRBTree()
		{
			return _tree.isRBTree();
		}

	private:

		struct set_key_of_T
		{
			const K& operator()(typename my_RBTree::RBTree<K, const K, set_key_of_T>::Node* cur)
			{
				return cur->_val;
			}
		};
	private:
		RBTree<K, const K, set_key_of_T> _tree;
	};


}

RBTree.hpp

#pragma once


#include <iostream>
#include <string>
#include <utility>
#include <vector>
#include <time.h>

using namespace std;


namespace my_RBTree
{
	enum Color
	{
		BLACK,
		RED
	};

	template<class K, class T>
	struct RBTreeNode
	{
		RBTreeNode(const T& val, Color col)
			:_col(col)
			,_val(val)
			,_left(nullptr)
			,_right(nullptr)
			,_parent(nullptr)
		{}

		Color _col;
		T _val;
		RBTreeNode<K, T>* _left;
		RBTreeNode<K, T>* _right;
		RBTreeNode<K, T>* _parent;
	};


	template<class K, class T, class Key_Of_T>
	class RBTree
	{
	public:
		typedef RBTreeNode<K, T> Node;

		Node* leftmost()
		{
			Node* cur = _root;
			while (cur && cur->_left)
				cur = cur->_left;
			return cur;
		}

		Node* rightmost()
		{
			Node* cur = _root;
			while (cur && cur->_right)
				cur = cur->_right;
			return cur;
		}

		void RotateR(Node* nodeG)
		{
			Node* nodeP = nodeG->_left, * nodeGP = nodeG->_parent, * nodeLR = nodeP->_right;

			nodeP->_right = nodeG, nodeP->_parent = nodeGP;
			if (nodeGP)
			{
				if (nodeGP->_left == nodeG)
					nodeGP->_left = nodeP;
				else
					nodeGP->_right = nodeP;
			}
			else
				_root = nodeP;
			
			nodeG->_parent = nodeP, nodeG->_left = nodeLR;
			if (nodeLR)
				nodeLR->_parent = nodeG;
		}

		void RotateL(Node* nodeG)
		{
			Node* nodeP = nodeG->_right, * nodeGP = nodeG->_parent, * nodeRL = nodeP->_left;

			nodeP->_left = nodeG, nodeP->_parent = nodeGP;
			if (nodeGP)
			{
				if (nodeGP->_left == nodeG)
					nodeGP->_left = nodeP;
				else
					nodeGP->_right = nodeP;
			}
			else
				_root = nodeP;

			nodeG->_parent = nodeP, nodeG->_right = nodeRL;
			if (nodeRL)
				nodeRL->_parent = nodeG;
		}

		pair<Node*, bool> insert(const T& val)
		{
			Node* nodeRet = nullptr;

			if (_root == nullptr)
			{
				_root = new Node(val, BLACK);
				return { _root, true };
			}

			Node* nodeC = _root, * nodeP = nullptr, * nodeN = new Node(val, RED);
			while (nodeC)
			{
				if (Key_Of_T()(nodeC) == Key_Of_T()(nodeN))
					return { nodeC, false };


				if (Key_Of_T()(nodeC) < Key_Of_T()(nodeN))
					nodeP = nodeC, nodeC = nodeC->_right;
				else
					nodeP = nodeC, nodeC = nodeC->_left;
			}

			if (Key_Of_T()(nodeP) < Key_Of_T()(nodeN))
			{
				nodeP->_right = new Node(val, RED);
				nodeC = nodeP->_right;
				nodeRet = nodeC;
				nodeC->_parent = nodeP;
			}
			else
			{
				nodeP->_left = new Node(val, RED);
				nodeC = nodeP->_left;
				nodeRet = nodeC;
				nodeC->_parent = nodeP;
			}

			Node* nodeG = nodeP->_parent;
			while (nodeP && nodeG)
			{
				if (nodeP->_col == BLACK)
					break;

				Node* nodeU = nullptr;
				if (nodeG->_left == nodeP)
					nodeU = nodeG->_right;
				else
					nodeU = nodeG->_left;

				//变色
				if (nodeU && nodeP->_col == RED && nodeU->_col == RED)
				{
					nodeP->_col = nodeU->_col = BLACK;
					if (nodeG != _root)
						nodeG->_col = RED;
					else
						nodeG->_col = BLACK;

					nodeC = nodeG, nodeP = nodeC->_parent;
					if (nodeP)
						nodeG = nodeP->_parent;
					else
						nodeG = nullptr;
				}
				
				//旋转,只涉及nodeG,nodeP,nodeC
				else
				{
					if (nodeG->_left == nodeP)
					{
						if (nodeP->_left == nodeC)
						{
							RotateR(nodeG);
							nodeP->_col = BLACK, nodeG->_col = RED;
						}
						else
						{
							RotateL(nodeP), RotateR(nodeG);
							nodeC->_col = BLACK, nodeG->_col = RED;
						}
					}
					else
					{
						if (nodeP->_right == nodeC)
						{
							RotateL(nodeG);
							nodeP->_col = BLACK, nodeG->_col = RED;
						}
						else
						{
							RotateR(nodeP), RotateL(nodeG);
							nodeC->_col = BLACK, nodeG->_col = RED;
						}
					}

					break;
				}

			}

	
			return { nodeRet, true };
		}


		bool isRBTree()
		{
			bool ret1 = _Contiguous_RNode(_root);
			bool ret2 = _Count_BNode(_root, 0, _Path_BNode(_root));

			if (ret1)
				cout << endl << "结点颜色均符合规则,根节点为黑且没有连续红节点!" << endl;
			else
				cout << endl << "颜色出现故障,请检查!" << endl;

			if (ret2)
				cout << endl << "每条路径的黑色节点数相同!" << endl;
			else
				cout << endl << "某条路径黑色节点数出现问题,请检查!" << endl;

			if (ret1 && ret2)
			{
				cout << endl << "经检查,本树为红黑树!" << endl;
				return true;
			}

			cout << endl << "经检查,本树不为红黑树!" << endl;
			return false;

		}

	private:
		bool _Contiguous_RNode(Node* cur_root)
		{
			if (cur_root == nullptr)
				return true;

			if (cur_root == _root && cur_root->_col == RED)
			{
				cout << "根节点为红色!" << endl;
				return false;
			}

			if (cur_root->_col == RED && cur_root->_parent->_col == RED)
				return false;

			return _Contiguous_RNode(cur_root->_left) && _Contiguous_RNode(cur_root->_right);
		}

		bool _Count_BNode(Node* cur_root, int count, int num_of_black_node)
		{
			if (cur_root == nullptr)
			{
				if (count == num_of_black_node)
					return true;
				return false;
			}

			if (cur_root->_col == BLACK)
				count++;

			return _Count_BNode(cur_root->_left, count, num_of_black_node) && _Count_BNode(cur_root->_right, count, num_of_black_node);
		}

		int _Path_BNode(Node* cur_root)
		{
			if (cur_root == nullptr)
				return 0;

			return (cur_root->_col == BLACK ? 1 : 0) + _Path_BNode(cur_root->_left);
		}

	private:
		Node* _root = nullptr;
	};

}

  • 26
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值