红黑树 基本介绍与创建

红黑树的性质(重点):

性质1:每个节点要么是黑色,要么是红色。
性质2:根节点是黑色。
性质3:每个叶子节点(NIL) 是黑色。
性质4:每个红色节点的两个子节点一 定都是黑色。不能有两个红色节点相连。
性质5:任意一节点到每个叶子节点的路径都包含数量相同的黑结点。俗
称:黑高!
从性质5又可以推出:
性质5.1:如果一个节点存在黑子节点,那么该结点肯定有两个子节点
只要这棵树满足以上性质,这棵树就是趋近与平衡状态的,

旋转和颜色变换规则:所有插入的点默认为红色
1.变颜色的情况:当前结点的父亲是红色,且它的祖父结点的另一个子结点
也是红色。( 叔叔结点) :
(1)把父节点设为黑色
( 2 )把叔叔也设为黑色
( 3 )把祖父也就是父亲的父亲设为红色(爷爷)
( 4 )把指针定义到祖父结点设为当前要操作的.(爷爷)分析的点变换的规则
2.左旋:当前父结点是红色,叔叔是黑色的时候,且当前的结点是右子树。左旋以父结点旋转
3.右旋:当前父结点是红色,叔叔是黑色的时候,且当前的结点是左子树。右旋
(1)把父结点变为黑色
(2)把祖父结点变为红色(爷爷)
( 3 右旋)以祖父结点旋转(爷爷) TreeMap JDK1.8大学给小学弟( )
添加后先变颜色
在这里插入图片描述5 12 左旋 再以12 19右旋
在这里插入图片描述

红黑树插入节点情景分析

