AVL平衡二叉树|学习笔记|数据结构

129 阅读7分钟

1、AVL平衡二叉树概念

平衡二叉树是一种高度平衡的二叉排列树,将二叉树上的结点的左子树深度减去右子树的值称为平衡因子BF,那么一颗子树的平衡因子则可以分为LH(1),EH(0),RH(-1)。只要二叉树上的一个结点的平衡因子的绝对值大于1则该二叉树就是不平衡的

2、左旋介绍

当一结点的平衡因子的右子树深度比左子树深度大1以上时,且以该结点为顶点的树为最小不平衡子树,我们可以对当前结点为顶点的树进行左旋,可以获得一个平衡的二叉树。

左旋1.png

左旋2.png

进行左旋的步骤:

1、T节点的右子树指向R结点的左子树

2、R结点的左子树指向T结点

3、R结点称为该树的顶点

实现代码:

TreeNode leftRotate(TreeNode node) {
        TreeNode rightNode = node.right;
        node.right = rightNode.left;
        rightNode.left = node;
        return rightNode;
    }

3、右旋介绍

当一结点的平衡因子的左子树深度比右子树深度大1以上时,且以该结点为顶点的树为最小不平衡子树,我们可以对当前结点为顶点的树进行右旋,可以获得一个平衡的二叉树。

右旋1.png

右旋2.png

进行右旋的步骤:

1、T节点的右子树指向L结点的右子树

2、L结点的右子树指向T结点

3、L结点称为该树的顶点

实现代码:

 TreeNode rightRotate(TreeNode node) {
        TreeNode leftNode = node.left;
        node.left = leftNode.right;
        leftNode.right = node;
        return leftNode;
    }

3、双旋介绍

需要旋转的结点情况分为LL、RR、LR、RL,当为RL和LR时需要进行双旋转,LR时需要先左旋后右旋,RL时需要先右旋后左旋。因为我们使用的是BF因子记录每个节点的左右子树平衡是否平衡,所以每次旋转时都需要更新BF因子。

因为我们使用递归进行插入和删除,所以当判断某个结点的平衡因子时候,默认该结点的子树的平衡因子已经进行过更新,但该结点的平衡因子没有进行更新。

LL计算需要更新的bf因子:

当前结点的BF为LH时,且左子树增高,则需要对左子树进行左子树平衡。并且当前结点的左孩子的BF为LH时则为LL型,需要进行右旋并且更新bf因子。

LL1.png

LL2.png

计算步骤:

1、在右旋之前,因为L结点的BF为LH,可得:

LL=LR+1;LL=LR+1;

2、在右旋之前,因为T结点的BF为LH,并且左子树增高,所以BF现在应该为2,可得:

TR+2=max(LL,LR)+1;TR+2=max(LL,LR)+1;

因为LL>LR,所以

TR+2=LL+1=LR+2;TR+2=LL+1=LR+2;

3、所以可以推出:

TR=LR;TR=LR;

4、所以在右旋后可以知道:

T.BF=EH;T.BF=EH;
L.BF=EH;L.BF=EH;

实现代码:

