java红黑树详解

1、红黑树概述

红黑树是一种近似平衡的树,没有像AVL树那样严格的平衡,但是AVL树为了保证它的绝对平衡,对插入和删除的效率有一定的影响,而红黑树插入和删除的效率就要高的多。同时,它又是一颗二叉查找树,使得它查找的效率也很高,查找的时间复杂度为O(logn),所以红黑树要优于AVL树。

2、红黑树特性

  1. 根结点为黑
  2. 结点为红或黑
  3. 不能有连续的两个红结点(红结点的子结点必须为黑)
  4. 任一结点到它们子孙结点的所有路径上黑结点的数量相等
  5. 叶子结点为黑(这里的叶子结点指的是null结点)

3、补充特性

  1. 最长路径上的结点数量不大于最短路径上结点数量的两倍
  2. 若结点只有一个子结点,则子结点的子结点为叶子结点,且子结点为红

4、左旋

在这里插入图片描述
步骤:

  1. 把5号结点变红,7号结点变黑
  2. 使5号结点的右孩子指向6号结点,6号结点的父节点设为5号结点
  3. 使5号结点的父结点指向7号结点,7号结点的父节点设为5号结点的父结点
  4. 使7号结点的左孩子指向5号结点,5号结点的父节点设为7号结点

5、右旋

在这里插入图片描述
步骤:

  1. 把5号结点变红,3号结点变黑
  2. 使5号结点的左孩子指向4号结点,4号结点的父节点设为5号结点
  3. 使5号结点的父结点指向3号结点,3号结点的父节点设为5号结点的父结点
  4. 使3号结点的右孩子指向5号结点,5号结点的父节点设为3号结点

6、查找

public TreeNode find(int key){
		if(!this.leaf&&this.key==key){
			return this;
		}else if(this.key<key&&!this.leaf){
			return this.right.find(key);
		}else if(this.key>key&&!this.leaf){
			return this.left.find(key);
		}else{
			return this;
		}
	}

说明:此处具有二分查找的思想,只不过是从数据结构的层面实现的,从一个结点开始往下找,如果比key大,则从左边递归寻找,如果比key小,则从右边递归寻找,如果找到叶子节点还没找到就结束,叶子结点指的是null结点

7、插入

从红黑树的性质可知,插入的结点只能是红结点,因为如果插入黑结点势必会造成树的不平衡,而插入红结点则不一定会造成树的不平衡,具体的操作分为以下几种情况:

情形1:插入结点为红,插入结点的父结点为黑

插入后不用进行任何处理

情形2:插入结点为红,插入结点的父节点为红

情形2.1:插入结点的父结点是祖父结点的左孩子,祖父结点的右孩子,也就是插入结点的叔叔结点为红

在这里插入图片描述
把父结点和叔叔结点变黑,祖父结点变红,以祖父结点为当前结点,继续进行平衡处理,处理完成后需把根结点变黑

情形2.2:插入结点的父结点是祖父结点的左孩子,叔叔结点为黑,根据红黑树的性质,此时叔叔结点只能为叶子结点
情形2.2.1:插入结点是父结点的右孩子

在这里插入图片描述
以父结点为旋转结点进行左旋,然后再以祖父结点进行右旋,平衡处理完成

情形2.2.2:插入结点是父结点的左孩子

在这里插入图片描述
以祖父结点为旋转结点进行右旋,平衡处理完成

说明:以上是处理插入结点的父结点是祖父结点的左孩子的情况,如果插入结点的父结点是祖父结点的右孩子,只需把上面的操作反过来即可

8、删除

红黑树的删除与普通二叉树的删除不同,它的原理是找到删除结点的后继结点或子结点来替换掉这个结点,由此也就把这个结点从红黑树中删除了,至于为何会这样做,一切都是为了使平衡操作更加简单,以下是具体的删除操作步骤:

情形1:删除结点只有一个子结点

在这里插入图片描述

由红黑树的性质可知,该删除结点只能为黑,且删除结点的子结点只能为红,子结点的子结点只能为叶子结点,此时只要让子结点的父结点指向删除结点的父结点,父结点指向删除结点的子结点即可完成删除操作,因为删掉了一个黑结点,红黑树不再平衡,所以还需要把删除结点的子结点变为黑才能使树重新平衡

情形2:删除结点只有叶子结点,且删除结点为红

在这里插入图片描述

因为删除结点为红,删除不影响树的平衡,而且删除结点只有叶子结点,叶子结点只能为黑,所以只要让叶子结点的父结点指向删除结点的父结点,父结点指向叶子结点即可完成删除操作

情形3:删除结点只有叶子结点,且删除结点为黑

在这里插入图片描述

