九种查找算法-红黑树

2023-10-27

红黑树

2-3查找树能保证在插入元素之后能保持树的平衡状态,最坏情况下即所有的子节点都是2-node,树的高度为lgn,从而保证了最坏情况下的时间复杂度。但是2-3树实现起来比较复杂,于是就有了一种简单实现2-3树的数据结构,即红黑树(Red-Black Tree)。

理解红黑树一句话就够了:红黑树就是用红链接表示3-结点的2-3树

现在我们对2-3树进行改造,改造成一个二叉树。怎么改造呢?对于2节点,保持不变;对于3节点,我们首先将3节点中左侧的元素标记为红色,然后我们将其改造成图3的形式;

再将3节点的位于中间的子节点的父节点设置为父节点中那个红色的节点,如图4的所示;最后我们将图4的形式改为二叉树的样子,如图5所示。图5是不是很熟悉,没错,这就是我们常常提到的大名鼎鼎的红黑树了。如下图所示。

5b3fba6642e0e1235248bde563bf7bed.png

2-3树转红黑树

为什么使用红黑树

  • 红黑树是一种平衡树,他复杂的定义和规则都是为了保证树的平衡性。如果树不保证他的平衡性就是下图:很显然这就变成一个链表了。

  • 保证平衡性的最大的目的就是降低树的高度,因为树的查找性能取决于树的高度。所以树的高度越低搜索的效率越高!

  • 这也是为什么存在二叉树、搜索二叉树等,各类树的目的。

红黑树性质

  • 每个节点要么是黑色,要么是红色。

  • 根节点是黑色。

  • 每个叶子节点(NIL)是黑色。

  • 每个红色结点的两个子结点一定都是黑色。

  • 任意一结点到每个叶子结点的路径都包含数量相同的黑结点。

算法思路

红黑树的思想就是对2-3查找树进行编码,尤其是对2-3查找树中的3-nodes节点添加额外的信息。红黑树中将节点之间的链接分为两种不同类型,红色链接,他用来链接两个2-nodes节点来表示一个3-nodes节点。黑色链接用来链接普通的2-3节点。特别的,使用红色链接的两个2-nodes来表示一个3-nodes节点,并且向左倾斜,即一个2-node是另一个2-node的左子节点。这种做法的好处是查找的时候不用做任何修改,和普通的二叉查找树相同

代码

#define BLACK 1
#define RED 0
#include <iostream>

using namespace std;

class bst
{
private:

	struct Node
	{
		int value;
		bool color;
		Node *leftTree, *rightTree, *parent;

		Node() : value(0), color(RED), leftTree(NULL), rightTree(NULL), parent(NULL) { }

		Node* grandparent()
		{
			if (parent == NULL)
			{
				return NULL;
			}
			return parent->parent;
		}

		Node* uncle()
		{
			if (grandparent() == NULL)
			{
				return NULL;
			}
			if (parent == grandparent()->rightTree)
				return grandparent()->leftTree;
			else
				return grandparent()->rightTree;
		}

		Node* sibling()
		{
			if (parent->leftTree == this)
				return parent->rightTree;
			else
				return parent->leftTree;
		}
	};

	void rotate_right(Node *p)
	{
		Node *gp = p->grandparent();
		Node *fa = p->parent;
		Node *y = p->rightTree;

		fa->leftTree = y;

		if (y != NIL)
			y->parent = fa;
		p->rightTree = fa;
		fa->parent = p;

		if (root == fa)
			root = p;
		p->parent = gp;

		if (gp != NULL)
		{
			if (gp->leftTree == fa)
				gp->leftTree = p;
			else
				gp->rightTree = p;
		}

	}

	void rotate_left(Node *p)
	{
		if (p->parent == NULL)
		{
			root = p;
			return;
		}
		Node *gp = p->grandparent();
		Node *fa = p->parent;
		Node *y = p->leftTree;

		fa->rightTree = y;

		if (y != NIL)
			y->parent = fa;
		p->leftTree = fa;
		fa->parent = p;

		if (root == fa)
			root = p;
		p->parent = gp;

		if (gp != NULL)
		{
			if (gp->leftTree == fa)
				gp->leftTree = p;
			else
				gp->rightTree = p;
		}
	}

