【C++】使用红黑树进行封装map和set

这篇具有很好参考价值的文章主要介绍了【C++】使用红黑树进行封装map和set。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘
🛸C++专栏C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

一、STL库中的set和map源码

本文难点:使用红黑树封装set和map,必须保证两种数据结构复用同一棵红黑树;且满足set和map的性质,set的value不可被改变,而map的value可以被改变。

【C++】使用红黑树进行封装map和set,C++修炼内功,c++,开发语言

二、利用模板区分map和set

template<class T>
struct RedBlackTreeNode
{
	T _data;
	RedBlackTreeNode<T>* _left;//该节点的左孩子
	RedBlackTreeNode<T>* _right;//该节点的右孩子
	RedBlackTreeNode<T>* _parent;//该节点是父亲节点
	Color _col;

	RedBlackTreeNode(const T& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}
};

map和set的区别在于value的不同,红黑树模板参数T,代表value用以区分set和map。

三、利用仿函数比较大小

我们会发现红黑树的插入等接口会对key值进行比较大小,像set直接对key进行比较,这没问题,但是map中的节点装的是pair<K,V>,pair的比较规则是first比完之后可能会再去比较second(而我们仅仅想要比较first,该比较规则不适用)。

通过源码启发,我们可以对红黑树新增一个模板参数:
仿函数KeyOfT,在set和map类中完善该仿函数的比较对象,
用于区分set和map的比较:

struct MapKeyOfT
{
    const K& operator()(const pair<K, V>& kv)
    {
        return kv.first;
    }
};
struct SetKeyOfT
{
    const K& operator()(const K& key)
    {
        return key;
    }
};

注:这里为什么只是返回key的数值,而不是在仿函数里面进行比较大小呢?

因为我们调用find的函数的时候不是传入两个数值进行比较的,而只是传一个。

使用红黑树封装的find

Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}

四、set和map的迭代器

STL源码采用下图结构,多搞了一个头结点。迭代器begin()可以指向header的左,迭代器end()指向header。

【C++】使用红黑树进行封装map和set,C++修炼内功,c++,开发语言

而小编使用的是无头结点进行封装的map和set,将nullptr作为结束

1、红黑树的begin()和end()

iterator begin()
{
    Node* leftMin = _root;
    while (leftMin && leftMin->_left)
    {
        leftMin = leftMin->_left;
    }

    return iterator(leftMin);
}

iterator end()
{
    return iterator(nullptr);
}

const_iterator begin() const
{
    Node* leftMin = _root;
    while (leftMin && leftMin->_left)
    {
        leftMin = leftMin->_left;
    }

    return const_iterator(leftMin);
}

const_iterator end() const
{
    return const_iterator(nullptr);
}

封装了const是为了适配const的map和set

2、operator++

1、如果_node的右不为空,找右孩子的最左节点

2、如果_node的右为空,如果孩子是父亲的左就返回父亲,否则就继续向上遍历,如果走到nullptr那就是遍历完成

Self& operator++()
{
    if (_node->_right)
    {
        Node* subleft = _node->_right;
        while (subleft->_left)
        {
            subleft = subleft->_left;
        }

        _node = subleft;
    }
    else
    {
        Node* cur = _node;
        Node* parent = cur->_parent;
        while (parent && cur == parent->_right)//parent不为空
        {
            cur = parent;
            parent = parent->_parent;
        }
        _node = parent;
    }
    return *this;
}

3、operator–

1、如果_node的左不为空,找左孩子的最右节点

2、如果_node的左为空,如果孩子是父亲的右就返回父亲,否则就继续向上遍历,如果走到nullptr那就是遍历完成

Self& operator--()
{
    if (_node->_left)
    {
        Node* subright = _node->_left;
        while (subright->_right)
        {
            subright = subright->_right;
        }

        _node = subright;
    }
    else
    {
        Node* cur = _node;
        Node* parent = cur->_parent;
        while (parent && cur == parent->_left)
        {
            cur = parent;
            parent = parent->_parent;
        }

        _node = parent;
    }
    return *this;
}

五、set的迭代器

对于set和map,它们的key都是不能改的。set的value不能修改,map的value可以修改。

因为set的value是不能改的,所以它的底层将普通迭代器和const迭代器全部封装成const迭代器来“解决”:

//注意这里是const_iterator变为iterator,在插入的时候会出现问题
typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator iterator;
typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator const_iterator;

这里iterator使用了const 的迭代器后,begin()和end()后面都需要加上const来解决问题;

iterator begin()const
{
    return _t.begin();
}
iterator end()const
{
    return _t.end();
}

这时使用迭代器调用上方函数会发现红黑树返回了普通迭代器类型的迭代器,类型不匹配。

在红黑树中补齐const版本的迭代器函数解决:

