二叉树-红黑树

二叉排序树是一种特殊的二叉树,其中左子树上的所有节点值小于根节点,右子树上的节点值大于或等于根节点。红黑树是二叉查找树的一种,通过颜色标记和特定规则保持平衡,确保插入、删除和查找操作的时间复杂度为O(logn)。在Java集合框架中,如HashMap、TreeMap等使用了红黑树,提供高效性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

二叉树

  • 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  • 若右子树不空,则右子树上所有结点的值均大于或等于它的根结点的值;
  • 左、右子树也分别为二叉排序树。

这里写图片描述

也就是说,二叉排序树中,左子树都比节点小,右子树都比节点大,递归定义。

根据二叉排序树这个特点我们可以知道,二叉排序树的中序遍历一定是从小到大的,比如上图,中序遍历结果是:

1 3 4 6 7 8 10 13 14

性能取决于

  • 最好的情况是 O(logn),存在于完全二叉排序树情况下,其访问性能近似于折半查找;
  • 最差时候会是 O(n),比如插入的元素是有序的,生成的二叉排序树就是一个链表,这种情况下,需要遍历全部元素才行(见下图 b)。
public class Demo {
    public static void main(String[] args) {
        //乱序插入到二叉排序树中
        BinarySearchTree binarySearchTree = new BinarySearchTree(null);
        binarySearchTree.insert(8);
        binarySearchTree.insert(3);
        binarySearchTree.insert(1);
        binarySearchTree.insert(6);
        binarySearchTree.insert(4);
        binarySearchTree.insert(7);
        binarySearchTree.insert(10);
        binarySearchTree.insert(13);
        binarySearchTree.insert(14);

        binarySearchTree.iterateMediumOrder(binarySearchTree.getRoot());
        System.out.println("");
        System.out.println(binarySearchTree.search(10).getData());
        binarySearchTree.delete(6);
        binarySearchTree.iterateMediumOrder(binarySearchTree.getRoot());

    }

}

class BinaryTreeNode{
    private int data;
    private BinaryTreeNode leftChild;
    private BinaryTreeNode rightChild;

    public BinaryTreeNode(int data, BinaryTreeNode leftChild, BinaryTreeNode rightChild) {
        this.data = data;
        this.leftChild = leftChild;
        this.rightChild = rightChild;
    }
    public BinaryTreeNode() {
    }

    public int getData() {
        return data;
    }

    public void setData(int data) {
        this.data = data;
    }

    public BinaryTreeNode getLeftChild() {
        return leftChild;
    }

    public void setLeftChild(BinaryTreeNode leftChild) {
        this.leftChild = leftChild;
    }

    public BinaryTreeNode getRightChild() {
        return rightChild;
    }

    public void setRightChild(BinaryTreeNode rightChild) {
        this.rightChild = rightChild;
    }
}

class BinarySearchTree{
    public BinaryTreeNode getRoot() {
        return root;
    }

    public void setRoot(BinaryTreeNode root) {
        this.root = root;
    }

    private BinaryTreeNode root;
    public BinarySearchTree(BinaryTreeNode root){
     this.root=root;
    }

    /**
     * 前序遍历
     * @param node
     */
    public void iterateFirstOrder(BinaryTreeNode node){
        if (node == null){
            return;
        }
        System.out.println(node.getData());
        iterateFirstOrder(node.getLeftChild());
        iterateFirstOrder(node.getRightChild());
    }

    /**
     * 中序遍历
     * @param node
     */
    public void iterateMediumOrder(BinaryTreeNode node){
        if (node == null){
            return;
        }
        iterateMediumOrder(node.getLeftChild());
        System.out.println(node.getData());
        iterateMediumOrder(node.getRightChild());
    }

    /**
     * 后续遍历
     * @param node
     */
    public void iterateLastOrder(BinaryTreeNode node){
        if (node == null){
            return;
        }
        iterateLastOrder(node.getLeftChild());
        iterateLastOrder(node.getRightChild());
        System.out.println(node.getData());
    }

