模拟实现map/set[改编红黑树实现map/set容器底层]

news2024/12/24 3:57:11

文章目录

  • 1.搭建框架
    • 1.1map
    • 1.2set
    • 1.3RBTree
    • 1.4图解
  • 2.代码剖析
    • 2.1RBTree.h
    • 2.2Map.h
    • 2.3Set.h
    • 2.4Test.cpp

在这里插入图片描述

1.搭建框架

1.1map

在这里插入图片描述

1.2set

在这里插入图片描述

1.3RBTree

在这里插入图片描述

在这里插入图片描述

1.4图解

在这里插入图片描述

2.代码剖析

2.1RBTree.h

#pragma once
#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <functional>
#include <assert.h>
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 RBTreeIterator
{
	typedef RBTreeNode<T> Node;
	typedef RBTreeIterator<T, Ref, Ptr> Self;
	Node* _node;

	//结点构造函数
	RBTreeIterator(Node* node)
		:_node(node)
	{
	
	}
	//迭代器构造函数
	RBTreeIterator(const RBTreeIterator<T, T&, T*>& it)
		:_node(it._node)
	{
	
	}

	//重载解引用运算符
	Ref operator*()
	{
		return _node->_data;
	}

	//重载成员访问运算符
	Ptr operator->()
	{
		return &_node->_data;
	}

	//关系运算符
	bool operator!=(const Self& s)
	{
		return _node != s._node;
	}

	//自增
	//一棵树 最小的[begin]在左下角 最大的在右下角 
	//[end指向最大的后一个即null ]
	//自增自减运算符遍历从小到大 
	Self& operator++()
	{
		//_node指向当前结点

		//右不空
		if (_node->_right)
		{
			//next为右子树最左节点

			//右子树根节点
			Node* subLeft = _node->_right;
			while (subLeft->_left)
			{
				subLeft = subLeft->_left;
			}
			//更改指向 ++成功
			_node = subLeft;
		}
		//右空:遍历时 一个结点右空 当前结点所在子树遍历结束
		else
		{
			//向上回溯--next特征:右为空的结点所在子树的上一代
			Node* cp = _node;
			Node* dad = cp->_parent;
			//如果cp是父右 向上回溯
			//不是父右 定是父左 循环结束
			
			//特殊:当遍历到最后一个结点[右下角]  一直回溯直到dad指向根时
			//此时再回溯一次 dad为空 循环结束 _node指向空
			while (dad && cp == dad->_right)
			{
				cp = dad;
				dad = dad->_parent;
			}
			//更改指向 ++成功
			_node = dad;
		}

		return *this;
	}

	//自减
	//++从小到大 --从大到小 ++找次小 --找次大
	Self& operator--()
	{
		//假定_node指向最后一个结点[右一定为空]
		if (_node->_left)
		{
			//左不空 次大为左子树最右节点
			Node* subRight = _node->_left;
			while (subRight->_right)
			{
				subRight = subRight->_right;
			}
			//更改指向 ++成功
			_node = subRight;
		}
		else
		{
			//左为空 遍历时 一个结点左空 当前结点所在子树遍历结束

			//向上回溯--prev特征:左为空的结点所在子树的上一代
			Node* cp = _node;
			Node* dad = cp->_parent;

			//如果cp是父左 向上回溯
			//不是父右 定是父左 循环结束

			//特殊:当遍历到最后一个结点[左下角]  一直回溯直到dad指向根时
			//此时再回溯一次 dad为空 循环结束 _node指向空
			while (dad && cp == dad->_left)
			{
				cp = dad;
				dad = dad->_parent;
			}
			//更改指向 ++成功
			_node = dad;
		}

		return *this;
	}

};

//红黑树类
template<class K, class T, class GetKey>
class RBTree
{
	typedef RBTreeNode<T> Node;
public:

	//析构函数
	~RBTree()
	{
		_Destroy(_root);
		_root = nullptr;
	}

public:
	typedef RBTreeIterator<T, T&, T*> itertaor;
	typedef RBTreeIterator<T, const T&, const T*> const_itertaor;

///  迭代器  
	//begin迭代器[左路最后一个结点]
	itertaor begin()
	{
		Node* cp = _root;
		//防止传入空树
		while (cp && cp->_left)
		{
			cp = cp->_left;
		}

		return itertaor(cp);
	}

	//end迭代器[最后一个数据的下一个位置]
	itertaor end()
	{
		return itertaor(nullptr);
	}

