红黑树原理详解及代码实现

1. 红黑树概念

在这里插入图片描述
下图就是一棵红黑树:
在这里插入图片描述

为了后续操作中不出现空指针异常,可以加入一个额外的哨兵节点T,T作为所有叶子节点的子节点,作为根节点的父节点,节点T要求是黑色,其他属性不做要求,如下图所示:
在这里插入图片描述

2. 节点定义

class Node {
	int key;
	Node left, right;
	Node parent;
	boolean color;

	public Node(boolean color) {
		super();
		this.color = color;
	}

	public Node(int key) {
		super();
		this.key = key;
	}

	@Override
	public String toString() {
		return "Node [key=" + key + ", color=" + color + " p=" + parent.key + "]";
	}

}

3. 旋转操作

在这里插入图片描述

左旋操作代码:

public void rotateLeft(Node x) {
		Node y = x.right;
		x.right = y.left;// y的左子树作为x的右子树
		if (y.left != nil) {// y存在左子树
			y.left.parent = x;// 该左子树的父节点变为x
		}
		y.parent = x.parent;// x之前的父节点现在成为y的父节点
		if (x.parent == nil) {// x是根节点
			root = y;// y替代x成为根节点
		} else if (x == x.parent.left) {// x是左子节点
			x.parent.left = y;// y替代x作为左子节点
		} else {// x是右子节点
			x.parent.right = y;// y替代x作为右子节点
		}
		y.left = x;// x作为y的左子节点
		x.parent = y;// y作为x的父节点
	}

右旋操作代码:

public void rotateRight(Node y) {
		Node x = y.left;
		y.left = x.right;
		if (x.right != null) {// x存在右子树
			x.right.parent = y;// x的右子树的父节点变为y
		}
		x.parent = y.parent;// y之前的父节点现在变为x的父节点
		if (y == nil) {// y是根节点
			root = x;// x替代y成为根节点
		} else if (y == y.parent.left) {// y是左子节点
			y.parent.left = x;// x代替y作为其父节点的左子节点
		} else {// y是右子节点
			y.parent.right = x;// x代替y作为其父节点的右子节点
		}
		x.right = y;// y变成x的右子节点
		y.parent = x;// x变成y的父节点
	}

在这里插入图片描述

4. 插入操作

public void insert(Node z) {
		Node y = nil;// y指向哨兵节点
		Node x = root;// x指向根节点
		while (x != nil) {
			y = x;// y保存更新前的x
			if (z.key < x.key) {// z的key较小 然后去x左子树中查找
				x = x.left;
			} else {// z的key较大 然后去x右子树中查找
				x = x.right;
			}
		}
		z.parent = y;// y作为z的父节点
		if (y == nil) {// 说明目前树为空 插入的是第一个节点
			root = z;// z成为根节点
		} else if (z.key < y.key) {
			y.left = z;// z的值比父节点值小 作为左子节点
		} else {
			y.right = z;// z的值比父节点值大 作为右子节点
		}
		z.left = nil;
		z.right = nil;// z是叶子节点
		z.color = RED;// 叶子节点红色
		insetFix(z);// 插入之后需要调整树的结构
	}

插入操作完成了还需要进行树的结构调整,先来看下面几种情况:

情况1: 新插入节点z的叔节点y是红色的
在这里插入图片描述

上图中的(a)或(b)违反了性质:一个红节点的两个子节点都是黑色的
解决方案:z的父节点和树节点颜色由红变黑,z的爷爷节点颜色由黑变红

				Node y=z.parent.parent.right;
				if(y.color==RED) {
					z.parent.color=BLACK;
					y.color=BLACK;
					z.parent.parent.color=RED;
					z=z.parent.parent;//z指向爷爷节点继续处理树的结构
				}

