C++进阶:map与set简单自实现

news2024/10/6 8:38:51

目录

  • 1. map与set封装红黑树的方式
    • 1.1 大概实现思路
    • 1.2 红黑树模板抽象
    • 1.3 红黑树的迭代器
  • 2. 红黑树模板的实现
    • 2.1 结点结构的定义
    • 2.2 红黑树迭代器的实现
      • 2.2.1 迭代器的结构
      • 2.2.2 迭代器的方法实现
    • 2.3 树结构的定义
    • 2.4 红黑树接口实现
      • 2.4.1 插入
      • 2.4.2 查找
      • 2.4.3 迭代器相关
  • 3. map与set的自实现
    • 3.1 set封装
    • 3.2 map封装

1. map与set封装红黑树的方式

1.1 大概实现思路

  1. STL库中,实现的map与set其底层都为红黑树这种数据结构,在前面的学习中,我们已经简单模拟实现了红黑树。
  2. 因此,对于map与set的实现无需再去从零开始一一构建,而是可以采用类似于栈与队列这两个容器适配器的形式,直接将红黑树进行封装,调用其的各种方法接口间接实现。

1.2 红黑树模板抽象

  1. 虽然map与set的底层数据结构都为红黑树,但是,它们也并不是完全相同,map中存储的数据结点为KV类型,而set中存储的数据结点为K类型,所以,两者在接口与使用上也有所不同。
  2. 可仅仅因此不大的差别就再写一份高度类同的红黑树代码,就不免冗余性过高,由此,我们仿照库中实现的类似方式,将红黑树抽象为模板,通过模板参数来调整细节的方式,来同时适配map与set的需求。
//红黑树模板参数
template<class K, class T, class KeyOfT>
//参数K:set与map的key值类型,K参数的存在是必要的,否则无法进行此类型函数参数的声明
//参数T:set中也为key值类型,而map中为pair<K, V>类型
//参数KeyOfT:仿函数类型,从T类型结点中提取出key值,进行查找,插入等操作必须获知与比对key值

1.3 红黑树的迭代器

  1. 当调用库中的map与set容器时,我们使用相应迭代器遍历其中存储数据,得到的是一串排序好的有序数据。
  2. 因为map与set的底层本质上为一棵搜索二叉树,这种遍历后得到的数据特点,我们不难知道,迭代器底层实则是在由整颗树最左侧结点做中序遍历,那么,其底层的逻辑究竟应该怎么去实现呢?

2. 红黑树模板的实现

2.1 结点结构的定义

//结点结构
enum colour
{
	Red,
	Black
};

template<class K, class T>
struct RBTreeNode
{
	RBTreeNode<K, T>* _left;
	RBTreeNode<K, T>* _right;
	RBTreeNode<K, T>* _parent;
	T _kv;
	colour _col;

	RBTreeNode(const T& kv)
		:_left(nullptr)
		,_right(nullptr)
		,_parent(nullptr)
		,_kv(kv)
		,_col(Red)
	{}
};

2.2 红黑树迭代器的实现

2.2.1 迭代器的结构

//定义一个迭代器模板,适配出普通迭代器与const迭代器
template<class K, class T, class Ref, class Ptr>
class RBTree_iterator
{
	typedef RBTreeNode<K, T> RBTNode;
public:
	//构造
	RBTree_iterator(RBTNode* node)
		:_node(node)
	{}
	
	//拷贝构造
	RBTree_iterator(const RBTree_iterator& it)
	{
		_node = it._node;
	}

	//后置++,左根右
	RBTree_iterator operator++(int);
	
	//后置--,右根左
	RBTree_iterator operator--(int);

	//const T&
	Ref operator*();

	//const T*
	Ptr operator->();

	//指向结点相同
	bool operator==(RBTree_iterator it);

	bool operator!=(RBTree_iterator it);

private:
	RBTNode* _node;
};

2.2.2 迭代器的方法实现

  1. 杂项
//const T&
Ref operator*()
{
	return _node->_kv;
}

//const T*
Ptr operator->()
{
	return &_node->_kv;
}

//指向结点相同
bool operator==(RBTree_iterator it)
{
	return _node == it._node;
}

bool operator!=(RBTree_iterator it)
{
	return _node != it._node;
}
  1. 后置++与–
    在这里插入图片描述
//先使用,再++
//operator++,左根右
RBTree_iterator operator++(int)
{
	RBTree_iterator cp(*this);

	//左根右
	if (_node->_right)
	{
		//向左找
		RBTNode* SubLeft = _node->_right;
		while (SubLeft->_left)
		{
			SubLeft = SubLeft->_left;
		}

		_node = SubLeft;
	}
	else
	{
		RBTNode* cur = _node;
		RBTNode* parent = cur->_parent;
		if (cur == parent->_left)
		{
			_node = parent;
		}
		else
		{
			//一层一层向上找,空结点检测
			while (parent && cur == parent->_right)
			{
				cur = parent;
				parent = cur->_parent;
			}

			_node = parent;
		}
	}

	return cp;
}

