数据结构与算法(七)红黑树

平衡二叉树

平衡二叉树的定义:它的左右树高度差不超过1

为什么需要平衡二叉树?一种极端的情况:二叉搜索树的结点为1、2、3、4、5,如下图,这样的二叉树退化成链表,查找一个节点的时间复杂度是O(n)。为了避免这种情况,我们将它转化成平衡二叉排序树,二叉排序树的查找平均复杂度是O(log(n))

我们该如何进行优化呢?因此有了平衡二叉树和红黑树。平衡二叉树定义左右树高度差不超过1,这样可以有效的避免直线型结构,但是并不是我们最理想的状态。

 

红黑树性质

红黑树是每个节点都带有颜色属性的二叉查找树,颜色或红色或黑色。在二叉查找树强制一般要求以外,对于任何有效的红黑树我们增加了如下的额外要求:

  1. 节点是红色或黑色。
  2. 根节点是黑色。
  3. 每个红色节点的两个子节点都是黑色。(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  4. 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点。

 

旋转和变换规则

1、颜色变化情况:当前节点的父节点是红色,且祖父节点的另一个子节点(叔叔节点)也是红色

把父节点变成黑色;把祖父节点的另一个节点变成黑色;把祖父节点变成红色;把指针定义到祖父节点设置成当前操作的。

2、左旋:父节点为红色,叔叔节点为黑色的时候,且当前节点为右子树。以父节点作为左旋。

3、右旋:父节点为红色,叔叔节点为黑色的时候,且当前节点为左子树。以父节点作为右旋。

把父节点变成黑色;祖父节点变成红色;把祖父节点旋转。

左旋如图

右旋效果图

Java代码实现

public class RedBlackTree<T extends Comparable<T>> {
	private RedBlackTreeNode<T> root; // 根结点
	private static final int RED = 0;
	private static final int BLACK = 1;
	public class RedBlackTreeNode<T extends Comparable<T>> {
		int color;
		T key;
		// 左节点
		RedBlackTreeNode<T> left;
		// 右节点
		RedBlackTreeNode<T> right;
		// 父结点
		RedBlackTreeNode<T> parent;
		public RedBlackTreeNode(T key, int color, RedBlackTreeNode<T> parent, RedBlackTreeNode<T> left,
				RedBlackTreeNode<T> right) {
			this.key = key;
			this.color = color;
			this.parent = parent;
			this.left = left;
			this.right = right;
		}
	}
}

 左旋

	private void leftRotate(RedBlackTreeNode<T> x) {
		// 设置x的右孩子为y
		RedBlackTreeNode<T> y = x.right;
		// 将 “y的左孩子” 设为 “x的右孩子”;
		x.right = y.left;
		// 如果y的左孩子非空,将 “x” 设为 “y的左孩子的父亲”
		if (y.left != null)
			y.left.parent = x;
		// 将 “x的父亲” 设为 “y的父亲”
		y.parent = x.parent;
		if (x.parent == null) {
			// 如果 “x的父亲” 是空节点,则将y设为根节点
			this.root = y;
		} else {
			// 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
			if (x.parent.left == x) {
				x.parent.left = y;
			} else {
				// 如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
				x.parent.right = y;
			}
		}
		// 将 “x” 设为 “y的左孩子”
		y.left = x;
		// 将 “x的父节点” 设为 “y”
		x.parent = y;
	}

右旋

	private void rightRotate(RedBlackTreeNode<T> y) {
		// 设置x是当前节点的左孩子。
		RedBlackTreeNode<T> x = y.left;
		// 将 “x的右孩子” 设为 “y的左孩子”;
		y.left = x.right;
		// 如果"x的右孩子"不为空的话,将 “y” 设为 “x的右孩子的父亲”
		if (x.right != null)
			x.right.parent = y;
		// 将 “y的父亲” 设为 “x的父亲”
		x.parent = y.parent;
		if (y.parent == null) {
			this.root = x; // 如果 “y的父亲” 是空节点,则将x设为根节点
		} else {
			if (y == y.parent.right)
				y.parent.right = x; // 如果 y是它父节点的右孩子,则将x设为“y的父节点的右孩子”
			else
				y.parent.left = x; // (y是它父节点的左孩子) 将x设为“x的父节点的左孩子”
		}
		// 将 “y” 设为 “x的右孩子”
		x.right = y;
		// 将 “y的父节点” 设为 “x”
		y.parent = x;
	}

参考链接

https://www.cnblogs.com/zedosu/p/6635034.html

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值