	void inorder(Node *p)
	{
		if (p == NIL)
			return;

		if (p->leftTree)
			inorder(p->leftTree);

		cout << p->value << " ";

		if (p->rightTree)
			inorder(p->rightTree);
	}

	string outputColor(bool color)
	{
		return color ? "BLACK" : "RED";
	}

	Node* getSmallestChild(Node *p)
	{
		if (p->leftTree == NIL)
			return p;
		return getSmallestChild(p->leftTree);
	}

	bool delete_child(Node *p, int data)
	{
		if (p->value > data)
		{
			if (p->leftTree == NIL)
			{
				return false;
			}
			return delete_child(p->leftTree, data);
		}
		else if (p->value < data)
		{
			if (p->rightTree == NIL)
			{
				return false;
			}
			return delete_child(p->rightTree, data);
		}
		else if (p->value == data)
		{
			if (p->rightTree == NIL)
			{
				delete_one_child(p);
				return true;
			}
			Node *smallest = getSmallestChild(p->rightTree);
			swap(p->value, smallest->value);
			delete_one_child(smallest);

			return true;
		}
		else
		{
			return false;
		}
	}

	void delete_one_child(Node *p)
	{
		Node *child = p->leftTree == NIL ? p->rightTree : p->leftTree;
		if (p->parent == NULL && p->leftTree == NIL && p->rightTree == NIL)
		{
			p = NULL;
			root = p;
			return;
		}

		if (p->parent == NULL)
		{
			delete  p;
			child->parent = NULL;
			root = child;
			root->color = BLACK;
			return;
		}

		if (p->parent->leftTree == p)
		{
			p->parent->leftTree = child;
		}
		else
		{
			p->parent->rightTree = child;
		}
		child->parent = p->parent;

		if (p->color == BLACK)
		{
			if (child->color == RED)
			{
				child->color = BLACK;
			}
			else
				delete_case(child);
		}

		delete p;
	}

	void delete_case(Node *p)
	{
		if (p->parent == NULL)
		{
			p->color = BLACK;
			return;
		}
		if (p->sibling()->color == RED)
		{
			p->parent->color = RED;
			p->sibling()->color = BLACK;
			if (p == p->parent->leftTree)
				rotate_left(p->sibling());
			else
				rotate_right(p->sibling());
		}
		if (p->parent->color == BLACK && p->sibling()->color == BLACK
			&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK)
		{
			p->sibling()->color = RED;
			delete_case(p->parent);
		}
		else if (p->parent->color == RED && p->sibling()->color == BLACK
			&& p->sibling()->leftTree->color == BLACK && p->sibling()->rightTree->color == BLACK)
		{
			p->sibling()->color = RED;
			p->parent->color = BLACK;
		}
		else
		{
			if (p->sibling()->color == BLACK)
			{
				if (p == p->parent->leftTree && p->sibling()->leftTree->color == RED
					&& p->sibling()->rightTree->color == BLACK)
				{
					p->sibling()->color = RED;
					p->sibling()->leftTree->color = BLACK;
					rotate_right(p->sibling()->leftTree);
				}
				else if (p == p->parent->rightTree && p->sibling()->leftTree->color == BLACK
					&& p->sibling()->rightTree->color == RED)
				{
					p->sibling()->color = RED;
					p->sibling()->rightTree->color = BLACK;
					rotate_left(p->sibling()->rightTree);
				}
			}
			p->sibling()->color = p->parent->color;
			p->parent->color = BLACK;
			if (p == p->parent->leftTree)
			{
				p->sibling()->rightTree->color = BLACK;
				rotate_left(p->sibling());
			}
			else
			{
				p->sibling()->leftTree->color = BLACK;
				rotate_right(p->sibling());
			}
		}
	}

