【C++进阶】map和set——下篇(红黑树的学习以及封装map和set)

news2025/7/18 5:52:24

🎇C++学习历程:入门


  • 博客主页:一起去看日落吗
  • 持续分享博主的C++学习历程
  • 博主的能力有限,出现错误希望大家不吝赐教
  • 分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记,树🌿成长之前也要扎根,也要在漫长的时光🌞中沉淀养分。静下来想一想,哪有这么多的天赋异禀,那些让你羡慕的优秀的人也都曾默默地翻山越岭🐾。

在这里插入图片描述

🍁 🍃 🍂 🌿


目录

  • 🌿 1. 红黑树
    • 🍃 1.1 红黑树的概念
    • 🍃 1.2 红黑树的性质
    • 🍃 1.3 红黑树节点的定义
    • 🍃 1.4 红黑树结构
    • 🍃 1.5 红黑树的插入操作
    • 🍃 1.6 红黑树的验证
    • 🍃 1.7 红黑树与AVL树的比较
    • 🍃 1.8 红黑树的模拟实现
  • 🌿 2. 红黑树模拟实现STL中的map与set
    • 🍃 2.1 改造红黑树
    • 🍃 2.2 map的模拟实现
    • 🍃 2.3 set的模拟实现
    • 🍃 2.4 测试

🌿 1. 红黑树

🍃 1.1 红黑树的概念

红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或
Black
。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路
径会比其他路径长出俩倍,因而是接近平衡的

在这里插入图片描述


🍃 1.2 红黑树的性质

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的两个孩子结点是黑色的
  4. 对于每个结点,从该结点到其所有后代叶结点的简单路径上,均 包含相同数目的黑色结点
  5. 每个叶子结点都是黑色的(此处的叶子结点指的是空结点)

思考:为什么满足上面的性质,红黑树就能保证:其最长路径中节点个数不会超过最短路径节点
个数的两倍?

答:假设黑色节点数量有N个,最短路径为:logN,最长路径为:2*logN,因此最长路径中节点个数不会超过最短路径中节点个数的两倍,所以如果用红黑树来完成查找效率也是非常高的。


🍃 1.3 红黑树节点的定义

//节点不是黑就是红,因此可以直接用枚举
enum Colour
{
	RED,
	BLACK
};//节点的颜色
template<class K, class V>
struct RBTreeNode
{
	RBTreeNode<K, V>* _left;//节点左孩子
	RBTreeNode<K, V>* _right;//节点右孩子
	RBTreeNode<K, V>* _parent;//节点的父亲
	pair<K, V> _kv;//节点中存放的键值对
	Colour _col;//节点的颜色
 
	RBTreeNode(const pair<K,V> kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(RED)
	{}
};

思考:在节点的定义中,为什么要将节点的默认颜色给成红色的?

答:在上面的性质中第三条和第四条是最重要的两点性质。假设我们默认颜色给黑色,那么必然会破环性质4(每条路径上包含相同数目的黑节点),如果破坏性质4代价会非常大,这是一种全局的破坏,其他每条路径的节点会因该路径增加了一个黑节点而不满足红黑树性质,调整起来非常麻烦;那么如果我们默认颜色给红色呢,必然会破坏性质3(没有连续的红节点),但是破坏这条性质只会影响当前路径,并不会对其他路径造成影响,影响面小。所以综上所述我们不难发现,默认给成红色是更好的。


🍃 1.4 红黑树结构

在STL中,红黑树的实现中增加一个头结点,因为跟节点必须为黑色,为了与根节点进行区分,将头结点给成黑色,并且让头结点的 pParent 域指向红黑树的根节点,pLeft域指向红黑树中最小的节点,_pRight域指向红黑树中最大的节点,如下:

在这里插入图片描述

注意: 在下面的实现中,我们采用不带头节点的方式来模拟实现红黑树


🍃 1.5 红黑树的插入操作

红黑树是在二叉搜索树的基础上加上其平衡限制条件,因此红黑树的插入可分为两步:

  1. 按照二叉搜索树规则插入新节点

  2. 检测新节点插入后,红黑树的性质是否造到破坏

因为新节点的默认颜色是红色,因此:如果其父节点的颜色是黑色,没有违反红黑树任何性质,则不需要调整;但当新插入节点的父节点颜色为红色时,就违反了性质三不能有连在一起的红色节点,此时需要对红黑树分情况来讨论:

在分情况讨论之前记住一句话:关键看叔叔的脸色!

