AVL树图解和Java实现

AVL树

AVL树是带有平衡条件二叉查找树。平衡条件为:某节点左右子树的高度差不超过1。

平衡操作-旋转

在AVL树的插入和删除操作中,需要更新树节点的高度信息以检查AVL树的平衡状态,如果插入和删除后出现了不平衡,还需要通过旋转操作来修正AVL树的平衡状态。

插入后平衡

假设插入后非平衡节点为N,4种情况和修正方案:

  • 向N的左儿子的左子树插入:右旋
  • 向N的右儿子的右子树插入:左旋
  • 向N的左儿子的右子树插入:先左旋,再右旋
  • 向N的右儿子的左子树插入:先右旋,再左旋

单旋转过程图:
这里写图片描述
上图为右旋,左旋是对称的,略去不表

从非递归编程的角度出发,基于这张图,分析要完成一次单旋转我们需要做哪些工作:

  1. 保存路径信息。在插入过程中用栈保存路径信息。
  2. 更新高度信息,检查平衡状态。插入完成后,从栈中依次弹出节点,更新其高度信息,并检查其平衡状态。如果没有遇到不平衡节点,则插入完成;如果遇到非平衡节点,继续弹出一个节点N_other代表树的其余部分。
  3. 旋转。图中是右旋。比较旋转前后的状态,大致需要五步操作完成。
    3.1 确定插入方式。从非平衡节点N1出发,与新节点N3比较两次即可确定是左左插入。
    3.2 周知树其余部分。更新树其余部分的儿子指向新的节点N2。
    3.3 儿子移交。将N2的右子树移交给N1的左儿子。
    3.4 新根上任。原N1作为N2的右儿子。
    3.5 高度信息更新。观察发现旋转过程中,只有N_other,N1,N2的高度信息可能发生变化,重新为它们计算高度信息。
    3.6 总结。这四步可以分为三步:1、确定插入方式2、更新三个相关节点的左右儿子信息3、更新三个节点的高度信息.
  4. 更新路径上余下节点的高度信息

至此一次完整的左左插入过程就完成了。
双旋转过程图:
这里写图片描述
上图是先左后右双旋,先右后左是对称的,略去不表

双旋转本质上是独立的两次单旋转。

以上图为例:在确定插入方式为左右插入后,确定应该左右旋转。此时读取到非平衡节点为N1,但是对于单左旋来说,应指定其非平衡节点为N2节点。左旋完成后,再以N1节点作为非平衡节点进行右旋。

删除后平衡

AVL树和二叉查找树的删除操作情况一致,都分为三种情况,只不过AVL树在删除节点后需要重新检查平衡性并修正,同时,删除操作与插入操作后的平衡修正区别在于,插入操作后只需要对插入栈中的弹出的第一个非平衡节点进行修正,而删除操作需要修正栈中的所有非平衡节点。

三种情况如下:假设被删除节点为N

  • N为叶子节点:直接删除
  • 被N只有一个儿子:将父亲节点的相应儿子引用直接指向N的儿子
  • N有两个儿子:找到N的右子树中最小的节点N_R_MIN,该节点一定无左儿子;我们将N_R_MIN的值赋给N节点,然后删除N_R_MIN即可。(此时删除N_R_MIN节点属于前两种情况)

删除操作的大致步骤如下:

  1. 以前两种情况为基础尝试删除节点,并将访问节点入栈。
  2. 如果尝试删除成功,则依次检查栈顶节点的平衡状态,遇到非平衡节点,即进行旋转平衡,直到栈空。
  3. 如果尝试删除失败,证明是第三种情况。这时先找到被删除节点的右子树最小节点并删除它,将访问节点继续入栈。
  4. 再依次检查栈顶节点的平衡状态和修正直到栈空。

对于删除操作造成的非平衡状态的修正,可以这样理解:对左或者右子树的删除操作相当于对右或者左子树的插入操作,然后再对应上插入的四种情况选择相应的旋转就好了。

以下为代码实现:

public class AVLBalanceTree<T extends Comparable<T>> {

    private TreeNode<T> root;

    public AVLBalanceTree() {
    }

    private class TreeNode<E> {
        T element;
        int height;
        TreeNode<T> left;
        TreeNode<T> right;

        TreeNode(T e) {
            element = e;
        }

        boolean isBalanced() {
            int leftHeight = left == null ? -1 : left.height;
            int rightHeight = right == null ? -1 : right.height;
            return Math.abs(leftHeight - rightHeight) <= 1 ? true : false;
        }

        public String toString() {
            return "Element:" + element + " Height:" + height + " Balanced:" + isBalanced();
        }
    }

    private int calHeight(TreeNode<T> node) {
        if (node == null) {
            return -1;
        }
        int leftHeight = node.left == null ? -1 : node.left.height;
        int rightHeight = node.right == null ? -1 : node.right.height;
        return Math.max(leftHeight, rightHeight) + 1;
    }

    // 非递归算法部分
    public void insertNonRecursive(T e) {
        LinkedList<TreeNode<T>> trace = new LinkedList<>();
        insertNonRecursive0(e, trace);
        reBalanceAfterInsert(trace, e);
    }