	void insert(Node *p, int data)
	{
		if (p->value >= data)
		{
			if (p->leftTree != NIL)
				insert(p->leftTree, data);
			else
			{
				Node *tmp = new Node();
				tmp->value = data;
				tmp->leftTree = tmp->rightTree = NIL;
				tmp->parent = p;
				p->leftTree = tmp;
				insert_case(tmp);
			}
		}
		else
		{
			if (p->rightTree != NIL)
				insert(p->rightTree, data);
			else
			{
				Node *tmp = new Node();
				tmp->value = data;
				tmp->leftTree = tmp->rightTree = NIL;
				tmp->parent = p;
				p->rightTree = tmp;
				insert_case(tmp);
			}
		}
	}

	void insert_case(Node *p)
	{
		if (p->parent == NULL)
		{
			root = p;
			p->color = BLACK;
			return;
		}
		if (p->parent->color == RED)
		{
			if (p->uncle()->color == RED)
			{
				p->parent->color = p->uncle()->color = BLACK;
				p->grandparent()->color = RED;
				insert_case(p->grandparent());
			}
			else
			{
				if (p->parent->rightTree == p && p->grandparent()->leftTree == p->parent)
				{
					rotate_left(p);
					rotate_right(p);
					p->color = BLACK;
					p->leftTree->color = p->rightTree->color = RED;
				}
				else if (p->parent->leftTree == p && p->grandparent()->rightTree == p->parent)
				{
					rotate_right(p);
					rotate_left(p);
					p->color = BLACK;
					p->leftTree->color = p->rightTree->color = RED;
				}
				else if (p->parent->leftTree == p && p->grandparent()->leftTree == p->parent)
				{
					p->parent->color = BLACK;
					p->grandparent()->color = RED;
					rotate_right(p->parent);
				}
				else if (p->parent->rightTree == p && p->grandparent()->rightTree == p->parent)
				{
					p->parent->color = BLACK;
					p->grandparent()->color = RED;
					rotate_left(p->parent);
				}
			}
		}
	}

	void DeleteTree(Node *p)
	{
		if (!p || p == NIL)
		{
			return;
		}
		DeleteTree(p->leftTree);
		DeleteTree(p->rightTree);
		delete p;
	}
public:

	bst()
	{
		NIL = new Node();
		NIL->color = BLACK;
		root = NULL;
	}

	~bst()
	{
		if (root)
			DeleteTree(root);
		delete NIL;
	}

	void inorder()
	{
		if (root == NULL)
			return;
		inorder(root);
		cout << endl;
	}

	void insert(int x)
	{
		if (root == NULL)
		{
			root = new Node();
			root->color = BLACK;
			root->leftTree = root->rightTree = NIL;
			root->value = x;
		}
		else
		{
			insert(root, x);
		}
	}

	bool delete_value(int data)
	{
		return delete_child(root, data);
	}
private:
	Node *root, *NIL;
};

int main()
{
	cout << "---【红黑树】---" << endl;
	// 创建红黑树
	bst tree;

	// 插入元素
	tree.insert(2);
	tree.insert(9);
	tree.insert(-10);
	tree.insert(0);
	tree.insert(33);
	tree.insert(-19);

	// 顺序打印红黑树
	cout << "插入元素后的红黑树:" << endl;
	tree.inorder();

	// 删除元素
	tree.delete_value(2);

	// 顺序打印红黑树
	cout << "删除元素 2 后的红黑树:" << endl;
	tree.inorder();

	// 析构
	tree.~bst();

	getchar();
	return 0;
}

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

