查找二叉树

本文详细介绍了二叉搜索树(Binary Search Tree)的概念、性质、遍历方式以及节点的前驱和后继节点查找。提供了一个Java实现的二叉搜索树类,包括插入、删除、查找、遍历等基本操作,并演示了如何找到树中的最大值和最小值。此外,还讲解了删除节点的策略,包括有子节点、无子节点或只有一个子节点的情况。

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

二叉搜索树

Binary Search Tree 能够用于快速查找的存储数据结构,左子树值<根节点值<右子树值

# 定义

它或者是一棵空树,或者是具有下列性质的二叉树:

  • 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  • 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  • 它的左、右子树也分别为二叉排序树。

# 遍历

以遍历根节点的顺序来定义

  • 前序遍历:根-左-右

  • 中序遍历:左-根-右 —>遍历结果为有序递增序列

  • 后序遍历:左-右-根

# 前驱节点和后继节点

前驱节点

定义: 二叉树中数据值小于该结点的"最大结点"。

  • 有左子树,就是左子树的最大值
  • 无左子树
    • 是父节点(不为空)的右子树,父节点为前驱
    • 是父节点的左子树,离它最近的有右孩子的节点即为前驱
后继节点

**定义:**二叉树中数据值大于该结点的"最小结点"。

  • 有右子树,就是右子树的最小值

  • 无右子树

    • 是父节点(不为空)的左子树,父节点为前驱

    • 是父节点的左子树,离它最近的左孩子的节点即为前驱

# 节点删除

  • 被删除节点有前驱: 前驱顶替
  • 被删除节点只有左子树或者右子树:孩子顶替
  • 被删除节点没有子树(根节点或叶子节点):直接删除
package com.piziwang.tree.bstree;

/**
 * 二叉查找树
 * @author PIZIWANG
 * @date 2022-05-04 10:50
 **/
public class BSTree<T extends Comparable<T>> {
    private BSTNode<T> mRoot;

    /**
     * bstnode
     * @author piziwang
     * @date 2022/05/04
     */
    public class BSTNode<T extends Comparable<T>>{
        /*值*/
        T key;
        /*左节点*/
         BSTNode<T> left;
        /*右节点*/
         BSTNode<T> right;
        /*父节点*/
         BSTNode<T> parent;

        public BSTNode(T key, BSTNode<T> left, BSTNode<T> right, BSTNode<T> parent) {
            this.key = key;
            this.left = left;
            this.right = right;
            this.parent = parent;
        }
    }

    /**
     * 插入
     * @param key 关键
     */
    public void insert(T key){
        BSTNode<T> newNode = new BSTNode<T>(key, null,null,null);
        if (newNode != null) {
            insert(this,newNode);
        }
    }

    /**
     * 插入
     * @param bst tbs树
     * @param newNode 新节点
     */
    private void insert(BSTree<T> bst, BSTNode<T> newNode) {
        BSTNode<T> current = bst.mRoot;
        BSTNode<T> currentParent = null;
        /*新插入节点与当前节点的比较标志*/
        int cmp;
        /*找到最终的插入位置*/
        while (current != null) {
            currentParent=current;
            cmp = newNode.key.compareTo(current.key);
            if (cmp < 0) {
                current = current.left;
            }else {
                current = current.right;
            }
        }
        /*插入*/
        newNode.parent = currentParent;
        if(currentParent == null){
            /*是空树*/
            bst.mRoot = newNode;
        }else {
            cmp = newNode.key.compareTo(currentParent.key);
            if (cmp < 0) {
                currentParent.left = newNode;
            }else {
                currentParent.right = newNode;
            }
        }
    }

    /**
     * 先序遍历
     * @param tree 树
     * 访问根结点;
     * 先序遍历左子树;
     * 先序遍历右子树
     */
    private void preOrder(BSTNode<T> tree){
        if (tree!= null) {
            System.out.print(tree.key+" ");
            preOrder(tree.left);
            preOrder(tree.right);
        }
    }

    public void preOrder(){
        preOrder(mRoot);
    }

    /**
     * 中序遍历左子树;
     * 访问根结点;
     * 中序遍历右子树。
     */
    private void inOrder(BSTNode<T> tree){
        if (tree!= null) {
            inOrder(tree.left);
            System.out.print(tree.key + " ");
            inOrder(tree.right);
        }
    }
    public void inOrder(){
        inOrder(mRoot);
    }

    /**
     * 后序遍历左子树;
     * 后序遍历右子树;
     * 访问根结点;
     */
    private void postOrder(BSTNode<T> tree){
        if (tree!= null) {
            postOrder(tree.left);
            postOrder(tree.right);
            System.out.print(tree.key + " ");
        }
    }
    public void postOrder(){
        postOrder(mRoot);
    }

    /**
     * 获取树的深度
     * @return
     */
    public int depth() {
        return depth(mRoot);
    }

    private int depth(BSTNode<T> bstNode) {
        int m,n;
        if (bstNode == null) {
            return  0;
        }
        else {
            m = depth(bstNode.left);
            n = depth(bstNode.right);
            if(m>n){
                return m+1;
            }else {
                return  n+1;
            }
        }
    }

