【数据结构与算法分析】08:红黑树(java实现)

简介

红黑树是一种二叉查找树,在每个结点上增加一个存储位表示结点颜色,可以是RED或者是BLACK,通过对任何一条从根到叶的路径上各个结点着色方式的限制,确保红黑树不会有一条路径会比其他路径长出两倍

性质:

  1. 每个结点或是红的,或是黑的;
  2. 根结点是黑色的;
  3. 每个叶结点(NIL)是黑的;
  4. 如果一个结点是红色,则他的两个儿子都是黑色;
  5. 对每个结点,从该结点到其子孙结点的所有路径上包含相同数据的黑结点。

代码实现:

public class RBTree<Key extends Comparable<Key>, Value> {

	private static final boolean RED = true;
	private static final boolean BLACK = false;
	private final Node nil = new Node(null, null, BLACK);

	private Node root = nil;

	public Node getRoot() {
		return root;
	}

	private class Node {
		Node left = nil;
		Node right = nil;
		Node Parent = nil;
		Key key;
		Value val;
		boolean color;

		Node(Key key, Value val, boolean color) {
			this.key = key;
			this.val = val;
			this.color = color;
		}
	}

	public Node get(Key key) {
		Node x = root;
		while (x != nil) {
			int cmp = key.compareTo(x.key);
			if (cmp == 0) {
				return x;
			}
			if (cmp > 0) {
				x = x.right;
			}
			if (cmp < 0) {
				x = x.left;
			}
		}
		return null;
	}

	public Node Tree_Minimun(Node x) {
		Node tmp = x;
		while (tmp.left != nil) {
			tmp = tmp.left;
		}
		return tmp;
	}

	public Node Tree_Maximun(Node x) {
		Node tmp = x;
		while (tmp.right != nil) {
			tmp = tmp.right;
		}
		return tmp;
	}

	/**
	 * 找x的后继
	 * 
	 * @param x
	 * @return
	 */
	public Node Tree_Successor(Node x) {

		if (x.right != nil) {
			return Tree_Minimun(x.right);
		}
		Node y = x.Parent;
		while (y != nil && x.key == y.right.key) {
			x = y;
			y = x.Parent;
		}
		return y;
	}

	/**
	 * 左旋操作
	 * 
	 * @param x
	 */
	public void Left_Rotate(Node x) {
		Node y = x.right;
		x.right = y.left;
		if (y.left != nil) {
			y.left.Parent = x;
		}
		y.Parent = x.Parent;
		if (x.Parent != nil) {
			if (x == x.Parent.left) {
				x.Parent.left = y;
			} else {
				x.Parent.right = y;
			}
		} else {
			root = y;
		}
		y.left = x;
		x.Parent = y;
	}

	/**
	 * 右旋操作
	 * 
	 * @param x
	 */
	public void Right_Rotate(Node x) {
		Node y = x.left;
		x.left = y.right;
		if (y.right != nil) {
			y.right.Parent = x;
		}
		y.Parent = x.Parent;
		if (x.Parent != nil) {
			if (x == x.Parent.left) {
				x.Parent.left = y;
			} else {
				x.Parent.right = y;
			}
		} else {
			root = y;
		}
		y.right = x;
		x.Parent = y;
	}

	// 插入
	public void RB_Insert(Key key, Value val) {

		Node z = new Node(key, val, RED);

		Node x = root;
		Node y = nil;
		while (x != nil) {// 找到应该插入位置的父节点,y
			y = x;
			if (z.key.compareTo(x.key) < 0) {
				x = x.left;
			} else {
				x = x.right;
			}
		}
		z.Parent = y;
		if (y == nil) { // y若为null,则插入的点为根节点
			root = z;
			root.color = BLACK;
			return;
		} else {// 否则,比较大小,看插入左边还是右边
			if (z.key.compareTo(y.key) < 0) {
				y.left = z;
			} else {
				y.right = z;
			}
		}
		z.left = nil;
		z.right = nil;
		z.color = RED;// 颜色置为红色
		RB_Insert_Fixup(z);// 进行插入后的调整,保持红黑性质

	}

