平衡二叉排序 / 搜索树

基本介绍

1、平衡二叉(排序/搜索)树:又被称为AVL树,改进BST结构不平衡的缺点,查询效率较高

2、它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树

3、平衡二叉树的常用实现方法有红黑树、AVL、替罪羊树、Treap、伸展树等

4、利用旋转实现调整树的平衡,以插入/删除节点

5、前驱节点:值小于当前节点的最大值节点

6、后继节点:值大于当前节点的最小值节点

7、平衡因子(Balance Factor)

(1)某节点的左子树与右子树的高度(深度)差即为该节点的平衡因子,平衡二叉树中不存在平衡因子大于 1 的节点

(2)在一棵平衡二叉树中,节点的平衡因子只能取 0、1 或 -1,分别对应着左右子树等高,左子树比较高,右子树比较高

 

对于高度为 n 的平衡二叉树

1、最少需 h(n) 个结点

(1)h(n) = h(n - 1) + h(n - 2) + 1

(2)h(0) = 0

(3)h(1) = 1

(4)h(2) = 2

2、最多需要 2n - 1 个结点

 

旋转

1、基本思想:检查是否因插入/删除而破坏了平衡,若破坏,则找出其中的最小不平衡二叉树,在保持二叉排序树特性的情况下,调整最小不平衡子树中节点之间的关系,以达 到新的平衡

2、最小不平衡子树:指离插入/删除节点最近(向上查找),且以平衡因子的绝对值大于1的节点作为根的子树

3、单旋转前判断是否需要双旋转

(1)左旋转之前判断:若当前节点的右子节点不为空,且其右子节点的左子树高度大于右子树高度,则为 RL

(2)右旋转之前判断:若当前节点的左子节点不为空,且其左子节点的右子树高度大于左子树高度,则为 LR

 

绕某元素左旋转

1、右子树高度 - 左子树高度 > 1

2、假设,旋转元素以自身为 root,旋转前,newRoot = root.right,旋转后,新的根节点为 newRoot

3、左旋转(LL):将 newRoot 的左子树移动到 root.right 作为 root 的新右子树,将 root 移动到 newRoot.left 作为 newRoot 的新左子树

 

绕某元素右旋转

1、左子树高度 - 右子树高度 > 1

2、假设,旋转元素以自身为 root,旋转前,newRoot = root.left,旋转后,新的根节点为 newRoot

3、右旋转(RR):将 newRoot 的右子树移动到 root.left 作为 root 的新左子树,将 root 移动到 newRoot.right 作为 newRoot 的新右子树

 

双旋转

1、LR:绕某元素的左子节点左旋转,接着再绕该元素自己右旋转

2、RL:绕某元素的右子节点右旋转,接着再绕该元素自己左旋转

 

代码实现

1、以下的插入节点、删除节点是以根节点进行旋转,而不是根据最小不平衡树的根节点进行旋转

public class AVLTree {
    public Node root;

    //添加节点,若有相同值的节点,则插入相同值节点的尾部
    public void add(Node node) {
        if (root == null) {
            root = node;
        } else {
            root.add(node);
            root = root.rotate();
        }
    }

    //查找节点
    public Node search(int value) {
        if (root == null) {
            return null;
        } else {
            return root.search(value);
        }
    }

    //查找节点的父节点
    public Node searchParent(int value) {
        if (root == null) {
            return null;
        } else {
            return root.searchParent(value);
        }
    }

    //删除节点,若有相同值的节点,删除找到的第一个节点
    public void delete(int value) {
        if (root == null) {
            return;
        }
        Node target = search(value);//找待删除的结点target
        if (target == null) {//没有找到
            return;
        }
        Node parent = searchParent(value);//找到target的父结点parent
        if (parent == null) {//说明待删除节点是root
            deleteRoot();
        } else if (target.left == null && target.right == null) {//说明待删除节点是叶子节点
            deleteLeaf(parent, value);
        } else {//说明删除非根、非叶子节点
            deleteNonLeaf(target, parent, value);
        }
        root = root.rotate();
    }

    //删除根节点
    public void deleteRoot() {
        if (root.left != null && root.right != null) {
            //删除root右子树的最小值节点,并用其值覆盖root的值
            root.value = deleteRightTreeMin(root);
            //删除root左子树的最大值节点,并用其值覆盖root的值
            //root.value = deleteLeftTreeMax(root);
        } else if (root.left != null) {
            root = root.left;
        } else if (root.right != null) {//
            root = root.right;
        } else {//root.left == null && root.right == null
            root = null;
        }
    }

    //删除叶子节点
    public void deleteLeaf(Node parent, int value) {
        //确定target是parent的左子节点还是右子节点
        if (parent.left != null && parent.left.value == value) {
            parent.left = null;
        } else if (parent.right != null && parent.right.value == value) {
            parent.right = null;
        }
    }