九种查找算法-红黑树 的相关文章

  • 电话号码的正则表达式,不允许全零

    需要您的正则表达式帮助 我当前的正则表达式是 d 8 最小长度为 8 不允许包含字母 特殊字符和空格 我还想禁止全零 如 00000000 Thanks 该模式应该可以满足您的需求 0 d 8 The 0 部分是负前瞻 将阻止仅输入零 Ex
  • setContextProperty 和对象的 setProperty 之间的区别

    我现在真的很困惑 有什么区别 QQmlApplicationEngine engine engine rootContext setContextProperty myObject userData and object gt setPro
  • 在动态事件处理程序中引用“this”

    在我的 myClass 类中 我使用 Reflection Emit 为 myClass 类成员之一动态编写事件处理程序 我已经成功地做到了这一点 现在 我想修改事件处理程序以调用 myClass 类中的实例方法之一 但是 我无法弄清楚如何
  • Accept() 是线程安全的吗?

    我目前正在用 C 语言为我正在做的课程编写一个简单的网络服务器 我们的一项要求是实现一个线程池来使用 pthread 处理连接 我知道我将如何粗略地执行此操作 在主线程中调用accept并将文件描述符传递给freee线程 但是我的朋友建议了
  • 使用 OpenGL 着色器进行数学计算 (C++)

    我有一个矩阵 例如 100x100 尺寸 我需要对每个元素进行计算 matrix i j tt 8 5例如 我有一个巨大的矩阵 我想使用 OpenGL 着色器来实现该算法 我想使用着色器 例如 uniform float val unifo
  • 有没有办法使用 i387 fsqrt 指令获得正确的舍入?

    有没有办法使用 i387 fsqrt 指令获得正确的舍入 除了改变精确模式在 x87 控制字中 我知道这是可能的 但这不是一个合理的解决方案 因为它存在令人讨厌的重入型问题 如果 sqrt 操作中断 精度模式将出错 我正在处理的问题如下 x
  • 在 MATLAB 中创建共享库

    一位研究人员在 MATLAB 中创建了一个小型仿真 我们希望其他人也能使用它 我的计划是进行模拟 清理一些东西并将其变成一组函数 然后我打算将其编译成C库并使用SWIG https en wikipedia org wiki SWIG创建一
  • 指向字节数组的指针

    由于 Misra C 的要求 我的一位同事想要使用指针声明 但我遇到了一些问题 Misra 安全关键指南 不会让我们纯粹的程序员使用指针 但会让我们对数组字节进行操作 他打算获取一个指向字节数组的指针 因此我们不会在堆栈上传递实际的数组 T
  • 防止GDB中的PLT(过程链接表)断点

    在最新版本的 GDB 中 在库函数调用上设置断点会导致多个实际断点 调用过程链接表 PLT 实际的函数调用 这意味着当调用库函数时 我们每次都会经历两次中断 在以前的 GDB 版本中 只会创建 2 因此您只能得到一次中断 那么问题来了 是否
  • 在简单注入器中注册具有多个构造函数和字符串依赖项的类型

    我正在尝试弄清楚如何使用 Simple Injector 我在项目中使用了它 注册简单服务及其组件没有任何问题 但是 当组件具有两个以上实现接口的构造函数时 我想使用依赖注入器 public DAL IDAL private Logger
  • dropdownlist DataTextField 由属性组成?

    有没有一种方法可以通过 C 使 asp net 中的下拉列表的 datatextfield 属性由对象的多个属性组成 public class MyObject public int Id get set public string Nam
  • 允许使用什么类型的内容作为 C 预处理器宏的参数?

    老实说 我很了解 C 编程语言的语法 但对 C 预处理器的语法几乎一无所知 尽管我有时在编程实践中使用它 所以问题来了 假设我们有一个简单的宏 它扩展为空 define macro param 可以放入宏调用构造中的语法有哪些限制 调用宏时
  • 设计 Javascript 前端 <-> C++ 后端通信

    在我最近的将来 我将不得不制作一个具有 C 后端和 Web 前端的系统 要求 目前 我对此了解不多 我认为前端将触发数据传输 而不是后端 所以不需要类似 Comet 的东西 由于在该领域的经验可能很少 我非常感谢您对我所做的设计决策的评论
  • 不兼容的类型 - 是因为数组已经是指针吗?

    在下面的代码中 我创建一个基于书籍结构的对象 并让它保存多个 书籍 我设置的是一个数组 即定义 启动的对象 然而 每当我去测试我对指针的了解 实践有帮助 并尝试创建一个指向创建的对象的指针时 它都会给我错误 C Users Justin D
  • ASP.NET Core Razor Page 多路径路由

    我正在使用 ASP NET Core 2 0 Razor Pages 不是 MVC 构建系统 但在为页面添加多个路由时遇到问题 例如 所有页面都应该能够通过 abc com language 访问segment shop mypage 或
  • 如何获取 QIcon 的文件/资源​​路径

    假设我做了这样的事情 QIcon myIcon resources icon ico 我稍后如何确定该图标的路径 例如 QString path myIcon getPath 问题是 没有getPath 会员 我找不到类似的东西 但肯定有办
  • C++ [Windows] 可执行文件所在文件夹的路径[重复]

    这个问题在这里已经有答案了 我需要访问一些文件fstream在我的 Windows 上的 C 应用程序中 这些文件都位于我的exe文件所在文件夹的子文件夹中 获取当前可执行文件的文件夹路径的最简单且更重要的 最安全的方法是什么 Use 获取
  • 使用 Chrome 和 Selenium 设置 LocalStorage

    我正在尝试使用 OpenQA Selenium 和 Chrome 设置本地存储键和值 我认为这相当微不足道 但我似乎无法让它发挥作用 我对 C 很陌生 所以我可能错过了一些东西 无论如何 我有这个功能 public static void
  • 启动画面后主窗口出现在其他窗口后面

    我有一个带有启动屏幕的 Windows 窗体应用程序 当我运行该应用程序时 启动屏幕显示正常 消失并加载应用程序的主窗体 但是 当我加载主窗体时 它出现在包含该应用程序的 Windows 资源管理器目录下 这是运行启动画面然后运行主窗体的代
  • 无法使 Polly 超时策略覆盖 HttpClient 默认超时

    我正在使用 Polly 重试策略 并且正如预期的那样 在重试过程中HttpClient达到 100 秒超时 我尝试了几种不同的方法来合并 Polly 超时策略 将超时移至每次重试而不是总计 但 100 秒超时仍然会触发 我读过大约 5 个

