平衡二叉树
平衡树(Balance Tree,BT) 指的是,任意节点的子树的高度差都小于等于1。常见的符合平衡树的有,B树(多路平衡搜索树)、AVL树(二叉平衡搜索树)等。平衡树可以完成集合的一系列操作, 时间复杂度和空间复杂度相对于“2-3树”要低,在完成集合的一系列操作中始终保持平衡,为大型数据库的组织、索引提供了一条新的途径。
平衡二叉树的实现方法:
左旋转:
满足条件:如果它的右子树的左子树的高度大于它的右子树高度,先对这个结点的右子结点进行右旋转
(1)创建一个新的结点newNode,值等于当前根结点的值
(2)把新结点的左子树设置为当前结点的左子树:newNode.left = this.left
(3)把新结点的右子树设置为当前结点的右子树的左子树:newNode.right = this.right.left
(4)把当前结点的值替换为右子结点的值:this.value = this.right.value
(5)把当前结点的右子树设置为右子树的右子树:this.right = this.right.right
(6)把当前结点的左子树设置为新结点:this.left = this.newNode
右旋转:
满足条件:如果它的左子树的右子树的高度大于它的左子树高度,先对这个结点的左结点进行左旋转
思路:
(1)创建一个新的结点newNode,newNode的值等于当前根结点的值
(2)把新结点的右子树设置为当前结点的右子树:newNode.right = this.right
(3)把新结点的左子树设置为当前结点的左子树的右子树:newNode.left = this.left.right
(4)把当前结点的值替换为左子结点的值:this.value = this.left.value
(5)把当前结点的左子树设置为当前结点的左子树的左子树:this.left = this.left.left
(6)把当前结点的右子树设置为新的结点:this.right.value = newNode
实现代码:
package com.avl;
import java.security.PublicKey;
public class AVLTreeDemo {
public static void main(String[] args) {
int[] arr = {10,7,11,6,8,9};
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.addNode(new Node(arr[i]));
}
System.out.println(avlTree.getRoot().height());
System.out.println("树的根结点:" + avlTree.getRoot());
System.out.println("树的左子树高度:" + avlTree.getRoot().leftHeight());
System.out.println("树的右子树高度:" + avlTree.getRoot().rightHeight());
}
}
//创建平衡二叉树
class AVLTree {
private Node root;
public Node getRoot() {
return root;
}
public void setRoot(Node root) {
this.root = root;
}
//查找需要删除结点的方法
public Node searchTarget(int value) {
if (root == null)
return null;
else
return root.searchTarget(value);
}
//查找需要删除结点的父节点(parentNode)
public Node searchparentNode(int value) {
if (root == null)
return null;
else
return root.searchParentNode(value);
}
//右子树向左删除最小结点
public int delRightTreeMin(Node node) {
//定义一个辅助变量指针
Node target = node;
while (target.getLeft() != null) {
//向左递归查找最小的结点
target = target.getLeft();
}
//删除最小结点
delNode(target.getValue());
return target.getValue();//返回当前最小的结点
}
//删除结点方法
public void delNode(int value) {
if (root == null)
return;
else {
//1、查找需要删除的结点(targetNode)
Node targetNode = searchTarget(value);
if (targetNode == null)//此时没有找到需要删除的结点
return;
//如果此时二叉排序树只有一个结点(根结点),直接删除
if (root.getLeft() == null && root.getRight() == null) {
root = null;
return;
}
//2、查找targetNode的父结点(parentNode)
Node parentNode = searchparentNode(value);
//一、如果要删除的结点是叶子结点
if (targetNode.getLeft() == null && targetNode.getRight() == null) {
//此时targetNode为叶子结点,判断targetNode是父节点的左子结点或右子结点
//此时targetNode是parentNode的左子结点
if (parentNode.getLeft() != null && parentNode.getLeft().getValue() == value)
parentNode.setLeft(null);
//此时targetNode是parentNode的左子结点
else if (parentNode.getRight() != null && parentNode.getRight().getValue() == value)
parentNode.setRight(null);
}
//二、如果要删除的是有两颗子树的结点
else if (targetNode.getLeft() != null && targetNode.getRight() != null) {
int minVal = delRightTreeMin(targetNode.getRight());//从右子树删除最小的结点
targetNode.setValue(minVal);
}
//三、如果要删除的是只有一颗子树的结点
else {
if (targetNode.getLeft() != null) {//如果要删除的结点有左子结点
if (parentNode != null) {
//targetNode是parentNode的左子结点
if (parentNode.getLeft().getValue() == value)
parentNode.setLeft(targetNode.getLeft());
//targetNode是parentNode的右子结点
else {
parentNode.setRight(targetNode.getLeft());
}
} else//此时删除的是根结点
{
root = targetNode.getLeft();
}
} else //如果删除的结点有右子结点
{
if (parentNode != null) {
//targetNode是parentNode的左子结点
if (parentNode.getLeft().getValue() == value) {
parentNode.setLeft(targetNode.getRight());
} else//targetNode是parentNode的右子结点
parentNode.setRight(targetNode.getRight());
} else {//此时删除的为根结点
root = targetNode.getRight();
}
}
}
}
}
//添加结点方法
public void addNode(Node node) {
if (root == null)//如果root为null,让root指向node
root = node;
else
root.addNode(node);
}
//中序遍历方法
public void infixOrder()
{
if (root != null)
root.infixOrder();
else
System.out.println("树为空");
}
}
//创建Node结点
class Node {
int value;
Node left;
Node right;
public Node(int value) {
this.value = value;
}
//左旋转的方法
public void leftRotate()
{
//1、创建新的结点,以当前根结点的值创建
Node newNode = new Node(this.value);
//2、把新的结点的左子树设置为当前结点的左子树
newNode.left = this.left;
//3、把新的结点的右子树设置为当前结点的右子树的左子树
newNode.right = this.right.left;
//4、把当前结点的值替换为右子结点的值
this.value = this.right.value;
//5、把当前结点的右子树设置为当前结点右子树的右子树
this.right = this.right.right;
//6、把当前结点的左子树设置为新结点
this.left = newNode;
}
//右旋转的方式
public void rightRotate()
{
//创建一个新的结点newNode,newNode的值等于当前根结点的值
Node newNode = new Node(value);
//把新结点的右子树设置为当前结点的右子树
newNode.right = this.right;
//把新结点的左子树设置为当前结点的左子树的右子树
newNode.left = this.left.right;
//把当前结点的值替换为左子结点的值
this.value = this.left.value;
//把当前结点的左子树设置为当前结点的左子树的左子树
this.left = this.left.left;
//把当前结点的右子树设置为新的结点
this.right = 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;
}
//查找需要删除的结点
//value->希望删除结点的值,如果找到返回该结点,否则返回null
public Node searchTarget(int value) {
if (value == this.value)//此时找到希望删除的结点
return this;
else if (value < this.value) {//如果查找的值小于当前结点,向左子树递归查找
if (this.left == null)//此时左子结点为空,找不到
return null;
else
return this.left.searchTarget(value);
} else//此时查找的值大于当前结点,向右子树递归查找
{
if (this.right == null)//此时右子树为空,找不到
return null;
else
return this.right.searchTarget(value);
}
}
//查找要删除结点的父结点(parentNode)
//value->要找到结点的值
public Node searchParentNode(int value) {
if ((this.left != null && this.left.value == value) ||
(this.right != null && this.right.value == value)) {
//此时this就位parentNode
return this;
} else {
//如果查找的值比当前的值要小,并且当前的左子结点不为空,则向左子树递归查找parentNode
if (value < this.value && this.left != null)
return this.left.searchParentNode(value);
//如果查找的值比当前的值要大,并且当前的右子结点不为空,则向右子树递归查找parentNode
else if (value >= this.value && this.right != null)
return this.right.searchParentNode(value);
else {//此时上述条件都不满足,即为没有父节点
return null;
}
}
}
//添加结点的方法
//以递归的形式添加结点,需要满足二叉排序树的要求
public void addNode(Node node) {
if (node == null)//此时没有添加内容
return;
//判断传入的值与当前子树根结点的值之间大小关系
if (node.value < this.value)//此时传入的值小于根结点的值,应放入根节点的左边
{
if (this.left == null)//判断根结点的左子结点是否为空
this.left = node;
else
this.left.addNode(node);//如果不为空,递归向左子树添加
} else //此时传入的值大于根结点的值,应放入根节点的右边
{
if (this.right == null)//判断根结点的右子结点是否为空
this.right = node;
else
this.right.addNode(node);//递归向右子结点添加
}
//当右子树高度-左子树高度大于1时,左旋转
if(rightHeight() - leftHeight() > 1)
{
//如果它的右子树的左子树高度大于它的右子树高度
if (this.right != null && this.right.leftHeight() > this.right.rightHeight()) {
this.right.rightRotate();//对右子结点进行右反转
//再对当前结点进行左旋转
leftRotate();
}else
{
//直接左旋转
leftRotate();
}
return;
}
//当左子树高度-右子树高度大于1时,右旋转
if (leftHeight() - rightHeight() > 1) { //如果
//如果它的左子树的右子树高度大于它的左子树高度
if(this.left != null && this.left.rightHeight() > this.left.leftHeight())
{
//先对当前结点的左结点进行左旋转
this.left.leftRotate();
//再对当前结点进行右旋转
rightRotate();
}else {
//直接进行右旋转
rightRotate();
}
return;
}
}
//中序遍历
public void infixOrder() {
if (this.left != null)
this.left.infixOrder();
System.out.println(this);
if (this.right != null)
this.right.infixOrder();
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeft() {
return left;
}
public void setLeft(Node left) {
this.left = left;
}
public Node getRight() {
return right;
}
public void setRight(Node right) {
this.right = right;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
'}';
}
}