二叉搜索树和红黑树的操作

二叉搜索树

性质:

1.对于二叉搜索树,对于任意节点,左子树上的节点值都小于该节点值,右子树上的节点值都大于该节点值。
2.对二叉搜索树进行中序遍历可以得到有序序列

定义:

public class BSTreeNode<T extends Comparable<T>> {

    public T val;

    public BSTreeNode<T> left;

    public BSTreeNode<T> right;

    public BSTreeNode<T> parent;

    public BSTreeNode(T val) {
        this.val = val;
    }

    @Override
    public String toString() {
        return val.toString();
    }
}

操作

1.查找

直接二分查找,上代码:

public BSTreeNode<T> find(T val) {
    return doFind(root, val);
}
private BSTreeNode<T> doFind(BSTreeNode<T> root, T val) {
    if (root == null) {
        return null;
    }
    if (root.val.equals(val)) {
        return root;
    } else if (val.compareTo(root.val) > 0) {
        return doFind(root.right, val);
    } else {
        return doFind(root.left, val);
    }
}
2.遍历

递归算法实现遍历,代码如下:

public List<T> inorderTravel() {
    List<T> res = new ArrayList<>();
    doInorderTravel(root, res);
    return res;
}
private void doInorderTravel(BSTreeNode<T> root, List<T> res) {
    if (root == null) {
        return;
    }
    doInorderTravel(root.left, res);
    res.add(root.val);
    doInorderTravel(root.right, res);
}
3.插入

递归插入,如果已经存在,结束操作,代码如下:
注意,这里处理parent,主要是为了删除的时候使用。

public BSTreeNode<T> insert(BSTreeNode<T> root, T val) {
   if (root == null) {
       return new BSTreeNode<>(val);
   }
   if (val.equals(root.val)) {
       return root;
   } else if (val.compareTo(root.val) > 0) {
       root.right = insert(root.right, val);
       root.right.parent = root;
   } else if (val.compareTo(root.val) < 0) {
       root.left = insert(root.left, val);
       root.left.parent = root;
   }
   return root;
}
4.删除

分三种不同情况:
(1)如果删除的节点是叶子结点;
直接删除即可
(2)如果删除的节点只有左子树或者右子树;
替换节点
(3)如果删除的节点包含了左子树和右子树;
找到后继节点并替换
代码如下:

public void delete(T val) {
    doDelete(root, val);
}
private void doDelete(BSTreeNode<T> root, T val) {
    if (root== null) {
        return;
    }
    if (val.compareTo(root.val) > 0) {
        doDelete(root.right, val);
    } else if (val.compareTo(root.val) < 0) {
        doDelete(root.left, val);
    } else {
        // case1:叶子结点,直接删除
        if (root.left == null && root.right == null) {
            if (root.parent.left == root) {
                root.parent.left = null;
            } else if (root.parent.right == root) {
                root.parent.right = null;
            }
        } else if (root.left == null) {
            // case2:只有右子树,没有左子树
            transplant(root, root.right);
        } else if (root.right == null) {
            // case3:只有左子树,没有右子树
            transplant(root, root.left);
        } else {
            // case4:左右子树都有
            BSTreeNode<T> tmp = root.right;
            while (tmp.left != null) {
                tmp = tmp.left;
            }
            if (tmp.parent != root) {
                transplant(tmp, tmp.right);
                tmp.right = root.right;
                tmp.right.parent = tmp;
            }
            transplant(root, tmp);
            tmp.left = root.left;
            tmp.left.parent = tmp;
        }
    }
}
private void transplant(BSTreeNode<T> u, BSTreeNode<T> v) {
   if (u.parent == null) {
        return;
    }
    if (u == u.parent.left) {
        u.parent.left = v;
    } else {
        u.parent.right = v;
    }
    if (v != null) {
        v.parent = u.parent;
    }
}

红黑树

性质:

一颗红黑树是满足下面红黑性质的二叉搜索树: 1、每个节点是红色或者黑色的; 2、根节点是黑色的; 3、每个叶子结点是黑色的;
4、如果一个节点是红色的,它的两个子节点都是黑色的; 5、对每个节点,从该节点到其所有后代也节点的简单路径上,均包含相同数目的黑色节点。

定义:

public class RBTreeNode {

    public int val;
    public RBTreeNode left;
    public RBTreeNode right;
    public RBTreeNode parent;
    public boolean red;

    public RBTreeNode(int val) {
        this.val = val;
    }
}

操作

1.查找

递归查找,代码如下:

public RBTreeNode find(int target) {
    return doFind(root, target);
}
2.旋转

画图操作:
在这里插入图片描述

public void leftRotate(RBTreeNode u) {
    if (u == null) {
        return;
    }
    RBTreeNode v = u.right;
    if (u.parent == null) {
        root = v;
    } else if (u.parent.left == u) {
        u.parent.left = v;
    } else if (u.parent.right == u) {
        u.parent.right = u;
    }
    v.parent = u.parent;
    u.parent = v;
    u.right = v.left;
    if (v.left != null) {
        v.left.parent = u;
    }
    v.left = u;
}
3.插入

红黑树新增节点一定是红色的,它插入到红黑树的时候有三种情况:
1.如果新增节点的叔叔节点是红色的;
变色,转移到情况2
在这里插入图片描述

2.如果新增节点的叔叔节点是黑色的并且新增节点是一个右孩子;
左旋,转移到情况3
在这里插入图片描述

3.如果新增节点的叔叔节点是黑色的并且新增节点是一个左孩子;
变色,右旋
在这里插入图片描述

右旋
在这里插入图片描述

代码:
首先找到插入位置

public RBTreeNode doInsert(RBTreeNode root, int val) {
    RBTreeNode newNode = new RBTreeNode(val);
    RBTreeNode node = root;
    RBTreeNode tmp = null;
    while (node != null) {
        tmp = node;
        if (val < node.val) {
            node = node.left;
        } else {
            node = node.right;
        }
    }
    newNode.parent = tmp;
    if (tmp == null) {
        root = newNode;
    } else if (val < tmp.val) {
        tmp.left = newNode;
    } else {
        tmp.right = newNode;
    }
    newNode.left = null;
    newNode.right = null;
    // 红黑树的性质4,保持自平衡
    newNode.red = true;
    insertFixup(root, newNode);
    return root;
}

随后进行变色,保持红黑树的性质
代码:

while (newNode != root && newNode.parent != root && newNode.parent.red) {
    RBTreeNode tmp;
    // 找到叔叔节点,记为tmp
    if (newNode.parent == newNode.parent.parent.left) {
        tmp = newNode.parent.parent.right;
    } else {
        tmp = newNode.parent.parent.right;
    }
    if (tmp.red) {
        // 第一种情况,父亲节点和叔叔节点都为红色,修改颜色,指针上移
        newNode.parent.red = false;
        tmp.red = false;
        newNode.parent.parent.red = true;
        newNode = newNode.parent.parent;
    } else if (newNode == newNode.parent.right) {
        // 第二种情况,父亲节点是红色,但是叔叔节点是黑色,且是右孩子,执行左旋
        newNode = newNode.parent;
        leftRotate(newNode);
    } else {
        // 第三种情况,父亲节点是红色,但是叔叔节点是黑色, 且是左孩子,执行右旋
        newNode.parent.red = false;
        newNode.parent.parent.red = true;
        rightRotate(newNode.parent.parent);
    }
}
root.red = false;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值