随机推荐

  • JAVA笔记

    目录 模板模式的理解 使用模板模式的例子 整合工厂模式 模板模式的理解 模板模式简单理解就是创建一个抽象父类作为模板 可以分两部分 一部分是定义了所有子类都需要执行的公共方法 这样就不用在每个子类中重复写相同代码 另一部分就是强制规定每个子
  • php面试题之四——PHP面向对象(基础部分)

    1 写出 php 的 public protected private 三种访问控制模式的区别 新浪网技术部 public 公有 任何地方都可以访问 protected 继承 只能在本类或子类中访问 在其它地方不允许访问 private 私
  • (Oracle技能篇) oracle数据库分页查询和各大数据库的分页查询

    1 oracle数据库分页 select from select a rownum rc from 表名 where rownum lt endrow a where a rc gt startrow 2 DB2数据库分页 Select f
  • 堆与栈的区别详细总结

    常见的数据结构 数组 栈 队列 链表 树 图 散列表 哈希表 堆与栈的区别 堆 队列优先 先进先出 FIFO firstinfirstout 栈 先进后出 FILO First In Last Out 一般情况下 如果有人把堆栈合起来说 那
  • js浏览器回到顶部方法_js 返回顶部按钮

    要求 当鼠标从顶部滚动后 显示返回顶部按钮 点击按钮 页面平滑滚动到顶部 按钮隐藏 1 css scrollTop position fixed bottom 20px right 20px height 0px width 45px li
  • [ Android实战 ] 开机时通过广播启动应用,但是很长时间才能接收到,如何解决?

    Android实战 开机时通过广播启动应用 但是很长时间才能接收到 如何解决 背景 测试 发送广播流程 广播分发流程 解决方案 思考 系统层面 应用层面 总结 转载请注明出处 我的博客 背景 前段时间在做一个项目 在适配客户应用的过程中发现
  • jmeter自动调试系统接口配置流程

    1 起因 最近测试的同事需要用jmeter工具压测一下我们项目的接口 因为接口中有token或者加密登录密码等逻辑 有的地方需要从上一步接口中拿到结果作为下一步的参数 进行传递 因为涉及的有点麻烦 就帮测试看了下这个工具 顺便记录一下 帮助
  • Markdown 实现页内跳转

    Markdown 实现页内跳转 在使用 Markdown 做一些论文笔记或者说写文档时 通常会出现这样一种情况 我们在文档的某个地方定义了一个 t a b l e o
  • 数据库的多表查询操作-查询只选修了1门课程的学生,显示学号、姓名、课程名。

    文章目录 前言 一 建立数据库和表 二 数据库展示 2 查询只选修了1门课程的学生 显示学号 姓名 课程名 总结 前言 在我看来数据库真的是一个神奇的东西 不但里面的只是点很深刻 而且对于我们学习起来还是有一定的压力的 关于数据的知识 我感
  • 【CSAPP】背景知识-调用者保存寄存器与被调用者保存寄存器

    1 调用者保存与被调用者保存 函数A调用了函数B 寄存器rbx在函数B中被修改了 逻辑上 rbx内容在调用函数B的前后应该保持一致 解决这个问题有两个策略 1 在函数A在调用函数B之前提前保存寄存器 rbx的内容 执行完函数B之后再恢复 r
  • 顶会常见的 python matplotlib 双Y轴柱状图

    效果图 代码如下 import matplotlib pyplot as plt import numpy as np plt rc font family Times New Roman x data 1 2 3 ax data 16 2
  • flex布局flex取值以及align-self、align-content、align-items的区别

    flex取值 flex是 flex grow flex shrink flex basis 的缩写 flex取值 flex grow flex shrink flex basis 默认值 0 1 auto none 0 0 auto aut
  • libevent库学习(1)

    一 初识 1 libevent介绍 Libevent 是一个用C语言编写的 轻量级的开源高性能事件通知库 主要有以下几个亮点 事件驱动 event driven 高性能 轻量级 专注于网络 不如 ACE 那么臃肿庞大 源代码相当精炼 易读
  • 计算机efs加密,EFS加密

    EFS Encrypting File System 加密文件系统 是Windows 2000 XP所特有的一个实用功能 对于NTFS卷上的文件和数据 都可以直接被操作系统加密保存 在很大程度上提高了数据的安全性 EFS加密简介 语音 EF
  • SystemUI模块总结

    SystemUI模块总结 1 SystemUI路径 SystemUI被放在 framework base packages apps SystemUI 在该目录的二级目录src com android下可看到SystemUI和Keyguar
  • NPN三极管和PNP三极管的工作原理

    记录一下 方便以后翻阅 学嵌入式还是要懂些电路知识的 NPN型三极管 由两块N型半导体中间夹着一块P型半导体组成 也称晶体三极管 是电子电路中最重要的器件 三极管的主要功能是电流放大和开关作用 可以把微弱的电信号变成一定强度的信号 三极管一
  • windows系统下安装Nginx以及简单使用(详解)

    一 背景 Nginx是一个很强大的高性能Web和反向代理服务 也是一种轻量级的Web服务器 可以作为独立的服务器部署网站 应用非常广泛 特别是现在前后端分离的情况下 而在开发过程中 我们常常需要在window系统下使用Nginx作为Web服
  • python-gRPC

    文章目录 python gRPC 一 简介 1 1 gRPC 1 2 protobuf 二 windows 环境下安装protobuf 2 1 下载环境包 2 2 解压缩 配置文件 2 3 验证是否安装成功 三 简单实例 3 1新建包 3
  • SonarQube 跳过指定检查

    ps 我使用了下面的项目过滤来做 因为一个项目会有多个分支 只想对部分项目来做过滤某些规则 这个规则还是有些重要的 环境 演示环境参考前边的文章 SonarQube 扫描 Java 代码 步骤 我们已经扫描一个 Java 项目 有 6 个
  • 九种查找算法-红黑树

    红黑树 2 3查找树能保证在插入元素之后能保持树的平衡状态 最坏情况下即所有的子节点都是2 node 树的高度为lgn 从而保证了最坏情况下的时间复杂度 但是2 3树实现起来比较复杂 于是就有了一种简单实现2 3树的数据结构 即红黑树 Re