情况2: 新插入节点z的叔节点y是黑色的并且z是一个右孩子节点
情况3: 新插入节点z的叔节点y是黑色的并且z是一个左孩子节点
情况2和情况3可以放在一张图中来讨论,因为情况2可以通过左旋操作进入情况3,入下图所示:
在这里插入图片描述

			else if(z==z.parent.right) {//情况2
					z=z.parent;
					rotateLeft(z);
					z.parent.color=BLACK;
					z.parent.parent.color=RED;
					rotateRight(z.parent.parent);
				}else if(z==z.parent.right) {//情况3
					z.parent.color=BLACK;
					z.parent.parent.color=RED;
					rotateRight(z.parent.parent);
				}

上面的三种情况都是基于新插入节点z的父节点是一个左子节点的情形,对于z的父节点是一个右子节点的情况也有三种,与上面三种情况对称,关于调整的完整代码如下:

public void insetFix(Node z) {

		while (z.parent.color == RED) {
			if (z.parent == z.parent.parent.left) {// z的父节点是左子节点
				Node y = z.parent.parent.right;
				if (y != nil && y.color == RED) {// 情况1
					z.parent.color = BLACK;
					y.color = BLACK;
					z.parent.parent.color = RED;
					z = z.parent.parent;
					continue;
				} else if (z == z.parent.right) {// 情况2
					z = z.parent;
					rotateLeft(z);
				}
				// 情况3
				z.parent.color = BLACK;
				z.parent.parent.color = RED;
				rotateRight(z.parent.parent);

			} else if (z.parent == z.parent.parent.right) {// z的父节点是右子节点
				Node y = z.parent.parent.left;
				if (y != nil && y.color == RED) {
					z.parent.color = BLACK;
					y.color = BLACK;
					z.parent.parent.color = RED;
					z = z.parent.parent;
					continue;
				} else if (z == z.parent.left) {
					z = z.parent;
					rotateRight(z);
				}
				z.parent.color = BLACK;
				z.parent.parent.color = RED;
				rotateLeft(z.parent.parent);
			}
			root.color = BLACK;
		}
	}

5. 删除操作

红黑树的删除操作和BST的删除操作类似,如果待删除节点x没有左子节点,就用右子节点替代x;如果待删除节点x没有右子节点,就用左子节点替代x; 如果x既有左子节点也有右子节点,可以使用x右子树中的最小节点y来替代x, y之前的右子树来替换y, x的右子树作为y新的右子树,只不过在红黑树中需要根据节点的颜色进行树结构的调整

// 用节点v 替代 节点u
	public void transplant(Node u, Node v) {
		if (u.parent == T) {// u是根节点
			T.left = v;// v替代u作为根节点
		} else if (u == u.parent.left) {// u是左子节点
			u.parent.left = v;
		} else if (u == u.parent.right) {
			u.parent.right = v;
		}
		v.parent = u.parent;// u的父节点成为v的父节点
	}

	public void delete(Node z) {//z是待删除节点
		Node y = z;
		Node x = null;
		boolean originColor = y.color;
		if (z.left == T) {// z没有左子节点
			x = z.right;
			transplant(z, z.right);//z的右子节点替代z
		} else if (z.right == T) {//z没有右子节点
			x = z.left;
			transplant(z, z.left);//z的左子节点替代z
		}else {//z的左右子节点都存在
			y=minimum(z.right);//y保存z右子树中的最小节点 用y替换z
			originColor=y.color;
			x=y.right;
			if(y.parent==z) {//y是z的直接右子节点 不需要else语句中的y的右子树替换y
				x.parent=y;
			}else {
				transplant(y, y.right);//y的右子树替换y
				y.right=z.right;//待删除节点z的右子树成为y的右子树
				y.right.parent=y;//更新y的右子树的的父节点
			}
			transplant(z, y);//用y替换z
			y.left=z.left;//z的左子树作为y的左子树
			y.left.parent=y;
			y.color=z.color;//更新颜色
		}
		if(originColor==BLACK) {//待删除节点的颜色是黑色 需要调整树的结构
			deleteFix(x);
		}
	}
	/*
	 * 寻找某个子树中的最小节点
	 */
	Node minimum(Node subTreeRoot) {
		while (subTreeRoot.left != T) {
			subTreeRoot = subTreeRoot.left;
		}
		return subTreeRoot;
	}