    /**
     * 搜索
     * @param current
     * @param key
     * @return
     */
    public BSTNode<T> search(BSTNode current,T key) {

        if (current == null) {
            return null;
        }
        int cmp = current.key.compareTo(key);
        if (cmp < 0) {
            return search(current.right,key);
        }else if (cmp> 0) {
            return search(current.left, key);
        }else {
            return  current;
        }


    }
    public BSTNode<T> search(T key) {
        return  search(mRoot,key);
    }

    /**
     * 获取树的最大值
     * @return {@link T}
     */
    public T maximum(){
        BSTNode<T> p = maximum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }

    private BSTNode<T> maximum(BSTNode<T> tree) {
        if (tree == null) {
            return null;
        }
        if (tree.right!=null) {
            return maximum(tree.right);
        }else {
            return tree;
        }
    }

    /**
     * 获取树的最小值
     * @return {@link T}
     */
    public T minimum() {
        BSTNode<T> p = minimum(mRoot);
        if (p != null) {
            return p.key;
        }
        return null;
    }

    private BSTNode<T> minimum(BSTNode<T> tree) {
        if (tree == null) {
            return null;
        }
        if (tree.left != null) {
            return  minimum(tree.left);
        }else {
            return tree;
        }
    }
    /**
     * 查找"二叉树中数据值小于该结点"的"最大结点"
     * 1.存在左子树,则左子树最大节点就是前驱
     * 2. 不存在左子树,找父节点,如果该节点是父节点右孩子,则父节点就是前驱,
     *    如果该节点是父节点的左孩子,则需要向上找到离x最近的,拥有右孩子的父节点
     */
    public BSTNode<T> predecessor( BSTNode<T> x) {
       // 1.存在左子树,则左子树最大节点就是前驱
        if (x.left!=null) {
            return maximum(x.left);
        }
        // 2. 不存在左子树,找父节点,如果该节点是父节点右孩子,则父节点就是前驱
        BSTNode<T> y = x.parent;
        while ((y!=null) && (x==y.left)) {
            x = y;
            y = y.parent;
        }
        return y;
    }

    /**
     * 查找"二叉树中数据值大于该结点"的"最小结点
     * 1.存在右子树,则右子树的最小值就是后驱
     * 2. 没有右子树,如果是父节点的左孩子,则父节点就是后驱
     *    向上找离x最近的,拥有左孩子的父节点
     * @param x
     * @return
     */
    public BSTNode<T> successor(BSTNode<T> x) {
        if (x.right != null) {
            return minimum(x.right);
        }

        BSTNode<T> y  = x.parent;
        while ((y != null)&&(x==y.right)) {
            x= y;
            y = y.parent;
        }
        return y;
    }

    /**
     * 删除值为key的节点
     * @param key 关键
     */
    public void remove(T key){
        BSTNode<T> z;
        // 待删除节点存在
        if ((z=search(mRoot,key))!=null) {
            remove(this,z);
        }
    }

    /**
     * 删除
     * 1.被删除节点有左右子树,用前驱顶替
     * 2. 被删除节点只有一个子树,直接用子树顶替
     * 3. 被删除节点没有子树,直接删除
     * @param bst bst
     * @param z   z
     * @return {@link BSTNode}<{@link T}>
     */
    private BSTNode<T> remove(BSTree<T> bst, BSTNode<T> z) {
        BSTNode<T> x = null;
        BSTNode<T> y = null;
        // 只有左子树或者右子树,直接用子树替代
        if ((z.left==null)||(z.right == null)) {
            y = z;
        }else {
            y = predecessor(z);
        }

        // 用x记录,将被用于顶替删除节点的节点的子树
        if(y.left!=null){
            x = y.left;
        }else {
            x = y.right;
        }

        // 调整子树的父节点
        if (x != null) {
            x.parent = y.parent;
        }

        // y是根节点
        if (y.parent == null) {
            bst.mRoot = x;
        } else if (y == y.parent.left) {
            y.parent.left = x;
        } else {
            y.parent.right = x;
        }

        // 替换删除的节点的值
        if (y != z) {
            z.key = y.key;
        }

        return y;
    }


    /**
     * 摧毁
     * @param tree 树
     */
    private void destroy(BSTNode<T> tree) {
        if (tree==null) {
            return ;
        }

        if (tree.left != null) {
            destroy(tree.left);
        }
        if (tree.right != null) {
            destroy(tree.right);
        }

        tree=null;
    }

    public void clear() {
        destroy(mRoot);
        mRoot = null;
    }


    /**
     * 打印
     * @param tree      树
     * @param key       关键
     * @param direction 0- 跟节点  1 右子树  -1 左子树
     */
    private void print(BSTNode<T> tree, T key, int direction) {

        if(tree != null) {
            // tree是根节点
            if(direction==0)
            {
                System.out.printf("%2d is root\n", tree.key);
            } else                // tree是分支节点
            {
                System.out.printf("%2d is %2d's %6s child\n", tree.key, key, direction==1?"right" : "left");
            }

            print(tree.left, tree.key, -1);
            print(tree.right,tree.key,  1);
        }
    }

    /**
     * 打印
     */
    public void print() {
        if (mRoot != null) {
            print(mRoot, mRoot.key, 0);
        }
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值