【数据结构-7】二叉搜索树/AVL树/红黑树

学习目标:

掌握基于二叉树变化的数据结构


学习内容:

例如:

  1. 二叉搜索树
  2. AVL树
  3. 红黑树

二叉搜索树

构建

public class BSTree {
    static class BSTNode {
        int key;
        Object value;
        BSTNode left;
        BSTNode right;

        public BSTNode(int key) {
            this.key = key;
        }

        public BSTNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public BSTNode(int key, Object value, BSTNode left, BSTNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }
    }

    BSTNode root;
}

根据key获取值

    public Object get(int key) {
        BSTNode node = root;
        while (node != null) {
            if (key < node.key) {
                node = node.left;
            } else if (key > node.key) {
                node = node.right;
            } else {
                return node.value;
            }
        }
        return null;
    }

返回最大最小值

    public Object min(){
        if(root == null)return null;
        BSTNode p = root;
        while(p.left!=null){
            p = p.left;
        }
        return p.value;
    }

    public Object max(){
        if(root==null)return  null;
        BSTNode p = root;
        while(p.right!=null){
            p = p.right;
        }
        return p.value;
    }

put(有则覆盖,无则存入)

public void put(int key,Object value){
    BSTNode p = root;
    BSTNode parent = null;
    while(p!=null){
        if(key<p.key){
            parent = p;
            p = p.left;
        } else if (key>p.key) {
            parent = p;
            p = p.right;
        }else {
            //找到了
            p.value = value;
            return;
        }
    }
    if(parent==null){
        root = new BSTNode(key, value);
        return;
    }

    //没找到
    if(key<parent.key){
        parent.left = new BSTNode(key,value);
    }else{
        parent.right = new BSTNode(key,value);
    }
}

找前任

需要重写max()和min(),改成有参方法

public Object predecessor(int key) {
    BSTNode p = root;
    BSTNode node = null;
    while (p != null) {
        if (key < p.key) {
            p = p.left;
        } else if (key > p.key) {
            node = p;
            p = p.right;
        } else {
            break;
        }
    }
    //没找到节点
    if (p == null) {
        return null;
    }
    /*
     * 找到节点
     * 情况1.节点有左子树,返回左子树最大值
     * 情况2:节点没有左子树,返回离它最近的,自左而来的祖先
     * */
    if (p.left != null) {
        return max(p.left);
    }
    return node != null ? node.value : null;
}

找后继

public Object successor(int key) {
    BSTNode p = root;
    BSTNode node = null;
    while (p != null) {
        if (key < p.key) {
            node = p;
            p = p.left;
        } else if (key > p.key) {
            p = p.right;
        } else {
            break;
        }
    }
    //没找到节点
    if (p == null) {
        return null;
    }
    if (p.right != null) {
        return min(p.right);
    }
    return node != null ? node.value : null;
}

根据关键字删除

public BSTNode delete(int key) {
    BSTNode p = root;
    BSTNode parent = null;
    while (p != null) {
        if (key < p.key) {
            parent = p;
            p = p.left;
        } else if (key > p.key) {
            parent = p;
            p = p.right;
        } else {
            break;
        }
    }
    if (p == null) {
        return null;
    }
    //执行删除操作
    if (p.left == null) {
        //情况1:左孩子空,右孩子非空
        //情况3:被删除节点左右孩子为空,已经涵盖在内
        shift(parent, p, p.right);
    }
    if (p.right == null) {
        //情况2:左孩子非空,右孩子空
        shift(parent, p, p.left);
    } else {
        //情况4:左右孩子都有
        //4.1被删除节点的后继结点
        BSTNode s = p.right;
        BSTNode sParent = p;
        while (s.left != null) {
            sParent = s;
            s = s.left;
        }
        //4.2处理后继节点后事
        if (sParent != p) {
            //不相邻
            shift(sParent, s, s.right);
            s.right = p.right;
        }
        //4.3后继取代被删除节点
        shift(parent,p,s);
        s.left = p.left;
    }
    return p;
}

/*
 * 托孤方法
 * */
