BST【二叉查找树】和RBT【红黑树】浅析

141 阅读1分钟

这是我参与8月更文挑战的第22天,活动详情查看:8月更文挑战

二叉搜索树

根据comparator进行比较,

大的在左边或大的在右边

实现代码

public class BSTree {
​
    BSTreeNode tree;
​
​
​
    public void insert(int val){
        insertVal(tree,val);
    }
​
    public boolean findVal(int val){
        return findNode(val) != null;
    }
​
    private BSTreeNode findMinNode(BSTreeNode head){
        while(head!=null) head = head.left;
        return head;
    }
​
    private BSTreeNode findMaxNode(BSTreeNode head){
        while(head.right!=null) head = head.right;
        return head;
    }
​
    BSTreeNode findNode(int val){
        BSTreeNode cur = tree;
        while(cur !=null){
            if(val == cur.val) return cur;
            if(val < cur.val) cur = cur.left;
            else cur = cur.right;
        }
        return null;
    }
​
​
    
}

节点定义:

private class BSTreeNode {
    int val;
    BSTreeNode left;
    BSTreeNode right;
    BSTreeNode prev;
​
    public BSTreeNode(int val) {
        this.val = val;
    }
}

插入:

  • 插入的操作其实很简单,只要根据大小,循环往下,找到为null的地方赋值即可。
void insertVal(BSTreeNode head, int val){
    BSTreeNode levelPrev = head,level = head;
    while(null != level){
        levelPrev = level;
        if(level.val <= val){
            level = level.right;
        }else{
            level = level.left;
        }
    }
    level = new BSTreeNode(val);
    level.prev = levelPrev;
    if(levelPrev.val < val) levelPrev.right = level;
    else levelPrev.left = level;
    }

删除:

  • 删除第一步,找到节点,再根据节点状态进行下一步处理

    • 删除替代节点的查找:

      • 左边最大的,或者右边最小的,其实都是可以的

        • 左边最大的:左子树的右右..右节点
        • 右边最小的:右子树的左左..左节点
public void remove(int val){
    BSTreeNode removeNode = findNode(val);
    if(null == removeNode) return;
    if(null == removeNode.prev){//root - make it null
        tree = null;
        return;
    }
    boolean left = removeNode.val <= removeNode.prev.val;
    BSTreeNode preNode = removeNode.prev;
    if(null == removeNode.left && null == removeNode.right){//both null
        if(left) removeNode.prev.left = null;
        else removeNode.prev.right = null;
        return;
    }
    if(null == removeNode.left || null == removeNode.right){//either is null - change ptr only
        if(left) preNode.left = null == removeNode.left?removeNode.right:removeNode.left;
        else preNode.right = null == removeNode.left?removeNode.right:removeNode.left;
​
    }else{//both exist
        if(left) {//left
            //can be replaced by findMaxNode(removeNode.left)
            BSTreeNode replacement = findMinNode(removeNode.right);
            BSTreeNode replacementPre = replacement.prev;
            replacement.prev = preNode;
            preNode.left = replacement;
            //
            replacementPre.left = replacement.right;
            replacement.right = removeNode.right;
            replacement.left = removeNode.left;
        }
        else{//right
            BSTreeNode replacement = findMaxNode(removeNode.left);
            BSTreeNode replacementPre = replacement.prev;
            replacement.prev = preNode;
            preNode.right = replacement;
            //
            replacementPre.right = replacement.left;
            replacement.left = removeNode.left;
            replacement.right = removeNode.right;
        }
    }
}

红黑树

红黑树定义和性质

红黑树是一种含有红黑结点并能自平衡的二叉查找树。它必须满足下面性质:

  • 性质1:每个节点要么是黑色,要么是红色。
  • 性质2:根节点是黑色。
  • 性质3:每个叶子节点(NIL)是黑色。
  • 性质4:每个红色结点的两个子结点一定都是黑色。
  • 性质5:任意一结点到每个叶子结点的路径都包含数量相同的黑结点。
相关关系描述
                P
         /   \
        PL   PR
              / \
             C   B

B为插入且未平衡的节点,则:

  • P为祖父节点
  • PR为父节点
  • PL为伯父节点
HashMap中RBTree的实现

在java-hashMap的实现中:

  • 节点的红黑值是通过boolean值来确定的,因此性质1是不需要检查的
  • TreeNode的节点初始化时,左右节点都是null,null值默认是黑,因此性质3也不需要检查
  • 在平衡检查中,预计插入的节点一开始就赋值为红

结构操作

左旋
  • 左旋

A(pp) A(pp)

| |

B(p) --------- >>>> C(r)

\ /

C(r) B(p)

/ \

D(RL) D(rl)

左旋就是将原本的右侧子节点作为父节点,左旋就意味着将旋转的节点变成了左节点

右旋

pp pp

| |

P --------------》 L

/ \

L P

\ /

LR LR

类似上面的,右转就是把当前节点和子节点地位互换,其他地方的结构不变。