map和set的简易封装(纯代码)

RBTree.h

#pragma once

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

enum colar
{   
	red,
	black
};

template<class T>//有效参数就一个 
struct RBTreeNode
{
	RBTreeNode(const T& data)
		:_left(nullptr)
		, _right(nullptr)
		, _parent(nullptr)
		, _data(data)
		, _co(red)
	{}

	RBTreeNode<T>* _left;
	RBTreeNode<T>* _right;
	RBTreeNode<T>* _parent;

	T _data;
	colar _co;

};

template<class T,class ref,class ptr>
struct _Tree_iterator
{
	typedef RBTreeNode<T> Node;
	typedef _Tree_iterator<T,ref,ptr> self;
	Node* _cur;

	_Tree_iterator(Node* tmp)
		:_cur(tmp)
	{}

	self& operator++()
	{
		//将当前节点看作父节点再分析
		if (_cur->_right == nullptr)//向上返回(前提是父的左孩子)如果是右孩子则表明父亲已经遍历过了
		{
			Node* parent = _cur->_parent;
			while (parent && parent->_right == _cur)//parent可能为空
			{
				_cur = parent;
				parent = _cur->_parent;
			}
			//指向parent指向的left等于_cur 或者parent为空(遍历结束)
			_cur = parent;

		}
		else//自己就属于父节点,找右子树的最左节点
		{
			Node* tmp = _cur->_right;
			while (tmp->_left)//tmp不可能为空
			{
				tmp = tmp->_left;
			}
			_cur = tmp;
		}
		return *this;
	}
	self& operator--()//相较于operator++而言就是 右子树 根 左子树 的遍历方式
	{
		if (_cur->_left == nullptr)//表明当前节点遍历完成,向上返回……
		{
			Node* parent = _cur->_parent;
			while (parent&&parent->_left == _cur)
			{
				_cur = parent;
				parent = parent->_parent;
			}
			_cur = parent;
		}
		else
		{
			//找左子树的最右节点
			_cur = _cur->_left;
			while (_cur->_right)
			{
				_cur = _cur->_right;
			}
		}
		return *this;
	}

	ref operator*()
	{
		return _cur->_data;
	}

	ptr operator->()
	{
		return &_cur->_data;
	}

	bool operator!=(const self& tmp)
	{
		return _cur != tmp._cur;
	}
};

template<class K, class T,class Com_T>
class RBTree
{
	typedef RBTreeNode<T> Node;
	
public:
	typedef _Tree_iterator<T,T&,T*> iterator;
	typedef _Tree_iterator<T,const T&,const T*> const_iterator;
	
