(恋上数据结构笔记):红黑树

目录

红黑树(Red Black Tree)

红黑树的等价变换

红黑树 VS 2-3-4树

几个重要的名词

一些辅助函数

RBTree.java 代码实现

染色

染成红色或黑色

判断颜色

判断为黑色或红色

BinaryTree.java代码实现

判断是左子节点或右子节点

判断是否为兄弟节点

添加

添加的所有情况

LL\RR情况

LR\RL情况

上溢 LL

上溢 RR

上溢 LR

上溢 RL

总结:12种情况

addAfter代码实现

测试

删除

删除 RED 节点

删除 BLACK 节点

删除 拥有一个 RED 节点的 BLACK 节点

删除 - BLACK 叶子节点 - sibling 为BLAC

删除 - BLACK 叶子节点 - sibling 为RED

removeAfter 代码实现

去掉replacement参数

红黑树的平衡

红黑树的平均时间复杂度

AVL树 VS 红黑树

BST VS AVL Tree VS RBTree

红黑树(Red Black Tree)

  • 下面这棵树是红黑树么?

  • 不是。不满足性质5,如节点38的叶子节点。

红黑树的等价变换

红黑树 VS 2-3-4树

几个重要的名词

一些辅助函数

RBTree.java 代码实现

染色

染成红色或黑色

判断颜色

判断为黑色或红色

BinaryTree.java代码实现

判断是左子节点或右子节点

判断是否为兄弟节点

添加

  • 由红黑树立即联想到对应的B树

添加的所有情况

LL\RR情况

LR\RL情况

上溢 LL

上溢 RR

上溢 LR

上溢 RL

总结:12种情况

  • 四种情况:父节点是黑色,不用处理。
  • 八种情况:父节点是红色
    • 四种情况:叔节点不是红色,通过旋转解决。LL\RR,父节点染黑作为根节点,祖父节点染成红色,LR\RL新增加的节点染成黑色,祖父节点染成红色。
    • 四种情况:叔节点是红色。祖节点染红,当做新节点向上合并,父节点,叔节点染黑。

addAfter代码实现

protected void afterAdd(Node<E> node) {
	Node<E> parent = node.parent;
	
	// 添加的是根节点 或者 上溢到达了根节点
	if (parent == null) {
		black(node);
		return;
	}
	
	// 如果父节点是黑色,直接返回
	if (isBlack(parent)) return;
	
	// 叔父节点
	Node<E> uncle = parent.sibling();
	// 祖父节点
	Node<E> grand = red(parent.parent);
	if (isRed(uncle)) { // 叔父节点是红色【B树节点上溢】
		black(parent);
		black(uncle);
		// 把祖父节点当做是新添加的节点
		afterAdd(grand);
		return;
	}
	
	// 叔父节点不是红色
	if (parent.isLeftChild()) { // L
		if (node.isLeftChild()) { // LL
			black(parent);
		} else { // LR
			black(node);
			rotateLeft(parent);
		}
		rotateRight(grand);
	} else { // R
		if (node.isLeftChild()) { // RL
			black(node);
			rotateRight(parent);
		} else { // RR
			black(parent);
		}
		rotateLeft(grand);
		}
}

测试

  • 重写RBtree 的 toString

  • 重写RBtree 的 creatorNode

  • 每次添加完进行打印

删除

删除 RED 节点

删除 BLACK 节点

删除 拥有一个 RED 节点的 BLACK 节点

删除 - BLACK 叶子节点 - sibling 为BLACK

  • 能借的情况:兄弟必须是黑色节点,且黑色兄弟节点有红色子节点

  • 总结:
    • 如果删除节点的兄弟节点是黑色,能借就通过旋转借。
    • 不能借,就拉父节点下来合并
      • 如果父节点是红色,则不会下溢
      • 如果父节点是黑色(已经有两个黑色子节点,不可能再有红色子节点了),必然下溢。把黑色父节点当做删除的黑色节点继续递归调用删除。

删除 - BLACK 叶子节点 - sibling 为RED

removeAfter 代码实现

  • 判断删除的节点是左还是右

  • 兄弟节点是红色,更换兄弟

  • 兄弟节点是黑色

  • 测试

