文章参考视频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 +
'}';
}
}
``