	iterator begin()
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return iterator(cur);
	}
	iterator end()
	{
		return iterator(nullptr);
	}
	const_iterator cbegin()const
	{
		Node* cur = _root;
		while (cur && cur->_left)
		{
			cur = cur->_left;
		}
		return const_iterator(cur);
	}
	const_iterator cend()const 
	{
		return const_iterator(nullptr);
	}

	//pair <iterator, bool> insert(const T& data)//data类型取决于T,而T又取决于map和set
	pair <Node*, bool> insert(const T& data)//data类型取决于T,而T又取决于map和set
	{
		Node* newroot = new Node(data);//默认为红
		if (_root == nullptr)
		{
			_root = newroot;
			_root->_co = black;//设为黑
			return make_pair(newroot,true);
		}
		Node* parent = _root, * cur = _root;
		//插入数据
		Com_T com;
		while (1)
		{
			parent = cur;
			if (com(cur->_data) > com(data))//这里data的类型可能是pair(不确定)
			{
				cur = cur->_left;
				if (cur == nullptr)
				{
					parent->_left = newroot;
					newroot->_parent = parent;
					break;
				}
			}
			else if (com(cur->_data) < com(data))
			{
				cur = cur->_right;
				if (cur == nullptr)
				{
					parent->_right = newroot;
					newroot->_parent = parent;
					break;
				}
			}
			else
			{
				return make_pair(cur, false);//数据相同返回相同数据的迭代器(类似是查找数据)
			}

		}

		//父节点的判断
		cur = newroot;//当前节点就是新插入的节点

		while (parent && parent->_co == red)//父亲节点可能不存在
		{
			Node* pparent = parent->_parent;//parent为红,不可能是根,一定存在pparent
			Node* uncle = nullptr;
			//找叔叔节点
			if (pparent->_right == parent)
				uncle = parent->_parent->_left;
			else
				uncle = parent->_parent->_right;

			if (uncle && uncle->_co == red)//叔叔存在且为红
			{
				//变色
				parent->_co = uncle->_co = black;
				pparent->_co = red;//祖父节点有可能是根节点
				//继续向上更新处理
				cur = pparent;
				parent = cur->_parent;
			}
			else//叔叔节点为空或为黑
			{
				//旋转
				if (pparent->_left == parent && parent->_left == cur)
				{
					//右单旋
					RotateR(pparent);
					parent->_co = black;
					pparent->_co = red;
				}
				else if (pparent->_right == parent && parent->_right == cur)
				{
					//左单旋
					RotateL(pparent);
					parent->_co = black;
					pparent->_co = red;
				}
				else if (pparent->_right == parent && parent->_left == cur)
				{
					//右左双旋
					RotateR(parent);
					RotateL(pparent);
					cur->_co = black;
					pparent->_co = red;
				}
				else if (pparent->_left == parent && parent->_right == cur)
				{
					//左右双旋
					RotateL(parent);
					RotateR(pparent);
					cur->_co = black;
					pparent->_co = red;
				}
				break;//旋转之后新的根节点都是黑色
			}

		}

		_root->_co = black;//循环体内很有可能将根节点改为红
		return make_pair(newroot, true);

	}


	void RotateL(Node* parent)//左单旋
	{
		Node* cur = parent->_right;
		Node* curl = cur->_left;
		Node* pparent = parent->_parent;//提前记录

		parent->_right = curl;
		if (curl)
		{
			curl->_parent = parent;
		}
		cur->_left = parent;
		parent->_parent = cur;

		//处理pparent与parent的连接
		if (_root == parent)
		{
			_root = cur;
			cur->_parent = nullptr;
		}
		else
		{
			if (pparent->_left == parent)
				pparent->_left = cur;
			else
				pparent->_right = cur;
			cur->_parent = pparent;
		}

	}
	void RotateR(Node* parent)//右单旋
	{
		{
			Node* cur = parent->_left;
			Node* curr = cur->_right;
			Node* pparent = parent->_parent;//提前记录

			parent->_left = curr;
			if (curr)
			{
				curr->_parent = parent;
			}
			cur->_right = parent;
			parent->_parent = cur;

			//处理pparent与parent的连接
			if (_root == parent)
			{
				_root = cur;
				cur->_parent = nullptr;
			}
			else
			{
				if (pparent->_left == parent)
					pparent->_left = cur;
				else
					pparent->_right = cur;
				cur->_parent = pparent;
			}

		}
	}

	void InOrder()
	{
		_InOrder(_root);
		cout << endl;
	}
	void _InOrder(Node* root)
	{
		if (root == nullptr)
			return;

		_InOrder(root->_left);
		cout << root->_kv.first << " ";
		_InOrder(root->_right);
	}

	// 根节点->当前节点这条路径的黑色节点的数量
	bool Check(Node* cur, int blacknum, int ref_val)
	{
		if (cur == nullptr)
		{
			if (blacknum == ref_val)
				return true;
			cout << "每条路径的黑色节点个数不同" << endl;
			return false;
		}
		Node* parent = cur->_parent;
		if (cur->_co == red && parent->_co == red)//向上判断,向下判断的节点可能为空或其它的。
			return false;
		if (cur->_co == black)
			blacknum++;

		return Check(cur->_left, blacknum, ref_val) && Check(cur->_right, blacknum, ref_val);
	}
	bool Is_balance()
	{
		if (_root->_co == red)
			return false;
		if (_root == nullptr)
			return true;

		//不能出现连续红节点
		//每条路径黑色节点要保证相同
		int blacknum = 0;//必须传值,相当于是每个节点都有一个变量表示从根到当前的黑节点个数
		int ref_val = 0;//参考值,求出任意一条路径中黑色节点数目
		Node* cur = _root;
		while (cur)
		{
			if (cur->_co == black)
				ref_val++;
			cur = cur->_left;
		}
		return Check(_root, blacknum, ref_val);
	}

