数据结构-树-源码分析(二叉查找树, 平衡二叉树)

数据结构-树-源码分析(二叉查找树, 平衡二叉树)

二叉查找树

所有子树满足 : 左子树上所有结点的值 < 根结点的值 < 右子树上所有结点的值

public class QBST<K extends Comparable<K>, V> {
    //树结点
    class QNode {
        K key;
        V value;
        QNode left;//左子树
        QNode right;//右子树

        //构造根结点  若树或子树为空时,创建根节点
        QNode(K key, V value) {
            this.key = key;
            this.value = value;
            this.left = null;
            this.right = null;
        }
    }

    private QNode root;//根结点 树的唯一标识
    private int count;//结点个数

    //构造空二叉树
    public QBST() {
        root = null;
        count = 0;
    }

    //获取结点个数
    public int size() {
        return count;
    }

    //判断二叉树是否为空
    public boolean isEmpty() {
        return count == 0;
    }

    //插入结点
    // 1.树为空则创建根结点
    // 2.key==根节点key, 更新value
    // 3.key<根节点key/key>根节点key 插入左子树/右子树
    public void insert(K key, V value) {
        root = insert(root, key, value);
    }

    //向以node为根的树中插入结点(key, value)
    //返回新的根节点
    private QNode insert(QNode node, K key, V value) {
        //检查key是否为空

        //node为空 创建根节点
        if (node == null) {
            count++;
            return new QNode(key, value);
        }
        //key > 根节点的key
        if (key.compareTo(node.key) == 1) {
            node.right = insert(node.right, key, value);
            //key < 根节点的key
        } else if (key.compareTo(node.key) == -1) {
            node.left = insert(node.left, key, value);
            //key == 根节点的key
        } else {
            node.value = value;
        }
        return null;
    }

}

缺点

会出现以下四种不平衡的情况:

LL型

在这里插入图片描述

LR型在这里插入图片描述
RL型

在这里插入图片描述

RR型

在这里插入图片描述

平衡二叉树

在二叉查找树的基础上:

所有子树满足 : 左右两个子树的高度差的绝对值不超过1

解决方案 : 通过左旋或则右旋让二叉树平衡

  1. LL :右旋 左子变父, 左边的右孙变右边的左孙

在这里插入图片描述

  1. RR : 左旋 右子变父, 右边的左孙变左边的右孙
    在这里插入图片描述

  2. LR : 先对左子树左旋, 再整体右旋

在这里插入图片描述

  1. RL : 先对右子树右旋, 再整体左旋
    在这里插入图片描述
public class BalancedBinaryTree<K extends Comparable<K>, V> {

    private AVLNode root;//根结点 树的唯一标识
    private int count;//结点个数

    //定义二叉树结点
    class AVLNode {
        K key;
        V value;
        public int depth;//结点深度/高度
        public int balance;//平衡度 1:左子树更深; -1:右子树更深; 0:左右平衡
        public AVLNode parent;//父节点 (根节点会发生改变, 需要用到父节点, 类似双向链表)
        public AVLNode left;//左子树
        public AVLNode right;//右子树

        //创建根节点
        public AVLNode(K key, V value) {
            this.key = key;
            this.value = value;
            depth = 1;
            balance = 0;
            left = null;
            right = null;
        }
    }

    //计算结点的深度
    public int cDepth(AVLNode root) {
        int depth = 0;
        if (root.left != null) {
            depth = root.left.depth;
        }
        if (root.right != null && root.right.depth > depth) {
            depth = root.right.depth;
        }
        depth++;
        return depth;
    }

    //计算平衡度
    public int cBalance(AVLNode root) {
        int left_depth = root.left == null ? 0 : root.left.depth;
        int right_depth = root.left == null ? 0 : root.right.depth;
        return left_depth - right_depth;
    }