	//const_begin迭代器
	const_itertaor begin() const
	{
		Node* cp = _root;
		while (cp && cp->_left)
		{
			cp = cp->_left;
		}

		return const_itertaor(cp);
	}

	//const_end迭代器
	const_itertaor end() const
	{
		return const_itertaor(nullptr);
	}

	//查找函数
	Node* Find(const K& key)
	{
		Node* cp = _root;
		GetKey get;
		while (cp)
		{
			if (get(cp->_data) < key)
			{
				cp = cp->_right;
			}
			else if (get(cp->_data) > key)
			{
				cp = cp->_left;
			}
			else
			{
				return cp;
			}
		}

		return nullptr;
	}

	//插入函数
	pair<itertaor, bool> Insert(const T& data)
	{
		//根节点为空
		if (_root == nullptr)
		{
			_root = new Node(data);
			_root->_col = BLACK;   //根节点颜色为黑

			return make_pair(itertaor(_root), true);
		}

		//不为空 定位至合适位置
		GetKey get;
		Node* dad = nullptr;
		Node* cp = _root;
		while (cp)
		{
			if (get(cp->_data) < get(data))
			{
				dad = cp;
				cp = cp->_right;
			}
			else if (get(cp->_data) > get(data))
			{
				dad = cp;
				cp = cp->_left;
			}
			else
			{
				return make_pair(itertaor(cp), false);
			}
		}

		cp = new Node(data);
		Node* tmp = cp;
		if (get(dad->_data) > get(data))
		{
			dad->_left = cp;
		}
		else
		{
			dad->_right = cp;
		}
		cp->_parent = dad;

		//cp父不空 && cp[红]  此时定位新插入位置 若父为红
		while (dad && dad->_col == RED)
		{
			//dad && dad->_col == RED
		    //二次循环来到此处 
		    // 1.父为空 则cur为根 [最后控制cur变黑] 循环结束
		    // 2.父为黑 无需操作

			//若父为红 祖父定存在且为黑
			Node* grandpa = dad->_parent;
			assert(grandpa);
			assert(grandpa->_col == BLACK);

			//父为左子树 uncle为右子树
			if (grandpa->_left == dad)
			{
				Node* uncle = grandpa->_right;
				//情况一、uncle存在为红 变色+回溯
			    //dad-uncle变黑 grandpa变红
				if (uncle && uncle->_col == RED)
				{
					//变色
					dad->_col = BLACK;
					uncle->_col = BLACK;
					grandpa->_col = RED;

					//回溯
					cp = grandpa;
					dad = cp->_parent;
				}
				//情况二-三:uncle不存在 存在且为黑
				else 
				{
					if (cp == dad->_left)
					{
						//情况二:右单旋+变色
						RotateR(grandpa);
						dad->_col = BLACK;
						grandpa->_col = RED;
					}
					else
					{
						//情况三:左右双旋+变色
						RotateL(dad);
						RotateR(grandpa);
						cp->_col = BLACK;
						grandpa->_col = RED;
					}

					break;
				}
			}
			//父为右子树 uncle为左子树
			else
			{
				Node* uncle = grandpa->_left;
				//情况一
				if (uncle && uncle->_col == RED)
				{
					//变色
					dad->_col = BLACK;
					uncle->_col = BLACK;
					grandpa->_col = RED;

					//回溯
					cp = grandpa;
					dad = cp->_parent;
				}
				//情况二 三
				else 
				{
					if (cp == dad->_right)
					{
						//情况二:左单旋+变色
						RotateL(grandpa);
						grandpa->_col = RED;
						dad->_col = BLACK;
					}
					else
					{
						//情况三:右左单旋+变色
						RotateR(dad);
						RotateL(grandpa);
						cp->_col = BLACK;
						grandpa->_col = RED;
					}

					break;
				}
			}
		}

		_root->_col = BLACK;

		return make_pair(itertaor(tmp), true);
	}

	//判断是否是红黑树
	bool IsRBTree()
	{
		if (_root && _root->_col == RED)
		{
			cout << "根节点颜色错误!" << endl;
			return false;
		}

		//事先计算出某一条路径黑色结点数量
		//后续每一条路径都应和certain相等 不想等报错
		int certain = 0;
		Node* cur = _root;
		while (cur)
		{
			if (cur->_col == BLACK)
				++certain;
			cur = cur->_left;
		}

		return PrevJudge(_root, 0, certain);
	}

	//高度接口
	int Height()
	{
		return _Height(_root);
	}

private:

	//后序遍历销毁红黑树
	void _Destroy(Node* root)
	{
		if (root == nullptr)
		{
			return;
		}

		_Destroy(root->_left);
		_Destroy(root->_right);
		delete root;
	}
	//高度接口
	int _Height(Node* root)
	{
		if (root == NULL)
			return 0;

		int leftH = _Height(root->_left);
		int rightH = _Height(root->_right);

		return leftH > rightH ? leftH + 1 : rightH + 1;
	}
	//前序遍历判断
	bool PrevJudge(Node* root, int BNcount, int& certain)
	{
		//某一条路径结束
		if (root == nullptr)
		{
			if (BNcount != certain)
			{
				cout << "性质4违例: 存在某一路径黑色节点的数量不等!" << endl;
				return false;
			}

			return true;
		}
		//遇黑--值++
		if (root->_col == BLACK)
		{
			++BNcount;
		}
		//遇红--连坐判断
		if (root->_col == RED
			&& root->_parent
			&& root->_parent->_col == RED)
		{
			cout << "性质3违例: 存在连续红节点!" << endl;
			return false;
		}

		return PrevJudge(root->_left, BNcount, certain)
			&& PrevJudge(root->_right, BNcount, certain);
	}
	//左旋
	void RotateL(Node* dad)
	{
		Node* subR = dad->_right;
		Node* subRL = subR->_left;

		dad->_right = subRL;
		if (subRL)
			subRL->_parent = dad;

		Node* grandpa = dad->_parent;

		subR->_left = dad;
		dad->_parent = subR;

		if (grandpa == nullptr)
		{
			_root = subR;
			_root->_parent = nullptr;
		}
		else
		{
			if (grandpa->_left == dad)
			{
				grandpa->_left = subR;
			}
			else
			{
				grandpa->_right = subR;
			}

			subR->_parent = grandpa;
		}
	}
	//右旋
	void RotateR(Node* dad)
	{
		Node* subL = dad->_left;
		Node* subLR = subL->_right;

		dad->_left = subLR;
		if (subLR)
			subLR->_parent = dad;

		Node* grandpa = dad->_parent;

		subL->_right = dad;
		dad->_parent = subL;

		if (dad == _root)
		{
			_root = subL;
			_root->_parent = nullptr;
		}
		else
		{
			if (grandpa->_left == dad)
			{
				grandpa->_left = subL;
			}
			else
			{
				grandpa->_right = subL;
			}
			subL->_parent = grandpa;
		}
	}

private:
	Node* _root = nullptr;
};

2.2Map.h

#pragma once
#include "RBTree.h"

namespace ape
{	
	//map类
	template<class K, class V>
	class map
	{
		//仿函数operator() 获得key
		struct GetKeyMap
		{
			const K& operator()(const pair<const K, V>& pair)
			{
				return pair.first;
			}
		};
    public:
		typedef typename RBTree<K, pair<const K, V>, GetKeyMap>::itertaor iterator;

		//begin迭代器
		iterator begin()
		{
			return _t.begin();
		}

		//end迭代器
		iterator end()
		{
			return _t.end();
		}

		//重载下标运算符[]
		V& operator[](const K& key)
		{
			pair<iterator, bool> pair = _t.Insert(make_pair(key, V()));
			return pair.first->second;
		}

		//插入函数
		pair<iterator, bool> insert(const pair<const K, V>& pair)
		{
			return _t.Insert(pair);
		}
    private:
		RBTree<K, pair<const K, V>, GetKeyMap> _t;
	};

/// 测试 

	void test_map1()
	{
		//插入
		map<string, string> m;
		string a = "Eddie", b = "彭于晏";
		m.insert(make_pair(a,b));
		m.insert(make_pair("Tom", "汤姆"));
		m.insert(make_pair("Jerry", "杰瑞"));
		//迭代器遍历
		map<string, string>::iterator it = m.begin();
		while (it != m.end())
		{
			cout << it->first << ":" << it->second << endl;
			/*
			it->first = "Ape";
			it->second = "阿猿";
			*/
			++it;
		}
		cout << endl;
		//for循环遍历
		for (auto& e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
		cout << endl;
	}

	void test_map2()
	{
		string s[] = { "陀螺", "陀螺", "洋娃娃", "陀螺", "洋娃娃", "洋娃娃", "陀螺",
				       "洋娃娃", "悠悠球", "洋娃娃", "悠悠球", "乐高" };
		//记录次数
		map<string, int> m;
		for (auto& e : s)
		{
			m[e]++;
		}

		//for循环遍历
		for (auto& e : m)
		{
			cout << e.first << ":" << e.second << endl;
		}
	}
}

2.3Set.h

#pragma once
#include "RBTree.h"
namespace ape
{
	//set类
	template<class K>
	class set
	{
		//仿函数operator() 获得key
		struct GetKeySet
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		
/// ///  迭代器  /
		