private:
	Node* _root=nullptr;
};

Set.h

#include"RBTree.h"

template<class  key>
class set
{
public:
	struct setCom//仿函数
	{
		const key& operator()(const key& k)
		{
			return k;
		}
	};

	//typedef _Tree_iterator<key> iterator;
	typedef typename RBTree<key, key, setCom>::const_iterator iterator;
	typedef typename RBTree<key, key, setCom>::const_iterator const_iterator;
	//对类模版取内嵌类型,加typename是为了告诉编译器这里是类型

	pair<iterator,bool> insert(const key& k)//此时pair的第一个参数类型是const_iterator
	{
		return _s.insert(k);//insert返回pair<Node*,bool>会构造出pair<iterator,bool>
	}

	iterator begin()const
	{
		return _s.cbegin();
	}
	iterator end()const
	{
		return _s.cend();
	}
	

private:
	RBTree<key, key, setCom> _s;//封装红黑树
};

Map.h

#include"RBTree.h"

template<class  key,class val>
class map
{
public:

	struct mapCom//仿函数
	{
		const key& operator()(const pair<key,val>& p)
		{
			return p.first;
		}
	}; 

	//typedef _Tree_iterator<pair<key,val>> iterator;
	typedef typename RBTree<key, pair<const key, val>, mapCom>::iterator iterator;
	typedef typename RBTree<key, pair<const key, val>, mapCom>::const_iterator const_iterator;
	//对类模版取内嵌类型,加typename是为了告诉编译器这里是类型

	pair<iterator, bool> insert(const pair<key, val>& kv)
	{
		return _m.insert(kv);
	}
	
	iterator begin()
	{
		return _m.begin();
	}
	iterator end()
	{
		return _m.end();
	}
	const_iterator cbegin()const
	{
		return _m.cbegin();
	}
	const_iterator cend()const
	{
		return _m.cend();
	}

	val& operator[](const key& k)
	{
		pair<key, val> tmp(k, val());//val给缺省值,tmp是创建变量
		pair<iterator,bool> ret = insert(tmp);//返回插入的节点的pair

		return (ret.first)->second;
	}

private: 
	RBTree<key, pair<const key, val>,mapCom> _m;//封装红黑树(参数类型决定着红黑树的数据类型)
};  

test.cpp(测试)

#include"Map.h"
#include"Set.h"
#include<string>