	public void RB_Insert_Fixup(Node z) {

		/**
		 * 三种情况: case1:若插入节点的父节点为红,则需要进行下一步讨论; case2:若插入节点父节点为黑,则无需做调整;
		 * case3:若插入节点为根,则最后将节点变为黑色
		 */
		while (z.Parent.color == RED) {// case1 父节点为红,则必有祖父节点
			/**
			 * 两种情况: case1.1 插入节点的父节点为左子树 case1.2 插入节点的父节点为右子树
			 */
			if (z.Parent == z.Parent.Parent.left) {
				Node y = z.Parent.Parent.right;// 获取z的叔父节点
				/**
				 * 三种情况: case1.1.1 叔父节点为红 case1.1.2 叔父节点为黑,且z为右孩子 case1.1.3 叔父节点为黑,且z为左孩子
				 */
				if (y.color == RED) {// case1.1.1
					/**
					 * 调整z的父节点与叔父节点颜色为黑色 调整z的祖父节点颜色为红色 将z上移两层
					 */
					z.Parent.color = BLACK;
					y.color = BLACK;
					z.Parent.Parent.color = RED;
					z = z.Parent.Parent;
				} else if (z == z.Parent.right) {// case1.1.2
					// 左旋操作,左旋后,z变为左孩子,变为case1.1.3情况
					z = z.Parent;
					Left_Rotate(z);
				} else {// case1.1.3
					z.Parent.color = BLACK;
					z.Parent.Parent.color = RED;
					Right_Rotate(z.Parent.Parent);
				}
			} else {// case1.2
				Node y = z.Parent.Parent.left;// 获取z的叔父节点
				/**
				 * 三种情况: case1.2.1 叔父节点为红; case1.2.2 叔父节点为黑,且z为左孩子; case1.2.3 叔父节点为黑,且z为右孩子
				 */
				if (y.color == RED) {// case1.2.1
					/**
					 * 调整z的父节点与叔父节点颜色为黑色 调整z的祖父节点颜色为红色 将z上移两层
					 */
					z.Parent.color = BLACK;
					y.color = BLACK;
					z.Parent.Parent.color = RED;
					z = z.Parent.Parent;
				} else if (z == z.Parent.left) {// case1.2.2

					// 右旋操作,右旋后,z变为右孩子,变为case1.2.3情况
					z = z.Parent;
					Right_Rotate(z);

				} else {// case1.2.3

					z.Parent.color = BLACK;
					z.Parent.Parent.color = RED;
					Left_Rotate(z.Parent.Parent);
				}
			}
		}

		// 最后,对根节点进行操作。保证性质2:根节点为黑色成立
		root.color = BLACK;
	}

	public void RB_Delete(Key key) {

		Node z = get(key);// 找到key值所在的位置
		Node y = null;

		// 通过下面的操作,保证y最多只有一个子树
		if (z.right == nil || z.left == nil) {// 若z点存在至多一个子树
			y = z;
		} else {
			y = Tree_Successor(z);// 找到后继节点,后继节点为右子树的最左节点,该点最多只有一个右子树
		}

		Node x = nil;
		if (y.left != nil) {
			x = y.left;
		} else {
			x = y.right;
		}

		x.Parent = y.Parent;
		/**
		 * 三种情况: case1:y为根节点; case2:y为右孩子; case3:y为左孩子
		 * 
		 */
		if (y.Parent == nil) {// case1
			root = x;
		} else if (y == y.Parent.right) {// case2
			y.Parent.right = x;
		} else {// case3
			y.Parent.left = x;
		}

		if (y != z) {// y为后继节点,将z所在点信息替换为y的数据
			z.key = y.key;
			z.val = y.val;

		}

		// 若y为红色,则其父节点与子节点均不可能为红,故直接删除无影响
		// 若y为黑色,则可能违反红黑树性质,需要进行修正
		if (y.color == BLACK) {
			RB_Delete_Fixup(x);
		}
	}