    public BinaryTreeNode search(int data){
        if(root==null||root.getData()==data){
            return root;
        }
        if(data<root.getData()){
            return search(root.getLeftChild(),data);
        }else{
            return search(root.getRightChild(),data);
        }
    }

    /**
     * 二叉树查找
     * 大于根节点 递归 左树
     * 小于 递归右树
     * 近似于二分查找
     */
    public BinaryTreeNode search(BinaryTreeNode root,int data){
        if(root==null||root.getData()==data){
            return root;
        }
        if(data<root.getData()){
            return search(root.getLeftChild(),data);
        }else{
            return search(root.getRightChild(),data);
        }
    }

    public BinaryTreeNode insert(int data){
        if(root==null){
            root=new BinaryTreeNode();
            root.setData(data);
            return root;
        }
        return searchAndInsert(null,root,data);
    }
    /**
     * 先查找有没有整个元素,有的话就不用插入了,直接返回;
     * 没有就插入到之前查到(对比)好的合适的位置。
     * 需要与父节点绑定
     * @param parent 父节点
     * @param node 当前比较节点
     * @param data
     * @return
     */
    public BinaryTreeNode searchAndInsert(BinaryTreeNode parent,BinaryTreeNode node,int data){
        if(node==null){
            node=new BinaryTreeNode();
            node.setData(data);
            if(parent!=null){
                if(data<parent.getData()){
                    parent.setLeftChild(node);//小于放在 左节点
                }else{
                    parent.setRightChild(node);//大于放在右节点
                }
            }
            return node;
        }
        if(node.getData()==data){
            return node;//已经存在
        }else if(data<node.getData()){//递归
            return searchAndInsert(node,node.getLeftChild(),data);
        }else{
            return searchAndInsert(node,node.getRightChild(),data);
        }
    }


    /**
     * 在指定节点下 查找指定数据节点的父亲节点
     *
     * @param parent 当前比较节点的父节点
     * @param node   当前比较的节点
     * @param data   查找的数据
     * @return
     */
    public BinaryTreeNode searchParent(int data) {
        return searchParent(null, root, data);
    }
    public BinaryTreeNode searchParent(BinaryTreeNode parent, BinaryTreeNode node, int data) {
        if (node == null) { //比较的节点为空返回空
            return null;
        }
        if (node.getData() == data) {    //找到了目标节点,返回父节点
            return parent;
        } else if (data < node.getData()) {   //数据比当前节点小,左子树中递归查找
            return searchParent(node, node.getLeftChild(), data);
        } else {
            return searchParent(node, node.getRightChild(), data);
        }
    }