删除-调整操作:

情形1:待调整节点x的兄弟节点w是红色
在这里插入图片描述

					w.color=BLACK;
					x.parent.color=RED;
					rotateLeft(x.parent);
					w=x.parent.right;

情形2:待调整节点x的兄弟节点w是黑色的,并且w的两个子节点也是黑色的
在这里插入图片描述

					w.color=RED;
					x=x.parent;
					continue;

情形3:待调整节点x的兄弟节点w是黑色的,w的左子节点为红色,右子节点为黑色
在这里插入图片描述

					w.left.color=BLACK;
					w.color=RED;
					rotateRight(w);
					w=x.parent.right;

情形4:待调整节点x的兄弟节点w是黑色的,w的右子节点为红色,左子节点不考虑
在这里插入图片描述

				w.color=x.parent.color;//case4
				x.parent.color=BLACK;
				w.right.color=BLACK;
				rotateLeft(x.parent);
				x=T.left;

可以发现情况1经过转化可以变成情况2,3,4中的一种,情况3经过转化后就变成了情况4

上面的4种情况是基于x是左子节点的情况,对于x是右子节点的情况,只一将上面代码中的left和right互换即可

刪除-调整代码如下:

public void deleteFix(Node x) {
		Node w = null;
		while (x != root && x.color == BLACK) {
			if (x == x.parent.left) {
				w = x.parent.right;
				if (w.color == RED) {// case1
					w.color = BLACK;
					x.parent.color = RED;
					rotateLeft(x.parent);
					w = x.parent.right;
				}
				if (w.left.color == BLACK && w.right.color == BLACK) {// case2
					w.color = RED;
					x = x.parent;
					continue;
				} else if (w.right.color == BLACK) {// case3
					w.left.color = BLACK;
					w.color = RED;
					rotateRight(w);
					w = x.parent.right;
				}
				w.color = x.parent.color;// case4
				x.parent.color = BLACK;
				w.right.color = BLACK;
				rotateLeft(x.parent);
				x = root;
			} else {
				w = x.parent.left;
				if (w.color == RED) {// case1
					w.color = BLACK;
					x.parent.color = RED;
					rotateRight(w);
					w = x.parent.left;
				}
				if (w.left.color == BLACK && w.right.color == BLACK) {// case2
					w.color = RED;
					x = x.parent;
					continue;
				} else if (w.right.color == BLACK) {// case3
					w.right.color = BLACK;
					w.color = RED;
					rotateLeft(w);
					w = x.parent.left;
				}
				w.color = x.parent.color;// case4
				x.parent.color = BLACK;
				w.left.color = BLACK;
				rotateRight(x.parent);
				x = root;
			}
		}
		x.color = BLACK;
	}

6. 完整代码

package datastructure.tree;

import java.util.LinkedList;
import java.util.Queue;

class Node {
	int key;
	Node left, right;
	Node parent;
	boolean color;

	public Node(boolean color) {
		super();
		this.color = color;
	}

	public Node(int key) {
		super();
		this.key = key;
	}

	@Override
	public String toString() {
		return "Node [key=" + key + ", color=" + color + " p=" + parent.key + "]";
	}

}

public class RedBlackTree {
	private static final boolean RED = true;
	private static final boolean BLACK = false;
	private Node nil;
	private Node root;

	/*
	 * 初始化操作中创建一个哨兵节点nil root开始等于nil
	 */
	public RedBlackTree() {
		nil = new Node(BLACK);
		nil.key = -1;
		root = nil;

	}

	public void rotateLeft(Node x) {
		Node y = x.right;
		x.right = y.left;// y的左子树作为x的右子树
		if (y.left != nil) {// y存在左子树
			y.left.parent = x;// 该左子树的父节点变为x
		}
		y.parent = x.parent;// x之前的父节点现在成为y的父节点
		if (x.parent == nil) {// x是根节点
			root = y;// y替代x成为根节点
		} else if (x == x.parent.left) {// x是左子节点
			x.parent.left = y;// y替代x作为左子节点
		} else {// x是右子节点
			x.parent.right = y;// y替代x作为右子节点
		}
		y.left = x;// x作为y的左子节点
		x.parent = y;// y作为x的父节点
	}