情景1:红黑树为空树
最简单的一种情景,直接把插入结点作为根结点就行
注意:根据红黑树性质2:根节点是色。还需要把插入结点设为色。
情景2:插入结点的Key已存在
处理:更新当前节点的值,为插入节点的值
在这里插入图片描述情景3:插入结点的父结点为黑结点
由于插入的结点是红色的,当插入结点的黑色时,并不会影响红黑树的平衡,直接插入即可,无需做自平衡。
在这里插入图片描述情景4:插入节点的父节点为红色
再次回想下红黑树的性质2:根结点是默色。如果插入节点的父结点为红结点,那么该父结点不可能为根结点,所以插入结点总是存在祖父结点。
这一点很关键,因为后续的旋转操作肯定需要祖父结点的参与。
在这里插入图片描述插入情景4.1:叔叔结点存在并且为红结点
依据红黑树性质4可知,红色节点不能相连==>祖父结点肯定为结点;
因为不可以同时存在两个相连的红结点。那么此时该插入子树的红黑层数的情况是:红红。显然最简单的处理方式是把其改为:红黑红
处理:
1.将P和U节点改为色
2将PP改为红色
3将PP设置为当前节点,进行后续处理
在这里插入图片描述可以看到,我们把PP结点设为红色了,如果PP的父结点是色,那么无需再做任何处理;
但如果PP的父结点是红色,则违反红黑树性质了。所以需要将PP设置为当前节点,继续做插入操作自平衡处理,直到平衡为止。
插入情景4.2:
叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的左子结点
注意:单纯从插入前来看,叔叔节点非红即空(NIL节点) ,否则的话破坏了红黑树性质5,此路径会比其它路径多-个黑色节点。
在这里插入图片描述插入情景4.2.1:新插入节点,为其父节点的左子节点(LL红色情况)
处理:
1.变颜色:将P设置为艷,将PP设置为红色
2.对PP节,点进行右旋
在这里插入图片描述在这里插入图片描述
插入情景4.2.2:新插入节点,为其父节点的右子节点(LR红色情况)
处理:
1.对P进行左旋
2.将P设置为当前节点,得到LL红色情况
3.按照LL红色情况处理(1 .变颜色2.右旋PP)
在这里插入图片描述插入情景4.3:叔叔结点不存在或为黑结点,并且插入结点的父亲结点是祖父结点的右子结点
该情景对应情景4.2,只是方向反转,直接看图。
在这里插入图片描述
插入情景4.3.1:新插入节点,为其父节点的右子节点(RR红色情况)
在这里插入图片描述
处理:
1.变颜色:将P设置为色,将PP设置为红色
2.对PP节,点进行左旋
在这里插入图片描述插入情景4.3.2:新插入节点,为其父节点的左子节点(RL红色情况)
在这里插入图片描述处理: .
1.对P进行右旋;
2.将P设置为当前节点,得到RR红色情况
3.按照RR红色情况处理(1 .变颜色2.左旋PP)![在这里插入图片描述](https://img-blog.csdnimg.cn/20200516162216380.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L0F6YWRvbw==,size_16,color_FFFFFF,t_70在这里插入图片描述创建红黑树代码如下


public class RedBlackTree <K extends Comparable<K>,V>{
	private static final boolean RED = true;
	private static final boolean BLACK = false;
	private RBNode root;

	/**
	 * @return the root
	 */
	public RBNode getRoot() {
		return root;
	}

	
	//获取当前节点的父节点
	private RBNode parentof(RBNode node) {
		if(node != null) {
			return node.parent;
		}else {
			return null;
		}
	}
	
	// 节点是否为红色
	private boolean isRed(RBNode node) {
		if (node != null) {
			return node.color == RED;
		}
		return false;
	}

	// 节点是否为黑色
	private boolean isBlack(RBNode node) {
		if (node != null) {
			return node.color == BLACK;
		}
		return false;
	}

	// 设置节点为红色
	private void setRed(RBNode node) {
		if (node != null) {
			node.color = RED;
		} else {
			System.out.println("输入值为空无法设置");
		}
	}

	// 设置节点为黑色
	private void setBlack(RBNode node) {
		if (node != null) {
			node.color = BLACK;
		} else {
			System.out.println("输入值为空无法设置");
		}
	}
	
	//中需打印二叉树
	public void inOrderPrint() {
		inOrderPrint(this.root);
	}
	public void inOrderPrint(RBNode node) {
		if(node != null) {
			inOrderPrint(node.left);
			System.out.println("ket"+node.key+"value"+node.value);
			inOrderPrint(node.right);
		}
	}
	//左旋
//	1.将x的右子节点指向y的左子节点(ly),将y的左子节点的父节点更新为x
//	2.当x的父节点(不为空时),更新y的父节点为x的父节点,并将x的父节点指定子树(当前x的子树位置)指定为y
//	3.将x的父节点更新为y,将y的左子节点更新为x
	private void leftRotate(RBNode x) {
		RBNode y = x.right;
		x.right = y.left;
		if(y.left != null) {
			y.left.parent = x;
		}
		if(x.parent != null) {
			y.parent = x.parent;
			if(x == x.parent.left) {
				//如果x为左子节点
				x.parent.left = y;
			}else {//x为右子节点
				x.parent.right = y;
			}
		}else {
			//x为根节点
			this.root = y;
			this.root.parent = null;
		}
		x.parent = y;
		y.left = x;
	}
	//右旋
//	1.将y的左子节点指向x的右子节点,并且更新x的右子节点的父节点为y
//	2.当y的父节点不为空时, 更新x的父节点为y的父节点, 更新y的父节点的指定子节点(y当前的位置)为x
//	3.更新y的父节点为x,更新x的右子节点为y
	private void rightRotate(RBNode y) {
		RBNode x = y.left;
		y.left = x.right;
		if(x.right != null) {
			x.right.parent = y;
		}
		if(y.parent != null) {
			x.parent = y.parent;
			if(y == y.parent.left) {
				y.parent.left = x;
			}else {
				y.parent.right = x;
			}
		}else {
			this.root = x;
			this.root.parent = null;
		}
		y.parent = x;
		x.right = y;
	}
	//公开的插入方法
	public void insert(K key,V value) {
		RBNode node = new RBNode();
		node.setKey(key);
		node.setValue(value);
		//新节点一定是红色
		node.setColor(RED);
	}
	public void insert(RBNode node) {
		//1.查找当前node的父节点
		RBNode parent = null;
		RBNode x = this.root;
		while(x != null) {
			parent = x;
			//cmp>0说明node.key大于x.key需要到右子树查找
			//cmp == 0 说明需要进行替换操作
			int cmp = node.key.compareTo(x.key);
			if(cmp > 0) {
				x = x.right;
			}else if(cmp == 0) {
				x.setValue(node.getValue());
				return;
			}else {
				x = x.left;
			}
		}
		node.parent = parent;
		if(parent != null) {//是否为空树
			
			//判断node是在左子树还是右子树
			int cmp = node.key.compareTo(parent.getValue());
			if(cmp > 0) {
				parent.right = node;
			}else {
				parent.left = node;
			}
		}else {
			this.root = node;
		}
		insertFixUp(node);
	}
	
//	插入后修复红黑树平衡的方法
//	情景1:红黑树为空树,将跟节点染色为黑色
//	情景2:插入节点的key已经存在, 不需要处理
//	情景3:插入节点的父节点为黑色,因为你所插入的路径,黑色节点没有变化,所以红黑树依然平衡,所以不需要处理。
//	情景4需要咱们去处理
//	---情景4: 插入节点的父节点为红色
//	情景4.1: 叔叔节点存在,并且为红色(父-叔双红),将爸爸和叔叔染色为黑色,将爷爷染色为红色,并且再以爷爷节点为当前节点进行下-轮处理
//	情景4.2:叔叔节点不存在,或者为黑色,父节点为爷爷节点的左子树
//	---情景4.2.1: 插入节点为其父节点的左子节点(LL情况),将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷节点右旋,就完成了
//	---情景4.2.2: 插入节点为其父节点的右子节点(LR情况),以爸爸节点进行-次左旋, 得到LL双红的情景(4.2.1),然后指定爸爸 节点为当前节点进行下一轮处理
//	---情景4.3: 叔叔节点不存在,或者为黑色,父节点为爷爷 节点的右子树
//	情景4.3.1:插入节点为其父节点的右子节点(RR情况),将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷节点左旋,就完成了
//	情景4.3.2:插入节点为其父节点的左子节点(RL情况),以爸爸节点进行一 一次右旋, 得到RR双红的情景(4.3.1),然后指定爸爸节点为当前节点进行下一 轮处理
	private void insertFixUp(RBNode node) {
		this.root.setColor(BLACK);//情景1
		RBNode parent = parentof(node);
		RBNode gradparent = parentof(parent);
		//情景4
		if(parent != null && isRed(parent)) {
			//父节点红色 那么一定存在爷爷节点(根节点一定为黑)
			RBNode uncle = null;
			if(parent == gradparent.left) {
				uncle = gradparent.right;
				//情景4.1叔叔节点存在并且为红色 父叔双红
				if(uncle != null && isRed(uncle)) {
					//将叔叔 父 染色为黑 爷爷染色为红 再以爷爷节点为当前节点 进行下一轮
					setBlack(parent);
					setBlack(uncle);
					setRed(gradparent);
					insertFixUp(gradparent);
					return;
				}
				//情景4.2叔叔节点不存在或者为黑色
				if(uncle == null || isBlack(uncle)) {
					//情景4.2.1: 插入节点为其父节点的左子节点(LL情况),将爸爸染色为黑色,将爷爷染色为红色,然后以爷爷节点右旋
					if(node == parent.left) {
						setBlack(parent);
						setRed(gradparent);
						rightRotate(gradparent);
						return;
					}
					//情景 4.2.2插入节点为其父节点的右子节点(LR情况),以爸爸节点进行-次左旋, 得到LL双红的情景(4.2.1),
					//然后指定爸爸 节点为当前节点进行下一轮处理
					if(node == parent.right) {
						leftRotate(parent);
						insertFixUp(parent);
						return;
					}		
				}				
			}else {
				//父节点为爷爷节点的右子节点 
				//情景4.1叔叔节点存在并且为红色 父叔双红
				uncle = gradparent.left;
				if(uncle != null && isRed(uncle)) {
					//将叔叔 父 染色为黑 爷爷染色为红 再以爷爷节点为当前节点 进行下一轮
					setBlack(parent);
					setBlack(uncle);
					setRed(gradparent);
					insertFixUp(gradparent);
					return;
				}
				//情景4.3: 叔叔节点不存在,或者为黑色,父节点为爷爷 节点的右子树
				if(uncle == null && isBlack(uncle)) {
					//情景4.3.1:插入节点为其父节点的右子节点(RR情况),将爸爸染色为黑色,将爷爷染色为红色
					//然后以爷爷节点左旋
					if(node == parent.right) {
						setBlack(parent);
						setRed(gradparent);
						leftRotate(gradparent);
						return;
					}
					//情景4.3.2:插入节点为其父节点的左子节点(RL情况),以爸爸节点进行一 一次右旋
					// 得到RR双红的情景(4.3.1),然后指定爸爸节点为当前节点进行下一 轮处理
					if (node == parent.left) {
						rightRotate(parent);
						insertFixUp(parent);
						return;
					}
				}
			}
		}
	}
	
	static class RBNode <K extends Comparable<K>,V>{
		private K key;
		private V value;
		private boolean color;//默认红色
		private RBNode left;
		private RBNode right;
		private RBNode parent;
		
		public RBNode() {
			
		}
		public RBNode(K key, V value, boolean color, RBNode left, RBNode right, RBNode parent) {
			super();
			this.key = key;
			this.value = value;
			this.color = color;
			this.left = left;
			this.right = right;
			this.parent = parent;
		}
		/**
		 * @return the key
		 */
		public K getKey() {
			return key;
		}
		/**
		 * @param key the key to set
		 */
		public void setKey(K key) {
			this.key = key;
		}
		/**
		 * @return the value
		 */
		public V getValue() {
			return value;
		}
		/**
		 * @param value the value to set
		 */
		public void setValue(V value) {
			this.value = value;
		}
		/**
		 * @return the color
		 */
		public boolean isColor() {
			return color;
		}
		/**
		 * @param color the color to set
		 */
		public void setColor(boolean color) {
			this.color = color;
		}
		/**
		 * @return the left
		 */
		public RBNode getLeft() {
			return left;
		}
		/**
		 * @param left the left to set
		 */
		public void setLeft(RBNode left) {
			this.left = left;
		}
		/**
		 * @return the right
		 */
		public RBNode getRight() {
			return right;
		}
		/**
		 * @param right the right to set
		 */
		public void setRight(RBNode right) {
			this.right = right;
		}
		/**
		 * @return the parent
		 */
		public RBNode getParent() {
			return parent;
		}
		/**
		 * @param parent the parent to set
		 */
		public void setParent(RBNode parent) {
			this.parent = parent;
		}
		
	}
	
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值