void test_set()
{
	set<int> s;
	s.insert(4);
	s.insert(1);
	s.insert(2);
	s.insert(3);
	s.insert(2);
	s.insert(0);
	s.insert(10);
	s.insert(5);

	set<int>::iterator it = s.begin();//浅拷贝
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

void test_map()
{
	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("sort", "xx"));
	dict.insert(make_pair("left", "左边"));
	dict.insert(make_pair("right", "右边"));

	map<string, string>::const_iterator it = dict.cbegin();
	while (it != dict.cend())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

	string arr[] = { "㽶", "香蕉","ƻ", "香蕉", "ƻ", "香蕉", "ƻ", "ƻ", "香蕉", "ƻ", "㽶", "ƻ", "㽶" };
	map<string, int> countMap;
	for (auto& e : arr)
	{
		countMap[e]++;
	}

	for (auto kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
	cout << endl;
}

int main()
{
	//test_set();
	test_map();

	return 0;
}

如有问题欢迎留言!!!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Vue bus事件总线的原理与使用

这里写自定义目录标题 一、 Vue Bus 总线原理二、Vue bus的使用1、创建总线&#xff1a; 在 Vue 应用中&#xff0c;可以创建一个 Vue 实例作为总线&#xff0c;用于管理事件。2、事件的发布与订阅&#xff1a; 组件通过订阅事件来监听总线上的消息&#xff0c;而其他组件则通过…

(免费)双相情感障碍筛查MDQ 在线测试双向情感障碍

MDQ用于筛查双相障碍&#xff0c;主要包含13个关于双相障碍症状的是非问题&#xff0c;当前测试采用的量表为2010年杨海晨博士翻译版。该量表为目前世界范围内最常用的双相障碍筛查量表&#xff0c;目前在精神科门诊最为常用的量表之一。 双向情感障碍筛查量表&#xff0c;也叫…

Linux(多用户下)查看cuda、cudnn版本、查看已经安装的cuda版本相关命令

查看已经安装的CUDA多个版本 linux 中cuda默认安装在/usr/local目录中&#xff1a; -可以使用命令&#xff1a; ls -l /usr/local | grep cuda查看该目录下有哪些cuda版本&#xff1a; 如果输出&#xff1a; lrwxrwxrwx 1 root root 21 Dec 17 2021 cuda -> /usr/loc…

监控直流防雷浪涌保护器综合方案

监控系统是一种广泛应用于安防、交通、工业、军事等领域的信息系统&#xff0c;它通过摄像机、传输线路、监控中心等设备&#xff0c;实现对目标区域的实时监视和控制。然而&#xff0c;监控系统也面临着雷电的威胁&#xff0c;雷电可能通过直击雷、感应雷、雷电波侵入等途径&a…

React实战演练项⽬一需求分析及vite_react搭建项目

React实战演练项⽬一需求分析及项目初始化 需求分析 刚学完React,开始找项目进行上手练习&#xff01; 页面组件拆分&#xff1a; 头部&#xff1a;导航tab、搜索框、登录注册 中间&#xff1a;分类导航、轮播图、新人福利、高单价产品导航 课程分类列表、底部内容、登陆提…

当攻防演练已成常态,企业应该相信西医还是老中医?

在面对疾病时&#xff0c;很多人常常会犹豫不决&#xff0c;不知道应该选择中医还是西医进行治疗。与疾病斗争的过程也是一场“战斗”&#xff0c;需要选择合适的“武器”和策略。有些人认为西医疗效快&#xff0c;能够迅速缓解症状&#xff1b;而另一些人则认为中医治疗更根本…

m1 rvm install 3.0.0 Error running ‘__rvm_make -j8‘

在使用M1 在安装cocopods 前时&#xff0c;安装 rvm install 3.0.0遇到 rvm install 3.0.0 Error running __rvm_make -j8 备注: 该图片是借用其他博客图片&#xff0c;因为我的环境解决完没有保留之前错误信息。 解决方法如下&#xff1a; 1. brew uninstall --ignore-depe…

泛型编程 -- 模板详解

一、模板 在没有模板之前&#xff0c;如果我们写一个swap()两数交换函数&#xff0c;因为我们要支持 int 与int 交换 、double 与 double 交换等等情况&#xff0c;所以要实现swap()函数的多个重载&#xff0c;显得很繁琐&#xff0c;于是就引入了模板。 模板就是在需要模板的地…

idea一键打包docker镜像并推送远程harbor仓库的方法(包含spotify和fabric8两种方法)--全网唯一正确,秒杀99%水文

我看了很多关于idea一键打包docker镜像并推送harbor仓库的文章&#xff0c;不论国内国外的&#xff0c;基本上99%都是瞎写的&#xff0c; 这些人不清楚打包插件原理&#xff0c;然后就是复制粘贴一大篇&#xff0c;写了一堆垃圾&#xff0c;然后别人拿来也不能用。 然后这篇文…

MySQL表操作

1.创建表 创建一个表 表的字符集为utf8&#xff0c;校验规则为utf8_bin 存储引擎为MYisam 2.查看表结构 desc 表名&#xff1b; 3.查看表内容 select * from 表名&#xff1b; 4.查看数据库有几个表 show tables; 附&#xff1a;查看创建表时的语句 show create table 表名…

怎么去掉邮件内容中的回车符

上图是Outlook 截图&#xff0c;可见1指向的总有回车符&#xff1b; 故障原因&#xff1a; 不小心误按了箭头4这个选项&#xff1b; 解决方法&#xff1a; 点击2箭头确保tab展开&#xff1b; 点击3以找到箭头4. 取消勾选或者多次点击&#xff0c;即可解决。

web3资讯及远程工作

各位如果想了解区块链相关的消息可以通过如下网址了解&#xff0c;里面还会有相关职位招聘&#xff08;包括远程工作&#xff09;&#xff0c;还可以在里面进行发帖&#xff0c;进入即可获得1000积分&#xff0c;后期可以兑换一些礼品Cryptosquare

Java简介、基本语法

一、Java简介&#xff1a; Java 是由 Sun Microsystems 公司于 1995 年 5 月推出的 Java 面向对象程序设计语言和 Java 平台的总称。 Java主要的特性&#xff1a; 1、Java语言是简单的的&#xff1a; Java语言的语法与C、C语言接近。Java丢弃了C中的一些特性&#xff0c;如操…

android 数独小游戏 经典数独·休闲益智

一款经典数独训练app 标题资源下载 &#xff08;0积分&#xff09;https://download.csdn.net/download/qq_38355313/88544810 首页页面&#xff1a; 1.包含有简单、普通、困难、大师四种难度的数独挑战供选择&#xff1b; 记录页面&#xff1a; 1.记录用户训练过的数独信息&…

HT8313 D/AB切换 音频功率放大器

HT8313具有AB类和D类的自Y切换功能&#xff0c;在受到D类功放EMI干扰困扰时&#xff0c;可随时切换至AB类音频功放模式&#xff08;此时电荷泵升压功能关闭&#xff09;。 HT8313内部固定28dB增益&#xff0c;内置的关断功能使待机电流Z小化&#xff0c;还集成了输出端过流保护…

“说”出一个UI原型稿:来自北邮课堂的一款文心应用

在2023年秋季学期&#xff0c;北京邮电大学联合百度飞桨&#xff0c;设计了“文心一言X产品设计”的集训营主题&#xff0c;并融入到了“移动交互设计”的课程中。在完成结业作业的过程中&#xff0c;同学们将基于文心一言开展产品原型设计与初步开发。 众所周知&#xff0c;产…

容性负载箱与电容器的关系是什么?

容性负载箱用于测试电容器性能的设备&#xff0c;电容器是储存电能的元件&#xff0c;具有储存和释放电荷的能力。容性负载箱通过对电容器施加不同的负载&#xff0c;可以测量电容器的容量、电压响应、损耗等参数。 容性负载箱与电容器的关系主要体现在以下几个方面&#xff1a…

外贸自建站怎么做?做外贸要怎样建设网站?

外贸自建站如何建立&#xff1f;海洋建站的具体步骤有哪些&#xff1f; 通过建立自己的外贸网站&#xff0c;您可以更好地展示公司的产品和服务&#xff0c;吸引更多的潜在客户&#xff0c;提高品牌知名度&#xff0c;拓展海外市场。那么&#xff0c;如何建立一个成功的外贸自…

系列三、GC垃圾回收【总体概览】

一、GC垃圾回收【总体概览】 JVM进行GC时&#xff0c;并非每次都对上面的三个内存区域&#xff08;新生区、养老区、元空间/永久代&#xff09;一起回收&#xff0c;大部分回收的是新生区里边的垃圾&#xff0c;因此GC按照回收的区域又分为了两种类型&#xff0c;一种是发生在新…

6块钱改变世界,网易和拼多多踏入同一条河流?

年底将至&#xff0c;各种颁奖盛典星光熠熠。如果要给今年深蹲反弹中的互联网大厂颁奖&#xff0c;2023表现最突出的可能是师出同门的兄弟网易和拼多多。 从市场表现来看&#xff0c;两家企业录得今年互联网中概股最高涨幅&#xff0c;被称为“中概股之光”&#xff1a;2023年…