其他分享
首页 > 其他分享> > 6.2 AVL树

6.2 AVL树

作者:互联网

  平衡二叉树,比较简单,二叉树+平衡。
  二叉树的意思,就是树只有两个节点,平衡的意思是左边和右边的层数要平衡。也就是层数相差不能超过一。而且每个子树也要是平衡二叉树。
  平衡二叉树还有个特性是排序。排序所谓排序,就是左边要比自己小,并且右边比自己大。
  可以先实现一个排序树。
  排序树的实现很简单。作为树,核心是两个类。
  要做这么一个复杂的树,必须要用到面向对象的思想。
  首先是树,子类是二叉树,再下面是排序树,再下面是平衡树。这样就做成了一个平衡二叉树。

一个是Tree,一个是Node.
Tree的代码可以是这样。

public interface Tree<T> {
    Node<T> getRoot();
    void add(T t);
    void delete(T t);
    Node<T> findNode(T t);
}
那么Node的代码,可以是这样:
public interface Node<T> {
    T getValue();
    Node<T> getParent();
    List<? extends Node<T>> getChildren();
}

这样就定义好了两个主要的接口。
类继承关系,可以是
Tree -> BinaryTree -> OrderBinaryTree -> AVLTree
总共四层。树,然后是二叉树,再是排序二叉树,最后是平衡排序二叉树。

平衡二叉树的旋转

  二叉树的核心就是添加完成之后,判断一下树是否平衡,如果不平衡就进行旋转调整。
  旋转类型分为四种。
  LL RR LR RL。
  LL旋转又叫右旋。是在左子树深度远大于右子树的情况下进行的旋转。
  LL的意思是左子树加了个左孩子的意思。
  同理,RR是右子树多了个右孩子。
  LR是左子树多了个右孩子。
  RL是右子树多了个左孩子。
  LL LR都属于L开头,意思是左边深度大于右边。
  旋转时是值得拷贝,而不是把根换掉。而且旋转时要寻找最小子树。
  以下是四大旋转的代码:

/**
 *   3    2
 *  2 -> 1 3
 * 1
 * @return
 */
private void ll() {
    final AVLNode<T> newRight = new AVLNode<>(this.value);
    newRight.setRight(this.getRight());
    newRight.setLeft(this.getLeft().getRight());
    this.setRight(newRight);

    this.value = this.getLeft().value;
    this.getLeft().parent = null;
    this.setLeft(this.getLeft().getLeft());
}

/**
 * 1      2
 *  2 -> 1 3
 *   3
 * @return
 */
private void rr() {
    final AVLNode<T> newLeft = new AVLNode<>(this.value);
    newLeft.setLeft(this.getLeft());
    newLeft.setRight(this.getRight().getLeft());
    this.setLeft(newLeft);

    this.value = this.getRight().value;
    this.getRight().parent = null;
    this.setRight(this.getRight().getRight());
}

/**
 *    7        3
 *   2    ->  2 7
 *    3
 * @return
 */
private void lr() {
    this.getLeft().rr();
    this.ll();
}

/**
 * 5       6
 *   7 -> 5 7
 *  6
 * @return
 */
private void rl() {
    this.getRight().ll();
    this.rr();
}
```java
寻找最小不平衡子树的代码:
```java
private void fixToBalance(AVLNode<T> avlNode) {
    // 往上找直到子树不平衡
    for(AVLNode<T> pointer = avlNode;pointer != null; pointer = (AVLNode<T>) pointer.parent) {
        if (pointer.getLeftHeight() - pointer.getRightHeight() > 1) {
            // LL 或 LR
            final AVLNode<T> left = pointer.getLeft();
            if (left.getLeftHeight() > left.getRightHeight()) {
                // LL
                pointer.ll();
                break;
            }

            if (left.getLeftHeight() < left.getRightHeight()) {
                // LR
                // 先子树RR,再整体LL
                pointer.lr();
                break;
            }
        }

        if (pointer.getLeftHeight() - pointer.getRightHeight() < -1) {
            // RR 或 RL
            final AVLNode<T> right = pointer.getRight();
            if (right.getLeftHeight() > right.getRightHeight()) {
                // RL
                pointer.rl();
                break;
            }

            if (right.getLeftHeight() < right.getRightHeight()) {
                // RR
                pointer.rr();
                break;
            }
        }
    }
}

从代码可以看出是从新加的节点进行向上寻找,然后判断不平衡的类型。

AVL树的删除

AVL树的删除比添加节点要复杂。
第一步是删除吧。但是删除之后要符合二叉树的特征。
比如这棵树

要删除6
可以把7代替原来的位置,也可以用5代替原来的位置。
但是如果删除的是8呢?
只能将7放过来。代替原来的位置。
具体操作就是一个判断,如果左子树不为空,在左子树一直找右子节点,直到没有了为止。这样就找到了最大值。
否则,如果右子树不为空,找右子树的最小值。
在删除之后就进行旋转调整,同样是四种旋转。
代码如下:

// 排序二叉树的删除
final OrderBinaryNode<T> node = findNode(t);
if (node == null) {
    return;
}
// 如果是root,并且为空
if (node == root && root.isEmpty()) {
    root = null;
    return;
}
// 如果是叶子
if (node.isEmpty()) {
    final BinaryNode<T> parent = node.getParent();
    parent.removeChild(node);
    return;
}

// 否则就自己调整了

node.delete();

删除但是不调整的代码如下:

public void delete() {
    // 排序树节点,删除自己之后还是个排序树。
    // 这是自己不空的情况
    // 分两种情况
    if (getLeft() != null) {
        // 左节点找出最大值
        OrderBinaryNode<T> node = this.getLeft();
        while (node.getRight() != null) {
            node = node.getRight();
        }
        // 这是最大值,删除了自己
        this.value = node.value;
        node.parent.removeChild(node);
    } else if (getRight() != null) {
        // 右节点找出最小值
        OrderBinaryNode<T> node = this.getRight();
        while (node.getLeft() != null) {
            node = node.getLeft();
        }
        this.value = node.value;
        node.parent.removeChild(node);
    }
}

而调节平衡调用添加时的调节方法就行了

标签:node,getLeft,getRight,value,AVL,6.2,pointer,二叉树
来源: https://blog.csdn.net/m0_66201040/article/details/122766072