首先让叶子结点的父结点指向删除结点的父结点,父结点指向叶子结点,即先把删除结点从树中删掉

情形3.1:叶子结点为父结点的左孩子,叶子结点的兄弟结点为红

在这里插入图片描述

以父结点为旋转结点进行左旋,重新判断叶子结点的兄弟结点的颜色,然后根据不同颜色进行不同的处理

情形3.2:叶子结点为父结点的左孩子,叶子结点的兄弟结点为黑,兄弟结点的左结点为黑,兄弟结点的右结点为黑

在这里插入图片描述

把兄弟结点变红,以叶子结点的父结点为当前结点,重新进行平衡处理

情形3.3:叶子结点为父结点的左孩子,叶子结点的兄弟结点为黑,兄弟结点的左结点为红,兄弟结点的右结点为黑

在这里插入图片描述

先以兄弟结点为旋转结点进行右旋,然后再把父结点的颜色赋给兄弟结点,父结点变黑,兄弟结点的右结点变黑,以父结点为旋转结点进行左旋,平衡处理完成

情形3.4:叶子结点为父结点的左孩子,叶子结点的兄弟结点为黑,兄弟结点的左结点颜色未知,兄弟结点的右结点为红

在这里插入图片描述

把父结点的颜色赋给兄弟结点,父结点变黑,兄弟结点的右结点变黑,以父结点为旋转结点进行左旋,平衡处理完成

情形4:删除结点有两个子结点

先找到删除结点的后继结点,然后用后继结点替换掉删除结点,由红黑树的性质可知,后继结点只能有一个子结点或只能有叶子结点,所以接下来就以后继结点为当前结点,继续进行上面3种情形的处理

说明:以上是叶子结点为父结点的左孩子的情况,如果叶子结点是父结点的右孩子,只需要把上面的操作反过来即可

9、源码

package com.yc.redblacktree;

import java.text.SimpleDateFormat;
import java.util.Date;

public class RedBlackTreeDemo {

	public static void main(String[] args) {
		//测试代码
		RedBlackTree tree = new RedBlackTree();
		/*
		tree.add(5, 'a');
		tree.add(7, 'e');
		tree.add(1, 'f');
		tree.add(3, 'c');
		tree.add(2, 'g');
		tree.add(6, 'b');
		tree.add(4, 'd');
		
		tree.delete(1);
		tree.delete(2);
		tree.delete(3);
		tree.delete(4);
		tree.delete(5);
		tree.delete(6);
//		tree.delete(7);
		
		tree.infixOrder();
		System.out.println();
		tree.preOrder();
		System.out.println();
		tree.sufixOrder();*/
		
		Date date = new Date();
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		String dateStr = format.format(date);
		System.out.println(dateStr);
		
		for(int i=0;i<10000000;i++){
			tree.add((int) (Math.random()*10000000), 'a');
		}
		
		Date date2 = new Date();
		String date2Str = format.format(date2);
		System.out.println(date2Str);
		
		for(int i=0;i<10000000;i++){
			tree.delete((int) (Math.random()*10000000));
		}
		
		Date date3 = new Date();
		String date3Str = format.format(date3);
		System.out.println(date3Str);
	}

}
class TreeNode{
	int key;//键
	char value;//值
	TreeNode parent;//父节点
	TreeNode left;//左孩子
	TreeNode right;//右孩子
	boolean red;//是否为红色
	boolean black;//是否为黑色
	boolean leaf;//是否为叶子节点
	