	public void rotateRight(Node y) {
		Node x = y.left;
		y.left = x.right;
		if (x.right != null) {// x存在右子树
			x.right.parent = y;// x的右子树的父节点变为y
		}
		x.parent = y.parent;// y之前的父节点现在变为x的父节点
		if (y == nil) {// y是根节点
			root = x;// x替代y成为根节点
		} else if (y == y.parent.left) {// y是左子节点
			y.parent.left = x;// x代替y作为其父节点的左子节点
		} else {// y是右子节点
			y.parent.right = x;// x代替y作为其父节点的右子节点
		}
		x.right = y;// y变成x的右子节点
		y.parent = x;// x变成y的父节点
	}

	public void insert(Node z) {
		Node y = nil;// y指向哨兵节点
		Node x = root;// x指向根节点
		while (x != nil) {
			y = x;// y保存更新前的x
			if (z.key < x.key) {// z的key较小 然后去x左子树中查找
				x = x.left;
			} else {// z的key较大 然后去x右子树中查找
				x = x.right;
			}
		}
		z.parent = y;// y作为z的父节点
		if (y == nil) {// 说明目前树为空 插入的是第一个节点
			root = z;// z成为根节点
		} else if (z.key < y.key) {
			y.left = z;// z的值比父节点值小 作为左子节点
		} else {
			y.right = z;// z的值比父节点值大 作为右子节点
		}
		z.left = nil;
		z.right = nil;// z是叶子节点
		z.color = RED;// 叶子节点红色
		insetFix(z);// 插入之后需要调整树的结构
	}

	public void insetFix(Node z) {

		while (z.parent.color == RED) {
			if (z.parent == z.parent.parent.left) {// z的父节点是左子节点
				Node y = z.parent.parent.right;
				if (y != nil && y.color == RED) {// 情况1
					z.parent.color = BLACK;
					y.color = BLACK;
					z.parent.parent.color = RED;
					z = z.parent.parent;
					continue;
				} else if (z == z.parent.right) {// 情况2
					z = z.parent;
					rotateLeft(z);
				}
				//情况2经过调整之后可以变成情况3
				// 情况3
				z.parent.color = BLACK;
				z.parent.parent.color = RED;
				rotateRight(z.parent.parent);

			} else if (z.parent == z.parent.parent.right) {// z的父节点是右子节点
				Node y = z.parent.parent.left;
				if (y != nil && y.color == RED) {
					z.parent.color = BLACK;
					y.color = BLACK;
					z.parent.parent.color = RED;
					z = z.parent.parent;
					continue;
				} else if (z == z.parent.left) {
					z = z.parent;
					rotateRight(z);
				}
				z.parent.color = BLACK;
				z.parent.parent.color = RED;
				rotateLeft(z.parent.parent);
			}
			root.color = BLACK;
		}
	}

	// 用节点v 替代 节点u
	public void transplant(Node u, Node v) {
		if (u.parent == nil) {// u是根节点
			root = v;// v替代u作为根节点
		} else if (u == u.parent.left) {// u是左子节点
			u.parent.left = v;
		} else if (u == u.parent.right) {
			u.parent.right = v;
		}
		v.parent = u.parent;// u的父节点成为v的父节点
	}

	public void delete(Node z) {
		Node y = z;
		Node x = null;
		boolean originColor = y.color;
		if (z.left == nil) {// z没有左子节点
			x = z.right;
			transplant(z, z.right);// z的右子节点替代z
		} else if (z.right == nil) {// z没有右子节点
			x = z.left;
			transplant(z, z.left);// z的左子节点替代z
		} else {// z的左右子节点都存在
			y = minimum(z.right);// y保存z右子树中的最小节点 用y替换z
			originColor = y.color;
			x = y.right;
			if (y.parent == z) {// y是z的直接右子节点 不需要else语句中的y的右子树替换y
				x.parent = y;
			} else {
				transplant(y, y.right);// y的右子树替换y
				y.right = z.right;// 待删除节点z的右子树成为y的右子树
				y.right.parent = y;// 更新y的右子树的的父节点
			}
			transplant(z, y);// 用y替换z
			y.left = z.left;
			y.left.parent = y;
			y.color = z.color;
		}
		if (originColor == BLACK) {// 节点y的颜色是黑色 需要调整树的结构
			deleteFix(x);
		}
	}

