平衡二叉树的具体实现

文章参考视频https://www.bilibili.com/video/BV1E4411H73v?p=141
平衡二叉树首先是一个排序二叉树。
特点如下:

在这里插入图片描述

创建二叉树的代码如下

package 算法.树;

public class BinarySortTreeDemo {
public static void main(String[] args) {
int[] arr = {7, 3, 10, 12, 5, 1, 9, 2};
BinarySortTree binarySortTree = new BinarySortTree();
for (int i = 0; i < arr.length; i++) {
binarySortTree.add(new SortTreeNode(arr[i]));
}
binarySortTree.midOrder();
binarySortTree.delNode(2);
binarySortTree.delNode(5);
binarySortTree.delNode(9);
binarySortTree.delNode(12);
binarySortTree.delNode(7);
binarySortTree.delNode(3);
binarySortTree.delNode(10);

     binarySortTree.delNode(1);

    System.out.println("");
    binarySortTree.midOrder();
    System.out.println(binarySortTree.getRoot());
}

}

//创建二叉排序树
class BinarySortTree {
private SortTreeNode root;

//添加结点的方法
public void add(SortTreeNode node) {
    if (root == null) {
        root = node;
    } else {
        root.add(node);
    }
}

public SortTreeNode getRoot() {
    return root;
}

//查找要删除的结点
public SortTreeNode search(int value) {
    if (root == null) {
        return null;
    } else {
        return root.search(value);
    }
}

//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
    if (root == null) {
        return null;
    } else {
        return root.searchParent(value);
    }
}

//删除结点
public void delNode(int value) {
    if (root == null) {
        return;
    } else {
        //找到要删除的结点
        SortTreeNode targetNode = root.search(value);
        if (targetNode == null) {
            //没找到
            return;
        }
        //如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
        if (root.left == null && root.right == null) {
            root = null;
            return;
        }

        //根据三种情况开始判断。
        SortTreeNode parentNode = root.searchParent(value);
        //第一种情况如果要删除的结点是叶子结点
        if (targetNode.left == null && targetNode.right == null) {
            //判断删除结点是父节点的左节点还是右结点
            if (parentNode.left != null && parentNode.left.value == value) {
                parentNode.left = null;
            } else if (parentNode.right != null && parentNode.right.value == value) {
                parentNode.right = null;
            }

        } else if (targetNode.left != null && targetNode.right != null) {
            //先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
            SortTreeNode temp = targetNode.right;
            while (temp.left != null) {
                temp = temp.left;//循环找到targetNode右子树的最小值
            }
            //删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
            delNode(temp.value);
            //把temp中的数据赋给target
            targetNode.value = temp.value;

        } else {
            //删除的结点只有一个结点

            if (targetNode.left != null) {//删除结点的左子节点不空
                //要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
                if (parentNode != null) {//父节点不为空
                    if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                        parentNode.left = targetNode.left;
                    } else {
                        parentNode.right = targetNode.left;
                    }
                } else {
                    root = targetNode.left;
                }
            } else {//targetNode的右子节点不为空
                if (parentNode != null) {
                    if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                        parentNode.left = targetNode.right;
                    } else {
                        parentNode.right = targetNode.right;
                    }
                } else {
                    root=targetNode.right;
                }

            }
        }
    }

}

//中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
public void midOrder() {
    if (root != null) {
        root.midOrder();
    } else {
        System.out.println("排序树为空");
    }
}

}

