数据结构(十 一) ---[实现红黑树(Red Black Tree)]
Posted 小智RE0
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构(十 一) ---[实现红黑树(Red Black Tree)]相关的知识,希望对你有一定的参考价值。
2-3树
首先23树是满足二分搜索树的性质;
节点可以存放一个或两个元素
例如这样一棵2-3树;
2-3树是绝对的平衡树
例如,现在给出这样一个数组,请将它整理为二分搜索树,以及 2-3 树
[12,23,6,9,45,36,86,25,46]
二分搜索树–>
[12,23,6,9,45,36,86,25,46]
2-3树形成过程
最后一步,添加节点46;
红黑树与2-3树的等价性
由于节点属性的话,线条颜色不好处理;那么就就节点的红黑表示红黑树中的关系;
红色节点是左倾斜的;
比如要把下面这棵2-3树变为红黑树
走到3节点17与33这块,17分离出去,
分离出的节点17的左子树也是个3节点,同样,分离
然后将3节点66与88分离
最终,整理左右关系;得到红黑树;
红黑树
- 红黑树的根结点为黑色;叶子节点为黑色;
- 若一个节点为红色节点,则它的子节点都为黑色节点;
- 由任意节点到叶子节点,经过的黑色节点个数是一样的;
来感受一下构建红黑树时添加节点的过程;
添加节点时,默认为红色节点;
首先添加节点42,更新根节点后,节点42置为黑色节点;
然后添加节点37,根据二分搜索树特性,它将被挂接到根结点42的左边;
试想一下,若先添加了节点37;然后再去添加节点42呢;
由于不符合红黑树的性质,就得需要左旋处理;并且颜色转换处理;
左旋转与颜色变换
那么来具体看看这个转换过程;
(其余节点临时添加上去,为演示左旋转效果)
颜色变换;
首先想一下在2-3树中的效果; 节点37
与节点42
是一个融合的3节点;
颜色翻转
在添加节点37和节点42后;
oK,接着添加节点66;
在2-3树中的效果是—>
考虑到节点42可能会向上融合其他节点成为3结点
,所以它得是红色的;
要是这个结点没有向上融合的话(也就是说这个结点是根结点喽),不必担心;
在写添加方法的时候已经考虑到了这个问题,将所有结点添加完成后,会将树的根节点置为黑色的;
//颜色反转处理;
private void colorFlip(TreeNode node)
//当前节点置为红色;(考虑到这个node节点可能向上融合;而融合节点为红色的)
node.isRed = true;
//子节点置为黑色;
node.left.isRed = false;
node.right.isRed = false;
右旋转与颜色变换;
OK,刚才是添加节点66的状况;我这里要是添加
节点12
呢;
在节点42和节点37的基础上;
在2-3树中的效果
就得考虑右旋转了;颜色变换,以及节点的颜色翻转;
这里也同样的,用几个临时节点参与演示效果;
特殊情况处理
OK,刚才是添加节点12的状况;我这里要是添加
节点40
呢;
在节点42和节点37的基础上;
具体操作
实现红黑树
/**
* @author by 小智RE0
* @date 2021-12-19
* 红黑树
*/
public class RedBlackTree<T extends Comparable<T>>
//内部类节点;
class TreeNode
//节点值;
T val;
//左节点;
TreeNode left;
//右结点;
TreeNode right;
//是否为红色节点;true:红色节点;false:黑色节点;
boolean isRed;
//初始化;
public TreeNode(T val)
this.val = val;
this.left = null;
this.right = null;
//节点默认为红色;
this.isRed = true;
//头结点;
private TreeNode root;
//定义树结点的个数;
private int size;
public RedBlackTree()
this.root = null;
this.size = 0;
//判空;
public boolean isEmpty()
return this.root == null;
//辅助方法;获取节点的颜色; 注意,根据属性isRed;true红色节点;false:黑色节点;
private boolean getColor(TreeNode node)
if (node == null) return false;
//根据节点的属性获取;
return node.isRed;
//颜色反转处理;
private void colorFlip(TreeNode node)
//当前节点置为红色;(考虑到这个node节点可能向上融合;而融合节点为红色的)
node.isRed = true;
//子节点置为黑色;
node.left.isRed = false;
node.right.isRed = false;
//左旋处理;且变换节点颜色;
/*
* Node x
* / \\ 左旋 / \\
* n1 x ==>> Node n3
* / \\ / \\
* n2 n3 n1 n2
* */
private TreeNode rotateLeft(TreeNode node)
//首先取出节点x,与x的左子节点;
TreeNode x = node.right;
TreeNode n2 = x.left;
x.left = node;
node.right = n2;
//颜色变换;
x.isRed = getColor(node);
node.isRed = true;
return x;
//右旋转处理,且变换节点的眼色;
/*
* Node y
* / \\ 右旋 / \\
* y n3 ==>> n1 Node
* / \\ / \\
*n1 n2 n2 n3
* */
public TreeNode rotateRight(TreeNode node)
//首先取出节点y,与y的右子节点;
TreeNode y = node.left;
TreeNode n2 = y.right;
y.right = node;
node.left = n2;
//颜色变换;
y.isRed = getColor(node);
node.isRed = true;
return y;
//添加元素;
public void add(T ele)
//判断是否存在;
if (contains(ele) != null)
return;
//每次将创建的根节点返回;
root = add(root, ele);
//添加之后,让根结点变为黑色;
root.isRed = false;
this.size += 1;
//递归添加元素的底层;
private TreeNode add(TreeNode node, T ele)
//不存在就创建;
if (node == null)
return new TreeNode(ele);
//添加的元素和之前的根结点进行比较;
if (ele.compareTo(node.val) > 0)
node.right = add(node.right, ele);
else
node.left = add(node.left, ele);
//红黑树操作;
//若右树为红色时,左旋转;
if (!getColor(node.left) && getColor(node.right))
node = rotateLeft(node);
//若左树为红色,左树的左树为红色时;
if (getColor(node.left) && getColor(node.left.left))
node = rotateRight(node);
//左树和右树都是红色时,进行颜色反转;
if (getColor(node.left) && getColor(node.right))
colorFlip(node);
return node;
//查询元素;
public TreeNode contains(T ele)
if (root == null)
return null;
return contains(root, ele);
//查询元素的底层;
private TreeNode contains(TreeNode node, T ele)
//递归结束点;
if (node == null)
return null;
//将当前根结点的值存储;
T val = node.val;
if (ele.compareTo(val) == 0)
return node;
else if (ele.compareTo(val) > 0)
return contains(node.right, ele);
else
return contains(node.left, ele);
以上是关于数据结构(十 一) ---[实现红黑树(Red Black Tree)]的主要内容,如果未能解决你的问题,请参考以下文章