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("-----");*/
}
}