class SortTreeNode {
int value;
SortTreeNode left;
SortTreeNode right;

public SortTreeNode(int value) {
    this.value = value;
}

//查找要删除结点的方法
public SortTreeNode search(int value) {
    if (this.value == value) {
        return this;
    } else if (value < this.value) {
        if (this.left == null) {
            return null;
        }
        return this.left.search(value);
    } else {//要查找的值不小于当前结点的value
        if (this.right == null) {
            return null;
        }
        return this.right.search(value);
    }
}

//查找要删除结点的父节点
public SortTreeNode searchParent(int value) {
    if ((this.left != null && this.left.value == value) ||
            (this.right != null && this.right.value == value)) {
        return this;
    } else {
        if (this.left != null && value < this.value) {
            return this.left.searchParent(value);
        } else if (this.right != null && value >= this.value) {
            return this.right.searchParent(value);

        } else {
            return null;//没有父节点
        }
    }

}

//添加结点的方法,要满足二叉排序树的要求
public void add(SortTreeNode node) {
    if (node == null) {
        return;
    }
    //判断传入的结点的值和根节点的关系
    if (node.value < this.value) {
        if (this.left == null) {
            this.left = node;
        } else {//向左子树递归
            this.left.add(node);
        }
    } else {//添加结点的值大于等于当前结点的值
        if (this.right == null) {
            this.right = node;
        } else {
            this.right.add(node);
        }

    }
}

//中序遍历
public void midOrder() {
    if (this.left != null) {
        this.left.midOrder();
    }
    System.out.println(this);
    if (this.right != null) {
        this.right.midOrder();
    }
}

@Override
public String toString() {
    return "SortTreeNode{" +
            "value=" + value +
            '}';
}

}

其中要特别说明一下的是,删除二叉排序树的结点时的情况

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

平衡二叉树是建立在排序二叉树的一个改进,特点如下

在左子树和右子树的高度差超过一时候,会进行一个旋转调整
在这里插入图片描述
左旋的思路,右旋同左旋原来类似。
在这里插入图片描述
上面的思路还有个特殊情况需要处理
在这里插入图片描述
解决思路
在这里插入图片描述
具体实现代码

package 算法.树.平衡二叉树;


public class AVLTreeDemo {
    public static void main(String[] args) {
        //int[] arr = {4,3,6,5,7,8};
        int[] arr = {10,12,8,9,7,6};
        AVLTree avlTree= new AVLTree();
        for (int i = 0; i <arr.length ; i++) {
            avlTree.add(new AVLTreeNode(arr[i]));
        }
        avlTree.midOrder();
        System.out.println(avlTree.getRoot().height());
        System.out.println(avlTree.getRoot().leftHeight());
        System.out.println(avlTree.getRoot().rightHeight());
    
    }
}

//创建AVLTree
class AVLTree {
    private AVLTreeNode root;



    //左旋方法


    //增加查找当前结点子树高度的方法
    //添加结点的方法
    public void add(AVLTreeNode node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
        }
    }

    public AVLTreeNode getRoot() {
        return root;
    }

    //查找要删除的结点
    public AVLTreeNode search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //查找要删除结点的父节点
    public AVLTreeNode searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //删除结点
    public void delNode(int value) {
        if (root == null) {
            return;
        } else {
            //找到要删除的结点
            AVLTreeNode targetNode = root.search(value);
            if (targetNode == null) {
                //没找到
                return;
            }
            //如果找到,但是根节点的左和右都为空,证明根节点就是要找的点,且只有一个根节点
            if (root.left == null && root.right == null) {
                root = null;
                return;
            }

            //根据三种情况开始判断。
            AVLTreeNode parentNode = root.searchParent(value);
            //第一种情况如果要删除的结点是叶子结点
            if (targetNode.left == null && targetNode.right == null) {
                //判断删除结点是父节点的左节点还是右结点
                if (parentNode.left != null && parentNode.left.value == value) {
                    parentNode.left = null;
                } else if (parentNode.right != null && parentNode.right.value == value) {
                    parentNode.right = null;
                }

            } else if (targetNode.left != null && targetNode.right != null) {
                //先判断targetNode有两个子结点的情况,因为只有一个子节点的判断过于繁琐
                AVLTreeNode temp = targetNode.right;
                while (temp.left != null) {
                    temp = temp.left;//循环找到targetNode右子树的最小值
                }
                //删除targetNode右子树最小的结点(最小的结点要么只有右子树,要么没有子结点,符合另外两种情况)
                delNode(temp.value);
                //把temp中的数据赋给target
                targetNode.value = temp.value;

            } else {
                //删除的结点只有一个结点

                if (targetNode.left != null) {//删除结点的左子节点不空
                    //要考虑当targetNOde是根节点,没有父节点,且它只有一个子节点
                    if (parentNode != null) {//父节点不为空
                        if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                            parentNode.left = targetNode.left;
                        } else {
                            parentNode.right = targetNode.left;
                        }
                    } else {
                        root = targetNode.left;
                    }
                } else {//targetNode的右子节点不为空
                    if (parentNode != null) {
                        if (parentNode.left != null && parentNode.left.value == targetNode.value) {
                            parentNode.left = targetNode.right;
                        } else {
                            parentNode.right = targetNode.right;
                        }
                    } else {
                        root = targetNode.right;
                    }

                }
            }
        }

    }

    //中序遍历,对二叉排序树进行中序遍历,刚好是顺序的
    public void midOrder() {
        if (root != null) {
            root.midOrder();
        } else {
            System.out.println("排序树为空");
        }
    }
}

