假设有数列{1,2,3,4,5,6} 创建二叉排序树后如下图所示。下面的二叉排序树处在一些问题:
1、左子树全部为空,从形式上看更像单链表
2、查询速度明显降低不能发挥BST的优势,其查询效率还不如单链表
采用平衡二叉树可以避免这种情况的发生
平衡二叉树(AVL树)是一颗空树或者它的左右两个子树的高度差的绝对值不超过1并且左右子树也是平衡二叉树。AVL树是最先发明的自平衡二叉查找树。在AVL树中任何节点的两个子树的高度最大差别为1,所以它也被称为高度平衡树。增加和删除可能需要通过一次或多次树旋转来重新平衡这个树。
在结点插入过程中,由于任意两个结点最多只有两个儿子,因此高度不平衡时,结点的两颗子树的高度相差2.容易看出,这种不平衡可能出现在下面4中情况中
下图动画展示的是左旋
在上述的4中情况中只有1、2可以通过一次左旋或者右旋得到平衡二叉树
如果3、4两种情况仅仅只做一次旋转我们发现它还是一颗非平衡二叉树
而是需要两次旋转才能得到一颗平衡二叉树
(1)先创建一个值和要旋转子树的根结点值相同的新结点
(2)把要旋转的子树的根节点的左子树设置为新结点的左子树
(3)把根节点的右子树的左子结点设置成新结点的右子树
(4)把根节点的右子结点的值赋值给新结点
(5)把根节点的右子树的右子树设置成根结点的右子树
(6)把新结点设置成根节点的右子结点
右旋的思路与左旋差不多
public class Node {
private int value;
private Node left;
private Node right;
public Node(int value){
this.value = value;
}
/**
* @return 返回当前结点的左子树的高度
*/
public int leftHeight(){
if (this.left == null) {
return 0;
}else {
return this.left.height();
}
}
/**
* @return 返回当前结点的右子树的高度
*/
public int rightHeight(){
if (this.right == null) {
return 0;
}else {
return this.right.height();
}
}
/**
* @return 返回以当前结点为根结点的树的高度
*/
public int height(){
return Math.max(left == null ? 0 :left.height(),right == null ? 0 : right.height())+1;
}
/**
* 左旋转
*/
public void leftRotate(){
/*先创建一个值和要旋转子树的根结点值相同的新结点*/
Node newNode = new Node(this.value);
/*把要旋转的子树的根节点的左子树设置为新结点的左子树*/
newNode.setLeft(this.left);
/*把根节点的右子树的左子结点设置成新结点的右子树*/
newNode.setRight(this.right.getLeft());
/*把根节点的右子结点的值赋值给新结点*/
this.value = this.right.getValue();
/*把根节点的右子树的右子树设置成根结点的右子树*/
this.right = this.right.getRight();
/*把新结点设置成根节点的左子结点*/
this.left = newNode;
}
/**
* 右旋转
*/
public void rightRotate(){
/*先创建一个值和要旋转子树的根结点值相同的新结点*/
Node newNode = new Node(this.value);
/*把要旋转的子树的根节点的右子树设置为新结点的右子树*/
newNode.setRight(this.right);
/*把根节点的左子树的右子结点设置成新结点的左子树*/
newNode.setLeft(this.left.getRight());
/*把根节点的左子结点的值赋值给新结点*/
this.value = this.left.getValue();
/*把根节点的左子树的左子树设置成根结点的左子树*/
this.left = this.left.getLeft();
/*把新结点设置成根节点的右子结点*/
this.right = newNode;
}
/**
* 添加结点
* @param node 要添加的结点
*/
public void add(Node 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 (this.right != null && this.right.leftHeight() > this.right.rightHeight()){
/*先对右子树进行右旋转*/
this.right.rightRotate();
}
leftRotate();
return;
}
if (leftHeight() - rightHeight() > 1){ /*右旋转*/
/*如果左子树的右子树高度大于左子树的左子树的高度*/
if (this.left != null && this.left.rightHeight() > this.left.leftHeight()){
/*先对左子树进行左旋转*/
this.left.leftRotate();
}
rightRotate();
}
}
/**
* 中序遍历
*/
public void midDisplay(){
if (this.left != null) {
this.left.midDisplay();
}
System.out.println(this);
if (this.right != null) {
this.right.midDisplay();
}
}
getter and setetr;
@Override
public String toString() {
return "Node{" + "value=" + value + '}';
}
public class AVLTree {
private Node root;
/**
* 添加结点
*/
public void addNode(Node node){
if (root == null) {
root = node;
}else {
root.add(node);
}
}
/**
* 中序遍历
*/
public void midShow(){
if (root != null) {
root.midDisplay();
}else {
System.out.println("树为空");
}
}
public Node getRoot() {
return root;
}
}
测试类
public class AVLTest {
public static void main(String[] args) {
int[] arr = {2,1,5,4,6,3};
AVLTree avlTree = new AVLTree();
for (int i = 0; i < arr.length; i++) {
avlTree.addNode(new Node(arr[i]));
}
avlTree.midShow();
System.out.println("平衡二叉树树高"+avlTree.getRoot().height());
System.out.println("左子树树高"+avlTree.getRoot().leftHeight());
System.out.println("右子树书高"+avlTree.getRoot().rightHeight());
}
}
Node{value=1}
Node{value=2}
Node{value=3}
Node{value=4}
Node{value=5}
Node{value=6}
平衡二叉树树高3
左子树树高2
右子树书高2
Process finished with exit code 0
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_60117382/article/details/122862571
内容来源于网络,如有侵权,请联系作者删除!