1. 红黑树简介:
对于红黑树 一种变异型的平衡二叉树,保持最坏的情况下查找时间的复杂度o(logN),在 红黑树的 插入操作过程中有两种 方式 自顶向下插入 和 自低向上的插入;相比较而言 各自有其优点,不管 采用何种方式 在 Insert 操作中 规定:将插入结点染成红色主要是满足 红黑树 性质5(从一个节点到NULL任一路径上有相同的黑色结点数)。
2. 自顶向下插入操作:每一次插入从Heade遍历开始,让插入点当成叶子节点进行插入
根据数据结构与算法分析Java,在自低向上插入过程中为了满足性质5 在树的结构体中 加入parent的父节点指针保留父链保存路径,同时在 调整树结构过程同时 需要更新父指针的指向,特别是在删除采用自低向上操作方式非常的复杂,如果采用自顶向下过程 向下遍历 树,不同于自低向上算法,这里 自顶向下过程中 发现当前节点有两个RED节点时候,进行处理,处理方式当前节点设置RED,两个儿子设置成BLACK,如果 当前节点与其父节点都是RED(违背性质2不能连续出现两个RED),必须进行 单旋(当前节点parent与祖父结点)或者双旋(当前节点与祖父结点进行双旋)--类似与AVL的旋转。
3. 自顶向下插入 步骤:
1). 新插入的结点 每一次从根结点起进行遍历current ,parent,gparent,great,分别表示 当前节点,当前节点父节点,其祖父结点,其曾祖父结点;
2) 如果 current 结点 有两个RED 儿子,那么就将两个RED 孩子 设置成BLACK,current 设置成RED,同时比较current 结点 与parent 结点的颜色 都是RED 执行步骤3 否则直接执行步骤4;
3). 目的处理 current 结点与 parent 结点都是RED 情形,那么其兄弟结点S必定是BLACk,因为从上向下遍历时候已经处理 儿子全部是RED 情况 对于这种 情形 出现两种子情况;(只进行左侧讨论,右侧讨论镜像)
case 1 . current.color=RED,parent.color=RED, current=parent.left , 形成是一字型,对parent与gparent单旋转 ,将gparent 变成parent的右孩子。
case 2. current.color=RED,parent.color=RED, current=parent.right. , 形成是之字型,需要进行双旋将 current 移动到 祖父结点上 --AVL 双旋可以转化成单旋 singleRightroate(gparent.left) 在之后singLeftRoate(greate.left)
最后记得 更新current= 旋转之后的结点 明显发现旋转之后之前 gparent设置成 RED
4). 完成 2(3)之后 ,继续往下前进遍历,重复2,4 过程,直到到达KEy或者 nullpoint点,至此current=null 从而进行
4. 自顶向下编程实现:
1). 实现 小技巧: ① 建立NULLNode 代表NULL,如果一个节点指向nullNode代表其指向NULl结点
② 根节点Heade不是真正的 root,Heade.right 才是真正的根节点,这样实现好处开始时候并不清楚,直到实现 自顶向下删除操作时候发现其妙处。
2). 接下来 介绍 处理两个red 孩子的 程序段(步骤3):
这里 程序中判断做 双旋还是单旋 小技巧 :判断 current 是之字形还是一字形:item(要插入结点) 与祖父结点比较结果 和item 与parent 比较结果 一致性,如果是一字形 item 在 parent的左子树中同时 item在祖父结点的左子树中, 如果之字型:item 在parent 在右子树中,在gparent的左子树中(镜像相反),综合结果 两者的比较结果一定不是相同 的
(compare(item,gparent)<0) == (compare(item,parent)<0)
/**
* 从顶向下 遍历 时候 current 结点 有 两个红色 儿子 --- 目的 保证 current的兄弟 结点 永远是黑色 不同于自低向上删除
* 设置 让 红色儿子成为 BLACk,当前结点 变成 RED 同时 与 其父节点 进行颜色 比较
* parent 是 RED 必须 进行旋转(旋转中判断 是进行 单旋 还是 双旋)
* */
private void handReorient(T item) {
current.color=RED;
current.left.color=BLACK;
current.right.color=BLACK;
//发现 current 与父节点 都是
if(parent.color==RED)
{
gparent.color=RED;
/**判断 是对 current 的 父结点 还是祖父结点 进行 旋转
* 插入 结点 比当前节点 父节点 小 一字型 旋转 祖父结点---单旋转 对 祖父结点
* 插入结点 比 当前 结点 父节点 大 之字型---双旋转 --先对 父节点 在对 祖父结点 */
if((compare(item, gparent)<0)!=(compare(item, parent)<0))
{
parent=roate(item,gparent);// 祖父 --的 儿子 (当前节点的 父节点 之字形) 运行之后还会//运行 下一行程序 相当于双旋
}
current=roate(item,great);// 旋转曾祖父的儿子 只进行单一 单旋
current.color=BLACK;// 调整 树结构 重新遍历 新插入点
}
heade.right.color=BLACK;// make the root BLACk
}
Roate 函数: 借助 AVL中 roateWithLeft 、roateWithRight:
翻转的 输入结点的儿子结点 ,因此为什么要其曾祖结点 great
/**
* roate 旋转 只是 输入结点的 儿子结点 ,更新 树的结构
* */
private RedBlackNode<T> roate(T item, RedBlackNode<T> parent) {
if(compare(item, parent)<0)
{
return parent.left=compare(item, parent.left)<0?
rotateWithLeftChild(parent.left): // LL
rotateWithRightChild(parent.left) ;//LR
}
else
return parent.right = compare( item, parent.right ) < 0 ?
rotateWithLeftChild( parent.right ) : // RL
rotateWithRightChild( parent.right ); // RR
}
package com.Tree;
import java.util.LinkedList;
import java.util.Queue;
public class RedBlackTree<T extends Comparable<? super T>> {
private static final boolean RED = true;
private static final boolean BLACK = false;
public RedBlackNode<T> heade;
public RedBlackNode<T> nullNode;
private RedBlackNode<T>current;
private RedBlackNode<T> parent;
private RedBlackNode<T>gparent;
private RedBlackNode<T> great;
public RedBlackTree()
{
heade=new RedBlackNode<T>(null);
nullNode=new RedBlackNode<T>(null);
nullNode.left=nullNode.right=nullNode;
heade.left=heade.right=nullNode;
}
private static class RedBlackNode<T>{
T element;
RedBlackNode<T> left;
RedBlackNode<T> right;
boolean color;
public RedBlackNode(T e)
{
this(e,BLACK,null,null);
}
public RedBlackNode(T e, boolean c, RedBlackNode<T> object, RedBlackNode<T>object2) {
this.element=e;
this.color=c;
this.left=object;
this.right=object2;
}
}
/**
* Insert into the tree
* 自顶向下的插入方法
* */
public void insert(T item)
{
insert(item,heade);
}
/**
* s输出 结点 层次遍历*/
public void printTree(RedBlackNode<T> heade)
{
DoPrint(heade.right,0);
}
// 利用 层次遍历
private void DoPrint(RedBlackNode<T> r,int depth)
{
if(r!=nullNode)
{
DoPrint(r.left, depth+1);
for(int i=0;i<depth;i++)
System.out.print(" ");
System.out.println(r.element+(r.color==RED?"RED":"BLACK"));
DoPrint(r.right,depth+1);
}
}
public static void main(String[] args)
{
int [] array={10,85,15,70,20,60,30,50,65,80,90,40,5,55};
RedBlackTree<Integer> redBlackTree=new RedBlackTree<>();
for(int i=0;i<array.length;i++)
redBlackTree.insert(array[i]);
redBlackTree.printTree(redBlackTree.heade);
}
public void LevelPrint(RedBlackNode<T> head)
{
RedBlackNode<T> root=head.right;
Queue<RedBlackNode<T>> queue=new LinkedList<RedBlackTree.RedBlackNode<T>>();
queue.add(root);
}
private void insert(T item, RedBlackNode<T> node)
{
// 自顶向下 的 插入 边 查找 一边进行 树的调整
current=parent=gparent=heade;
nullNode.element=item;
while(compare(item,current)!=0)
{
/*顶 往下 遍历 定位到 其 插入位置**/
great=gparent;gparent=parent;parent=current;
current=compare(item, current)<0? current.left:current.right;
/**左右 儿子 是 RED 自顶向下的 插入过程中 主要遇到情形--目的 让兄弟结点 U 永远是 黑色*/
if(current.left.color==RED && current.right.color==RED)
handReorient(item);
}
// Insert fails if already present
if(current!=nullNode)
return ;
// 遍历 直接 遍历到其叶子节点上
current=new RedBlackNode<T>(item,BLACK,nullNode,nullNode);
if(compare(item, parent)<0)
parent.left=current;
else
parent.right=current;
handReorient(item);
}
/**
* 从顶向下 遍历 时候 current 结点 有 两个红色 儿子 --- 目的 保证 current的兄弟 结点 永远是黑色 不同于自低向上删除
* 设置 让 红色儿子成为 BLACk,当前结点 变成 RED 同时 与 其父节点 进行颜色 比较
* parent 是 RED 必须 进行旋转(旋转中判断 是进行 单旋 还是 双旋)
* */
private void handReorient(T item) {
current.color=RED;
current.left.color=BLACK;
current.right.color=BLACK;
//发现 current 与父节点 都是
if(parent.color==RED)
{
gparent.color=RED;
/**判断 是对 current 的 父结点 还是祖父结点 进行 旋转
* 插入 结点 比当前节点 父节点 小 一字型 旋转 祖父结点---单旋转 对 祖父结点
* 插入结点 比 当前 结点 父节点 大 之字型---双旋转 --先对 父节点 在对 祖父结点 */
if((compare(item, gparent)<0)!=(compare(item, parent)<0))
{
parent=roate(item,gparent);// 祖父 --的 儿子 (当前节点的 父节点 之字形) 运行之后还会运行 下一行程序 相当于双旋
}
current=roate(item,great);// 旋转曾祖父的儿子 只进行单一 单旋
current.color=BLACK;// 调整 树结构 重新遍历 新插入点
}
heade.right.color=BLACK;
}
/**
* roate 旋转 只是 输入结点的 儿子结点 ,更新 树的结构
* */
private RedBlackNode<T> roate(T item, RedBlackNode<T> parent) {
if(compare(item, parent)<0)
{
return parent.left=compare(item, parent.left)<0?
rotateWithLeftChild(parent.left): // LL
rotateWithRightChild(parent.left) ;//LR
}
else
return parent.right = compare( item, parent.right ) < 0 ?
rotateWithLeftChild( parent.right ) : // RL
rotateWithRightChild( parent.right ); // RR
}
/**
* Rotate binary tree node with left child.
*/
private RedBlackNode<T> rotateWithLeftChild( RedBlackNode<T> k2 )
{
RedBlackNode<T> k1 = k2.left;
k2.left = k1.right;
k1.right = k2;
return k1;
}
/**
* Rotate binary tree node with right child.
*/
private RedBlackNode<T> rotateWithRightChild( RedBlackNode<T> k1 )
{
RedBlackNode<T> k2 = k1.right;
k1.right = k2.left;
k2.left = k1;
return k2;
}
private int compare(T item, RedBlackNode<T> current) {
if (current==heade)
return 1;
else
return item.compareTo(current.element);
}
public void LevelPrint(RedBlackNode<T> head)
{
RedBlackNode<T> root=head.right;
Queue<RedBlackNode<T>> queue=new LinkedList<RedBlackTree.RedBlackNode<T>>();
queue.add(root);
RedBlackNode<T> current;
while(!queue.isEmpty())
{
/**按层次输出*/
int L=queue.size();// 记录上一层 结点数
for(int i=0;i<L;i++)
{
current=queue.poll();
System.out.print(current.element +(current.color==RED?"RED":"BLACK")+" ");
if(current.left!=nullNode) queue.add(current.left);
if(current.right!=nullNode ) queue.add(current.right);
}
System.out.println();
}
}
}