TreeNode leftBalance(TreeNode node) {
        ......
       case 1:
                node.bf = EH;
                leftNode.bf = EH;
                res = rightRotate(node);
                break;
        ......
}
TreeNode insert(TreeNode node, int num, int idx) {
    .......
    if (node.data > num || node.data == num) {
                node.left = insert(node.left, num, idx);
                if (taller) {
                    switch (node.bf) {
                        case 1:
                            taller = false;
                            return leftBalance(node);
                        case 0:
                            node.bf = LH;
                            taller = true;
                            return node;
                        case -1:
                            node.bf = EH;
                            taller = false;
                            return node;
                    }
                }
        ......
}

LR计算需要更新的bf因子:

当前结点的BF为LH时,且左子树增高,则需要对左子树进行左子树平衡。并且当前的左孩子的右子树比左子树高,则当前的树为LR型。

LR1.png

LR型进行平衡需要先对L结点进行左旋,然后再对T结点进行右旋,平衡后的树的结构如下图。

LR2.png

计算步骤:

1、在平衡前,从LR结点为LH可得:

LRL=LRR+1;LRL=LRR+1;

2、在平衡前,从L结点为RH可得:

LL+1=max(LRL,LRR)+1=LRL+1;LL+1=max(LRL,LRR)+1=LRL+1;

3、在平衡前,由T结点为LH,且左子树增高可得:

TR+2=max(LL,max(LRL,LRR)+1)+1=LRL+2TR+2=max(LL,max(LRL,LRR)+1)+1=LRL+2;

4、根据上列公式,可得平衡后有:

TR=LRL=LRR+1;TR=LRL=LRR+1;
TR+1=LRL+1TR+1=LRL+1
LRL=LL;LRL=LL;

5、可以推出平衡后:

T.BF=RH;T.BF=RH;
LR.BF=EH;LR.BF=EH;
L.BF=LH;L.BF=LH;

实现代码:

TreeNode leftBalance(TreeNode node) {
		......
        switch (leftNode.bf) {
        ......
            case -1:
                leftRight = leftNode.right;
                switch (leftRight.bf) {
                    case -1:
                        node.bf = EH;
                        leftNode.bf = LH;
                        break;
                    case 1:
                        leftNode.bf = EH;
                        node.bf = RH;
                        break;
                    case 0:
                        leftNode.bf = EH;
                        node.bf = EH;
                        break;
                }
                leftRight.bf = EH;
                node.left = leftRotate(leftNode);
                res = rightRotate(node);
                break;
        }
        return res;
    }
 TreeNode insert(TreeNode node, int num, int idx) {
        .......
            if (node.data > num || node.data == num) {
                node.left = insert(node.left, num, idx);
                if (taller) {
                    switch (node.bf) {
                        case 1:
                            taller = false;
                            return leftBalance(node);
                  	.......
                    }
                }
            } else {
              ........
        }
        return node;
    }

4、AVL树插入

自平衡二叉搜索树插入和二叉搜索树插入操作差不多,但是插入之后需要判断是否需要进行旋转调整。

需要调整的情况(举例插入到左子树):

如果左子树深度增高(taller),并且当前结点的BF因子为LH,则需要进行左平衡操作

实现代码:

TreeNode insert(TreeNode node, int num, int idx) {
    if (root == null) {
        root = new TreeNode(null, null, num, idx, EH);
        return root;
    }
    if (node == null) {
        taller = true;
        return new TreeNode(null, null, num, idx, EH);
    } else {
        if (node.data == num && node.idx == idx) {
            System.out.println("已存在当前节点");
            taller = false;
            return node;
        }
        if (node.data > num || node.data == num) {
            node.left = insert(node.left, num, idx);
            if (taller) {
                switch (node.bf) {
                    case 1:
                        taller = false;
                        return leftBalance(node);
                    case 0:
                        node.bf = LH;
                        taller = true;
                        return node;
                    case -1:
                        node.bf = EH;
                        taller = false;
                        return node;
                }
            }
        } else {
            node.right = insert(node.right, num, idx);
            if (taller) {
                switch (node.bf) {
                    case 1:
                        node.bf = EH;
                        taller = false;
                        return node;
                    case 0:
                        node.bf = RH;
                        taller = true;
                        return node;
                    case -1:
                        taller = false;
                        return rightBalance(node);
                }
            }
        }
    }
    return node;
}

5、AVL树删除

自平衡二叉搜索树插入和二叉搜索树删除操作差不多,但是删除之后需要判断是否需要进行旋转调整。

需要调整的情况(举例删除节点到左子树):

如果左子树深度降低(shorter),并且当前结点的BF因子为RH,则需要进行右平衡操作。

实现代码:

TreeNode delete(TreeNode node, int num, int idx) {
        if (node == null) {
            return null;
        } else {
            if (node.data == num && node.idx == idx) {
                //找到要删除的节点
                if (node.left == null && node.right != null) {
                    shorter = true;
                    return node.right;
                } else if (node.left != null && node.right == null) {
                    shorter = true;
                    return node.left;
                } else if (node.left != null && node.right != null) {
                    //找到左子树的最大数值节点
                    TreeNode left = node.left;
                    while (left.right != null) {
                        left = left.right;
                    }
                    //从当前的左子树中删除该节点
                    node = delete(node, left.data, left.idx);
                    //替换当前节点
                    node.data = left.data;
                    node.idx = left.idx;
                    shorter = true;
                    return node;
                } else {
                    shorter = true;
                    return null;
                }
            } else if (node.data > num || node.data == num) {
                node.left = delete(node.left, num, idx);
                if (shorter) {
                    //左子树高度减少1
                    switch (node.bf) {
                        case 1:
                            node.bf = EH;
                            shorter = true;
                            return node;
                        case 0:
                            node.bf = RH;
                            shorter = false;
                            return node;
                        case -1:
                            shorter = false;
                            TreeNode treeNode = rightBalance(node);
                            if (node == root) root = treeNode;
                            return treeNode;
                    }
                }
            } else {
                node.right = delete(node.right, num, idx);
                if (shorter) {
                    switch (node.bf) {
                        case 1:
                            shorter = false;
                            TreeNode treeNode = leftBalance(node);
                            if (node == root) root = treeNode;
                            return treeNode;
                        case 0:
                            node.bf = LH;
                            shorter = false;
                            return node;
                        case -1:
                            node.bf = EH;
                            shorter = true;
                            return node;
                    }
                }
            }
        }
        return node;
    }

6、全部代码

class AVLTree {
    public static final Integer LH = 1;
    public static final Integer EH = 0;
    public static final Integer RH = -1;
    boolean taller;
    boolean shorter;

    class TreeNode {
        TreeNode left, right;
        Integer data;
        Integer idx;
        Integer bf;

        public TreeNode(TreeNode left, TreeNode right, Integer data, Integer idx, Integer bf) {
            this.left = left;
            this.right = right;
            this.data = data;
            this.idx = idx;
            this.bf = bf;
        }
    }

    TreeNode root;

    public AVLTree() {
        root = null;
        taller = false;
        shorter = false;
    }

    TreeNode rightRotate(TreeNode node) {
        TreeNode leftNode = node.left;
        node.left = leftNode.right;
        leftNode.right = node;
        return leftNode;
    }

    TreeNode leftRotate(TreeNode node) {
        TreeNode rightNode = node.right;
        node.right = rightNode.left;
        rightNode.left = node;
        return rightNode;
    }

    TreeNode leftBalance(TreeNode node) {
        TreeNode res = null;
        TreeNode leftNode = node.left;
        TreeNode leftRight;
        switch (leftNode.bf) {
            case 1:
                node.bf = EH;
                leftNode.bf = EH;
                res = rightRotate(node);
                break;
            case 0:
                node.bf = LH;
                leftNode.bf = RH;
                res = rightRotate(node);
                break;
            case -1:
                leftRight = leftNode.right;
                switch (leftRight.bf) {
                    case -1:
                        node.bf = EH;
                        leftNode.bf = LH;
                        break;
                    case 1:
                        leftNode.bf = EH;
                        node.bf = RH;
                        break;
                    case 0:
                        leftNode.bf = EH;
                        node.bf = EH;
                        break;
                }
                leftRight.bf = EH;
                node.left = leftRotate(leftNode);
                res = rightRotate(node);
                break;
        }
        return res;
    }

    TreeNode rightBalance(TreeNode node) {
        TreeNode res = null;
        TreeNode rightNode = node.right;
        TreeNode rightLeft;
        switch (rightNode.bf) {
            case 1:
                rightLeft = rightNode.left;
                switch (rightLeft.bf) {
                    case -1:
                        node.bf = LH;
                        rightNode.bf = EH;
                        break;
                    case 1:
                        node.bf = EH;
                        rightNode.bf = RH;
                        break;
                    case 0:
                        node.bf = EH;
                        rightNode.bf = EH;
                        break;
                }
                rightLeft.bf = EH;
                node.right = rightRotate(rightNode);
                res = leftRotate(node);
                break;
            case 0:
                node.bf = RH;
                rightNode.bf = LH;
                res = leftRotate(node);
                break;
            case -1:
                node.bf = EH;
                rightNode.bf = EH;
                res = leftRotate(node);
                break;
        }
        return res;
    }

    TreeNode insert(TreeNode node, int num, int idx) {
        if (root == null) {
            root = new TreeNode(null, null, num, idx, EH);
            return root;
        }
        if (node == null) {
            taller = true;
            return new TreeNode(null, null, num, idx, EH);
        } else {
            if (node.data == num && node.idx == idx) {
                System.out.println("已存在当前节点");
                taller = false;
                return node;
            }
            if (node.data > num || node.data == num) {
                node.left = insert(node.left, num, idx);
                if (taller) {
                    switch (node.bf) {
                        case 1:
                            taller = false;
                            return leftBalance(node);
                        case 0:
                            node.bf = LH;
                            taller = true;
                            return node;
                        case -1:
                            node.bf = EH;
                            taller = false;
                            return node;
                    }
                }
            } else {
                node.right = insert(node.right, num, idx);
                if (taller) {
                    switch (node.bf) {
                        case 1:
                            node.bf = EH;
                            taller = false;
                            return node;
                        case 0:
                            node.bf = RH;
                            taller = true;
                            return node;
                        case -1:
                            taller = false;
                            return rightBalance(node);
                    }
                }
            }
        }
        return node;
    }

    TreeNode delete(TreeNode node, int num, int idx) {
        if (node == null) {
            return null;
        } else {
            if (node.data == num && node.idx == idx) {
                //找到要删除的节点
                if (node.left == null && node.right != null) {
                    shorter = true;
                    return node.right;
                } else if (node.left != null && node.right == null) {
                    shorter = true;
                    return node.left;
                } else if (node.left != null && node.right != null) {
                    //找到左子树的最大数值节点
                    TreeNode left = node.left;
                    while (left.right != null) {
                        left = left.right;
                    }
                    //从当前的左子树中删除该节点
                    node = delete(node, left.data, left.idx);
                    //替换当前节点
                    node.data = left.data;
                    node.idx = left.idx;
                    shorter = true;
                    return node;
                } else {
                    shorter = true;
                    return null;
                }
            } else if (node.data > num || node.data == num) {
                node.left = delete(node.left, num, idx);
                if (shorter) {
                    //左子树高度减少1
                    switch (node.bf) {
                        case 1:
                            node.bf = EH;
                            shorter = true;
                            return node;
                        case 0:
                            node.bf = RH;
                            shorter = false;
                            return node;
                        case -1:
                            shorter = false;
                            TreeNode treeNode = rightBalance(node);
                            if (node == root) root = treeNode;
                            return treeNode;
                    }
                }
            } else {
                node.right = delete(node.right, num, idx);
                if (shorter) {
                    switch (node.bf) {
                        case 1:
                            shorter = false;
                            TreeNode treeNode = leftBalance(node);
                            if (node == root) root = treeNode;
                            return treeNode;
                        case 0:
                            node.bf = LH;
                            shorter = false;
                            return node;
                        case -1:
                            node.bf = EH;
                            shorter = true;
                            return node;
                    }
                }
            }
        }
        return node;
    }

    void infixOrder() {
        infixOrder(root);
        System.out.println();
    }

    //中序遍历
    void infixOrder(TreeNode node) {
        if (node == null) {
            return;
        }
        infixOrder(node.left);
        System.out.print(node.data + " ");
        infixOrder(node.right);
    }

    Integer search(int target) {
        if (root == null) return -1;
        TreeNode node = root;
        while (node != null) {
            if (node.data < target) {
                node = node.right;
            } else if (node.data > target) {
                node = node.left;
            } else return node.idx;
        }
        return -1;
    }
}