protected void afterRemove(Node<E> node, Node<E> replacement) {
	// 如果删除的节点是红色
	if (isRed(node)) return;
	
	// 用以取代node的子节点是红色
	if (isRed(replacement)) {
		black(replacement);
		return;
	}
	
	Node<E> parent = node.parent;
	// 删除的是根节点
	if (parent == null) return;
	
	// 删除的是黑色叶子节点【下溢】
	// 判断被删除的node是左还是右
	boolean left = parent.left == null || node.isLeftChild();
	Node<E> sibling = left ? parent.right : parent.left;
	if (left) { // 被删除的节点在左边,兄弟节点在右边
		if (isRed(sibling)) { // 兄弟节点是红色
			black(sibling);
			red(parent);
			rotateLeft(parent);
			// 更换兄弟
			sibling = parent.right;
		}
		
		// 兄弟节点必然是黑色
		if (isBlack(sibling.left) && isBlack(sibling.right)) {
			// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
			boolean parentBlack = isBlack(parent);
			black(parent);
			red(sibling);
			if (parentBlack) {
				afterRemove(parent, null);
			}
		} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
			// 兄弟节点的左边是黑色,兄弟要先旋转
			if (isBlack(sibling.right)) {
				rotateRight(sibling);
				sibling = parent.right;
			}
			
			color(sibling, colorOf(parent));
			black(sibling.right);
			black(parent);
			rotateLeft(parent);
		}
	} else { // 被删除的节点在右边,兄弟节点在左边
		if (isRed(sibling)) { // 兄弟节点是红色
			black(sibling);
			red(parent);
			rotateRight(parent);
			// 更换兄弟
			sibling = parent.left;
		}
	
		// 兄弟节点必然是黑色
		if (isBlack(sibling.left) && isBlack(sibling.right)) {
			// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
			boolean parentBlack = isBlack(parent);
			black(parent);
			red(sibling);
			if (parentBlack) {
				afterRemove(parent, null);
			}
		} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
			// 兄弟节点的左边是黑色,兄弟要先旋转
			if (isBlack(sibling.left)) {
				rotateLeft(sibling);
				sibling = parent.left;
			}
			
			color(sibling, colorOf(parent));
			black(sibling.left);
			black(parent);
			rotateRight(parent);
		}
	}
}

去掉replacement参数

  • 在BST.java中,删除replacement参数

private void remove(Node<E> node) {
	if (node == null) return;
	
	size--;
	
	if (node.hasTwoChildren()) { // 度为2的节点
		// 找到后继节点
		Node<E> s = successor(node);
		// 用后继节点的值覆盖度为2的节点的值
		node.element = s.element;
		// 删除后继节点
		node = s;
	}
	
	// 删除node节点(node的度必然是1或者0)
	Node<E> replacement = node.left != null ? node.left : node.right;
	
	if (replacement != null) { // node是度为1的节点
		// 更改parent
		replacement.parent = node.parent;
		// 更改parent的left、right的指向
		if (node.parent == null) { // node是度为1的节点并且是根节点
			root = replacement;
		} else if (node == node.parent.left) {
			node.parent.left = replacement;
		} else { // node == node.parent.right
			node.parent.right = replacement;
		}
		
		// 删除节点之后的处理
		afterRemove(replacement);
	} else if (node.parent == null) { // node是叶子节点并且是根节点
		root = null;
		
		// 删除节点之后的处理
		afterRemove(node);
	} else { // node是叶子节点,但不是根节点
		if (node == node.parent.left) {
			node.parent.left = null;
		} else { // node == node.parent.right
			node.parent.right = null;
		}
		
		// 删除节点之后的处理
		afterRemove(node);
	}
}
  • RBTree.java

protected void afterRemove(Node<E> node) {
	// 如果删除的节点是红色
	// 或者 用以取代删除节点的子节点是红色
	if (isRed(node)) {
		black(node);
		return;
	}
	
	Node<E> parent = node.parent;
	// 删除的是根节点
	if (parent == null) return;
	
	// 删除的是黑色叶子节点【下溢】
	// 判断被删除的node是左还是右
	boolean left = parent.left == null || node.isLeftChild();
	Node<E> sibling = left ? parent.right : parent.left;
	if (left) { // 被删除的节点在左边,兄弟节点在右边
		if (isRed(sibling)) { // 兄弟节点是红色
			black(sibling);
			red(parent);
			rotateLeft(parent);
			// 更换兄弟
			sibling = parent.right;
		}
		
		// 兄弟节点必然是黑色
		if (isBlack(sibling.left) && isBlack(sibling.right)) {
			// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
			boolean parentBlack = isBlack(parent);
			black(parent);
			red(sibling);
			if (parentBlack) {
				afterRemove(parent);
			}
		} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
			// 兄弟节点的左边是黑色,兄弟要先旋转
			if (isBlack(sibling.right)) {
				rotateRight(sibling);
				sibling = parent.right;
			}
			
			color(sibling, colorOf(parent));
			black(sibling.right);
			black(parent);
			rotateLeft(parent);
		}
	} else { // 被删除的节点在右边,兄弟节点在左边
		if (isRed(sibling)) { // 兄弟节点是红色
			black(sibling);
			red(parent);
			rotateRight(parent);
			// 更换兄弟
			sibling = parent.left;
		}
		
		// 兄弟节点必然是黑色
		if (isBlack(sibling.left) && isBlack(sibling.right)) {
			// 兄弟节点没有1个红色子节点,父节点要向下跟兄弟节点合并
			boolean parentBlack = isBlack(parent);
			black(parent);
			red(sibling);
			if (parentBlack) {
				afterRemove(parent);
			}
		} else { // 兄弟节点至少有1个红色子节点,向兄弟节点借元素
			// 兄弟节点的左边是黑色,兄弟要先旋转
			if (isBlack(sibling.left)) {
				rotateLeft(sibling);
				sibling = parent.left;
			}
			
			color(sibling, colorOf(parent));
			black(sibling.left);
			black(parent);
			rotateRight(parent);
		}
	}
}

红黑树的平衡

红黑树的平均时间复杂度

AVL树 VS 红黑树

BST VS AVL Tree VS RBTree

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值