算法-leetcode-树相关问题- 1382. 将二叉搜索树变平衡

33, 1382. 将二叉搜索树变平衡

https://leetcode-cn.com/problems/balance-a-binary-search-tree/

  • balanceBST方法
package com.shangguigu.dachang.algrithm.A09_basicTree;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.List;

/**
 * @author : 不二
 * @date : 2022/4/22-下午4:43
 * @desc : 二叉排序树
 * 为了方便直接处理,我们把节点直接写在内部
 *
 * 
 *
 **/
public class A11_binarySortTree {
    public static void main(String[] args) {
        // int[] nums = {3, 4, 1, 7, 6, 5, 2, 0, 9, 8, 10};
        int[] nums = {1, 15, 14, 17, 7, 2, 12, 3, 9, 11};

//        int[] nums = {2, 1, 3, 4};
        BSTree bsTree = new BSTree();
        for (int i = 0; i < nums.length; i++) {
            // 把数组中数据添加到二叉排序树中
            bsTree.addNode(new BSTree.Node(nums[i]));
        }

        // 现在拿到一个BST树,对其平衡
        List<List<Integer>> lists1 = bsTree.levelorderTraversal();
        System.out.println(lists1);



        // 二叉排序树的中序遍历就相当于排好序了
        // bsTree.preorderTraversal();
//        bsTree.deleteNode(new BSTree.Node(0));
//        bsTree.deleteNode(new BSTree.Node(1));
//        bsTree.deleteNode(new BSTree.Node(2));
//        bsTree.deleteNode(new BSTree.Node(3));
//        bsTree.deleteNode(new BSTree.Node(4));
        // bsTree.deleteNode(new BSTree.Node(5));
        bsTree.root = BSTree.balanceBST(bsTree.root);


        // 二叉排序树的中序遍历就相当于排好序了
        // bsTree.inorderTraversal();
        List<List<Integer>> lists = bsTree.levelorderTraversal();
        System.out.println(lists);

    }
}

class BSTree{

    /**
     * 这里做成内部类
     */
    static class Node {
        int val;
        Node left;
        Node right;
        public Node(int val) {
            this.val = val;
        }
    }

    Node root;


    // 计算高度
    // 给定node,计算改node的高度
    public static int height(Node node) {
        if (node == null) {
            return 0;
        }
        return 1 + Math.max(height(node.left), height(node.right));
    }


    /**
     * 把bst树转换成平衡二叉树
     * @param root
     * @return
     */
    public static Node balanceBST(Node root) {

        if (root == null) {
            return null;
        }

        root.left = balanceBST(root.left);
        root.right = balanceBST(root.right);

        if (height(root.left) - height(root.right) > 1) {
            // 说明不是平衡树,左边高于右边, 向右旋转
            if (height(root.left.right) > height(root.left.left)) {
                root.left = leftRotate(root.left);
            }
            root = rightRotate(root);

            /*// 如果对于此处的root调整后,要重新调用以防止旋转后的数据依然没有平衡
            root.left = balanceBST(root.left);
            root.right = balanceBST(root.right);*/
            root = balanceBST(root);
        } else if (height(root.right) - height(root.left) >1) {
            // 说明不是平衡树, 右边高于左边
            if (height(root.right.left) > height(root.right.right)) {
                root.right = rightRotate(root.right);
            }
            root = leftRotate(root);

            /*// 如果对于此处的root调整后,要重新调用以防止旋转后的数据依然没有平衡
            root.left = balanceBST(root.left);
            root.right = balanceBST(root.right);*/
            root = balanceBST(root);
        }

        System.out.println(root.val + ": 高度是:" + height(root));
        System.out.println(root.val + ":左子高度是:" + height(root.left));
        System.out.println(root.val + ":右子高度是:" + height(root.right));

        return root;
    }


    // 如果左子树的高度比右子树高度 高出超过1的高度,需要把树进行右旋转
    public static Node rightRotate(Node node){
        // 不需要限定。有些情况等于1也需要旋转
        /*while (height(node.left) - height(node.right) > 1) {
        }*/

        // 1, 首先创建一个新的节点,和root节点值一致
        Node theTmpNode = new Node(node.val);
        // 该新的节点是作为root的临时哨兵节点
        // 该哨兵节点右侧不变和root一样
        theTmpNode.right = node.right;
        // 该哨兵节点左侧变为:root左侧节点的右侧节点(因为稍后要把root的左侧节点提升到root位置)
        theTmpNode.left = node.left.right;

        // 2, 然后把root的左侧节点提神到
        node = node.left;

        // 3, 最后再把提升后的当前root右侧关联到哨兵节点上
        // 左侧不需要更改
        node.right = theTmpNode;

        return node;
    }


    // 右子树的高度比左子树高度 高出超过1的高度,需要把树进行左旋转
    public static Node leftRotate(Node node){
        // 说明不是平衡二叉树,需要进行左旋转
        // 不需要限定。有些情况等于1也需要旋转
        /*while (height(node.right) - height(node.left) > 1) {
        }*/

        // 1, 首先创建一个新的节点,和root节点值一致
        Node theTmpNode = new Node(node.val);
        // 该新的节点是作为root这个节点的临时哨兵节点
        // 该哨兵节点左侧不变和root一样
        theTmpNode.left = node.left;
        // 右侧变为:root右侧节点的左侧节点(因为稍后要把右侧节点提升为root)
        theTmpNode.right = node.right.left;

        // 2, 然后把root的右侧节点提升到root位置
        node = node.right;

        // 3,然后再把root的左侧关联到新建的临时哨兵节点,
        // 右侧关联到:之前root右侧节点的右侧(右侧这个没有断开过,所以不需要设置)
        node.left = theTmpNode;

        return node;
    }