	public void RB_Delete_Fixup(Node x) {
		while (x != root && x.color == BLACK) {
			/**
			 * 两种情况: case1:x为左子树 case2:x为右子树
			 */
			if (x == x.Parent.left) {// case1

				Node w = x.Parent.right;// 找到兄弟节点

				/**
				 * 四种情况: case1.1:兄弟节点w为红色,则x与w的父节点必为黑色,且w的两个孩子必为黑色 ;
				 * case1.2:兄弟节点w为黑色,w的两个孩子都是黑色; case1.3:兄弟节点w为黑色,w的左孩子为红,右孩子为黑;
				 * case1.4:兄弟节点w为黑色,且右孩子为红;
				 */
				if (w != nil && w.color == RED) {// case1.1
					// 修改w与其父节点颜色,对其父节点进行左旋
					// 左旋后,变为case1.2/1.3/1.4情况
					w.color = BLACK;
					x.Parent.color = RED;
					Left_Rotate(x.Parent);
				} else if (w.left.color == BLACK && w.right.color == BLACK) {// case 1.2
					// 将w变红,x上移一层,跳出while循环
					w.color = RED;
					x = x.Parent;
				} else if (w.right.color == BLACK) {// case1.3
					// 转化为case1.4
					w.left.color = BLACK;
					w.color = RED;
					Right_Rotate(w);
					w = x.Parent.right;
				} else {// case 1.4

					/**
					 * w为黑,w的黑色节点路径比x多1; w的左子树黑色节点路径长度与x节点黑色路径长度相等;
					 * 对x父节点着黑色,并左旋,则x的兄弟节点变为左旋前w的左孩子,黑色路径长度相等; x左旋后父节点为黑,祖父节点为红,增加了一层黑色节点
					 * x左旋后叔父节点为左旋前w的右子树,并改变颜色从红到黑,增加了一层黑色节点 最后左右平衡
					 */

					w.color = RED; // 保持父节点为红
					w.right.color = BLACK;// 右边加一层黑色
					w.Parent.color = BLACK;// 左边加一层黑色

					Left_Rotate(w.Parent);
				}
			} else {// case2

				Node w = x.Parent.left;// 找到兄弟节点

				/**
				 * 四种情况: case2.1:兄弟节点w为红色,则x与w的父节点必为黑色,且w的两个孩子必为黑色 ;
				 * case2.2:兄弟节点w为黑色,w的两个孩子都是黑色; case2.3:兄弟节点w为黑色,w的左孩子为黑,右孩子为红;
				 * case2.4:兄弟节点w为黑色,且左孩子为红;
				 */
				if (w.color == RED) {// case2.1
					// 修改w与其父节点颜色,对其父节点进行右旋
					// 右旋后,变为case2.2/2.3/2.4情况
					w.color = BLACK;
					x.Parent.color = RED;
					Right_Rotate(x.Parent);
				} else if (w.left.color == BLACK && w.right.color == BLACK) {// case 2.2
					// 将w变红,x上移一层,跳出while循环
					w.color = RED;
					x = x.Parent;
				} else if (w.right.color == BLACK) {// case2.3
					// 转化为case2.4
					w.right.color = BLACK;
					w.color = RED;
					Left_Rotate(w);
					w = x.Parent.left;
				} else {// case 2.4

					/**
					 * w为黑,w的黑色节点路径比x多1; w的右子树黑色节点路径长度与x节点黑色路径长度相等;
					 * 对x父节点着黑色,并右旋,则x的兄弟节点变为右旋前w的右孩子,黑色路径长度相等; x右旋后父节点为黑,祖父节点为红,增加了一层黑色节点
					 * x右旋后叔父节点为右旋前w的左子树,并改变颜色从红到黑,增加了一层黑色节点 最后左右平衡
					 */

					w.color = RED; // 保持父节点为红
					w.left.color = BLACK;// 左边加一层黑色
					w.Parent.color = BLACK;// 右边加一层黑色

					Right_Rotate(w.Parent);
				}
			}

		}

		x.color = BLACK;
	}

	public void Inorder_Tree_Walk(Node x) {
		if (x != nil) {
			Inorder_Tree_Walk(x.left);
			System.out.println(x.key);
			Inorder_Tree_Walk(x.right);
		}
	}
}

测试代码

int a[] = { 16, 3, 7, 11, 9, 26, 18, 14, 15 };
		RBTree<Integer, Integer> t = new RBTree<Integer, Integer>();
		for (int i = 0; i < a.length; i++) {
			t.RB_Insert(a[i], a[i]);
		}

		t.Inorder_Tree_Walk(t.getRoot());

以上代码只是简单做了个测试,如果存在其他问题,请随时指教,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值