const_iterator begin() const
{
    Node* leftMin = _root;
    while (leftMin && leftMin->_left)
    {
        leftMin = leftMin->_left;
    }

    return const_iterator(leftMin);
}

const_iterator end() const
{
    return const_iterator(nullptr);
}

六、map的迭代器

map的value是可以改的,所以需要分别设计普通迭代器和const迭代器

typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

iterator begin()
{
    return _t.begin();
}

iterator end()
{
    return _t.end();
}

const_iterator begin() const
{
    return _t.begin();
}

七、迭代器的拷贝构造

STL库中的普通迭代器都可以转换为const迭代器,这是迭代器类的拷贝构造所支持的。

这个拷贝构造有点特殊:

struct __TreeIterator
{
	typedef RedBlackTreeNode<T> Node;
	Node* _node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	typedef __TreeIterator<T, T&, T*> iterator;

	__TreeIterator(const iterator& it)
		:_node(it._node)
	{}

	__TreeIterator(Node* node)
		:_node(node)
	{}
}

1、当这个模板的的Ref和PTR被实例化为T&和T*时,__RBTreeIterator(const iterator& it)就是一个拷贝构造(没啥意义)

2、当这个模板的的Ref和PTR被实例化为const T&和const T*时,__RBTreeIterator(const iterator& it)就是一个构造函数,

支持用普通迭代器去构造const迭代器。此时const迭代器的拷贝构造函数则由编译器自动生成,刚好满足迭代器值拷贝的特点。文章来源地址https://www.toymoban.com/news/detail-729431.html

八、源码

1、set.h

#include "9.14RedBlackTree.h"

namespace sqy
{
	template<class K>
	class set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator iterator;//注意这里是const_iterator变为iterator,在插入的时候会出现问题
		typedef typename RedBlackTree<K, K, SetKeyOfT>::const_iterator const_iterator;

		pair<iterator,bool> insert(const K& key)
		{
			pair<typename RedBlackTree<K, K, SetKeyOfT>::iterator,bool> ret = _t.Insert(key);//这里调用insert返回的是普通迭代器
			return pair<iterator, bool>(ret.first, ret.second);//这里需要用普通迭代器拷贝构造const的迭代器
		}
		iterator begin()const 
		{
			return _t.begin();
		}

		iterator end() const
		{
			return _t.end();
		}

	private:
		RedBlackTree<K, K, SetKeyOfT> _t;
	};
}

2、map.h

#include "9.14RedBlackTree.h"
namespace sqy
{
	template<class K,class V>
	class map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename RedBlackTree<K, pair<const K, V>, MapKeyOfT>::const_iterator const_iterator;

		iterator begin()
		{
			return _t.begin();
		}

		iterator end()
		{
			return _t.end();
		}

		const_iterator begin() const
		{
			return _t.begin();
		}

		V& operator[](const K& key)
		{
			pair<iterator, bool>ret = _t.Insert(make_pair(key, V()));
			return ret.first->second;
		}

		const_iterator end()const
		{
			return _t.end();
		}
		pair<iterator,bool> insert(const pair<K,V>& kv)
		{
			return _t.Insert(kv);
		}
	private:
		RedBlackTree<K, pair<const K,V>, MapKeyOfT> _t;
	};
}

3、RedBlackTree.h

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

enum Color
{
	RED,
	BLACK
};


template<class T>
struct RedBlackTreeNode
{
	T _data;
	RedBlackTreeNode<T>* _left;//该节点的左孩子
	RedBlackTreeNode<T>* _right;//该节点的右孩子
	RedBlackTreeNode<T>* _parent;//该节点是父亲节点
	Color _col;

	RedBlackTreeNode(const T& data)
		: _data(data)
		, _left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _col(RED)
	{}
};

template<class T,class Ref,class Ptr>
struct __TreeIterator
{
	typedef RedBlackTreeNode<T> Node;
	Node* _node;
	typedef __TreeIterator<T,Ref,Ptr> Self;
	typedef __TreeIterator<T, T&, T*> iterator;

	__TreeIterator(const iterator& it)
		:_node(it._node)
	{}

	__TreeIterator(Node* node)
		:_node(node)
	{}

	Ref operator*()
	{
		return _node->_data;
	}

	Ptr operator->()
	{
		return &_node->_data;
	}

	Self& operator++()
	{
		if (_node->_right)
		{
			Node* subleft = _node->_right;
			while (subleft->_left)
			{
				subleft = subleft->_left;
			}

			_node = subleft;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)//parent不为空
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}

		return *this;
	}

	Self& operator--()
	{
		if (_node->_left)
		{
			Node* subright = _node->_left;
			while (subright->_right)
			{
				subright = subright->_right;
			}

			_node = subright;
		}
		else
		{
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}

			_node = parent;
		}

		return *this;
	}

	bool operator!=(const Self& s)const
	{
		return _node != s._node;
	}

	bool operator==(const Self& s)const
	{
		return _node == s._node;
	}
};