    /**
     * 删除节点相对比较麻烦:可以从小到大入手:
     * 1,如果是删除的叶子节点
     * 2,如果删除的是非root的根节点,而且只有一个叶子
     *   2.1, left叶子存在
     *   2.2, right叶子存在
     * 3,如果删除的是非root的根节点,但是有两个叶子
     * 4,如果删除的是root节点
     * @param node
     */
    public void deleteNode(Node node) {
        Node preNode = null;
        boolean isLeft = false;
        Node currNode = root;
        // Node nxtNode = null;
        while (true) {
            if (node.val < currNode.val) {
                if (currNode.left == null) {
                    break;
                }
                preNode = currNode;
                isLeft = true;
                currNode = currNode.left;
            } else if (node.val > currNode.val) {
                if (currNode.right == null) {
                    break;
                }
                preNode = currNode;
                isLeft = false;
                currNode = currNode.right;
            } else {
                //说明相等了
                break;
            }
        }
        System.out.println(currNode);
        if (node.val != currNode.val) {
            return;
        }

        if (preNode == null) {
            // 说明是root节点
            root = currNode.right;

            // 直接把当前的左侧 挂在新的节点的 最左即可
            Node theTmpLeft = currNode.left;
            Node thePositionNode = currNode.right;
            while (thePositionNode.left != null) {
                thePositionNode = thePositionNode.left;
            }
            thePositionNode.left = theTmpLeft;

        } else {
            // 说明找到删除的节点
            // currNode为待删除节点, preNode是待删除节点的前一个节点
            // 1, 这种情况说明是叶子节点,直接比前一个节点的下一个节点设置为null即可
            if (currNode.left == null && currNode.right == null) {
                if (isLeft) {
                    preNode.left = null;
                } else {
                    preNode.right = null;
                }
            }

            // 2, 说明是一个根节点,左子树为null,右子树不为null
            if(currNode.left == null && currNode.right != null){
                if (isLeft) {
                    preNode.left = currNode.right;
                } else{
                    preNode.right = currNode.right;
                }
            }

            // 3, 说明是一个根节点,左子树不为null,右子树为null
            if(currNode.left != null && currNode.right == null){
                if (isLeft) {
                    preNode.left = currNode.left;
                } else{
                    preNode.right = currNode.left;
                }
            }

            // 4, 说明删除的是两个叶子都有的根节点(但是非root点,因为进来就代表preNode != nul)
            if (currNode.left != null && currNode.right != null) {
                // 说明是两个叶子节点都有的根节点
                // 这里就超级麻烦了。。。
                // 我们直接把小的节点重新添加一下
                if (isLeft) {
                    preNode.left = currNode.right;
                    // 直接把当前的左侧 挂在新的节点的 最左即可
                    Node theTmpLeft = currNode.left;
                    Node thePositionNode = currNode.right;
                    while (thePositionNode.left != null) {
                        thePositionNode = thePositionNode.left;
                    }
                    thePositionNode.left = theTmpLeft;

                } else {
                    preNode.right = currNode.right;
                    Node theTmpLeft = currNode.left;
                    Node thePositionNode = currNode.right;
                    while (thePositionNode.left != null) {
                        thePositionNode = thePositionNode.left;
                    }
                    thePositionNode.left = theTmpLeft;
                }
            }
        }

        System.out.println("-----");
    }


    public List<List<Integer>> levelorderTraversal() {
        List<List<Integer>> result = new ArrayList<>();
        if (root == null) {
            return result;
        }
        Deque<Node> deque = new ArrayDeque<>();
        deque.offer(root);
        while (deque.size() > 0) {
            int length = deque.size();
            List<Integer> tmp = new ArrayList<>();
            for (int i = 0; i < length; i++) {
                Node node = deque.poll();
                System.out.println("当前是:" + node.val +
                        ", 左子树高度是:" + height(node.left) +
                        ", 右子树高度是:" + height(node.right));
                tmp.add(node.val);
                if (node.left != null) {
                    deque.add(node.left);
                }
                if (node.right != null) {
                    deque.add(node.right);
                }
            }
            result.add(tmp);
        }

        return result;
    }

    public void inorderTraversal(){
        inorderTraversal(root);
    }
    // 对于二叉排序树,中序遍历就是从小到大排序了
    public void inorderTraversal(Node node){
        if (node == null) {
            return;
        }
        if (node.left != null) {
            inorderTraversal(node.left);
        }
        System.out.println(node.val);

        if (node.right != null) {
            inorderTraversal(node.right);
        }
    }

    // 把给定的节点按照二叉排序树进行添加到树中
    public void addNode(Node node) {
        if (node == null) {
            return;
        }
        if (root == null) {
            root = node;
            return;
        }

        Node curr = root;
        // 找到需要添加的根节点
        while (true) {
            if (node.val <= curr.val) {
                if (curr.left == null) {
                    break;
                }
                curr = curr.left;
            }
            if (node.val > curr.val) {
                if (curr.right == null) {
                    break;
                }
                curr = curr.right;
            }
        }

        // 走到这里,说明找到需要添加位置到根节点了
        if (node.val <= curr.val) {
            curr.left = node;
        } else {
            curr.right = node;
        }
        /*System.out.println(curr.val);
        System.out.println("-----");*/
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值