    private void reBalanceAfterInsert(LinkedList<TreeNode<T>> trace, T e) {
        TreeNode<T> unBalanceNode = null;
        TreeNode<T> unBalNodeParent = null;
        TreeNode<T> n = null;
        while ((n = trace.peek()) != null && unBalanceNode == null) {
            n = trace.pop();
            n.height = calHeight(n);
            if (!n.isBalanced()) {
                unBalanceNode = n;
            }
        }
        if (trace.peek() != null) {
            unBalNodeParent = trace.pop();
        }
        // 平衡状态 返回
        if (unBalanceNode == null) {
            return;
        }
        // 确定插入方式
        int compareInt = unBalanceNode.element.compareTo(e);
        if (compareInt > 0) {
            if (calHeight(unBalanceNode.left.left) - calHeight(unBalanceNode.left.right) > 0) {
                rotateRight(unBalanceNode, unBalNodeParent);
            } else {
                // 先左旋时,视原非平衡节点的左儿子为非平衡节点
                rotateLeft(unBalanceNode.left, unBalanceNode);
                rotateRight(unBalanceNode, unBalNodeParent);
            }
        } else if (compareInt < 0) {
            if (calHeight(unBalanceNode.right.left) - calHeight(unBalanceNode.right.right) < 0) {
                rotateLeft(unBalanceNode, unBalNodeParent);
            } else {
                rotateRight(unBalanceNode.right, unBalanceNode);
                rotateLeft(unBalanceNode, unBalNodeParent);
            }
        }
        // 旋转完毕,旋转子树的高度信息已经更新过了,继续更新非平衡节点以上的高度信息
        while ((n = trace.peek()) != null) {
            n = trace.pop();
            n.height = calHeight(n);
        }
    }

    private void rotateRight(TreeNode<T> unBalanceNode, TreeNode<T> unBalNodeParent) {
        TreeNode<T> newRoot = unBalanceNode.left;
        TreeNode<T> ubParent = unBalNodeParent;
        // 周知树其余部分
        if (ubParent != null) {
            if (ubParent.left != null && ubParent.left.equals(unBalanceNode)) {
                ubParent.left = newRoot;
            } else {
                ubParent.right = newRoot;
            }
        } else {
            this.root = newRoot;
        }
        // 儿子移交
        if (newRoot.right != null) {
            unBalanceNode.left = newRoot.right;
        } else {
            unBalanceNode.left = null;
        }
        // 新根上任,左儿子无需处理
        newRoot.right = unBalanceNode;

        // 更新三个节点的高度信息
        unBalanceNode.height = calHeight(unBalanceNode);
        newRoot.height = calHeight(newRoot);
        if (ubParent != null) {
            ubParent.height = calHeight(ubParent);
        }
    }

    private void rotateLeft(TreeNode<T> unBalanceNode, TreeNode<T> unBalNodeParent) {
        TreeNode<T> newRoot = unBalanceNode.right;
        TreeNode<T> ubParent = unBalNodeParent;

        if (ubParent != null) {
            if (ubParent.left != null && ubParent.left.equals(unBalanceNode)) {
                ubParent.left = newRoot;
            } else {
                ubParent.right = newRoot;
            }
        } else {
            this.root = newRoot;
        }

        if (newRoot.left != null) {
            unBalanceNode.right = newRoot.left;
        } else {
            unBalanceNode.right = null;
        }

        newRoot.left = unBalanceNode;

        unBalanceNode.height = calHeight(unBalanceNode);
        newRoot.height = calHeight(newRoot);
        if (ubParent != null) {
            ubParent.height = calHeight(ubParent);
        }
    }

    private void insertNonRecursive0(T e, LinkedList<TreeNode<T>> trace) {
        TreeNode<T> node = new TreeNode<>(e);
        if (root == null) {
            root = node;
            root.height = 0;
            return;
        }
        TreeNode<T> current = root;
        trace.push(current);
        int compareInt = 0;
        while (true) {
            compareInt = current.element.compareTo(e);
            if (compareInt > 0) {
                if (current.left == null) {
                    current.left = node;
                    trace.push(node);
                    return;
                }
                current = current.left;
                trace.push(current);
            } else if (compareInt < 0) {
                if (current.right == null) {
                    current.right = node;
                    trace.push(node);
                    return;
                }
                current = current.right;
                trace.push(current);
            } else {
                // 相等 不插入
                return;
            }

        }
    }

    public void removeNonRecursive(T e) {
        LinkedList<TreeNode<T>> trace = new LinkedList<>();
        TreeNode<T> current = root;
        trace.push(current);
        // 尝试删除,该节点没有儿子或者只有一个儿子
        if (tryRemove(e, trace, current)) {
            // 更新节点高度信息,修正平衡状态
            // 此时栈顶为被删除节点的父节点或者空栈
            if (trace.isEmpty()) {
                return;
            }
            // 弹出栈,更新高度信息并检查平衡
            reBalanceAfterRemove(trace, e);
        } else {
            // 尝试失败,该节点有两个儿子,此时栈顶元素即为将要删除的节点A
            current = trace.peek();
            // 找到A的右子树中最小的节点B,该节点没有左儿子
            TreeNode<T> rMin = findMin(current.right);
            // 此时删除目标变为最小的右儿子B
            tryRemove(rMin.element, trace, current);
            reBalanceAfterRemove(trace, rMin.element);
            // 删除完成,将B的值替换到原来要删除的节点A上
            current.element = rMin.element;
        }
    }