//operator--,右根左
RBTreeNode_iterator operator--(int)
{
	RBTree_iterator cp(*this);

	//右根左
	if (_node->_left)
	{
		//向右找
		RBTNode* SubRight = _node->_left;
		while (SubRight->_right)
		{
			SubRight = SubRight->_right;
		}

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

		_node = parent;//处理逻辑同时符合两种情况
	}

	return cp;
}

2.3 树结构的定义

//树结构
template<class K, class T, class KeyOfT>
class RBTree
{
	typedef RBTreeNode<K, T> RBTNode;
public:
	//查找
	bool Find(const K& key);

	//插入
	pair<iterator, bool> Insert(const T& kv);
	
private:
	RBTNode* _root = nullptr;
	KeyOfT con;
}

2.4 红黑树接口实现

2.4.1 插入

//插入
pair<iterator, bool> Insert(const T& kv)
{
	//找到插入位置
	RBTNode* cur = _root;
	RBTNode* parent = nullptr;
	while (cur)
	{
		if (con(kv) < con(cur->_kv))
		{
			parent = cur;
			cur = cur->_left;
		}
		else if (con(kv) > con(cur->_kv))
		{
			parent = cur;
			cur = cur->_right;
		}
		else
		{
			//有相同值,插入失败
			//return false;
			return make_pair(iterator(cur), false);
		}
	}

	//插入
	RBTNode* newnode = new RBTNode(kv);
	cur = newnode;
	if (_root == nullptr)//根结点
	{
		_root = cur;
		_root->_col = Black;
		//return true;
		return make_pair(iterator(cur), true);
	}
	else
	{
		if (con(kv) < con(parent->_kv))
		{
			parent->_left = cur;
			cur->_parent = parent;
		}
		else
		{
			parent->_right = cur;
			cur->_parent = parent;
		}
	}

	//判断与调整
	while (parent)
	{
		//父亲结点为黑色
		if (parent->_col == Black)
		{
			break;
		}
		else//父结点为红色
		{
			RBTNode* grandparent = parent->_parent;
			RBTNode* uncle = grandparent->_left;
			if (parent == grandparent->_left)
			{
				uncle = grandparent->_right;
			}

			//叔叔结点存在且为红色
			if (uncle && uncle->_col == Red)
			{
				parent->_col = uncle->_col = Black;
				grandparent->_col = Red;
				cur = grandparent;
				//注,父节点存放变量也需重置
				parent = cur->_parent;
			}
			else//叔叔结点为黑色/不存在
			{
				if (parent == grandparent->_left)//左外高
				{
					if (cur == parent->_left)//右单旋
					{
						//      g
						//   p      u
						//c
						RotateR(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					else//左右双旋
					{
						//      g
						//   p      u
						//     c
						RotateLR(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}
				}
				else//右外高
				{
					if (cur == parent->_right)//左单旋
					{
						//   g
						//u      p
						//          c
						RotateL(grandparent);
						parent->_col = Black;
						grandparent->_col = Red;
					}
					else//右左双旋
					{
						//   g
						//u      p
						//     c
						RotateRL(grandparent);
						cur->_col = Black;
						grandparent->_col = Red;
					}
				}

				break;
			}
		}
	}

	_root->_col = Black;

	return make_pair(iterator(newnode), true);
}

2.4.2 查找

//查找
bool Find(K key)
{
	RBTNode* cur = _root;
	while (cur)
	{
		if (key < con(cur->_kv))
		{
			cur = cur->_left;
		}
		else if (key > con(cur->_kv))
		{
			cur = cur->_right;
		}
		else
		{
			return true;
		}
	}

	return false;
}

2.4.3 迭代器相关

typedef RBTree_iterator<K, T, T&, T*> iterator;
typedef RBTree_iterator<K, T, const T&, const T*> const_iterator;

iterator begin()
{
	RBTNode* cur = _root;
	while (cur->_left)
	{
		cur = cur->_left;
	}

	return cur;
}

iterator end()
{
	return nullptr;
}

const iterator cbegin() const
{
	RBTNode* cur = _root;
	while (cur->_left)
	{
		cur = cur->_left;
	}

	return cur;
}

const iterator cend() const
{
	return nullptr;
}

3. map与set的自实现

3.1 set封装

template<class K>
class myset
{
	struct KeyOfT
	{
		K operator()(const K& key)
		{
			return key;
		}
	};

	typedef RBTree_iterator<K, K, K&, K*> iterator;
	typedef RBTree_iterator<K, K, const K&, const K*> const_iterator;
public:
	bool Find(K key)
	{
		return tree.Find(key);
	}

	pair<iterator, bool> Insert(K key)
	{
		return tree.Insert(key);
	}

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

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

	const_iterator cbegin() const
	{
		return tree.cbegin();
	}

	const_iterator cend() const
	{
		return tree.cend();
	}

	void InOrder()
	{
		tree.InOrder();
	}

private:
	RBTree<K, K, KeyOfT> tree;
};

3.2 map封装

template<class K, class V>
class mymap
{
	struct KeyOfT
	{
		K operator()(const pair<const K, V> kv)
		{
			return kv.first;
		}
	};
	typedef RBTree_iterator<K, pair<const K, V>, pair<const K, V>&, pair<const K, V>*> iterator;
	typedef RBTree_iterator<K, pair<const K, V>, const pair<const K, V>&, const pair<const K, V>*> const_iterator;
public:
	bool Find(K key)
	{
		return tree.Find(key);
	}

	pair<iterator, bool> Insert(const pair<K, V> kv)
	{
		return tree.Insert(kv);
	}

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

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

	const_iterator cbegin() const
	{
		return tree.cbegin();
	}

	const_iterator cend() const
	{
		return tree.cend();
	}

	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>, KeyOfT> tree;
};

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

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

相关文章

python3有serial库吗

一、概述 pyserial模块封装了对串口的访问。 二、特性 在支持的平台上有统一的接口。 通过python属性访问串口设置。 支持不同的字节大小、停止位、校验位和流控设置。 可以有或者没有接收超时。 类似文件的API&#xff0c;例如read和write&#xff0c;也支持readline等…

利用大语言模型(KIMI)生成OPC UA 信息模型

在大语言模型没有出现之前&#xff0c;人们更倾向使用图形化工具或者基于窗口的软件来构建信息模型&#xff0c;图形化工具能够直观地表达信息模型中各元素之间的相互关系。但是图形化工具也有缺点&#xff0c;当描述一个复杂的信息模型时&#xff0c;图形会变得非常复杂和庞大…

Hibernate认识

一、定义 Hibernate 是一种开源的 Java 对象关系映射 (ORM) 框架&#xff0c;用于将面向对象的领域模型持久化到关系数据库中。它为开发人员提供了一种简便的方法来操作数据库&#xff0c;而无需编写繁琐的 SQL 代码。 ORM&#xff08;对象关系映射&#xff09;&#xff1a;Ob…

【XR806开发板试用】SPI驱动数码管显示

准备工作 安装repo 创建repo安装目录。 mkdir ~/bin下载repo wget https://storage.googleapis.com/git-repo-downloads/repo -P ~/bin/改变执行权限 chmod ax ~/bin/repo设置环境变量&#xff0c;在~/.bashrc文件的最后输入 export PATH~/bin:$PATH和export REPO_URLhttps://…

ANSI转义序列

一、ASCII码 ASCII&#xff08;American Standard Code for Information Interchange&#xff0c;美国信息交换标准代码&#xff09;最初的设计是一个7位的字符编码&#xff0c;使用了从0到127的数字来表示字符。这意味着它总共可以表示128个不同的字符。这包括了英文大小写字…

[鸟哥私房菜]4.首次登录与在线求助

第4章 首次登录与在线求助 4.1.3 X Window 与命令行模式的切换 通常我们称命令行界面为终端界面、Terminal 或 Console。Linux 默认的情况下会提供六个终端&#xff08;Terminal&#xff09;来让用户登录&#xff0c; 切换的方式为使用&#xff1a;[Ctrl] [Alt] [F1]~[F6] …

Joplin:自由、安全、多功能的笔记应用

什么是 Joplin&#xff1f; Joplin是一款免费、开源的笔记和待办事项应用程序&#xff0c;可以处理整理到笔记本中的大量笔记。这些笔记是可搜索的&#xff0c;可以直接从应用程序或从您自己的文本编辑器中复制、标记和修改。笔记采用Markdown 格式 功能亮点 功能丰富&#x…

超标量处理器设计:重排序缓存(ROB)

★超标量处理器的很多地方用到了重排序缓存&#xff0c;但是我对它不是很了解&#xff0c;所以我整理一下重排序缓存的知识点。 重排序缓存(ROB)在确保乱序执行的指令能够正确地完成和提交(Commit)&#xff0c;也可以用来寄存器重命名。 ROB是一个先进先出的表&#xff0c;每个…

基于Springboot的线上教学平台

基于SpringbootVue的线上教学平台设计与实现 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringbootMybatis工具&#xff1a;IDEA、Maven、Navicat 系统展示 用户登录 首页 学习资料 交流论坛 试卷列表 公告信息 后台登录 后台首页 学员管理 资料类型…

深入理解Linux中TCP/IP协议栈的实现原理与具体过程

一、Linux内核与网络体系结构 在我们了解整个linux系统的网络体系结构之前&#xff0c;我们需要对整个网络体系调用&#xff0c;初始化和交互的位置&#xff0c;同时也是Linux操作系统中最为关键的一部分代码-------内核&#xff0c;有一个初步的认知。 1、Linux内核的结构 …

pytest教程-43-钩子函数-pytest_report_header

领取资料&#xff0c;咨询答疑&#xff0c;请➕wei: June__Go 上一小节我们学习了pytest_runtest_makereport钩子函数的使用方法&#xff0c;本小节我们讲解一下pytest_report_header钩子函数的使用方法。 pytest_report_header 钩子函数允许你在 pytest 的终端报告的头部添…

系统运维(虚拟化)

1.VLAN VLAN&#xff08;Virtual Local Area Network&#xff09;即虚拟局域网&#xff0c;是将一个物理的LAN在逻辑上划分成多个广播域的通信技术。 每个VLAN是一个广播域&#xff0c;VLAN内的主机间可以直接通信&#xff0c;而VLAN间则不能直接互通。这样&#xff0c;广播报…

NOIP,CSP-J,CSP-S——图

一、图的基本概念 图是顶点和边的集合 1、无向图: 每一条边都是无方向的 2、有向图: 每一条边都是有方向的 3、完全图: 任意两个顶点都有一条边相连接; 4、结论 若n个顶点的无向图有n(n-1)/2条边,称为无向完成图; 若n个顶点的有向图有n(n-1)条边,称为有向完成图…

模型onnx转ncnn小记

前期准备 Netron 模型准备&#xff1a;onnx模型,这里使用模型face【det_10g.onnx】 大佬文档引用&#xff1a;手工优化ncnn模型结构 - 知乎 ncnn算子描述参考&#xff1a;ncnn 算子操作描述-CSDN博客 模型优化 安装 pip install onnx-simplifier 先把我要转的模型优化合…

extern关键字的使用。keil中编译时,出现error:identifier xxx is undefined

问题 编译时&#xff0c;出现error&#xff1a; identifier “Reg_Flag” is undefined extern Reg_Flag reg_flag; 很奇怪&#xff0c;我明明已经定义了。无非就是定义是在extern的下面&#xff0c;会不会是这个原因&#xff1f; 解决 果然&#xff0c;把extern的部分放到…

类和对象test

一、初始化列表 引言&#xff1a; 虽然上述构造函数调用之后&#xff0c;对象中已经有了一个初始值&#xff0c;但是不能将其称为对对象中成员变量 的初始化&#xff0c;构造函数体中的语句只能将其称为赋初值&#xff0c;而不能称作初始化。因为初始化只能初始 化一次&#x…

[华为OD] C卷 田忌赛马 DFS 200

题目&#xff1a; 给定两个只包含数字的数组a, b,调整数组a里面数字的顺序&#xff0c;使得尽可能多的a[i] >b[i]。 数组a和b中的数字各不相同。 输出所有可以达到最优结果的a数组的数量 输入描述 输入的第一行是数组a中的数字&#xff0c;其中只包含数字&#xff0c;每…

网页主题自动适配:网页跟随系统自动切换主题

主题切换是网站设计中一个非常有趣的功能&#xff0c;它允许用户在多种预先设计的样式之间轻松切换&#xff0c;以改变网站的视觉表现。最常见的就是白天和黑夜主题的切换&#xff0c;用户可以根据自己的喜好进行设置。 除了让用户手动去切换主题外&#xff0c;如果能够让用户第…

2024年3月份宠物行业线上市场数据分析:市场呈现出精细化、品质化趋势

近些年来&#xff0c;养宠物的家庭越来越多&#xff0c;宠物经济也逐渐衍生开来。宠物经济主要是围绕宠物产生的一整条产业链&#xff0c;包括宠物食品、宠物家居、宠物美容和最近火起来的宠物保险等多个领域。目前随着居民人均收入的提高&#xff0c;宠物市场也得到稳步发展。…

PC端网页特效异读

pc网页特效 一、三大系列1.元素偏移量&#xff08;offset系列&#xff09;&#xff08;1&#xff09;一些属性&#xff08;2).offset和style的区别(3).一些例子 2.元素可视区(client系列&#xff09;(1).一些属性(2).flexible.js源码分析 3.scroll系列(4).三大系列小结 其他&am…