template<class K, class T, class KeyOfT>
class RedBlackTree
{
	typedef RedBlackTreeNode<T> Node;
public:
	typedef __TreeIterator<T, T&, T*> iterator;
	typedef __TreeIterator<T, const T&, const T*> const_iterator;

	iterator begin()
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return iterator(leftMin);
	}

	iterator end()
	{
		return iterator(nullptr);
	}

	const_iterator begin() const
	{
		Node* leftMin = _root;
		while (leftMin && leftMin->_left)
		{
			leftMin = leftMin->_left;
		}

		return const_iterator(leftMin);
	}

	const_iterator end() const
	{
		return const_iterator(nullptr);
	}
	Node* Find(const K& key)
	{
		Node* cur = _root;
		KeyOfT kot;
		while (cur)
		{
			if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			else if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else
			{
				return cur;
			}
		}

		return nullptr;
	}
	pair<iterator,bool> Insert(const T& data)
	{
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;
			return make_pair(iterator(_root),true);
		}
		KeyOfT kot;
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)
		{
			if (kot(cur->_data) < kot(data))
			{
				parent = cur;
				cur = cur->_right;
			}
			else if (kot(cur->_data) > kot(data))
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(iterator(cur), false);
			}
		}

		cur = new Node(data);
		Node* newnode = cur;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = cur;
		}
		else
		{
			parent->_left = cur;
		}

		cur->_parent = parent;

		// ... 控制平衡
		while (parent && parent->_col == RED)//parent不为空并且为红进循环
		{
			Node* grandfather = parent->_parent;
			if (grandfather->_left == parent)
			{
				if (parent->_left == cur)
				{
					Node* uncle = grandfather->_right;
					if (uncle && uncle->_col == RED)//叔叔节点为红
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else //叔叔节点为空或者为黑的情况
					{
						RotateR(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
						break;
					}
				}
				else
				{
					Node* uncle = grandfather->_right;
					if (uncle && uncle->_col == RED)//叔叔存在并且叔叔节点为红
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else //叔叔节点为空或者为黑的情况
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
						break;
					}
				}
			}
			else
			{
				if (parent->_right == cur)
				{
					Node* uncle = grandfather->_left;
					if (uncle && uncle->_col == RED)//叔叔节点为红
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else //叔叔节点为空或者为黑的情况
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
						break;
					}
				}
				else
				{
					Node* uncle = grandfather->_left;
					if (uncle && uncle->_col == RED)//叔叔节点为红
					{
						parent->_col = uncle->_col = BLACK;
						grandfather->_col = RED;
						cur = grandfather;
						parent = cur->_parent;
					}
					else //叔叔节点为空或者为黑的情况
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
						break;
					}
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}

	bool IsBalance()
	{
		return _IsBalance(_root);
	}
private:
	bool checkColour(Node* root, int blacknum, int beachmark)
	{
		if (root == nullptr)
		{
			if (blacknum != beachmark)
			{
				return false;
			}
			return true;
		}

		if (root->_col == BLACK)
		{
			++blacknum;
		}

		if (root->_col == RED && root->_parent && root->_parent->_col == RED)
		{
			cout << root->_kv.first << "出现连续红色节点" << endl;
			return false;
		}

		return checkColour(root->_left, blacknum, beachmark) && 
				checkColour(root->_right, blacknum, beachmark);
	}
	bool _IsBalance(Node* root)
	{
		if (root == nullptr)
		{
			return true;
		}
		if (root->_col != BLACK)
		{
			return false;
		}

		//基准值
		int beanchmark = 0;
		Node* cur = root;
		while (cur)
		{
			if (cur->_col == BLACK)
			{
				++beanchmark;
			}
			cur = cur->_left;
		}

		return checkColour(root, 0, beanchmark);
	}
	void RotateR(Node* parent)
	{
		Node* cur = parent->_left;
		Node* curRight = cur->_right;

		parent->_left = curRight;
		cur->_right = parent;
		Node* ppNode = parent->_parent;
		if (curRight)
		{
			curRight->_parent = parent;
		}

		parent->_parent = cur;

		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppNode->_left == parent)
			{
				ppNode->_left = cur;
			}
			else
			{
				ppNode->_right = cur;
			}
			cur->_parent = ppNode;
		}
	}

	void RotateL(Node* parent)
	{
		Node* cur = parent->_right;
		Node* curleft = cur->_left;

		parent->_right = curleft;
		if (curleft)//判断是否为空,空的话就不用接上父亲节点
		{
			curleft->_parent = parent;
		}

		cur->_left = parent;

		Node* ppnode = parent->_parent;

		parent->_parent = cur;


		if (parent == _root)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (ppnode->_left == parent)
			{
				ppnode->_left = cur;
			}
			else
			{
				ppnode->_right = cur;

			}

			cur->_parent = ppnode;
		}

	}