	/**
	 * 有参构造
	 * @param key
	 * @param value
	 */
	public TreeNode(int key,char value){
		this.key = key;
		this.value = value;
	}
	/**
	 * 无参构造
	 */
	public TreeNode(){}
	/**
	 * 添加平衡
	 * @param tree
	 */
	public void addBalance(RedBlackTree tree){
		while(this.parent!=null&&this.parent.red){
			if(this.parent==this.parent.parent.left){
				if(this.parent.parent.right.red){
					this.parent.black = true;
					this.parent.red = false;
					this.parent.parent.right.black = true;
					this.parent.parent.right.red = false;
					this.parent.parent.red = true;
					this.parent.parent.black = false;
					this.parent.parent.addBalance(tree);
					break;
				}else if(this==this.parent.right){
					this.parent.rotateLeft(tree);
					this.parent.rotateRight(tree);
					break;
				}else{
					this.parent.parent.rotateRight(tree);
					break;
				}
			}else{
				if(this.parent.parent.left.red){
					this.parent.black = true;
					this.parent.red = false;
					this.parent.parent.left.black = true;
					this.parent.parent.left.red = false;
					this.parent.parent.red = true;
					this.parent.parent.black = false;
					this.parent.parent.addBalance(tree);
					break;
				}else if(this==this.parent.left){
					this.parent.rotateRight(tree);
					this.parent.rotateLeft(tree);
					break;
				}else{
					this.parent.parent.rotateLeft(tree);
					break;
				}
			}
		}
		if(this.parent==null){
			this.black = true;
			this.red = false;
			tree.root = this;
		}
	}
	/**
	 * 左旋变颜色
	 * @param tree
	 */
	public void rotateLeft(RedBlackTree tree){
		TreeNode temp = this.right;
		temp.red = false;
		temp.black = true;
		this.red = true;
		this.black = false;
		temp.parent = this.parent;
		if(this.parent!=null&&this==this.parent.left){
			temp.parent.left = temp;
		}else if(this.parent!=null){
			temp.parent.right = temp;
		}
		this.right = temp.left;
		temp.left.parent = this;
		this.parent = temp;
		temp.left = this;
		if(temp.parent==null){
			tree.root = temp;
		}
	}
	/**
	 * 左旋不变颜色
	 * @param tree
	 */
	public void rotateLeftNoColor(RedBlackTree tree){
		TreeNode temp = this.right;
		temp.parent = this.parent;
		if(this.parent!=null&&this==this.parent.left){
			temp.parent.left = temp;
		}else if(this.parent!=null){
			temp.parent.right = temp;
		}
		this.right = temp.left;
		temp.left.parent = this;
		this.parent = temp;
		temp.left = this;
		if(temp.parent==null){
			tree.root = temp;
		}
	}
	/**
	 * 右旋变颜色
	 * @param tree
	 */
	public void rotateRight(RedBlackTree tree){
		TreeNode temp = this.left;
		temp.black = true;
		temp.red = false;
		this.red = true;
		this.black = false;
		temp.parent = this.parent;
		if(this.parent!=null&&this==this.parent.left){
			temp.parent.left = temp;
		}else if(this.parent!=null){
			temp.parent.right = temp;
		}
		this.left = temp.right;
		temp.right.parent = this;
		temp.right = this;
		this.parent = temp;
		if(temp.parent==null){
			tree.root = temp;
		}
	}
	/**
	 * 右旋不变颜色
	 * @param tree
	 */
	public void rotateRightNoColor(RedBlackTree tree){
		TreeNode temp = this.left;
		temp.parent = this.parent;
		if(this.parent!=null&&this==this.parent.left){
			temp.parent.left = temp;
		}else if(this.parent!=null){
			temp.parent.right = temp;
		}
		this.left = temp.right;
		temp.right.parent = this;
		temp.right = this;
		this.parent = temp;
		if(temp.parent==null){
			tree.root = temp;
		}
	}
	/**
	 * 寻找结点
	 * @param key
	 * @return
	 */
	public TreeNode find(int key){
		if(!this.leaf&&this.key==key){
			return this;
		}else if(this.key<key&&!this.leaf){
			return this.right.find(key);
		}else if(this.key>key&&!this.leaf){
			return this.left.find(key);
		}else{
			return this;
		}
	}
	/**
	 * 中序遍历
	 */
	public void infixOrder(){
		if(this.leaf){
			return;
		}
		this.left.infixOrder();
		System.out.println("key="+this.key+" value="+this.value);
		this.right.infixOrder();
	}
	/**
	 * 先序遍历
	 */
	public void preOrder(){
		if(this.leaf){
			return;
		}
		System.out.println("key="+this.key+" value="+this.value);
		this.left.preOrder();
		this.right.preOrder();
	}
	/**
	 * 后序遍历
	 */
	public void sufixOrder(){
		if(this.leaf){
			return;
		}
		this.left.sufixOrder();
		this.right.sufixOrder();
		System.out.println("key="+this.key+" value="+this.value);
	}
	/**
	 * 删除平衡
	 * @param tree
	 */
	public void deleteBalance(RedBlackTree tree) {
		while(this.parent!=null&&!this.red){
			if(this==this.parent.left){
				if(this.parent.right.red){
					this.parent.rotateLeft(tree);
					if(this.parent.right.leaf){
						break;
					}
				}
				if(this.parent.right.black&&(this.parent.right.left.black||this.parent.right.left.leaf)&&(this.parent.right.right.black||this.parent.right.right.leaf)){
					this.parent.right.red = true;
					this.parent.right.black = false;
					this.parent.deleteBalance(tree);
					return;
				}else if(this.parent.right.black&&this.parent.right.left.red&&(this.parent.right.right.black||this.parent.right.right.leaf)){
					this.parent.right.rotateRight(tree);
				}
				if(this.parent.right.black&&this.parent.right.right.red){
					this.parent.right.right.black = true;
					this.parent.right.right.red = false;
					this.parent.right.red = this.parent.red;
					this.parent.right.black = this.parent.black;
					this.parent.black = true;
					this.parent.red = false;
					this.parent.rotateLeftNoColor(tree);
					return;
				}
			}else{
				if(this.parent.left.red){
					this.parent.rotateRight(tree);
					if(this.parent.left.leaf){
						break;
					}
				}
				if(this.parent.left.black&&(this.parent.left.left.black||this.parent.left.left.leaf)&&(this.parent.left.right.black||this.parent.left.right.leaf)){
					this.parent.left.red = true;
					this.parent.left.black = false;
					this.parent.deleteBalance(tree);
					return;
				}else if(this.parent.left.black&&(this.parent.left.left.black||this.parent.left.left.leaf)&&this.parent.left.right.red){
					this.parent.left.rotateLeft(tree);
				}
				if(this.parent.left.black&&this.parent.left.left.red){
					this.parent.left.left.black = true;
					this.parent.left.left.red = false;
					this.parent.left.red = this.parent.red;
					this.parent.left.black = this.parent.black;
					this.parent.black = true;
					this.parent.red = false;
					this.parent.rotateRightNoColor(tree);
					return;
				}
			}
		}
		this.red = false;
		this.black = true;
		if(this.parent==null){
			tree.root = this;
		}
	}
	/**
	 * 寻找后继结点
	 * @param key
	 * @return
	 */
	public TreeNode findBack(int key) {
		if(this.left.leaf){
			return this;
		}else{
			return this.left.findBack(key);
		}
	}
}