		typedef typename RBTree<K, K, GetKeySet>::const_itertaor iterator;
		typedef typename RBTree<K, K, GetKeySet>::const_itertaor const_iterator;

		//begin迭代器
		iterator begin()
		{
			return _t.begin();
		}
		//end迭代器
		iterator end()
		{
			return _t.end();
		}
        //插入
		pair<iterator, bool> insert(const K& key)
		{
			return _t.Insert(key);
		}

    private:
		RBTree<K, K, GetKeySet> _t;
	};

/// 测试 

	void test_set()
	{
		//插入
		int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		set<int> s;
		for (auto e : a)
		{
			s.insert(e);
		}
		//迭代器遍历
		set<int>::iterator it = s.begin();
		while (it != s.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;
		//for循环遍历
		for (auto e : s)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

2.4Test.cpp

#define _CRT_SECURE_NO_WARNINGS 
#include <iostream>
#include <list>
#include <vector>
#include <algorithm>
#include <array>
#include <time.h>
#include <queue>
#include <stack>
#include <string>
#include <set>
#include <map>
#include <functional>
using namespace std;

#include "AVLTree.h"
#include "RBTree.h"
#include "Map.h"
#include "Set.h"

int main()
{
	ape::test_set();
	//ape::test_map2();
	return 0;
}


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

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

相关文章

.net 温故知新:Asp.Net Core WebAPI 入门使用及介绍

在Asp.Net Core 上面由于现在前后端分离已经是趋势,所以asp.net core MVC用的没有那么多,主要以WebApi作为学习目标。 一、创建一个WebApi项目 我使用的是VS2022, .Net 7版本。 在创建界面有几项配置: 配置Https启用Docker使用控制器启用OpenAPI支持不使用顶级语句其中配置…

YOLOv5-PTQ量化部署

目录 前言一、PTQ量化浅析二、YOLOv5模型训练1. 项目的克隆和必要的环境依赖1.1 项目克隆1.2 项目代码结构整体介绍1.3 环境安装 2. 数据集和预训练权重的准备2.1 数据集2.2 预训练权重准备 3. 训练模型3.1 修改数据配置文件3.2 修改模型配置文件3.3 训练模型3.4 mAP测试 三、Y…

[NOIP2012 提高组] 国王游戏(贪心,排序,高精度)

[NOIP2012 提高组] 国王游戏 题目描述 恰逢 H 国国庆&#xff0c;国王邀请 n n n 位大臣来玩一个有奖游戏。首先&#xff0c;他让每个大臣在左、右手上面分别写下一个整数&#xff0c;国王自己也在左、右手上各写一个整数。然后&#xff0c;让这 n n n 位大臣排成一排&…

基于SpringBoot的大学生租房平台

基于SpringBoot的大学生租房平台的设计与实现&#xff0c;前后端分离 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBootMyBatisVue工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 【主要功能】 角色&#xff1a;用户、管理员、房东 管理员&#…

ck 计算留存

1.函数介绍 参数聚合函数 | ClickHouse Docs Retention​ 该函数将一组条件作为参数&#xff0c;类型为1到32个 UInt8 类型的参数&#xff0c;用来表示事件是否满足特定条件。 任何条件都可以指定为参数&#xff08;如 WHERE). 除了第一个以外&#xff0c;条件成对适用&…

第七章 查找 十、散列查找

一、哈希表&#xff08;散列表&#xff09; 哈希表的数据元素的关键字与其存储地址直接相关。 二、解决冲突的方法 三、散列表中元素的查找 总共对比了3个关键字&#xff0c;所以查找长度为3. 四、查找效率计算 &#xff08;1&#xff09;成功的概率 需要对比一次的关键字为…

保存锁屏壁纸 win11

经常在锁屏看见自己超级喜欢的壁纸&#xff0c;但是找不到在哪保存。这次把查到的方法总结在这里。 1.WinR调出运行框 2.输入以下内容后回车 C:\Users\你的用户名\AppData\Local\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets3.得到的…

Fiddler抓取手机https包的步骤

做接口测试时&#xff0c;有时我们需要使用fiddler进行抓包分析&#xff0c;那么如何抓取https包。主要分为以下七步&#xff1a; 1.设置fiddler选项&#xff1a;Tools->Options,按如下图勾选 2.下载并安装Fiddler证书生成器 下载地址&#xff1a;http://www.telerik.com/…

Python脚本实现xss攻击

实验环境&#xff1a;zd靶场、vscode 知识点 requests.session() 首先我们需要先利用python requests模块进行登录&#xff0c;然后利用开启session记录&#xff0c;保持之后的操作处于同一会话当中 requests.session()用于创建一个会话(session)的实例对象。使用requests库…

spark ui 指南

spark ui 指南 1.sparkUI 基本介绍2.jobs页面3.stages 页面4.storage 页面5.environment 页面6.ececutor 页面7 sql 页面  spark ui 是反应一个spark 作业执行情况的页面,通过查看作业的执行情况,分析作业运行的状态. 1.sparkUI 基本介绍 进入运行主页面如下,主要有6各部…

毛玻璃跟随鼠标移动

效果展示 页面结构组成 从上述的效果图可以看出&#xff0c;此页面的布局比较简单&#xff0c;采用常规的布局就可以实现 CSS / JavaScript 知识点 backdrop-filter 属性回顾mousemove 事件 实现页面布局 <section><h2>Frosted Glass</h2><div class…

【STL】用一棵红黑树封装map和set

⭐博客主页&#xff1a;️CS semi主页 ⭐欢迎关注&#xff1a;点赞收藏留言 ⭐系列专栏&#xff1a;C进阶 ⭐代码仓库&#xff1a;C进阶 家人们更新不易&#xff0c;你们的点赞和关注对我而言十分重要&#xff0c;友友们麻烦多多点赞&#xff0b;关注&#xff0c;你们的支持是我…

独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位——“51单片机”

各位CSDN的uu们你们好呀&#xff0c;今天依旧是小雅兰的51单片机的内容&#xff0c;内容主要是&#xff1a;独立按键控制LED亮灭、独立按键控制LED状态、独立按键控制LED显示二进制、独立按键控制LED移位&#xff0c;下面&#xff0c;让我们进入51单片机的世界吧&#xff01;&a…

【探索排序算法的魅力:优化、性能与实用技巧】

本章重点 排序的概念及其运用 常见排序算法的实现 排序算法复杂度及稳定性分析 1.排序的概念及其运用 1.1排序的概念 排序&#xff1a;所谓排序&#xff0c;就是使一串记录&#xff0c;按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 稳定性…

Edge扩展插件推荐专业视频下载器

专业视频下载器&#xff0c;这款扩展插件非常好用&#xff0c;强烈推荐。只要能打开的视频&#xff0c;都能下载。 安装完成是这样的&#xff1a; 有用记得点赞。

编程前置:怎么知道一句话的重点?

怎么知道一句话的重点&#xff1f; <small> 之所以要这个问题&#xff0c;是因为① 对标题进行分词 ② 找到标题中的重点词 ③ 然后找到主题中唯一的词语 ④ 然后对这个词语进行绘图说和看&#x1f440;来看&#x1f440;去&#xff0c;也就是文字成图的步骤啦&#xff…

Linux作业2

Linux中的 stdin 、stderr、stdout分别是什么意思 在 Linux 中&#xff0c;stdin、stdout 和 stderr 是标准的输入、标准的输出和标准的错误的缩写&#xff0c;它们是与终端相关联的默认文件描述符&#xff0c;用于处理输入和输出。以下是它们的详细含义&#xff1a; stdin&am…

日期范围搜索

1.日期范围选择界面 <?xml version"1.0" encoding"utf-8"?> <ScrollViewandroid:layout_width"fill_parent"android:layout_height"fill_parent"xmlns:android"http://schemas.android.com/apk/res/android">…

构建一个TypeScript环境的node项目

本文 我们用一种不太一样的方式来创建项目 这里 我们事先创建了一个文件夹作为项目目录 然后打开项目终端 输入 npm init然后 在新弹出的对话框中 大体就是 名字随便写一个 然后 后面的回车&#xff0c;到最后一个输入 yes 然后回车 这样 我们就有一个基础的 node项目结构了…

Alibaba Cloud Linux 3安装Docker

出现以上报错&#xff0c;进行以下操作&#xff1a; cd /etc/yum.repos.d/ rm -rf docker-ce.repo 然后进行docker安装&#xff08;以社区版为例&#xff09; 1.添加docker-ce的dnf源 dnf config-manager --add-repohttps://mirrors.aliyun.com/docker-ce/linux/centos/do…