二叉搜索树&平衡二叉树
1 二叉树-简介
二叉搜索树(Binary Search Tree,简称BST)是一种特殊的二叉树,它的左子树上所有节点的值都小于它的根节点的值,而右子树上所有节点的值都大于它的根节点的值。这种特性使得二叉搜索树在搜索、插入和删除操作中有着很高的效率。
说明:左右子树有且只有一方的取值范围可以等于根节点,例如【左子树上所有节点的值都小于等于它的根节点的值】
特性:
- 左子树:如果左子树不空,则左子树上所有节点的值均小于它的根节点的值。
- 右子树:如果右子树不空,则右子树上所有节点的值均大于它的根节点的值。
- 中序遍历:对二叉搜索树进行中序遍历(左-根-右),得到的结果是一个有序序列。
2 二叉搜索树操作
2.1 搜索
搜索步骤
- 从根节点开始:搜索操作从二叉搜索树的根节点开始。
- 比较目标值与当前节点值
- 如果目标值等于当前节点的值,则表示找到了目标节点,搜索成功。
- 如果目标值小于当前节点的值,则在左子树中继续搜索。
- 如果目标值大于当前节点的值,则在右子树中继续搜索。
- 递归搜索:如果当前节点不是目标节点,且其左子树或右子树不为空,则递归地在相应子树中执行搜索操作。
代码实现
/**
* 搜索
* @param root
* @param data
* @return
*/
private MyBinaryTreeNode<Integer> search(MyBinaryTreeNode<Integer> root, Integer data) {
if (root == null) return null;
if (root.data > data){ // 如果当前节点的值大于要搜索的值,则向左子树搜索【递归】
return search(root.left, data);
}if (root.data < data){ // 如果当前节点的值小于要搜索的值,则向右子树搜索【递归】
return search(root.right, data);
}
return root; // 找到要搜索的值,返回该节点
}
2.2 插入
基本步骤
- 初始化:给定二叉搜索树的根节点
root
和要插入树中的值value
。 - 比较值
- 如果树为空(即
root
为null
),则创建一个新节点,将其值设置为value
,并将其作为根节点。 - 如果树不为空,从根节点开始,比较
value
与当前节点的值。
- 如果树为空(即
- 选择子树
- 如果
value
小于当前节点的值,则在左子树中继续搜索或插入。 - 如果
value
大于当前节点的值,则在右子树中继续搜索或插入。
- 如果
- 递归插入
- 如果当前节点的左子树或右子树为空,将新节点作为该子树的根节点。
- 如果当前节点的左子树或右子树不为空,则递归地在相应的子树中执行插入操作。
示例
- 输入:
root = [4,2,7,1,3], val = 5
private void insert(MyBinaryTreeNode<Integer> root, Integer data) {
if (root.data >= data){
if (root.left != null){
insert(root.left, data);
}else{
root.left = new MyBinaryTreeNode<>(data, root);
}
}else{
if (root.right != null){
insert(root.right, data);
}else{
root.right = new MyBinaryTreeNode<>(data, root);
}
}
}
2.3 删除
删除分为三种情况
- 要删除的结点是叶子结点
- 直接删除该节点,并返回。
- 要删除的结点有两颗子树
- 找到后继节点【后继节点:待删除节点的右子树的最左节点】
- 后继节点提升到要删除的节点的位置,并返回
- 要删除的结点只有一个子树(左或者右)
- 将该子节点提升到要删除的节点的位置,并返回。
**第一种情况:**要删除的结点是叶子结点
if (delNode.left == null && delNode.right == null){ // 第一种情况:删除叶子节点
// 直接将叶子节点与父节点的关系断开即可
if (delNode.parent.left == delNode){
delNode.parent.left = null;
} else{
delNode.parent.right = null;
}
}
**第二种情况:**要删除的结点有两颗子树
else if (delNode.left != null && delNode.right != null) { // 第二种情况:删除有两个子节点的节点
// 找到后继节点,后继节点 = 当前节点的右子树的最左节点
MyBinaryTreeNode<Integer> afterNode = delNode.right;
while (afterNode.left != null){
afterNode = afterNode.left;
}
// 断开待删除节点与父节点的连接,并将后继节点与父节点连接
if (delNode == delNode.parent.left){
// 步骤① [断开 11 - 7] 和 步骤④ [连接 11 - 8]
delNode.parent.left = afterNode;
// 步骤⑤ [连接 6 - 8]
afterNode.left = delNode.left;
// 步骤⑥ [连接 8 - 9]
afterNode.right = delNode.right == afterNode ? null : delNode.right;
}else{
delNode.parent.right = afterNode;
afterNode.left = delNode.left;
afterNode.right = delNode.right == afterNode ? null : delNode.right;
}
// 步骤③ 如图断开 8 - 9
afterNode.parent.left = null;
afterNode.parent = delNode.parent;
// 注意需要断开其他节点的parent指向删除节点
afterNode.left.parent = afterNode;
afterNode.right.parent = afterNode;
}
**第三种情况:**要删除的结点只有一个子树(左或者右)
else{ // 第三种情况:删除只有一个子节点的节点
// 使用子节点替换待删除的节点
MyBinaryTreeNode<Integer> subNode = delNode.left != null ? delNode.left : delNode.right;
// 第①步 第②步 断开待删除节点与父节点的连接、连接孙子节点与父节点
if (delNode.parent.right != null){
delNode.parent.right = subNode;
subNode.parent = delNode.parent;
} else if (delNode.parent.left != null) {
delNode.parent.left = subNode;
subNode.parent = delNode.parent;
}
}
2.4 完整代码
/**
* 二叉搜索树
* 二叉搜索树:左子树所有节点的值,小于根节点的值;右子树所有节点的值,大于根节点的值
* api :
* 搜索 search
* 插入 insert
* 删除 delete
* 中序遍历 middleSort
*/
public class MyBinarySearchTreeDemo {
private MyBinaryTreeNode<Integer> root;
public MyBinarySearchTreeDemo(Integer data) {
this.root = new MyBinaryTreeNode<>(data);
}
public Boolean search(Integer data){
return search(root, data) != null;
}
public void insert(Integer data){
insert(root, data);
}
public boolean delete(Integer data){
return delete(root, data);
}
public void middleSort(){
middleSort(root);
}
private void middleSort(MyBinaryTreeNode<Integer> root) {
if (root.left != null){
middleSort(root.left);
}
System.out.print(root.getData() + " ");
if (root.right != null){
middleSort(root.right);
}
}
private boolean delete(MyBinaryTreeNode<Integer> root, Integer data) {
MyBinaryTreeNode<Integer> delNode = search(root, data);
if (delNode == null) { // 数据不存在,直接返回true
return true;
}
if (delNode.left == null && delNode.right == null){ // 第一种情况:删除叶子节点
// 直接将叶子节点与父节点的关系断开即可
if (delNode.parent.left == delNode){
delNode.parent.left = null;
} else{
delNode.parent.right = null;
}
} else if (delNode.left != null && delNode.right != null) { // 第二种情况:删除有两个子节点的节点
// 找到后继节点,后继节点 = 当前节点的右子树的最左节点
MyBinaryTreeNode<Integer> afterNode = delNode.right;
while (afterNode.left != null){
afterNode = afterNode.left;
}
// 断开待删除节点与父节点的连接,并将后继节点与父节点连接
if (delNode == delNode.parent.left){
// 步骤① [断开 11 - 7] 和 步骤④ [连接 11 - 8]
delNode.parent.left = afterNode;
// 步骤⑤ [连接 6 - 8]
afterNode.left = delNode.left;
// 步骤⑥ [连接 8 - 9]
afterNode.right = delNode.right == afterNode ? null : delNode.right;
}else{
delNode.parent.right = afterNode;
afterNode.left = delNode.left;
afterNode.right = delNode.right == afterNode ? null : delNode.right;
}
// 步骤③ 如图断开 8 - 9
afterNode.parent.left = null;
afterNode.parent = delNode.parent;
// 注意需要断开其他节点的parent指向删除节点
afterNode.left.parent = afterNode;
afterNode.right.parent = afterNode;
}else{ // 第三种情况:删除只有一个子节点的节点
// 使用子节点替换待删除的节点
MyBinaryTreeNode<Integer> subNode = delNode.left != null ? delNode.left : delNode.right;
// 第①步 第②步 断开待删除节点与父节点的连接、连接孙子节点与父节点
if (delNode.parent.right != null){
delNode.parent.right = subNode;
subNode.parent = delNode.parent;
} else if (delNode.parent.left != null) {
delNode.parent.left = subNode;
subNode.parent = delNode.parent;
}
}
// 断开删除节点与其他节点的连接
delNode.parent = null;
delNode.left = null;
delNode.right = null;
return true;
}
private void insert(MyBinaryTreeNode<Integer> root, Integer data) {
if (root.data >= data){
if (root.left != null){
insert(root.left, data);
}else{
root.left = new MyBinaryTreeNode<>(data, root);
}
}else{
if (root.right != null){
insert(root.right, data);
}else{
root.right = new MyBinaryTreeNode<>(data, root);
}
}
}
/**
* 搜索
* @param root
* @param data
* @return
*/
private MyBinaryTreeNode<Integer> search(MyBinaryTreeNode<Integer> root, Integer data) {
if (root == null) return null;
if (root.data > data){ // 如果当前节点的值大于要搜索的值,则向左子树搜索【递归】
return search(root.left, data);
}if (root.data < data){ // 如果当前节点的值小于要搜索的值,则向右子树搜索【递归】
return search(root.right, data);
}
return root; // 找到要搜索的值,返回该节点
}
public static void main(String[] args) {
MyBinarySearchTreeDemo tree = new MyBinarySearchTreeDemo(4);
tree.insert(2);
tree.insert(11);
tree.insert(1);
tree.insert(3);
tree.insert(7);
tree.insert(6);
tree.insert(9);
tree.insert(8);
tree.insert(10);
tree.middleSort();
System.out.println();
System.out.println("查找数据【10】,res:【 "+tree.search(10) + " 】");
System.out.println("查找数据【12】,res:【 "+tree.search(12) + " 】");
tree.delete(7);
System.out.print("删除数据【7】 end :");
tree.middleSort();
}
}
3 平衡二叉树-简介
平衡二叉树(Balanced Binary Tree)是一种特殊的二叉树
3.1 平衡二叉树的特性
- 二叉搜索树(BST)的性质:首先,平衡二叉树是一棵二叉搜索树(BST),这意味着对于树中的每个节点,其左子树上所有节点的值都小于该节点的值,而其右子树上所有节点的值都大于该节点的值。
- 平衡性:平衡二叉树的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。这里的高度是指从该节点到其最远叶子节点的最长简单下降路径上的节点数。
3.2 常见的平衡二叉树:
- AVL树(Adelson-Velsky和Landis树):是最早被发明的自平衡二叉搜索树。在AVL树中,任何节点的两个子树的高度最大差别为1。
- 红黑树(Red-Black Tree):是一种自平衡的二叉搜索树,它在计算机科学中用作关联数组,存储数据并且允许它们以后被快速检索。红黑树的每个节点都有一个颜色属性,可以是红色或黑色。通过对颜色和新插入节点进行特定的调整,红黑树可以在插入、删除或查找时保持大致的平衡。
3.3 平衡二叉树的左旋
左旋定义
指将根节点的右侧往左拉,原先的右子节点变成新的父节点,并把多余的左子节点出让,给已经降级的根节点当右子节点。这样做的目的是为了调整树的结构,防止二叉树出现“长短腿”的情况,保持树的平衡,从而优化查找效率。
基本步骤
选择节点:选择一个需要进行左旋的节点X,其右子节点Y必须存在(即Y不能为空)。
调整连接:
将X的右子节点Y提升为新的父节点。
将X变为Y的左子节点。
如果Y有右子节点Z,则Z保持不变,仍然是Y的右子节点。
如果Y有左子节点,则将其设置为X的右子节点。
示例
左旋之前
10 (进行左旋的节点)
/ \
6 12 (12是X的右子节点,将成为新的父节点)
/ \
11 20
\
22
左旋之后
12 (进行左旋的节点)
/ \
10 20 (6是X的右子节点,将成为新的父节点)
/ \ \
6 11 22
3.4 平衡二叉树的右旋
右旋定义
右旋通常发生在左子树比右子树高,并且左子树的右子树高度过高的情况下。具体来说,假设一个节点y有左子节点x,当需要调整树的平衡时,可以进行右旋操作,即以y为支点,将x提升为根节点,y变成x的右子节点。
基本步骤
- 选择节点:选择一个需要进行右旋的节点X,其左子节点Y必须存在(即Y不能为空)。
- 调整连接
- 将x的左子节点y提升为新的根节点。
- 将x变为y的右子节点。
- x的右子节点(如果存在)保持不变,成为新树中x的右子树。
- y的右子节点(如果存在),成为新树中x的左子树。
示例
右旋之前
10 (进行右旋的节点)
/ \
6 12 (6是X的左子节点,将成为新的父节点)
/ \
4 8
/
2
右旋之后
6 (进行右旋的节点)
/ \
4 10 (6是X的左子节点,将成为新的父节点)
/ / \
2 8 12
4 平衡二叉树-示例
4.1 左旋
/**
* 平衡二叉树的左旋
*/
public MyBinaryTreeNode<Integer> leftRotate(MyBinaryTreeNode<Integer> node) {
MyBinaryTreeNode<Integer> right = node.getRight();
if (right == null){
return node;
}
MyBinaryTreeNode<Integer> right_left = right.getLeft();
// 将右节点提升为根节点
right.setLeft(node);
right.setParent(null);
node.setParent(right);
// 将右子树的左子树作为新树的左子树的右子树
node.setRight(right_left);
right_left.setParent(node);
return right;
}
4.2 右旋
/**
* 平衡二叉树的右旋
*/
public MyBinaryTreeNode<Integer> rightRotate(MyBinaryTreeNode<Integer> node) {
MyBinaryTreeNode<Integer> left = node.getLeft();
if (left == null){
return node;
}
MyBinaryTreeNode<Integer> left_right = left.getRight();
// 将左节点提升为根节点
left.setRight(node);
left.setParent(null);
node.setParent(left);
// 将左子树的右子树作为新树的左子树的左子树
node.setLeft(left_right);
left_right.setParent(node);
return left;
}
4.3 插入
/**
* 入口函数:添加节点
* @param data
*/
public void addNode(Integer data) {
// 如果根节点为null,新增节点直接作为根节点
if (root == null) {
root = new MyBinaryTreeNode<>(data);
return;
}
// 新增节点
addNode(root,new MyBinaryTreeNode<>(data));
// 旋转
rotate(root);
}
/**
* 递归函数:添加节点
* @param node
*/
private void addNode(MyBinaryTreeNode<Integer> source_node,MyBinaryTreeNode<Integer> node) {
if (node.data >= source_node.getData()){
if (source_node.right != null){
addNode(source_node.right,node);
}else{
source_node.setRight(node);
node.setParent(source_node);
}
}else {
if (source_node.left != null){
addNode(source_node.left,node);
}else {
source_node.setLeft(node);
node.setParent(source_node);
}
}
}
4.4 删除
/**
* 入口函数:删除节点
* @param data
*/
public void deleteNode(Integer data) {
if (root == null) return;
// 删除节点
deleteNode(root,data);
// 旋转
rotate(root);
}
/**
* 递归函数:删除节点
* @param root
* @param data
*/
private void deleteNode(MyBinaryTreeNode<Integer> root, Integer data) {
if (root.data != data){
if (root.left != null && root.data > data){
deleteNode(root.left,data);
} else if (root.right != null && root.data < data) {
deleteNode(root.right,data);
}
}else{
// 删除节点 , 分三种情况
if (root.left == null && root.right == null){ // 删除的节点为叶子节点
// 直接删除当前叶子节点
if (root.parent == null) root = null;
if (root.parent.left == root){ // 删除的节点为父节点的左子树
root.parent.left = null;
}else{ // 删除的节点为父节点的右子树
root.parent.right = null;
}
} else if (root.left != null && root.right != null) { // 删除的节点,存在左右两个子树
// 获取到后继节点,后继节点 = 待删除节点的右子树的最左节点
MyBinaryTreeNode<Integer> afterNode = root.right;
while (afterNode.left != null){
afterNode = afterNode.left;
}
MyBinaryTreeNode<Integer> l_node = root.left;
MyBinaryTreeNode<Integer> r_node = root.right;
// 使用后继节点替换到待删除节点的位置
if (root.parent == null){
this.root = afterNode;
}else if (root.parent.left == root){
root.parent.left = afterNode;
} else if (root.parent.right == root) {
root.parent.right = afterNode;
}
// 断开后继节点的父节点
if (afterNode.parent != null){
if (afterNode.parent.left == afterNode){
afterNode.parent.left = null;
}else{
afterNode.parent.right = null;
}
}
// 后继节点连接待删除节点的左右子树和父节点
afterNode.parent = root.parent;
afterNode.left = l_node;
afterNode.right = r_node == afterNode ? null : r_node;
}else{ // 删除的节点,只存在一个子树(左子树 or 右子树)
MyBinaryTreeNode<Integer> subNode = root.left != null ? root.left : root.right;
// 将删除节点的子树,替换到删除节点的位置
if (root.parent == null) // 如果待删除节点是根节点,将跟节点的子树替换到根节点
this.root = subNode;
if (root.parent.left == root){ // 待删除节点为父节点的左子树
root.parent.left = subNode;
}else{ // 待删除节点为父节点的右子树
root.parent.right = subNode;
}
if (subNode != null)
subNode.parent = root.parent;
}
// 断开待删除节点与其他节点的链接
root.left = null;
root.right = null;
root.parent = null;
}
}
4.5 辅助-节点旋转
/**
* 旋转
* @param n_node : 待旋转的节点
*/
private void rotate(MyBinaryTreeNode n_node) {
if (n_node == null) return;
// 递归对每一个子树进行旋转
if (n_node != null){
rotate(n_node.left);
rotate(n_node.right);
}
// 判断当前节点是否需要旋转,需要的话,确定需要左旋还是右旋
int leftHeight = getHeight(n_node.left);
int rightHeight = getHeight(n_node.right);
if (Math.abs(leftHeight - rightHeight) > 1){
MyBinaryTreeNode rotate = null;
MyBinaryTreeNode parent = n_node.parent;
if (leftHeight > rightHeight){ // 当前节点的左子树高度大于右子树高度,需要右旋
rotate = rightRotate(n_node);
}else { // 当前节点的右子树高度大于左子树高度,需要左旋
rotate = leftRotate(n_node);
}
// 旋转后,将旋转后的节点,替换到当前节点的位置
if (parent != null){
if (parent.left == n_node){
parent.left = rotate;
} else if (parent.right == n_node) {
parent.right = rotate;
}
} else {
root = rotate;
}
}
}
private int getHeight(MyBinaryTreeNode<Integer> node) {
if (node == null){
return 0;
}
// 递归求取当前节点的最大的高度
return Math.max(getHeight(node.left), getHeight(node.right)) + 1;
}
4.6 完整代码
/**
* 平衡二叉树
*/
public class MyBalanceBinaryTreeDemo {
public MyBinaryTreeNode<Integer> root;
public MyBalanceBinaryTreeDemo(Integer data) {
this.root = new MyBinaryTreeNode<>(data);
}
/**
* 添加节点
* @param data
*/
public void addNode(Integer data) {
// 如果根节点为null,新增节点直接作为根节点
if (root == null) {
root = new MyBinaryTreeNode<>(data);
return;
}
// 新增节点
addNode(root,new MyBinaryTreeNode<>(data));
// 旋转
rotate(root);
}
/**
* 入口函数:删除节点
* @param data
*/
public void deleteNode(Integer data) {
if (root == null) return;
// 删除节点
deleteNode(root,data);
// 旋转
rotate(root);
}
/**
* 递归函数:删除节点
* @param root
* @param data
*/
private void deleteNode(MyBinaryTreeNode<Integer> root, Integer data) {
if (root.data != data){
if (root.left != null && root.data > data){
deleteNode(root.left,data);
} else if (root.right != null && root.data < data) {
deleteNode(root.right,data);
}
}else{
// 删除节点 , 分三种情况
if (root.left == null && root.right == null){ // 删除的节点为叶子节点
// 直接删除当前叶子节点
if (root.parent == null) root = null;
if (root.parent.left == root){ // 删除的节点为父节点的左子树
root.parent.left = null;
}else{ // 删除的节点为父节点的右子树
root.parent.right = null;
}
} else if (root.left != null && root.right != null) { // 删除的节点,存在左右两个子树
// 获取到后继节点,后继节点 = 待删除节点的右子树的最左节点
MyBinaryTreeNode<Integer> afterNode = root.right;
while (afterNode.left != null){
afterNode = afterNode.left;
}
MyBinaryTreeNode<Integer> l_node = root.left;
MyBinaryTreeNode<Integer> r_node = root.right;
// 使用后继节点替换到待删除节点的位置
if (root.parent == null){
this.root = afterNode;
}else if (root.parent.left == root){
root.parent.left = afterNode;
} else if (root.parent.right == root) {
root.parent.right = afterNode;
}
// 断开后继节点的父节点
if (afterNode.parent != null){
if (afterNode.parent.left == afterNode){
afterNode.parent.left = null;
}else{
afterNode.parent.right = null;
}
}
// 后继节点连接待删除节点的左右子树和父节点
afterNode.parent = root.parent;
afterNode.left = l_node;
afterNode.right = r_node == afterNode ? null : r_node;
}else{ // 删除的节点,只存在一个子树(左子树 or 右子树)
MyBinaryTreeNode<Integer> subNode = root.left != null ? root.left : root.right;
// 将删除节点的子树,替换到删除节点的位置
if (root.parent == null) // 如果待删除节点是根节点,将跟节点的子树替换到根节点
this.root = subNode;
if (root.parent.left == root){ // 待删除节点为父节点的左子树
root.parent.left = subNode;
}else{ // 待删除节点为父节点的右子树
root.parent.right = subNode;
}
if (subNode != null)
subNode.parent = root.parent;
}
// 断开待删除节点与其他节点的链接
root.left = null;
root.right = null;
root.parent = null;
}
}
/**
* 旋转
* @param n_node : 待旋转的节点
*/
private void rotate(MyBinaryTreeNode n_node) {
if (n_node == null) return;
// 递归对每一个子树进行旋转
if (n_node != null){
rotate(n_node.left);
rotate(n_node.right);
}
// 判断当前节点是否需要旋转,需要的话,确定需要左旋还是右旋
int leftHeight = getHeight(n_node.left);
int rightHeight = getHeight(n_node.right);
if (Math.abs(leftHeight - rightHeight) > 1){
MyBinaryTreeNode rotate = null;
MyBinaryTreeNode parent = n_node.parent;
if (leftHeight > rightHeight){ // 当前节点的左子树高度大于右子树高度,需要右旋
rotate = rightRotate(n_node);
}else { // 当前节点的右子树高度大于左子树高度,需要左旋
rotate = leftRotate(n_node);
}
// 旋转后,将旋转后的节点,替换到当前节点的位置
if (parent != null){
if (parent.left == n_node){
parent.left = rotate;
} else if (parent.right == n_node) {
parent.right = rotate;
}
} else {
root = rotate;
}
}
}
private int getHeight(MyBinaryTreeNode<Integer> node) {
if (node == null){
return 0;
}
// 递归求取当前节点的最大的高度
return Math.max(getHeight(node.left), getHeight(node.right)) + 1;
}
/**
* 递归函数--添加节点
* @param node
*/
private void addNode(MyBinaryTreeNode<Integer> source_node,MyBinaryTreeNode<Integer> node) {
if (node.data >= source_node.getData()){
if (source_node.right != null){
addNode(source_node.right,node);
}else{
source_node.setRight(node);
node.setParent(source_node);
}
}else {
if (source_node.left != null){
addNode(source_node.left,node);
}else {
source_node.setLeft(node);
node.setParent(source_node);
}
}
}
/**
* 平衡二叉树的左旋
*/
public MyBinaryTreeNode<Integer> leftRotate(MyBinaryTreeNode<Integer> node) {
MyBinaryTreeNode<Integer> right = node.getRight();
if (right == null){
return node;
}
MyBinaryTreeNode<Integer> right_left = right.getLeft();
// 将右节点提升为根节点
right.setLeft(node);
right.setParent(node.parent);
node.setParent(right);
// 将右子树的左子树作为新树的左子树的右子树
node.setRight(right_left);
if (right_left != null)right_left.setParent(node);
return right;
}
/**
* 平衡二叉树的右旋
*/
public MyBinaryTreeNode<Integer> rightRotate(MyBinaryTreeNode<Integer> node) {
MyBinaryTreeNode<Integer> left = node.getLeft();
if (left == null){
return node;
}
MyBinaryTreeNode<Integer> left_right = left.getRight();
// 将左节点提升为根节点
left.setRight(node);
left.setParent(node.parent);
node.setParent(left);
// 将左子树的右子树作为新树的左子树的左子树
node.setLeft(left_right);
if (left_right != null)left_right.setParent(node);
return left;
}
public void show() {
if (root == null) {
System.out.println("EMPTY!");
return ;
}
// 得到树的深度
int treeDepth = getHeight(root);
// 最后一行的宽度为2的(n - 1)次方乘3,再加1
// 作为整个二维数组的宽度
int arrayHeight = treeDepth * 2 - 1;
int arrayWidth = (2 << (treeDepth - 2)) * 3 + 1;
// 用一个字符串数组来存储每个位置应显示的元素
String[][] res = new String[arrayHeight][arrayWidth];
// 对数组进行初始化,默认为一个空格
for (int i = 0; i < arrayHeight; i++) {
for (int j = 0; j < arrayWidth; j++) {
res[i][j] = " ";
}
}
// 从根节点开始,递归处理整个树
writeArray(root, 0, arrayWidth / 2, res, treeDepth);
// 此时,已经将所有需要显示的元素储存到了二维数组中,将其拼接并打印即可
for (String[] line : res) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < line.length; i++) {
sb.append(line[i]);
if (line[i].length() > 1 && i <= line.length - 1) {
i += line[i].length() > 4 ? 2 : line[i].length() - 1;
}
}
System.out.println(sb.toString());
}
}
private void writeArray(MyBinaryTreeNode<Integer> currNode, int rowIndex, int columnIndex, String[][] res, int treeDepth) {
// 保证输入的树不为空
if (currNode == null)
return;
// 先将当前节点保存到二维数组中
res[rowIndex][columnIndex] = String.valueOf(currNode.data);
// 计算当前位于树的第几层
int currLevel = ((rowIndex + 1) / 2);
// 若到了最后一层,则返回
if (currLevel == treeDepth)
return;
// 计算当前行到下一行,每个元素之间的间隔(下一行的列索引与当前元素的列索引之间的间隔)
int gap = treeDepth - currLevel - 1;
// 对左儿子进行判断,若有左儿子,则记录相应的"/"与左儿子的值
if (currNode.left != null) {
res[rowIndex + 1][columnIndex - gap] = "/";
writeArray(currNode.left, rowIndex + 2, columnIndex - gap * 2, res, treeDepth);
}
// 对右儿子进行判断,若有右儿子,则记录相应的"\"与右儿子的值
if (currNode.right != null) {
res[rowIndex + 1][columnIndex + gap] = "\\";
writeArray(currNode.right, rowIndex + 2, columnIndex + gap * 2, res, treeDepth);
}
}
}
4.7 测试&结果
package cn.zxc.demo.leetcode_demo.advanced_data_structure.tree;
public class BalanceBinaryTreeTest {
public static void main(String[] args) {
MyBalanceBinaryTreeDemo myBalanceBinaryTreeDemo = new MyBalanceBinaryTreeDemo(1);
myBalanceBinaryTreeDemo.addNode(2);
myBalanceBinaryTreeDemo.addNode(3);
myBalanceBinaryTreeDemo.addNode(4);
myBalanceBinaryTreeDemo.addNode(5);
myBalanceBinaryTreeDemo.show(); // 输出二叉树结构
System.out.println("-----------------新增 6 -----------------------");
myBalanceBinaryTreeDemo.addNode(6);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 7 -----------------------");
myBalanceBinaryTreeDemo.addNode(7);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 8 -----------------------");
myBalanceBinaryTreeDemo.addNode(8);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 9 -----------------------");
myBalanceBinaryTreeDemo.addNode(9);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 10 -----------------------");
myBalanceBinaryTreeDemo.addNode(10);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------删除 9-----------------------");
myBalanceBinaryTreeDemo.deleteNode(9);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------删除 8-----------------------");
myBalanceBinaryTreeDemo.deleteNode(8);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 6-----------------------");
myBalanceBinaryTreeDemo.addNode(6);
myBalanceBinaryTreeDemo.show();
System.out.println("-----------------新增 11-----------------------");
myBalanceBinaryTreeDemo.addNode(11);
myBalanceBinaryTreeDemo.show();
}
}
结果输出
2
/ \
1 4
/ \
3 5
-----------------新增 6 旋转之前 -----------------------
2
/ \
1 4
/ \
3 5
\
6
-----------------新增 6 旋转后-----------------------
4
/ \
2 5
/ \ \
1 3 6
-----------------新增 7 旋转之前 -----------------------
4
/ \
2 5
/ \ \
1 3 6
\
7
-----------------新增 7 旋转后-----------------------
4
/ \
2 6
/ \ / \
1 3 5 7
-----------------新增 8 旋转之前 -----------------------
4
/ \
2 6
/ \ / \
1 3 5 7
\
8
-----------------新增 8 旋转后-----------------------
4
/ \
2 6
/ \ / \
1 3 5 7
\
8
-----------------新增 9 旋转之前 -----------------------
4
/ \
2 6
/ \ / \
1 3 5 7
\
8
\
9
-----------------新增 9 旋转后-----------------------
4
/ \
2 6
/ \ / \
1 3 5 8
/ \
7 9
-----------------新增 10 旋转之前 -----------------------
4
/ \
2 6
/ \ / \
1 3 5 8
/ \
7 9
\
10
-----------------新增 10 旋转后-----------------------
4
/ \
2 8
/ \ / \
1 3 6 9
/ \ \
5 7 10
-----------------删除 9-----------------------
4
/ \
2 8
/ \ / \
1 3 6 10
/ \
5 7
-----------------删除 8-----------------------
4
/ \
2 6
/ \ / \
1 3 5 10
/
7
-----------------新增 6 旋转之前 -----------------------
4
/ \
2 6
/ \ / \
1 3 5 10
/
7
/
6
-----------------新增 6 旋转之后 -----------------------
4
/ \
2 6
/ \ / \
1 3 5 7
/ \
6 10
-----------------新增 11-----------------------
4
/ \
2 7
/ \ / \
1 3 6 10
/ \ \
5 6 11