    //左旋: 右子变父, 右边的左孙 -> 左边的右孙
    public void left_rotate(AVLNode p) {
        AVLNode pParent = p.parent;
        AVLNode pRightSon = p.right;//右子
        AVLNode pGrandLeftSon = p.right.left;//右边的左孙
        //右子变父
        pRightSon.parent = p.parent;
        //判断p是左子树还是右子树
        if (pParent != null) {
            if (pParent.left == p) {
                pParent.left = pRightSon;
            } else {
                pParent.right = pRightSon;
            }
        }
        pRightSon.left = p;
        p.parent = pRightSon;
        //右边的左孙 -> 左边的右孙
        p.right = pGrandLeftSon;
        if (pGrandLeftSon != null) {
            pGrandLeftSon.parent = p;
        }
        //左旋后重新计算结点的深度和平衡度
        // p和pRightSon受到影响
        p.depth = cDepth(p);
        p.balance = cBalance(p);
        pRightSon.depth = cDepth(pRightSon);
        pRightSon.balance = cBalance(pRightSon);
    }

    //右旋: 左子变父, 左边的右孙 -> 右边的左孙
    public void right_rotate(AVLNode p) {
        AVLNode pParent = p.parent;
        AVLNode pLeftSon = p.left;//左子
        AVLNode pGrandRightSon = p.left.right;//左边的右孙
        //左子变父
        pLeftSon.parent = p.parent;
        //判断p是左子树还是右子树
        if (pParent != null) {
            if (pParent.left == p) {
                pParent.left = pLeftSon;
            } else {
                pParent.right = pLeftSon;
            }
        }
        pLeftSon.right = p;
        p.parent = pLeftSon;
        //左边的右孙 -> 右边的左孙
        p.left = pGrandRightSon;
        if (pGrandRightSon != null) {
            pGrandRightSon.parent = p;
        }
        //右旋后重新计算结点的深度和平衡度
        // p和pLeftSon受到影响
        p.depth = cDepth(p);
        p.balance = cBalance(p);
        pLeftSon.depth = cDepth(pLeftSon);
        pLeftSon.balance = cBalance(pLeftSon);
    }

    //插入结点
    public void insert(K key, V value) {
        insert(root, key, value);
    }

    public void insert(AVLNode node, K key, V value) {
        //判断key是否为空

        //key > root.key
        if (key.compareTo(node.key) == 1){
            if (node.right != null ) {
                insert(node.right, key, value);
            } else {
                root.right = new AVLNode(key, value);
                root.right.parent = root;
            }
            //key < root.key
        } else if(key.compareTo(node.key) == -1) {
            if (root.left != null) {
                insert(root.left, key, value);
            } else {
                root.left = new AVLNode(key, value);
                root.left.parent = root;
            }
            //key == root.key
        } else {
            node.value = value;
            return;
        }
        
        //插入结点后修改结点的平衡度
        node.balance = cBalance(node);
        //左树深度大于右树2层, 树不平衡了需要旋转
        //分为两种 1.LL型 2.LR型
        if (node.balance == 2) {
            if (node.left.balance == 1) {
                //LL型 对node右旋
                right_rotate(node);
            } else if(node.left.balance == -1) {
                //LR型 先对左子树左旋, 在对node右旋
                left_rotate(node.left);
                right_rotate(node);
            }
        }
        //右树深度大于左树2层, 树不平衡了需要旋转
        //分为两种 1.RR型 2.RL型
        if (node.balance == -2) {
            if (node.right.balance == -1) {
                //RR型 对node左旋
                left_rotate(node);
            } else if(node.right.balance == 1) {
                //RL型 先对右子树右旋, 在对node左旋
                right_rotate(node.right);
                left_rotate(node);
            }
        }
        //修改深度和平衡度
        node.depth = cDepth(node);
        node.balance = cBalance(node);
    }

}
  • 参考文章链接

https://blog.csdn.net/qq_42419025/article/details/105915670

https://www.cnblogs.com/start1225/p/10182710.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值