    //删除非根、非叶子节点
    public void deleteNonLeaf(Node target, Node parent, int value) {
        //要删除的节点有两个子节点
        if (target.left != null && target.right != null) {
            //删除target后继节点,并用其值覆盖target的值
            target.value = deleteRightTreeMin(target);
            //或删除target前驱节点,并用其值覆盖target的值
            //target.value = deleteLeftTreeMax(target);
        } else if (target.left != null) {//要删除的节点只有一个左子节点
            if (parent.left.value == value) {//要删除的节点是父节点的左子节点
                parent.left = target.left;
            } else {//parent.right.value == value,要删除的节点是父节点的右子节点
                parent.right = target.left;
            }
        } else {//target.right != null,要删除的节点只有一个右子节点
            if (parent.left.value == value) {//要删除的节点是父节点的左子节点
                parent.left = target.right;
            } else {//parent.right.value == value,要删除的节点是父节点的右子节点
                parent.right = target.right;
            }
        }
    }

    //返回parent后继节点的值,并删除该后继节点
    public int deleteRightTreeMin(Node parent) {
        Node target = parent.right;
        if (target.left != null) {//parent的右子节点有左子节点
            parent = parent.right;
            target = target.left;
            while (target.left != null) {//循环的查找左节点,直到找到最小值
                parent = parent.left;
                target = target.left;
            }
            parent.left = target.right;//删除该最小值节点,此时target没有左右子节点或只有右节点
        } else {//target.left == null,parent的右子节点没有有左子节点
            parent.right = target.right;//删除该最小值节点,此时target没有左右子节点或只有右节点
        }
        return target.value;
    }

    //返回parent前驱节点的值,并删除该前驱节点
    public int deleteLeftTreeMax(Node parent) {
        Node target = parent.left;
        if (target.right != null) {//parent的左子节点有右子节点
            parent = parent.left;
            target = target.right;
            while (target.right != null) {//循环的查找右节点 直到找到最大值
                parent = parent.right;
                target = target.right;
            }
            parent.right = target.left;//删除该最大值节点,此时target没有左右子节点或只有左节点
        } else {//target.right == null,parent的左子节点没有有右子节点
            parent.left = target.left;//删除该最大值节点,此时target没有左右子节点或只有左节点
        }
        return target.value;
    }
}

class Node {
    public int value;
    public Node left;
    public Node right;

    public Node(int value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return "Node{" +
                "value=" + value +
                '}';
    }

    //返回左子树的高度
    public int leftHeight() {
        return left == null ? 0 : left.height();
    }

    //返回右子树的高度
    public int rightHeight() {
        return right == null ? 0 : right.height();
    }

    // 返回以该结点为根结点的树的高度
    public int height() {
        return Math.max(left == null ? 0 : left.height(), right == null ? 0 : right.height()) + 1;
    }

    //左旋转
    public Node leftRotate(Node root) {
        Node newRoot = root.right;
        root.right = newRoot.left;
        newRoot.left = root;
        return newRoot;
    }

    //右旋转
    public Node rightRotate(Node root) {
        Node newRoot = root.left;
        root.left = newRoot.right;
        newRoot.right = root;
        return newRoot;
    }

    //封装旋转方法,root调用
    public Node rotate() {
        if (rightHeight() - leftHeight() > 1) {//判断是否要左旋转
            if (right != null && right.leftHeight() > right.rightHeight()) {//判断左旋转前是否要右旋转
                right = rightRotate(right);//对右子结点进行右旋转
            }
            return leftRotate(this);//对当前结点进行左旋转
        } else if (leftHeight() - rightHeight() > 1) {//判断是否要右旋转
            if (left != null && left.rightHeight() > left.leftHeight()) {//判断右旋转前是否要左旋转
                left = leftRotate(left);//对左子结点进行左旋转
            }
            return rightRotate(this);//对当前结点进行右旋转
        }
        return this;
    }

    //添加节点
    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 (node.value > this.value){
            if (this.right == null) {
                this.right = node;
            } else {
                this.right.add(node);
            }
        } else {//若是相同节点,则不变
            return;
        }
    }

    //查找节点,value:节点的值;找到则返回该节点,否则返回null
    public Node search(int value) {
        if (value == this.value) {//找到该节点
            return this;
        } else if (value < this.value) {
            if (this.left == null) {
                return null;
            } else {
                return this.left.search(value);//向左子树递归查找
            }
        } else {//value >= this.value
            if (this.right == null) {
                return null;
            } else {
                return this.right.search(value);//向右子树递归查找
            }
        }
    }

    //查找节点的父节点,value:节点的值;找到返回节点的父节点,否则返回null
    public Node searchParent(int value) {
        if (this.left != null && this.left.value == value || this.right != null && this.right.value == value) {
            return this;//当前节点即为要删除节点的父节点
        } else {
            if (value < this.value && this.left != null) {
                return this.left.searchParent(value);//递归的向左子树查找
            } else if (value >= this.value && this.right != null) {
                return this.right.searchParent(value);//递归的向右子树查找
            } else {
                return null;//没有父节点
            }
        }
    }
}

 

posted @   半条咸鱼  阅读(89)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
点击右上角即可分享
微信分享提示