882 lines
23 KiB
Java
882 lines
23 KiB
Java
package com.ruoyi.system.ControllerUtil;
|
||
|
||
import java.util.*;
|
||
|
||
/**
|
||
* 红黑树算法实现
|
||
*
|
||
* 红黑树是一种自平衡二叉搜索树,具有以下特性:
|
||
* 1. 每个节点要么是红色,要么是黑色
|
||
* 2. 根节点是黑色
|
||
* 3. 所有叶子节点(NIL)都是黑色
|
||
* 4. 如果一个节点是红色,则它的两个子节点都是黑色
|
||
* 5. 从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点
|
||
*
|
||
* 时间复杂度:
|
||
* - 查找:O(log n)
|
||
* - 插入:O(log n)
|
||
* - 删除:O(log n)
|
||
*
|
||
* 应用场景:
|
||
* - Java TreeMap/TreeSet 底层实现
|
||
* - 数据库索引
|
||
* - 内存管理
|
||
* - 任务调度
|
||
*
|
||
* @author Mr. Zhang Pan
|
||
* @date 2025-01-03
|
||
* @version 1.0
|
||
* @param <T> 数据类型,必须实现Comparable接口
|
||
*/
|
||
public class RedBlackTree<T extends Comparable<T>> {
|
||
|
||
// ============================== 节点颜色常量 ==============================
|
||
|
||
/** 红色节点 */
|
||
private static final boolean RED = true;
|
||
|
||
/** 黑色节点 */
|
||
private static final boolean BLACK = false;
|
||
|
||
// ============================== 内部节点类 ==============================
|
||
|
||
/**
|
||
* 红黑树节点
|
||
*/
|
||
private class Node {
|
||
/** 节点数据 */
|
||
T data;
|
||
|
||
/** 左子节点 */
|
||
Node left;
|
||
|
||
/** 右子节点 */
|
||
Node right;
|
||
|
||
/** 父节点 */
|
||
Node parent;
|
||
|
||
/** 节点颜色 */
|
||
boolean color;
|
||
|
||
/**
|
||
* 构造函数
|
||
*
|
||
* @param data 节点数据
|
||
* @param color 节点颜色
|
||
*/
|
||
Node(T data, boolean color) {
|
||
this.data = data;
|
||
this.color = color;
|
||
this.left = null;
|
||
this.right = null;
|
||
this.parent = null;
|
||
}
|
||
|
||
/**
|
||
* 构造函数 - 默认红色
|
||
*
|
||
* @param data 节点数据
|
||
*/
|
||
Node(T data) {
|
||
this(data, RED);
|
||
}
|
||
|
||
/**
|
||
* 判断是否为红色节点
|
||
*
|
||
* @return true=红色,false=黑色
|
||
*/
|
||
boolean isRed() {
|
||
return color == RED;
|
||
}
|
||
|
||
/**
|
||
* 判断是否为黑色节点
|
||
*
|
||
* @return true=黑色,false=红色
|
||
*/
|
||
boolean isBlack() {
|
||
return color == BLACK;
|
||
}
|
||
|
||
/**
|
||
* 设置为红色
|
||
*/
|
||
void setRed() {
|
||
color = RED;
|
||
}
|
||
|
||
/**
|
||
* 设置为黑色
|
||
*/
|
||
void setBlack() {
|
||
color = BLACK;
|
||
}
|
||
|
||
@Override
|
||
public String toString() {
|
||
return String.format("%s(%s)", data, color == RED ? "R" : "B");
|
||
}
|
||
}
|
||
|
||
// ============================== 实例变量 ==============================
|
||
|
||
/** 根节点 */
|
||
private Node root;
|
||
|
||
/** 树的大小 */
|
||
private int size;
|
||
|
||
// ============================== 构造方法 ==============================
|
||
|
||
/**
|
||
* 默认构造函数
|
||
*/
|
||
public RedBlackTree() {
|
||
root = null;
|
||
size = 0;
|
||
}
|
||
|
||
/**
|
||
* 构造函数 - 从数组创建
|
||
*
|
||
* @param array 数据数组
|
||
*/
|
||
public RedBlackTree(T[] array) {
|
||
this();
|
||
if (array != null) {
|
||
for (T item : array) {
|
||
if (item != null) {
|
||
insert(item);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 构造函数 - 从集合创建
|
||
*
|
||
* @param collection 数据集合
|
||
*/
|
||
public RedBlackTree(Collection<T> collection) {
|
||
this();
|
||
if (collection != null) {
|
||
for (T item : collection) {
|
||
if (item != null) {
|
||
insert(item);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ============================== 基本操作 ==============================
|
||
|
||
/**
|
||
* 获取树的大小
|
||
*
|
||
* @return 节点数量
|
||
*/
|
||
public int size() {
|
||
return size;
|
||
}
|
||
|
||
/**
|
||
* 判断树是否为空
|
||
*
|
||
* @return true=空树,false=非空
|
||
*/
|
||
public boolean isEmpty() {
|
||
return size == 0;
|
||
}
|
||
|
||
/**
|
||
* 清空树
|
||
*/
|
||
public void clear() {
|
||
root = null;
|
||
size = 0;
|
||
}
|
||
|
||
// ============================== 查找操作 ==============================
|
||
|
||
/**
|
||
* 查找指定数据
|
||
*
|
||
* @param data 要查找的数据
|
||
* @return true=存在,false=不存在
|
||
*/
|
||
public boolean contains(T data) {
|
||
return findNode(data) != null;
|
||
}
|
||
|
||
/**
|
||
* 查找节点
|
||
*
|
||
* @param data 要查找的数据
|
||
* @return 找到的节点,null表示不存在
|
||
*/
|
||
private Node findNode(T data) {
|
||
if (data == null) return null;
|
||
|
||
Node current = root;
|
||
while (current != null) {
|
||
int cmp = data.compareTo(current.data);
|
||
if (cmp == 0) {
|
||
return current;
|
||
} else if (cmp < 0) {
|
||
current = current.left;
|
||
} else {
|
||
current = current.right;
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
/**
|
||
* 查找最小值
|
||
*
|
||
* @return 最小值,null表示空树
|
||
*/
|
||
public T findMin() {
|
||
Node minNode = findMinNode(root);
|
||
return minNode != null ? minNode.data : null;
|
||
}
|
||
|
||
/**
|
||
* 查找最大值
|
||
*
|
||
* @return 最大值,null表示空树
|
||
*/
|
||
public T findMax() {
|
||
Node maxNode = findMaxNode(root);
|
||
return maxNode != null ? maxNode.data : null;
|
||
}
|
||
|
||
/**
|
||
* 查找最小节点
|
||
*
|
||
* @param node 起始节点
|
||
* @return 最小节点
|
||
*/
|
||
private Node findMinNode(Node node) {
|
||
if (node == null) return null;
|
||
while (node.left != null) {
|
||
node = node.left;
|
||
}
|
||
return node;
|
||
}
|
||
|
||
/**
|
||
* 查找最大节点
|
||
*
|
||
* @param node 起始节点
|
||
* @return 最大节点
|
||
*/
|
||
private Node findMaxNode(Node node) {
|
||
if (node == null) return null;
|
||
while (node.right != null) {
|
||
node = node.right;
|
||
}
|
||
return node;
|
||
}
|
||
|
||
// ============================== 插入操作 ==============================
|
||
|
||
/**
|
||
* 插入数据
|
||
*
|
||
* @param data 要插入的数据
|
||
* @return true=插入成功,false=数据已存在
|
||
*/
|
||
public boolean insert(T data) {
|
||
if (data == null) return false;
|
||
|
||
// 空树情况
|
||
if (root == null) {
|
||
root = new Node(data, BLACK);
|
||
size++;
|
||
return true;
|
||
}
|
||
|
||
// 查找插入位置
|
||
Node parent = null;
|
||
Node current = root;
|
||
int cmp = 0;
|
||
|
||
while (current != null) {
|
||
parent = current;
|
||
cmp = data.compareTo(current.data);
|
||
if (cmp == 0) {
|
||
return false; // 数据已存在
|
||
} else if (cmp < 0) {
|
||
current = current.left;
|
||
} else {
|
||
current = current.right;
|
||
}
|
||
}
|
||
|
||
// 创建新节点
|
||
Node newNode = new Node(data, RED);
|
||
newNode.parent = parent;
|
||
|
||
// 插入新节点
|
||
if (cmp < 0) {
|
||
parent.left = newNode;
|
||
} else {
|
||
parent.right = newNode;
|
||
}
|
||
|
||
size++;
|
||
|
||
// 修复红黑树性质
|
||
insertFixup(newNode);
|
||
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 插入后修复红黑树性质
|
||
*
|
||
* @param node 新插入的节点
|
||
*/
|
||
private void insertFixup(Node node) {
|
||
while (node != root && node.parent.isRed()) {
|
||
if (node.parent == node.parent.parent.left) {
|
||
// 父节点是祖父节点的左子节点
|
||
Node uncle = node.parent.parent.right;
|
||
|
||
if (uncle != null && uncle.isRed()) {
|
||
// 情况1:叔叔节点是红色
|
||
node.parent.setBlack();
|
||
uncle.setBlack();
|
||
node.parent.parent.setRed();
|
||
node = node.parent.parent;
|
||
} else {
|
||
if (node == node.parent.right) {
|
||
// 情况2:叔叔节点是黑色,当前节点是右子节点
|
||
node = node.parent;
|
||
leftRotate(node);
|
||
}
|
||
// 情况3:叔叔节点是黑色,当前节点是左子节点
|
||
node.parent.setBlack();
|
||
node.parent.parent.setRed();
|
||
rightRotate(node.parent.parent);
|
||
}
|
||
} else {
|
||
// 父节点是祖父节点的右子节点(对称情况)
|
||
Node uncle = node.parent.parent.left;
|
||
|
||
if (uncle != null && uncle.isRed()) {
|
||
// 情况1:叔叔节点是红色
|
||
node.parent.setBlack();
|
||
uncle.setBlack();
|
||
node.parent.parent.setRed();
|
||
node = node.parent.parent;
|
||
} else {
|
||
if (node == node.parent.left) {
|
||
// 情况2:叔叔节点是黑色,当前节点是左子节点
|
||
node = node.parent;
|
||
rightRotate(node);
|
||
}
|
||
// 情况3:叔叔节点是黑色,当前节点是右子节点
|
||
node.parent.setBlack();
|
||
node.parent.parent.setRed();
|
||
leftRotate(node.parent.parent);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 根节点始终为黑色
|
||
root.setBlack();
|
||
}
|
||
|
||
// ============================== 删除操作 ==============================
|
||
|
||
/**
|
||
* 删除指定数据
|
||
*
|
||
* @param data 要删除的数据
|
||
* @return true=删除成功,false=数据不存在
|
||
*/
|
||
public boolean delete(T data) {
|
||
if (data == null) return false;
|
||
|
||
Node nodeToDelete = findNode(data);
|
||
if (nodeToDelete == null) {
|
||
return false;
|
||
}
|
||
|
||
deleteNode(nodeToDelete);
|
||
size--;
|
||
return true;
|
||
}
|
||
|
||
/**
|
||
* 删除节点
|
||
*
|
||
* @param node 要删除的节点
|
||
*/
|
||
private void deleteNode(Node node) {
|
||
Node y = node;
|
||
Node x;
|
||
boolean yOriginalColor = y.color;
|
||
|
||
if (node.left == null) {
|
||
x = node.right;
|
||
transplant(node, node.right);
|
||
} else if (node.right == null) {
|
||
x = node.left;
|
||
transplant(node, node.left);
|
||
} else {
|
||
y = findMinNode(node.right);
|
||
yOriginalColor = y.color;
|
||
x = y.right;
|
||
|
||
if (y.parent == node) {
|
||
if (x != null) x.parent = y;
|
||
} else {
|
||
transplant(y, y.right);
|
||
y.right = node.right;
|
||
if (y.right != null) y.right.parent = y;
|
||
}
|
||
|
||
transplant(node, y);
|
||
y.left = node.left;
|
||
if (y.left != null) y.left.parent = y;
|
||
y.color = node.color;
|
||
}
|
||
|
||
if (yOriginalColor == BLACK) {
|
||
deleteFixup(x);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 节点移植
|
||
*
|
||
* @param u 被替换的节点
|
||
* @param v 替换节点
|
||
*/
|
||
private void transplant(Node u, Node v) {
|
||
if (u.parent == null) {
|
||
root = v;
|
||
} else if (u == u.parent.left) {
|
||
u.parent.left = v;
|
||
} else {
|
||
u.parent.right = v;
|
||
}
|
||
|
||
if (v != null) {
|
||
v.parent = u.parent;
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 删除后修复红黑树性质
|
||
*
|
||
* @param node 替换节点
|
||
*/
|
||
private void deleteFixup(Node node) {
|
||
while (node != root && (node == null || node.isBlack())) {
|
||
if (node == getParent(node).left) {
|
||
Node sibling = getParent(node).right;
|
||
|
||
if (sibling != null && sibling.isRed()) {
|
||
sibling.setBlack();
|
||
getParent(node).setRed();
|
||
leftRotate(getParent(node));
|
||
sibling = getParent(node).right;
|
||
}
|
||
|
||
if ((sibling == null) ||
|
||
(getLeft(sibling) == null || getLeft(sibling).isBlack()) &&
|
||
(getRight(sibling) == null || getRight(sibling).isBlack())) {
|
||
if (sibling != null) sibling.setRed();
|
||
node = getParent(node);
|
||
} else {
|
||
if (getRight(sibling) == null || getRight(sibling).isBlack()) {
|
||
if (getLeft(sibling) != null) getLeft(sibling).setBlack();
|
||
if (sibling != null) sibling.setRed();
|
||
rightRotate(sibling);
|
||
sibling = getParent(node).right;
|
||
}
|
||
|
||
if (sibling != null) sibling.color = getParent(node).color;
|
||
getParent(node).setBlack();
|
||
if (getRight(sibling) != null) getRight(sibling).setBlack();
|
||
leftRotate(getParent(node));
|
||
node = root;
|
||
}
|
||
} else {
|
||
Node sibling = getParent(node).left;
|
||
|
||
if (sibling != null && sibling.isRed()) {
|
||
sibling.setBlack();
|
||
getParent(node).setRed();
|
||
rightRotate(getParent(node));
|
||
sibling = getParent(node).left;
|
||
}
|
||
|
||
if ((sibling == null) ||
|
||
(getRight(sibling) == null || getRight(sibling).isBlack()) &&
|
||
(getLeft(sibling) == null || getLeft(sibling).isBlack())) {
|
||
if (sibling != null) sibling.setRed();
|
||
node = getParent(node);
|
||
} else {
|
||
if (getLeft(sibling) == null || getLeft(sibling).isBlack()) {
|
||
if (getRight(sibling) != null) getRight(sibling).setBlack();
|
||
if (sibling != null) sibling.setRed();
|
||
leftRotate(sibling);
|
||
sibling = getParent(node).left;
|
||
}
|
||
|
||
if (sibling != null) sibling.color = getParent(node).color;
|
||
getParent(node).setBlack();
|
||
if (getLeft(sibling) != null) getLeft(sibling).setBlack();
|
||
rightRotate(getParent(node));
|
||
node = root;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (node != null) node.setBlack();
|
||
}
|
||
|
||
// ============================== 旋转操作 ==============================
|
||
|
||
/**
|
||
* 左旋转
|
||
*
|
||
* @param node 旋转节点
|
||
*/
|
||
private void leftRotate(Node node) {
|
||
Node rightChild = node.right;
|
||
node.right = rightChild.left;
|
||
|
||
if (rightChild.left != null) {
|
||
rightChild.left.parent = node;
|
||
}
|
||
|
||
rightChild.parent = node.parent;
|
||
|
||
if (node.parent == null) {
|
||
root = rightChild;
|
||
} else if (node == node.parent.left) {
|
||
node.parent.left = rightChild;
|
||
} else {
|
||
node.parent.right = rightChild;
|
||
}
|
||
|
||
rightChild.left = node;
|
||
node.parent = rightChild;
|
||
}
|
||
|
||
/**
|
||
* 右旋转
|
||
*
|
||
* @param node 旋转节点
|
||
*/
|
||
private void rightRotate(Node node) {
|
||
Node leftChild = node.left;
|
||
node.left = leftChild.right;
|
||
|
||
if (leftChild.right != null) {
|
||
leftChild.right.parent = node;
|
||
}
|
||
|
||
leftChild.parent = node.parent;
|
||
|
||
if (node.parent == null) {
|
||
root = leftChild;
|
||
} else if (node == node.parent.right) {
|
||
node.parent.right = leftChild;
|
||
} else {
|
||
node.parent.left = leftChild;
|
||
}
|
||
|
||
leftChild.right = node;
|
||
node.parent = leftChild;
|
||
}
|
||
|
||
// ============================== 辅助方法 ==============================
|
||
|
||
/**
|
||
* 安全获取父节点
|
||
*/
|
||
private Node getParent(Node node) {
|
||
return node != null ? node.parent : null;
|
||
}
|
||
|
||
/**
|
||
* 安全获取左子节点
|
||
*/
|
||
private Node getLeft(Node node) {
|
||
return node != null ? node.left : null;
|
||
}
|
||
|
||
/**
|
||
* 安全获取右子节点
|
||
*/
|
||
private Node getRight(Node node) {
|
||
return node != null ? node.right : null;
|
||
}
|
||
|
||
// ============================== 遍历操作 ==============================
|
||
|
||
/**
|
||
* 中序遍历
|
||
*
|
||
* @return 有序数据列表
|
||
*/
|
||
public List<T> inorderTraversal() {
|
||
List<T> result = new ArrayList<>();
|
||
inorderTraversal(root, result);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 中序遍历递归方法
|
||
*
|
||
* @param node 当前节点
|
||
* @param result 结果列表
|
||
*/
|
||
private void inorderTraversal(Node node, List<T> result) {
|
||
if (node != null) {
|
||
inorderTraversal(node.left, result);
|
||
result.add(node.data);
|
||
inorderTraversal(node.right, result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 前序遍历
|
||
*
|
||
* @return 数据列表
|
||
*/
|
||
public List<T> preorderTraversal() {
|
||
List<T> result = new ArrayList<>();
|
||
preorderTraversal(root, result);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 前序遍历递归方法
|
||
*
|
||
* @param node 当前节点
|
||
* @param result 结果列表
|
||
*/
|
||
private void preorderTraversal(Node node, List<T> result) {
|
||
if (node != null) {
|
||
result.add(node.data);
|
||
preorderTraversal(node.left, result);
|
||
preorderTraversal(node.right, result);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 后序遍历
|
||
*
|
||
* @return 数据列表
|
||
*/
|
||
public List<T> postorderTraversal() {
|
||
List<T> result = new ArrayList<>();
|
||
postorderTraversal(root, result);
|
||
return result;
|
||
}
|
||
|
||
/**
|
||
* 后序遍历递归方法
|
||
*
|
||
* @param node 当前节点
|
||
* @param result 结果列表
|
||
*/
|
||
private void postorderTraversal(Node node, List<T> result) {
|
||
if (node != null) {
|
||
postorderTraversal(node.left, result);
|
||
postorderTraversal(node.right, result);
|
||
result.add(node.data);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 层序遍历
|
||
*
|
||
* @return 数据列表
|
||
*/
|
||
public List<T> levelOrderTraversal() {
|
||
List<T> result = new ArrayList<>();
|
||
if (root == null) return result;
|
||
|
||
Queue<Node> queue = new LinkedList<>();
|
||
queue.offer(root);
|
||
|
||
while (!queue.isEmpty()) {
|
||
Node node = queue.poll();
|
||
result.add(node.data);
|
||
|
||
if (node.left != null) {
|
||
queue.offer(node.left);
|
||
}
|
||
if (node.right != null) {
|
||
queue.offer(node.right);
|
||
}
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
// ============================== 验证方法 ==============================
|
||
|
||
/**
|
||
* 验证红黑树性质
|
||
*
|
||
* @return true=符合红黑树性质,false=不符合
|
||
*/
|
||
public boolean isValidRedBlackTree() {
|
||
return isValidRedBlackTree(root) != -1;
|
||
}
|
||
|
||
/**
|
||
* 验证红黑树性质(递归)
|
||
*
|
||
* @param node 当前节点
|
||
* @return 黑色节点数量,-1表示不符合红黑树性质
|
||
*/
|
||
private int isValidRedBlackTree(Node node) {
|
||
if (node == null) return 1; // NIL节点为黑色
|
||
|
||
// 检查红色节点的子节点必须是黑色
|
||
if (node.isRed()) {
|
||
if ((node.left != null && node.left.isRed()) ||
|
||
(node.right != null && node.right.isRed())) {
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
// 递归检查左右子树
|
||
int leftBlackHeight = isValidRedBlackTree(node.left);
|
||
int rightBlackHeight = isValidRedBlackTree(node.right);
|
||
|
||
// 检查黑色高度是否相等
|
||
if (leftBlackHeight == -1 || rightBlackHeight == -1 ||
|
||
leftBlackHeight != rightBlackHeight) {
|
||
return -1;
|
||
}
|
||
|
||
// 返回黑色节点数量
|
||
return leftBlackHeight + (node.isBlack() ? 1 : 0);
|
||
}
|
||
|
||
/**
|
||
* 获取树的高度
|
||
*
|
||
* @return 树的高度
|
||
*/
|
||
public int getHeight() {
|
||
return getHeight(root);
|
||
}
|
||
|
||
/**
|
||
* 获取树的高度(递归)
|
||
*
|
||
* @param node 当前节点
|
||
* @return 高度
|
||
*/
|
||
private int getHeight(Node node) {
|
||
if (node == null) return 0;
|
||
return 1 + Math.max(getHeight(node.left), getHeight(node.right));
|
||
}
|
||
|
||
/**
|
||
* 获取黑色高度
|
||
*
|
||
* @return 黑色高度
|
||
*/
|
||
public int getBlackHeight() {
|
||
return getBlackHeight(root);
|
||
}
|
||
|
||
/**
|
||
* 获取黑色高度(递归)
|
||
*
|
||
* @param node 当前节点
|
||
* @return 黑色高度
|
||
*/
|
||
private int getBlackHeight(Node node) {
|
||
if (node == null) return 1;
|
||
int leftHeight = getBlackHeight(node.left);
|
||
return leftHeight + (node.isBlack() ? 1 : 0);
|
||
}
|
||
|
||
// ============================== 工具方法 ==============================
|
||
|
||
/**
|
||
* 转换为数组
|
||
*
|
||
* @return 有序数组
|
||
*/
|
||
@SuppressWarnings("unchecked")
|
||
public T[] toArray() {
|
||
List<T> list = inorderTraversal();
|
||
return (T[]) list.toArray();
|
||
}
|
||
|
||
/**
|
||
* 转换为列表
|
||
*
|
||
* @return 有序列表
|
||
*/
|
||
public List<T> toList() {
|
||
return inorderTraversal();
|
||
}
|
||
|
||
/**
|
||
* 打印树结构
|
||
*
|
||
* @return 树结构字符串
|
||
*/
|
||
public String printTree() {
|
||
if (root == null) return "Empty tree";
|
||
|
||
StringBuilder sb = new StringBuilder();
|
||
printTree(root, "", true, sb);
|
||
return sb.toString();
|
||
}
|
||
|
||
/**
|
||
* 打印树结构(递归)
|
||
*
|
||
* @param node 当前节点
|
||
* @param prefix 前缀
|
||
* @param isLast 是否为最后一个节点
|
||
* @param sb 字符串构建器
|
||
*/
|
||
private void printTree(Node node, String prefix, boolean isLast, StringBuilder sb) {
|
||
if (node != null) {
|
||
sb.append(prefix);
|
||
sb.append(isLast ? "└── " : "├── ");
|
||
sb.append(node.toString()).append("\n");
|
||
|
||
List<Node> children = new ArrayList<>();
|
||
if (node.left != null) children.add(node.left);
|
||
if (node.right != null) children.add(node.right);
|
||
|
||
for (int i = 0; i < children.size(); i++) {
|
||
boolean last = (i == children.size() - 1);
|
||
String newPrefix = prefix + (isLast ? " " : "│ ");
|
||
if (children.get(i) == node.left) {
|
||
printTree(node.left, newPrefix, node.right == null, sb);
|
||
} else {
|
||
printTree(node.right, newPrefix, true, sb);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@Override
|
||
public String toString() {
|
||
return String.format("RedBlackTree[size=%d, height=%d, valid=%s]",
|
||
size, getHeight(), isValidRedBlackTree());
|
||
}
|
||
} |