class RedBlackTree{
	//根结点
	TreeNode root;
	
	/**
	 * 中序遍历
	 */
	public void infixOrder(){
		if(root!=null){
			root.infixOrder();
		}
	}
	/**
	 * 先序遍历
	 */
	public void preOrder(){
		if(root!=null){
			root.preOrder();
		}
	}
	/**
	 * 后序遍历
	 */
	public void sufixOrder(){
		if(root!=null){
			root.sufixOrder();
		}
	}
	/**
	 * 删除
	 * @param key
	 */
	public synchronized void delete(int key){
		if(root==null){
			return;
		}
		TreeNode findNode = root.find(key);
		if(findNode.leaf){
			return;
		}else{
			TreeNode y = null;
			if(!findNode.left.leaf&&!findNode.right.leaf){
				y = findNode.right.findBack(key);
			}else{
				y = findNode;
			}
			TreeNode x = null;
			if(!y.left.leaf){
				x = y.left;
			}else{
				x = y.right;
			}
			x.parent = y.parent;
			if(y.parent!=null&&y==y.parent.left){
				y.parent.left = x;
			}else if(y.parent!=null){
				y.parent.right = x;
			}else{
				root = x;
			}
			if(y!=findNode){
				findNode.value = y.value;
			}
			if(y.black){
				if(x.red){
					x.black = true;
					x.red = false;
				}else{
					x.deleteBalance(this);
				}
			}
		}
	}
	/**
	 * 添加
	 * @param key
	 * @param value
	 */
	public synchronized void add(int key,char value){
		if(root==null){
			root = new TreeNode(key, value);
			root.black = true;
			TreeNode lLeaf = new TreeNode();
			lLeaf.leaf = true;
			TreeNode rLeaf = new TreeNode();
			rLeaf.leaf = true;
			root.left = lLeaf;
			lLeaf.parent = root;
			root.right = rLeaf;
			rLeaf.parent = root;
			return;
		}
		TreeNode findNode = root.find(key);
		if(!findNode.leaf){
			findNode.value = value;
		}else{
			TreeNode insertNode = new TreeNode(key,value);
			insertNode.red = true;
			if(key<findNode.parent.key){
				insertNode.left = findNode;
				insertNode.parent = findNode.parent;
				findNode.parent.left = insertNode;
				findNode.parent = insertNode;
				TreeNode rLeaf = new TreeNode();
				rLeaf.leaf = true;
				insertNode.right = rLeaf;
				rLeaf.parent = insertNode;
			}else{
				insertNode.right = findNode;
				insertNode.parent = findNode.parent;
				findNode.parent.right = insertNode;
				findNode.parent = insertNode;
				TreeNode lLeaf = new TreeNode();
				lLeaf.leaf = true;
				insertNode.left = lLeaf;
				lLeaf.parent = insertNode;
			}
			if(insertNode.parent.red){
				insertNode.addBalance(this);
			}
		}
	}
}

10、总结

红黑树很复杂,这些都是我的个人理解,如果有不对的地方大家可以指出来,一起学习一起进步

  • 4
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值