  • 情况一: cur为红,p为红,g为黑,u存在且为红

在这里插入图片描述

  • 情况二: cur为红,p为红,g为黑,u不存在/u为黑

在这里插入图片描述

  • 情况三: cur为红,p为红,g为黑,u不存在/u为黑

在这里插入图片描述

  • 总结
    在这里插入图片描述
pair<Node*, bool> Insert(const pair<K, V> kv)
	{
		if (_root == nullptr)
		{
			_root = new Node(kv);
			_root->_col = BLACK;//根节点给黑色
			return make_pair(_root, true);
		}
 
		Node* cur = _root;
		Node* parent = nullptr;
		while (cur)//循环去找空位
		{
			if (kv.first > cur->_kv.first)
			{
				parent = cur;
				cur = cur->_right;
			}
			else if(kv.first < cur->_kv.first)
			{
				parent = cur;
				cur = cur->_left;
			}
			else
			{
				return make_pair(cur, false);
			}
		}
 
		Node* newnode = new Node(kv);
		newnode->_col = RED;//链接上新节点
		if (kv.first > parent->_kv.first)
		{
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		else
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		cur = newnode;
 
		//父节点存在且父节点为红色时需要继续处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			//关键看叔叔的脸色
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				if (uncle && uncle->_col == RED)
				{
					//具体变色的情况需要画图分析:
					grandfather->_col = RED;
					parent->_col = uncle->_col = BLACK;
 
					//注意需要继续向上处理,容易忘记
					cur = grandfather;
					parent = cur->_parent;
				}
				else
				{
					if (cur == parent->_left)
					{
						RotateR(grandfather);//右单旋
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else
					{
						RotateL(parent);//先以父节点为旋转点左旋
						RotateR(grandfather);//再以g为旋转点右旋
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;//旋转+变色过后一定是满足所有性质的直接退出循环
				}
			}
			else
			{
				Node* uncle = grandfather->_left;
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = parent->_col = BLACK;
					grandfather->_col = RED;
 
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2:+ 情况3:
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else // cur == parent->_left
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					break;
				}
			}
		}
		_root->_col = BLACK;
		return make_pair(newnode, true);
	}

🍃 1.6 红黑树的验证

红黑树的检测分为两步:

  1. 检测其是否满足二叉搜索树(中序遍历是否为有序序列)
  2. 检测其是否满足红黑树的性质
bool _CheckBlance(Node* root, int blackNum, int count)//balckNum相当于一个标尺,count就是用来记录每条路径的黑节点数目
	{
		if (root == nullptr)//如果root走到空节点
		{
			if (count != blackNum)//count不等于最左路径的黑色节点数
			{
				cout << "黑色节点的数量不相等" << endl;
				return false;//返回假
			}
			return true;//否则返回真
		}
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}
		if (root->_col == BLACK)
		{
			count++;
		}
		return _CheckBlance(root->_left, blackNum, count)
			&& _CheckBlance(root->_right, blackNum, count);//再递归到各子树的子问题
	}
	bool CheckBlance()
	{
		if (_root == nullptr)
		{
			return true;
		}
		if (_root->_col == RED)
		{
			cout << "根节点是红色的" << endl;
			return false;
		}
		// 找最左路径做黑色节点数量参考值
		int blackNum = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
			{
				blackNum++;
			}
			left = left->_left;
		}
		int count = 0;
		return _CheckBlance(_root, blackNum, count);
	}

🍃 1.7 红黑树与AVL树的比较

红黑树和AVL树都是高效的平衡二叉树,增删改查的时间复杂度都是O( l o g 2 N log_2 N log2N),红黑树不追
求绝对平衡,其只需保证最长路径不超过最短路径的2倍,相对而言,降低了插入和旋转的次数,
所以在经常进行增删的结构中性能比AVL树更优,而且红黑树实现比较简单,所以实际运用中红
黑树更多。


🍃 1.8 红黑树的模拟实现

RBTree.h

//
//  RBTree.hpp
//  RedBlackTree
//
//  Created by 卜绎皓 on 2022/11/17.
//

#pragma once
#include<iostream>
using namespace std;
 