private:
	Node* _root = nullptr;
};

到了这里,关于【C++】使用红黑树进行封装map和set的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ | 红黑树以及map与set的封装

    目录 前言 一、红黑树 1、红黑树的基本概念 2、红黑树相关特性 3、红黑树结点的定义 4、红黑树的查找 5、红黑树的插入 6、二叉树的拷贝构造与析构 7、红黑树的检测 8、红黑树总结 二、map与set的封装 1、红黑树的结点 2、红黑树迭代器 3、set的封装 4、map的封装 三、源码仓库

    2024年02月14日
    浏览(37)
  • C++【一棵红黑树封装 set 和 map】

    ✨个人主页: 北 海 🎉所属专栏: C++修行之路 🎃操作环境: Visual Studio 2019 版本 16.11.17 红黑树的基本情况我们已经在上一篇文章中学习过了,本文主要研究的是红黑树的实际应用:封装实现 set 和 map ,看看如何通过一棵红黑树满足两个不同的数据结构;在正式封装之前,

    2024年02月11日
    浏览(41)
  • 【C++】map与set容器——红黑树底层封装

    💭STL中,容器大概可分为两种类型——序列式容器和关联式容器。在前面的系列文章中,我们已经介绍了诸多序列式容器,如:vector、list、stack、queue等,它们以序列的形式存储数据。 💭而关联式容器也是一种非常重要的容器。标准的STL关联式容器分为set(集合)和map(映

    2023年04月11日
    浏览(40)
  • 【C++】用红黑树迭代器封装map和set

    封装有点难 - . - 文章目录 前言 一、红黑树原先代码的修改 二、红黑树迭代器的实现 总结 因为我们要将红黑树封装让map和set使用,所以我们要在原来的基础上将红黑树代码进行修改,最主要的是修改模板参数,下面我们直接进入正题: 首先我们拿出STL中的源代码,看看大佬

    2024年02月06日
    浏览(42)
  • 【C++】用一棵红黑树同时封装出map和set

    苦厄难夺凌云志,不死终有出头日。 1. 在源码里面,对于map和set的实现,底层是用同一棵红黑树封装出来的,并不是用了两棵红黑树,一个红黑树结点存key,一个红黑树结点存key,value的键值对,这样的方式太不符合大佬的水准了,实际上在红黑树底层中用了一个模板参数Va

    2023年04月13日
    浏览(43)
  • Learning C++ No.23【红黑树封装set和map】

    北京时间:2023/5/17/22:19,不知道是以前学的不够扎实,还是很久没有学习相关知识,对有的知识可以说是遗忘了许多,以该篇博客有关知识为例,我发现我对迭代器和模板的有关知识的理解还不够透彻,不知道是对以前知识的遗忘,还是现在所学确实有难度,反正导致我很懵

    2024年02月05日
    浏览(50)
  • 【C++】STL——用一颗红黑树封装出map和set

    我们都知道set是K模型的容器,而map是KV模型的容器,但是它俩的底层都是用红黑树实现的,上篇博文中我们模拟实现了一颗红黑树,接下来将对其进行改造,继而用一颗红黑树封装出map和set。 本质上map和set其内部的主要功能都是套用了红黑树现成的接口,只是稍作改动即可

    2023年04月15日
    浏览(35)
  • AVL树,红黑树,红黑树封装map和set

    二叉搜索树虽可以缩短查找的效率,但如果 数据有序或接近有序二叉搜索树将退化为单支树 ,查找元素相当于在顺序表中搜索元素, 效率低下 。因此,咱们中国的邻居俄罗斯的数学家G.M.Adelson-Velskii和E.M.Landis在1962年发明了一种解决上述问题的方法: 当向二叉搜索树中插入

    2023年04月25日
    浏览(61)
  • 红黑树封装set和map(插入部分)

    之前我们实现了红黑树的插入的部分,本文主要介绍将之前实现的红黑树封装成map和set。我们是以学习的角度来封装容器,不用非要把库中容器所有功能都实现出来。我们主要目的是学习库中代码设计技巧和模板复用的思想。 我们在实现之前还是和以前一样去看看库中是怎么

    2024年02月07日
    浏览(40)
  • 【C++】 使用红黑树模拟实现STL中的map与set

    前面的文章我们学习了红黑树,也提到了C++STL中的map和set的底层其实就是用的红黑树来实现的(而map和set的使用我们前面也学过了)。 既然红黑树我们也学习过了,那这篇文章我们就用红黑树来简单实现一下STL中的map和set,重点是学习它的框架。 上一篇文章我们实现了红黑

    2024年02月12日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包