    private void reBalanceAfterRemove(LinkedList<TreeNode<T>> trace, T e) {
        while (true) {
            TreeNode<T> n = null;
            TreeNode<T> unBalanceNode = null;
            TreeNode<T> unBalNodeParent = null;
            while ((n = trace.peek()) != null && unBalanceNode == null) {
                n = trace.pop();
                n.height = calHeight(n);
                if (!n.isBalanced()) {
                    unBalanceNode = n;
                }
            }

            // 平衡态
            if (unBalanceNode == null) {
                return;
            }

            unBalNodeParent = trace.peek();

            int compareInt = unBalanceNode.element.compareTo(e);

            // 被删除节点是不平衡节点的左儿子,则右子树高于左子树
            if (compareInt > 0) {
                // 如果不平衡节点的右儿子的左子树高于右子树,则需要先右后左双旋转(相当于右左插入)
                if (calHeight(unBalanceNode.right.left) - calHeight(unBalanceNode.right.right) > 0) {
                    rotateRight(unBalanceNode.right, unBalanceNode);
                    rotateLeft(unBalanceNode, unBalNodeParent);
                } else {
                    // 相等和小于的情况 都可以通过左旋平衡
                    rotateLeft(unBalanceNode, unBalNodeParent);
                }
            } else {
                // 相当于左右插入
                if (calHeight(unBalanceNode.left.left) - calHeight(unBalanceNode.left.right) < 0) {
                    rotateLeft(unBalanceNode.left, unBalanceNode);
                    rotateRight(unBalanceNode, unBalNodeParent);
                } else {
                    // 相等和大于的情况 都可以通过右旋平衡
                    rotateRight(unBalanceNode, unBalNodeParent);
                }
            }

            for (TreeNode<T> n1 : trace) {
                n1.height = calHeight(n1);
            }
        }
    }

    private boolean tryRemove(T e, LinkedList<TreeNode<T>> trace, TreeNode<T> current) {
        while (true) {
            int compareInt = current.element.compareTo(e);
            if (compareInt > 0) {
                if (current.left == null) {
                    // 没有找到节点 直接返回
                    return true;
                }
                current = current.left;
                trace.push(current);
            } else if (compareInt < 0) {
                if (current.right == null) {
                    // 没有找到节点
                    return true;
                }
                current = current.right;
                trace.push(current);
            } else {
                // 找到元素,情况一:该节点为叶子节点
                if (current.left == null && current.right == null) {
                    // 弹出被删除节点
                    trace.pop();
                    if (current == root) {
                        root = null;
                        return true;
                    }
                    TreeNode<T> parent = trace.peek();
                    // 查询父节点
                    if (parent.left == current) {
                        parent.left = null;
                        return true;
                    } else {
                        parent.right = null;
                        return true;
                    }
                }
                // 找到元素,情况二:该节点只有一个儿子

                if (current.left == null) {
                    trace.pop();// 弹出被删除节点
                    TreeNode<T> son = current.right;
                    if (current == root) {
                        root = son;
                        return true;
                    }
                    TreeNode<T> parent = trace.peek();
                    if (parent.left == current) {
                        parent.left = son;
                        return true;
                    } else {
                        parent.right = son;
                        return true;
                    }
                }

                if (current.right == null) {
                    trace.pop();// 弹出被删除节点
                    TreeNode<T> son = current.left;
                    if (current == root) {
                        root = son;
                        return true;
                    }
                    TreeNode<T> parent = trace.peek();
                    if (parent.left == current) {
                        parent.left = son;
                        return true;
                    } else {
                        parent.right = son;
                        return true;
                    }
                }
                // 找到节点,情况三:但是节点有两个儿子,返回false
                return false;
            }
        }

    }
}
已标记关键词 清除标记
做一门精致,全面详细的 java数据结构与算法!!! 让天下没有难学的数据结构, 让天下没有难学的算法, 不吹不黑,我们的讲师及其敬业,可以看到课程视频,课件,代码的录制撰写,都是在深夜,如此用心,其心可鉴,他不掉头发,谁掉头发??? 总之你知道的,不知道的,我们都讲,并且持续更新,走过路过,不要错过,不敢说是史上最全的课程,怕违反广告法,总而言之,言而总之,这门课你值得拥有,好吃不贵,对于你知识的渴求,我们管够管饱 话不多说,牛不多吹,我们要讲的本门课程内容: 稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式、中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉、二叉与数组转换、二叉排序(BST)、AVL、线索二叉、赫夫曼、赫夫曼编码、多路查找(BB+和B*)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页