enum Colour
{
    RED,
    BLACK
};//节点的颜色
template<class K,class V>
struct RBTreeNode
{
    RBTreeNode<K,V>* _left;//节点左孩子
    RBTreeNode<K,V>* _right;//节点右孩子
    RBTreeNode<K,V>* _parent;//节点的父亲
    pair<K,V> _kv;//节点中存放的T类型的数据
    Colour _col;//节点的颜色
 
    RBTreeNode(const pair<K,V> kv)
        :_left(nullptr)
        ,_right(nullptr)
        ,_parent(nullptr)
        ,_kv(kv)
        ,_col(RED)
    {}
};
 
template<class K,class V>
class RBTree
{
    typedef RBTreeNode<K, V> Node;
public:
    RBTree()
        :_root(nullptr)
    {}
    //拷贝构造和赋值拷贝也需要自行实现这里不做赘述
    ~RBTree()
    {
        Destory(_root);
        _root = nullptr;
    }
    void Destory(Node* root)
    {
        if (root == nullptr)
        {
            return;
        }
        //利用后序遍历释放节点
        Destory(root->_left);
        Destory(root->_right);
        delete root;
    }
    void RotateR(Node* parent)//右单旋
    {
        Node* subL = parent->_left;
        Node* subLR = subL->_right;
 
        parent->_left = subLR;
        if (subLR != nullptr)//注意:这里一定要判断不为空的,因为下面可能会出现空指针的解引用
        {
            subLR->_parent = parent;
        }
        subL->_right = parent;
        Node* parentParent = parent->_parent;//一定要在改变链接关系之前把这个指针存下来
        parent->_parent = subL;
 
        //if (parentParent == nullptr)或者采用这个条件也是可以的
        if (parent == _root)
        {
            _root = subL;
            _root->_parent = nullptr;
        }
        else
        {
            //这里注意:parent还有父母时,链接之前需要注意判断到底是右孩子还是左孩子
            if (parentParent->_left == parent)
                parentParent->_left = subL;
            else
                parentParent->_right = subL;
 
            subL->_parent = parentParent;//最后还要把父指针关系链接上
        }
    }
    void RotateL(Node* parent)
    {
        Node* subR = parent->_right;
        Node* subRL = subR->_left;
 
        //先把subR的左孩子赋值给parent的右节点
        parent->_right = subRL;
        if (subRL != nullptr)//注意一定要判断是否为空的情况
        {
            subRL->_parent = parent;//然后链接parent指针
        }
 
        //然后subR的左节点链接上parent
        subR->_left = parent;
        Node* parentParent = parent->_parent;//提前记录
        parent->_parent = subR;
        //if (parentParent == nullptr)
        if (parent == _root)
        {
            _root = subR;
            _root->_parent = nullptr;
        }
        else
        {
            if (parentParent->_left == parent)
                parentParent->_left = subR;
            else
                parentParent->_right = subR;
 
            subR->_parent = parentParent;
        }
    }
    pair<Node*, bool> Insert(const pair<K, V> kv)
    {
        if (_root == nullptr)
        {
            _root = new Node(kv);
            _root->_col = BLACK;//根节点给黑色
            return make_pair(_root, true);
        }
 
        Node* cur = _root;
        Node* parent = nullptr;
        while (cur)//循环去找空位
        {
            if (kv.first > cur->_kv.first)
            {
                parent = cur;
                cur = cur->_right;
            }
            else if(kv.first < cur->_kv.first)
            {
                parent = cur;
                cur = cur->_left;
            }
            else
            {
                return make_pair(cur, false);
            }
        }
 
        Node* newnode = new Node(kv);
        newnode->_col = RED;//链接上新节点
        if (kv.first > parent->_kv.first)
        {
            parent->_right = newnode;
            newnode->_parent = parent;
        }
        else
        {
            parent->_left = newnode;
            newnode->_parent = parent;
        }
        cur = newnode;
 
        //父节点存在且父节点为红色时需要继续处理
        while (parent && parent->_col == RED)
        {
            Node* grandfather = parent->_parent;
            //关键看叔叔的脸色
            if (parent == grandfather->_left)
            {
                Node* uncle = grandfather->_right;
                if (uncle && uncle->_col == RED)
                {
                    //具体变色的情况需要画图分析:
                    grandfather->_col = RED;
                    parent->_col = uncle->_col = BLACK;
 
                    //注意需要继续向上处理,容易忘记
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else
                {
                    if (cur == parent->_left)
                    {
                        RotateR(grandfather);//右单旋
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else
                    {
                        RotateL(parent);//先以父节点为旋转点左旋
                        RotateR(grandfather);//再以g为旋转点右旋
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;//旋转+变色过后一定是满足所有性质的直接退出循环
                }
            }
            else
            {
                Node* uncle = grandfather->_left;
                if (uncle && uncle->_col == RED)
                {
                    uncle->_col = parent->_col = BLACK;
                    grandfather->_col = RED;
 
                    cur = grandfather;
                    parent = cur->_parent;
                }
                else // 情况2:+ 情况3:
                {
                    if (cur == parent->_right)
                    {
                        RotateL(grandfather);
                        parent->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    else // cur == parent->_left
                    {
                        RotateR(parent);
                        RotateL(grandfather);
                        cur->_col = BLACK;
                        grandfather->_col = RED;
                    }
                    break;
                }
            }
        }
        _root->_col = BLACK;
        return make_pair(newnode, true);
    }
    void _InOrder(Node* root)//中序遍历递归打印
    {
        if (root == nullptr)
        {
            return;
        }
 
        _InOrder(root->_left);
        cout << root->_kv.first << ":"<<root->_kv.second<<endl;
        _InOrder(root->_right);
    }
    void InOrder()
    {
        _InOrder(_root);
        cout << endl;
    }
    bool _CheckBlance(Node* root, int blackNum, int count)//balckNum相当于一个标尺,count就是用来记录每条路径的黑节点数目
    {
        if (root == nullptr)//如果root走到空节点
        {
            if (count != blackNum)//count不等于最左路径的黑色节点数
            {
                cout << "黑色节点的数量不相等" << endl;
                return false;//返回假
            }
            return true;//否则返回真
        }
        if (root->_col == RED && root->_parent->_col == RED)
        {
            cout << "存在连续的红色节点" << endl;
            return false;
        }
        if (root->_col == BLACK)
        {
            count++;
        }
        return _CheckBlance(root->_left, blackNum, count)
            && _CheckBlance(root->_right, blackNum, count);//再递归到各子树的子问题
    }
    bool CheckBlance()
    {
        if (_root == nullptr)
        {
            return true;
        }
        if (_root->_col == RED)
        {
            cout << "根节点是红色的" << endl;
            return false;
        }
        // 找最左路径做黑色节点数量参考值
        int blackNum = 0;
        Node* left = _root;
        while (left)
        {
            if (left->_col == BLACK)
            {
                blackNum++;
            }
            left = left->_left;
        }
        int count = 0;
        return _CheckBlance(_root, blackNum, count);
    }
private:
    Node* _root;
};


🌿 2. 红黑树模拟实现STL中的map与set

对map和set进行封装,虽然要传不同的参数类型,但是我们只需传个模版,根据不同的需要取不同的参数类型


🍃 2.1 改造红黑树

	因为关联式容器中存储的是<key, value>的键值对,因此
	k为key的类型,
	ValueType: 如果是map,则为pair<K, V>; 如果是set,则为k
	KeyOfValue: 通过value来获取key的一个仿函数类

迭代器的好处是可以方便遍历,是数据结构的底层实现与用户透明

#pragma once
#include<iostream>
using namespace std;
 
enum Colour
{
	RED,
	BLACK
};//节点的颜色
template<class T>
struct RBTreeNode
{
	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;
	T _data;
	Colour _col;
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _col(RED)
	{}
};
template<class T, class Ref, class Ptr>
struct __TreeIterator//封装迭代器
{
	typedef RBTreeNode<T> Node;//迭代器就是节点的指针
	typedef __TreeIterator<T, Ref, Ptr> Self;
 
	Node* _node;//
	__TreeIterator(Node* node)
		:_node(node)
	{}
	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator != (const Self& s) const
	{
		return _node != s._node;
	}
	bool operator == (const Self& s) const
	{
		return _node == s._node;
	}
	// 迭代器的难点
	Self& operator++()
	{
		//++注意画图分析
		if (_node->_right)//节点的右子树不为空
		{
			// 下一个访问就是右树中,中序的第一个节点
			Node* left = _node->_right;
			while (left->_left)//循环去找最左节点
			{
				left = left->_left;
			}
			_node = left;
		}
		else
		{
			// 找祖先里面不是父亲的右的那个节点
			// 因为 cur 右为空,说明cur所在的子树已经访问完了
			// cur是parent的右的,说明parent包括其所在子树都访问完了,循环往上去找
			Node* cur = _node;
			Node* parent = cur->_parent;
			while (parent && cur == parent->_right)//如果父亲不为空或者cur为父亲的右节点
			{
				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;
			//如果父节点存在且cur一直为父节点的左节点循环继续向上找
			while (parent && cur == parent->_left)
			{
				cur = parent;
				parent = parent->_parent;
			}
			_node = parent;
		}
		return *this;
	}
};
template<class K, class T, class KeyOfT>//多添加一个模板参数用以map和set进行封装
class RBTree
{
	typedef RBTreeNode<T> Node;
public:
	typedef __TreeIterator < T, T&, T* > iterator;
	typedef __TreeIterator < T, const T&, const T* > const_iterator;
 
	iterator begin()
	{
		Node* left = _root;
		while (left && left->_left)
		{
			left = left->_left;
		}
		return iterator(left);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
	RBTree()
		:_root(nullptr)
	{}
	void Destory(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
 
		Destory(root->_left);
		Destory(root->_right);
		delete root;
	}
	~RBTree()
	{
		Destory(_root);
		_root = nullptr;
	}
	Node* Find(const K& key)
	{
		KeyOfT kot;
		Node* cur = _root;
		while (cur)
		{
			if (kot(cur->_data) > key)
			{
				cur = cur->_left;
			}
			else if (kot(cur->_data) < key)
			{
				cur = cur->_right;
			}
			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;//仿函数对象,重载operator()即可
		Node* parent = nullptr;
		Node* cur = _root;
		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);
			}
		}
		Node* newnode = new Node(data);
		newnode->_col = RED;
		if (kot(parent->_data) < kot(data))
		{
			parent->_right = newnode;
			newnode->_parent = parent;
		}
		else
		{
			parent->_left = newnode;
			newnode->_parent = parent;
		}
		cur = newnode;
 
		// 如果父亲存在,且颜色为红色就需要处理
		while (parent && parent->_col == RED)
		{
			Node* grandfather = parent->_parent;
			// 关键是看叔叔
			if (parent == grandfather->_left)
			{
				Node* uncle = grandfather->_right;
				// 情况1:uncle存在且为红
				if (uncle && uncle->_col == RED)
				{
					parent->_col = uncle->_col = BLACK;
					grandfather->_col = RED;
 
					// 继续往上处理
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2+3:uncle不存在 uncle存在且为黑
				{
					// 情况2:单旋
					if (cur == parent->_left)
					{
						RotateR(grandfather);
						grandfather->_col = RED;
						parent->_col = BLACK;
					}
					else // 情况3:双旋
					{
						RotateL(parent);
						RotateR(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
 
					break;
				}
			}
			else // parent == grandfather->_right
			{
				Node* uncle = grandfather->_left;
				// 情况1:
				if (uncle && uncle->_col == RED)
				{
					uncle->_col = parent->_col = BLACK;
					grandfather->_col = RED;
 
					cur = grandfather;
					parent = cur->_parent;
				}
				else // 情况2:+ 情况3:
				{
					if (cur == parent->_right)
					{
						RotateL(grandfather);
						parent->_col = BLACK;
						grandfather->_col = RED;
					}
					else // cur == parent->_left
					{
						RotateR(parent);
						RotateL(grandfather);
						cur->_col = BLACK;
						grandfather->_col = RED;
					}
					// 插入结束
					break;
				}
			}
		}
 
		_root->_col = BLACK;
		return make_pair(iterator(newnode), true);
	}
 
	void RotateL(Node* parent)
	{
		Node* subR = parent->_right;
		Node* subRL = subR->_left;
 
		parent->_right = subRL;
		if (subRL)
		{
			subRL->_parent = parent;
		}
 
		subR->_left = parent;
		Node* parentParent = parent->_parent;
		parent->_parent = subR;
 
		if (parent == _root)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
			{
				parentParent->_left = subR;
			}
			else
			{
				parentParent->_right = subR;
			}
			subR->_parent = parentParent;
		}
	}
 
	void RotateR(Node* parent)
	{
		Node* subL = parent->_left;
		Node* subLR = subL->_right;
 
		parent->_left = subLR;
		if (subLR)
			subLR->_parent = parent;
 
		subL->_right = parent;
		Node* parentParent = parent->_parent;
		parent->_parent = subL;
 
		if (parent == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (parentParent->_left == parent)
				parentParent->_left = subL;
			else
				parentParent->_right = subL;
 
			subL->_parent = parentParent;
		}
	}
 
	bool _CheckBlance(Node* root, int blackNum, int count)
	{
		if (root == nullptr)
		{
			if (count != blackNum)
			{
				cout << "黑色节点的数量不相等" << endl;
				return false;
			}
 
			return true;
		}
 
		if (root->_col == RED && root->_parent->_col == RED)
		{
			cout << "存在连续的红色节点" << endl;
			return false;
		}
 
		if (root->_col == BLACK)
		{
			count++;
		}
 
		return _CheckBlance(root->_left, blackNum, count)
			&& _CheckBlance(root->_right, blackNum, count);
	}
 
	bool CheckBlance()
	{
		if (_root == nullptr)
		{
			return true;
		}
 
		if (_root->_col == RED)
		{
			cout << "根节点是红色的" << endl;
			return false;
		}
 
		// 找最左路径做黑色节点数量参考值
		int blackNum = 0;
		Node* left = _root;
		while (left)
		{
			if (left->_col == BLACK)
			{
				blackNum++;
			}
 
			left = left->_left;
		}
 
		int count = 0;
		return _CheckBlance(_root, blackNum, count);
	}
 
	void _InOrder(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}
 
		_InOrder(root->_left);
		cout << root->_kv.first << ":"<<root->_kv.second<<endl;
		_InOrder(root->_right);
	}
	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
private:
	Node* _root;
};

🍃 2.2 map的模拟实现

#pragma once
#include"RBTree.h"
 
namespace Bernard
{
	template<class K, class V>
	class map
	{
		struct MapKeyOfT//定义map的仿函数内部类
		{
			//map的V类型为pair键值对
			const K& operator()(const pair<const K, V>& kv)
			{
				return kv.first;//返回pair中的第一个值
			}
		};
	public:
		typedef typename RBTree<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		pair<iterator, bool> insert(const pair<const K, V>& kv)
		{
			return _t.Insert(kv);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = insert(make_pair(key, V()));
			return ret.first->second;
		}
	private:
		RBTree<K, pair<const K, V>, MapKeyOfT> _t;
	};
}

🍃 2.3 set的模拟实现

#pragma once
#include"RBTree.h"
 
namespace Bernard
{
	template<class K>
	class set
	{
		//set里面定义内部类重载operator()
		//set的V类型为K
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;//直接返回key
			}
		};
	public:
		typedef typename RBTree<K, K, SetKeyOfT>::iterator iterator;
		iterator begin()
		{
			return _t.begin();
		}
		iterator end()
		{
			return _t.end();
		}
		pair<iterator, bool> insert(const K& k)
		{
			return _t.Insert(k);
		}
	private:
		RBTree<K, K, SetKeyOfT> _t;
	};
}

🍃 2.4 测试

//
//  main.cpp
//  封装map和set
//
//  Created by 卜绎皓 on 2022/11/17.
//

#include "RedBlackTree.hpp"
#include "my_map.hpp"
#include "my_set.hpp"
 
 
int main()
{
    Bernard::map<int, int> m;
    m.insert(make_pair(1, 1));
    m.insert(make_pair(3, 3));
    m.insert(make_pair(0, 0));
    m.insert(make_pair(9, 9));
 
    Bernard::map<int, int>::iterator it = m.begin();
    while (it != m.end())
    {
        cout << (*it).first << ":" << (*it).second << endl;
        cout << it->first << ":" << it->second << endl;
        ++it;
    }
 
    Bernard::set<int> s;
    s.insert(1);
    s.insert(5);
    s.insert(2);
    s.insert(1);
    s.insert(13);
    s.insert(0);
    s.insert(15);
    s.insert(18);
 
    Bernard::set<int>::iterator sit = s.begin();
    while (sit != s.end())
    {
        cout << *sit << " ";
        ++sit;
    }
    cout << endl;
    return 0;
}

请添加图片描述


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17402.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

ggplot2图形简单绘制

文章目录一、所用函数1. sort、rank、order用法2. arrange 用法3. reorder用法4. cumsum 用法5. interaction用法二、散点图三、折线图、直方图、箱线图四、柱状图1. 单一变量 &#xff08;统计单一变量的属性值分布&#xff09;2. 单一变量fill &#xff08;列联表&#xff09…

mybatis详解

学习到现在,作为我们的java萌新来说,是时候来学习一点偷懒的武林秘籍了,今天我给大家介绍的就是在无上秘宝--mybatis持久型框架. 学习一个东西之前,我们得首先了解他的前世今生... 前世:原是Apache的一个开源项目iBatis, 2010年6月这个项目由ApacheSoftware Foundation 迁移到…

SQL注入靶机练习:BUU SQL COURSE 1

SQL注入靶机练习&#xff1a;BUU SQL COURSE 1一、SQL注入知识点二、前置知识三、SQL注入测试的一般步骤四、解题过程一、SQL注入知识点 可参考SQL注入详解 二、前置知识 参考来源&#xff1a;渗透攻防Web篇-深入浅出SQL注入 mysql5.0以上版本中存在一个重要的系统数据…

无敌,全面对标字节跳动2-2:算法与数据结构突击手册(leetcode)

学习是一种基础性的能力。然而&#xff0c;“吾生也有涯&#xff0c;而知也无涯。”&#xff0c;如果学习不注意方法&#xff0c;则会“以有涯随无涯&#xff0c;殆矣”。 学习就像吃饭睡觉一样&#xff0c;是人的一种本能&#xff0c;人人都有学习的能力。我们在刚出生的时候…

某城市道路桥梁设计计算书+cad图纸

第一章 工程概述 41 1.1设计题目 41 1.2 设计资料 42 1&#xff0e;3 桥梁设计的基本要求 42 1.3.1 使用上的要求&#xff1a; 42 1.3.2 经济上的要求&#xff1a; 42 1.3.3 结构和尺寸上的要求&#xff1a; 42 1.3.4 施工上的要求&#xff1a; 42 1.3.5美观上的要求&#xff1…

Aspose.PDF for Java Crack by Xacker

Aspose.PDF for Java 是一个本地库&#xff0c;使开发人员能够将 PDF 处理功能添加到他们的应用程序中。API 可用于构建任何类型的 32 位和 64 位应用程序&#xff0c;以在不使用 Adob​​e Acrobat 的情况下生成或读取、转换和操作 PDF 文件。 Aspose.PDF for Java API 允许执…

嵌入式(驱动开发)(内核内存管理)

一、内核内存管理框架 内核将物理内存等分成N块4KB&#xff0c;称之为一页&#xff0c;每页都用一个struct page来表示&#xff0c;采用伙伴关系算法维护 内核地址空间划分图&#xff1a; 3G~3G896M&#xff1a;低端内存&#xff0c;直接映射 虚拟地址 3G 物理地址 ​ 细…

Disk Drill for Mac v5.0.1043 苹果数据恢复软件

Disk Drill Enterprise for Mac&#xff0c;数据恢复软件&#xff0c;Disk Drill for Mac 解锁钥匙&#xff0c;Disk Drill for Mac序列号&#xff0c;顶尖的Mac数据恢复软件&#xff0c;你需要恢复在Mac OS X中已经删除的文件吗&#xff1f;比如重要商业文档、音乐、图片或者视…

Linux开发工具(5)——git

文章目录git版本控制器git是什么git的操作clone仓库到本地上传本地文件到gitgit版本控制器 git是什么 标题也说了git就是一个版本控制器&#xff0c;版本控制器是用来保存一个文件的历史版本&#xff0c;如果有需要可以进行回溯&#xff0c;也就是取得以前编辑完成的版本。 比…

Flutter基础知识

const 常量构造函数&#xff0c;如果控件是&#xff0c;则前面加&#xff1b;如果常量构造函数包含有非常量构造函数&#xff0c;则不加&#xff0c;内部是的控件单独加 1.Flutter学习网址 Flutter 中文官网 https://flutter.cn/ Flutter 官网&#xff1a;https://flutter.de…

移动WEB开发之流式布局--移动端基础

浏览器现状 PC端常见浏览器 360浏览器、谷歌浏览器、火狐浏览器、QQ浏览器、百度浏览器、搜狗浏览器、IE浏览器。 移动端常见浏览器 UC浏览器&#xff0c;QQ浏览器&#xff0c;欧朋浏览器&#xff0c; 百度手机浏览器&#xff0c;360安全浏览器&#xff0c;谷歌 浏览器&…

数学建模笔记

文章目录层次分析法——评价类问题原理代码插值算法原理代码拟合算法原理代码相关系数person 相关系数spearman 相关系数二分类和多分类二分类逻辑回归费希尔判别多分类Fisher判别聚类模型分析时间序列分析层次分析法——评价类问题 原理 1. 首先确定评价的目标&#xff0c;可…

智慧医院解决方案-最新全套文件

智慧医院解决方案-最新全套文件一、背景概述二、建设面临的挑战1、网络安全威胁2、关键业务宕机风险3、核心业务数据丢失风险三、总体建设架构四、建设方案五、获取 - 智慧医院全套最新解决方案合集一、背景概述 医院医疗信息化建设已走过两个阶段&#xff1a;第1阶段就是建立…

Java阻塞队列中的异类,SynchronousQueue底层实现原理剖析

上篇文章谈到BlockingQueue的使用场景&#xff0c;并重点分析了ArrayBlockingQueue的实现原理&#xff0c;了解到ArrayBlockingQueue底层是基于数组实现的阻塞队列。 但是BlockingQueue的实现类中&#xff0c;有一种阻塞队列比较特殊&#xff0c;就是SynchronousQueue&#xf…

mysql的服启动以及用户登录

目录 1.mysql的启动 A.使用电脑图形化界面打开 B.使用命令行 2.数据库的登录 A.使用命令行加密码直接登录 B.使用命令行再后面输入密码实现登录 C.访问同一台电脑上的不同数据库 D.访问其他主机上的数据库 E.退出mysql 1.mysql的启动 A.使用电脑图形化界面打开 在电脑当中…

Spring MVC面试题

什么是Spring MVC&#xff1f;简单介绍下你对Spring MVC的理解&#xff1f; SpringMVC是一个基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架&#xff0c;把复杂的web应用分成逻辑清晰的表示层、控制层、业务层&#xff08;服务层&#xff09;、持久层&#xff0c;…

谈谈从DAMA、DCMM和DGI三大数据治理框架详细了解数据战略规划的关键要素

当前,数据作为新的生产要素提到了关键位置,众多组织认为数据是重要的战略资产。可是,如何发挥数据要素的生产力,数据资产又如何为企业创造价值,确有些无从下手。那么,这就是数据战略要解决的问题。企业怎么看待数据资产、数据的价值如何定位,对数据价值的期望是什么,数…

javaweb 之 会话技术 Cookie Session 登录注册案例 验证码

会话跟踪技术 会话&#xff1a;用户打开浏览器&#xff0c;访问web服务器的资源&#xff0c;会话建立&#xff0c;直到有一方断开连接&#xff0c;会话结束。在一次会话中可以包含多次请求和响应 会话跟踪&#xff1a;一种维护浏览器状态的方法&#xff0c;服务器需要识别多次…

《面试系列篇》——11种常用的设计模式

目录 【一】前言 【二】单例模式 2.1概念 2.2 饿汉模式 2.3 懒汉模式 多线程版本&#xff1a; 【三】简单工厂模式 【四】工厂方法模式 【五】抽象工厂模式 【六】策略模式 【七】装饰模式 7.1 定义 7.2 使用示例 【八】代理模式 8.1 定义 8.2 使用的优势 8.3…

【机器学习】岭回归和LASSO回归详解以及相关计算实例-加利福尼亚的房价数据集、红酒数据集

文章目录一,岭回归和LASSO回归1.1 多重共线性1.2 岭回归接手1.3 线性回归的漏洞&#xff08;线性系数的矛盾解释&#xff09;1.4 Ridge Regression1.5 岭回归实例&#xff08;加利福尼亚的房价数据集&#xff09;1.6 MSE1.7 LASSO1.8 Ridge岭回归和Lasso套索回归的比较1.9 Lass…