private void shift(BSTNode parent, BSTNode deleted, BSTNode child) {
    if (parent == null) {
        root = child;
    } else if (parent.left == deleted) {
        parent.left = child;
    } else {
        parent.right = child;
    }
}

范围查询

查询小于key的所有key对应的值

public List<Object> less(int key){
    ArrayList<Object> result = new ArrayList<>();
    BSTNode p = root;
    LinkedList<BSTNode> stack = new LinkedList<>();
    while(p!=null||!stack.isEmpty()){
        if(p!=null){
            stack.push(p);
            p = p.left;
        }else {
            BSTNode pop = stack.pop();
            //处理值
            if(pop.key<key){
                result.add(pop.value);
            }else {
                break;
            }
            p = pop.right;
        }
    }
    return result;
}

二叉搜索树相关练习

查找最近公共祖先节点

public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
    //如果p和q在a的两侧,说明a是p和q的最近祖先节点
    TreeNode a = root;
    while (p.val < a.val && q.val < a.val || p.val > a.val && q.val > a.val) {
        if (p.val < a.val){
            a = a.left;
        }else{
            a = a.right;
        }
    }
    return a;
}

验证二叉搜索树

static ArrayList<Integer> list = new ArrayList<>();

//验证二叉搜索树
public static boolean isBSTree(TreeNode root) {
    inOrder(list, root);
    System.out.println(list);
    for (int i = 0; i < list.size() - 1; i++) {
        if (list.get(i) > list.get(i + 1)) {
            return false;
        }
    }
    return true;
}

private static void inOrder(ArrayList<Integer> list, TreeNode root) {
    if (root == null) return;
    inOrder(list, root.left);
    list.add(root.val);
    inOrder(list, root.right);
}

范围求和

//求范围和
int sum = 0;

public int sumRange(TreeNode root, int low, int high) {
    TreeNode p = root;
    inOrder(root,low,high);
    return sum;
}

private void inOrder(TreeNode node, int low, int high) {
    if (node == null) return;
    inOrder(node.left, low, high);
    if (node.val > high) {
        return;
    }
    if (node.val > low) {
        sum += node.val;
    }
    inOrder(node.right, low, high);
}

AVL树

AVL树:自平衡二叉搜索树,任意左右子树高度差不超过1,插入和删除节点时自动平衡

构造AVL树

public class AVLTree {
    static class AVLNode {
        int key;
        Object value;
        AVLNode left;
        AVLNode right;
        int height = 1;

        public AVLNode(int key, Object value) {
            this.key = key;
            this.value = value;
        }

        public AVLNode(int key) {
            this.key = key;
        }

        public AVLNode(int key, Object value, AVLNode left, AVLNode right) {
            this.key = key;
            this.value = value;
            this.left = left;
            this.right = right;
        }

    }

    //求节点高度(处理node==null的高度)
    private int height(AVLNode node) {
        if (node == null) return 0;
        return node.height;
    }

    //更新节点高度(新增,删除,旋转)
    private void updateHeight(AVLNode node) {
        node.height = Math.max(height(node.left), height(node.right)) + 1;
    }

    //平衡因子(balance factor) = 左子树高度-右子树高度
    private int bf(AVLNode node) {
        // 0 1 -1 都是平衡的,其他值都是不平衡
        return height(node.left) - height(node.right);
    }

    //参数:要旋转的节点,返回值:新的根节点
    private AVLNode rightRotate(AVLNode red) {
        AVLNode yellow = red.left;
        AVLNode green = yellow.right;
        yellow.right = red; //上位
        red.left = green; //换爹
        updateHeight(red);
        updateHeight(yellow);
        return yellow;
    }

    private AVLNode leftRotate(AVLNode red) {
        AVLNode yellow = red.right;
        AVLNode green = yellow.left;
        yellow.left = red;
        red.right = green;
        updateHeight(red);
        updateHeight(yellow);
        return yellow;
    }

    //先左旋左子树,再右旋根节点
    private AVLNode leftRightRotate(AVLNode node) {
        node.left = leftRotate(node.left); //更新左子节点
        return rightRotate(node);
    }