	/*
	 * 寻找某个子树中的最小节点
	 */
	Node minimum(Node subTreeRoot) {
		while (subTreeRoot.left != nil) {
			subTreeRoot = subTreeRoot.left;
		}
		return subTreeRoot;
	}

	public void deleteFix(Node x) {
		Node w = null;
		while (x != root && x.color == BLACK) {
			if (x == x.parent.left) {
				w = x.parent.right;
				if (w.color == RED) {// case1
					w.color = BLACK;
					x.parent.color = RED;
					rotateLeft(x.parent);
					w = x.parent.right;
				}
				if (w.left.color == BLACK && w.right.color == BLACK) {// case2
					w.color = RED;
					x = x.parent;
					continue;
				} else if (w.right.color == BLACK) {// case3
					w.left.color = BLACK;
					w.color = RED;
					rotateRight(w);
					w = x.parent.right;
				}
				//case3 经过调整之后可以变成case4
				w.color = x.parent.color;// case4
				x.parent.color = BLACK;
				w.right.color = BLACK;
				rotateLeft(x.parent);
				x = root;
			} else {
				w = x.parent.left;
				if (w.color == RED) {// case1
					w.color = BLACK;
					x.parent.color = RED;
					rotateRight(w);
					w = x.parent.left;
				}
				if (w.left.color == BLACK && w.right.color == BLACK) {// case2
					w.color = RED;
					x = x.parent;
					continue;
				} else if (w.right.color == BLACK) {// case3
					w.right.color = BLACK;
					w.color = RED;
					rotateLeft(w);
					w = x.parent.left;
				}
				w.color = x.parent.color;// case4
				x.parent.color = BLACK;
				w.left.color = BLACK;
				rotateRight(x.parent);
				x = root;
			}
		}
		x.color = BLACK;
	}
	/*
	 * 层次遍历
	 */
	public void printTreeByLevel(Node root) {
		Queue<Node> q = new LinkedList<>();
		q.offer(root);
		while (!q.isEmpty()) {
			int sz = q.size();
			Node node = null;
			for (int i = 0; i < sz; i++) {
				node = q.poll();
				System.out.print(node + " ");
				if (node.left != nil)
					q.offer(node.left);
				if (node.right != nil)
					q.offer(node.right);
			}
			System.out.println();
		}

	}

	/*
	 * 根据key查找某个节点
	 */
	public Node find(Node root, int key) {
		if (root == nil) {
			return null;
		}
		if (root.key < key) {
			return find(root.right, key);
		} else if (root.key > key) {
			return find(root.left, key);
		} else if (key == root.key) {
			return root;
		}
		return null;
	}

	/*
	 * 中序遍历
	 */
	public void printInorder(Node root) {
		if (root == nil)
			return;
		printInorder(root.left);
		System.out.println(root);
		printInorder(root.right);
	}

	public static void main(String[] args) {
		RedBlackTree tree = new RedBlackTree();
		System.out.println("插入数据:");
		for (int i = 1; i <= 10; i++) {
			tree.insert(new Node(i));
		}
		System.out.println("层次遍历:");
		tree.printTreeByLevel(tree.root);
		System.out.println("中序遍历:");
		tree.printInorder(tree.root);
		System.out.println("删除数据:");
		int[] delete = { 1, 2,3, 4,5, 6,7,8, 9,10 };
		for (int num : delete) {
			Node delNode = tree.find(tree.root, num);
			System.out.println("删除了元素" + delNode);
			tree.delete(delNode);

			System.out.println("中序遍历:");
			tree.printInorder(tree.root);
		}
	}
}

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodePanda@GPF

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值