class AVLTreeNode {
    int value;
    AVLTreeNode left;
    AVLTreeNode right;

    public AVLTreeNode(int value) {
        this.value = value;
    }
    //右旋的方法
    private void rightRotate(){
        AVLTreeNode newNode =  new AVLTreeNode(value);
        newNode.right=right;
        newNode.left=left.right;
        value=left.value;
        left=left.left;
        right=newNode;
    }

    //左旋的方法
    private void leftRotate(){
        //根据当前根节点的值创建一个新的结点
        AVLTreeNode newNode= new AVLTreeNode(value);
        //新结点的左子树指向根节点的左子树
        newNode.left=left;
        //新结点的右子树指向根节点的右子树的左子树
        newNode.right= this.right.left;
        //把当前根节点的值替换成它右子树的值
        this.value=right.value;
        //当前结点的右子树设置成右子树的右子树
        this.right=right.right;
        //当前结点的左子树指向新结点
        left= newNode;
    }

    //返回左子树高度
    public int leftHeight() {
        if (left == null) {
            return 0;
        } else {
            return left.height();
        }
    }

    //返回右子树高度
    public int rightHeight() {
        if (right == null) {
            return 0;
        } else {
            return right.height();
        }
    }

    //返回以当前结点为根节点的树高度
    public int height() {
        return Math.max(left == null ? 0 : left.height() , right == null ? 0 : right.height())+1;
    }

    //查找要删除结点的方法
    public AVLTreeNode search(int value) {
        if (this.value == value) {
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            }
            return this.left.search(value);
        } else {//要查找的值不小于当前结点的value
            if (this.right == null) {
                return null;
            }
            return this.right.search(value);
        }
    }

    //查找要删除结点的父节点
    public AVLTreeNode searchParent(int value) {
        if ((this.left != null && this.left.value == value) ||
                (this.right != null && this.right.value == value)) {
            return this;
        } else {
            if (this.left != null && value < this.value) {
                return this.left.searchParent(value);
            } else if (this.right != null && value >= this.value) {
                return this.right.searchParent(value);

            } else {
                return null;//没有父节点
            }
        }

    }

    //添加结点的方法,要满足二叉排序树的要求
    public void add(AVLTreeNode node) {
        if (node == null) {
            return;
        }
        //判断传入的结点的值和根节点的关系
        if (node.value < this.value) {
            if (this.left == null) {
                this.left = node;
            } else {//向左子树递归
                this.left.add(node);
            }
        } else {//添加结点的值大于等于当前结点的值
            if (this.right == null) {
                this.right = node;
            } else {
                this.right.add(node);
            }

        }
        //当添加一个结点后,右子树的高度-左子树的高度>1,进行左旋
        if (rightHeight()-leftHeight()>1){
            //如果根节点的右子节点的左子树高度大于右子树
            if(right.leftHeight()>rightHeight()){
                right.rightRotate();
            }
            this.leftHeight();
            return;//如果进行了一次左旋,就不进行右旋的判断了
        }
        if(leftHeight()-rightHeight()>1){
           //如果根节点的左子结点的右子树高度大于左子树
            if (left.rightHeight()>leftHeight()){
                //左子结点进行一次左旋
                left.leftRotate();
            }
            this.rightRotate();
        }
    }

    //中序遍历
    public void midOrder() {
        if (this.left != null) {
            this.left.midOrder();
        }
        System.out.println(this);
        if (this.right != null) {
            this.right.midOrder();
        }
    }

    @Override
    public String toString() {
        return "AVLTreeNode{" +
                "value=" + value +
                '}';
    }
}
``

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值