    //先右旋右子树,再左旋根节点
    private AVLNode rightLeftRotate(AVLNode node) {
        node.right = rightRotate(node.right);
        return leftRotate(node);
    }

    //检查节点是否失衡,重新平衡代码
    private AVLNode balance(AVLNode node) {
        if (node == null) {
            return null;
        }
        int bf = bf(node);

        if (bf > 1 && bf(node.left) >= 0) { // LL
            return rightRotate(node);
        } else if (bf > 1 && bf(node.left) < 0) { // LR
            return leftRightRotate(node);
        } else if (bf < -1 && bf(node.right) > 0) { // RL
            return rightLeftRotate(node);
        } else if (bf < -1 && bf(node.right) <= 0) { // RR
            return leftRightRotate(node);
        }
        return node;
    }

}

新增

public void put(int key,Object value){
    root = doPut(root,key,value);
}

private AVLNode doPut(AVLNode node,int key,Object value){
    //1.找到空位,创建新节点返回
    if(node == null){
        return new AVLNode(key,value);
    }
    //2.key已经存在,直接更新
    if(key == node.key){
        node.value = value;
        return node;
    }
    //3.继续查找
    if(key<node.key){
        node.left = doPut(node.left,key,value);
    }else {
        node.right = doPut(node.right,key,value);
    }
    updateHeight(node);
    return balance(node);
}

删除

private AVLNode doRemove(AVLNode node, int key) {
    //1.node==null
    if (node == null) {
        return null;
    }
    //2.没找到key
    if (key < node.key) {
        node.left = doRemove(node.left, key);
    } else if (key > node.key) {
        node.right = doRemove(node.right, key);
    } else {
        //3.找到key 3.1没有孩子 3.2只有一个孩子 3.3有两个孩子
        if (node.left == null && node.right == null) {
            return null;
        } else if (node.left == null) {
            node = node.right;
        } else if (node.right == null) {
            node = node.left;
        } else {
            AVLNode s = node.right;
            while (s.left != null) {
                s = s.left;
            }
            //s为后继节点
            s.right = doRemove(node.right, s.key);
            s.left = node.left;
            node = s;
        }
    }

    //4.更新高度
    updateHeight(node);
    //5.balance
    return balance(node);
}


红黑树

红黑树也是一种自平衡的二叉搜索树,相比于AVL,插入和删除时旋转次数更少。

特点:
1.所有节点都有两种颜色:红和黑
2.所有null视为黑色
3.红色节点不能相邻
4.根节点是黑色
5.从根到任意一个叶子节点,路径中黑色节点数一样

【代码建议结合演示图分析】

public class RedBlackTree {
    enum Color {
        RED, BLACK;
    }

    private Node root;

    private static class Node {
        int key;
        Object value;
        Node left;
        Node right;
        Node parent; //父节点
        Color color = RED; //新节点默认红色

        //是否是左孩子
        boolean isLeftChild() {
            return parent != null && parent.left == this;
        }

        //叔叔
        Node uncle() {
            if (parent == null || parent.parent == null) {
                return null;
            }
            if (parent.isLeftChild()) {
                return parent.parent.right;
            } else {
                return parent.parent.left;
            }
        }

        //兄弟
        Node sibling() {
            if (parent == null) {
                return null;
            }
            if (this.isLeftChild()) {
                return parent.right;
            } else {
                return parent.left;
            }
        }
    }

    //判断红黑
    boolean isRed(Node node) {
        return node != null && node.color == RED;
    }

    boolean isBlack(Node node) {
        return node == null || node.color == BLACK;
    }

    //右旋 1.parent的处理 2.旋转后新根的父子关系
    private void rightRotate(Node pink) {
        Node parent = pink.parent;
        Node yellow = pink.left;
        Node green = yellow.right;
        if (green != null) {
            green.parent = pink;
        }
        yellow.right = pink;
        yellow.parent = parent;
        pink.left = green;
        pink.parent = yellow;
        if(parent==null){
            root = yellow;
        }else if (parent.left == pink) {
            parent.left = yellow;
        } else {
            parent.right = yellow;
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值