    /**
     * 删除指定数据的节点
     *      * 删除
     *      * 如果要删除的节点正好是叶子节点,直接删除;
     *      * 如果要删除的节点还有子节点,就需要建立父节点和子节点的关系:
     *      * 如果只有左孩子或者右孩子,直接把这个孩子上移放到要删除的位置就好了;
     *      * 如果有两个孩子,就需要选一个合适的孩子节点作为新的根节点,该节点称为 继承节点。
     *      * 新节点要求要比所有左子树大,比所有右子树小,怎么选择呢?
     *      * **要比所有左子树的值大、右子树小,就从右子树里找最小的好了;
     *      * 同样也可以从左子树里找最大的。**
     *      * 两种选择方法都可以,本文选用右子树里最小的节点,也就是右子树中最左边的节点。
     *      *
     *
     */
    public void delete(int data) {
        if (root == null || root.getData() == data) {  //根节点为空或者要删除的就是根节点,直接删掉
            root = null;
            return;
        }
        //在删除之前需要找到它的父亲
        BinaryTreeNode parent = searchParent(data);
        if (parent == null) {        //如果父节点为空,说明这个树是空树,没法删
            return;
        }
        //接下来该找要删除的节点了
        BinaryTreeNode deleteNode = search(parent, data);
        if (deleteNode == null) {    //树中找不到要删除的节点
            return;
        }
        //删除节点有 4 种情况
        //1.左右子树都为空,说明是叶子节点,直接删除
        if (deleteNode.getLeftChild() == null && deleteNode.getRightChild() == null) {
            //删除节点
            deleteNode = null;
            //重置父节点的孩子状态,告诉他你以后没有这个儿子了
            if (parent.getLeftChild() != null && parent.getLeftChild().getData() == data) {
                parent.setLeftChild(null);
            } else {
                parent.setRightChild(null);
            }
            return;
        } else if (deleteNode.getLeftChild() != null && deleteNode.getRightChild() == null) {
            //2.要删除的节点只有左子树,左子树要继承位置
            if (parent.getLeftChild() != null && parent.getLeftChild().getData() == data) {
                parent.setLeftChild(deleteNode.getLeftChild());
            } else {
                parent.setRightChild(deleteNode.getLeftChild());
            }
            deleteNode = null;
        } else if (deleteNode.getRightChild() != null && deleteNode.getRightChild() == null) {
            //3.要删除的节点只有右子树,右子树要继承位置
            if (parent.getLeftChild() != null && parent.getLeftChild().getData() == data) {
                parent.setLeftChild(deleteNode.getRightChild());
            } else {
                parent.setRightChild(deleteNode.getRightChild());
            }
            deleteNode = null;
        } else {
            //4.要删除的节点儿女双全,既有左子树又有右子树,需要选一个合适的节点继承,这里使用右子树中最左节点
            BinaryTreeNode copyOfDeleteNode = deleteNode;   //要删除节点的副本,指向继承节点的父节点
            BinaryTreeNode heresNode = deleteNode.getRightChild(); //要继承位置的节点,初始为要删除节点的右子树的树根
            //右子树没有左孩子了,他就是最小的,直接上位
            if (heresNode.getLeftChild() == null) {
                //上位后,兄弟变成了孩子
                heresNode.setLeftChild(deleteNode.getLeftChild());
            } else {
                //右子树有左孩子,循环找到最左的,即最小的
                while (heresNode.getLeftChild() != null) {
                    copyOfDeleteNode = heresNode;       //copyOfDeleteNode 指向继承节点的父节点
                    heresNode = heresNode.getLeftChild();
                }
                //找到了继承节点,继承节点的右子树(如果有的话)要上移一位
                copyOfDeleteNode.setLeftChild(heresNode.getRightChild());
                //继承节点先继承家业,把自己的左右孩子变成要删除节点的孩子
                heresNode.setLeftChild(deleteNode.getLeftChild());
                heresNode.setRightChild(deleteNode.getRightChild());
            }
            //最后就是确认位置,让要删除节点的父节点认识新儿子
            if (parent.getLeftChild() != null && parent.getLeftChild().getData() == data) {
                parent.setLeftChild(heresNode);
            } else {
                parent.setRightChild(heresNode);
            }
        }
    }
}

 

红黑树

https://blog.csdn.net/u011240877/article/details/53329023

红黑树本质上是一种二叉查找树,但它在二叉查找树的基础上额外添加了一个标记(颜色),同时具有一定的规则。这些规则使红黑树保证了一种平衡,插入、删除、查找的最坏时间复杂度都为 O(logn)。

它的统计性能要好于平衡二叉树(AVL树),因此,红黑树在很多地方都有应用。比如在 Java 集合框架中,很多部分(HashMap, TreeMap, TreeSet 等)都有红黑树的应用,这些集合均提供了很好的性能。

黑色高度:从根节点到叶节点的路径上黑色节点的个数,叫做树的黑色高度。

shixinzhang

  1. 每个节点要么是红色,要么是黑色;
  2. 根节点永远是黑色的;
  3. 所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);
  4. 每个红色节点的两个子节点一定都是黑色;
  5. 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值