【Java 数据结构与算法】-二叉搜索树

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏【Java 数据结构与算法】
分享:曾梦想仗剑走天涯,看一看世界的繁华。——许巍《曾经的你》
主要内容:二叉搜索树的概念,二叉搜索树的查找、新增,保姆级别分析二叉搜索树的删除问题。

在这里插入图片描述

一、概念理解

@ 什么是二叉搜索树?

  • 二叉搜索树又叫二叉排序树,又叫二叉查找树。
  • 二叉搜索树有可能是空树
  • 不是空树时:
    它的左子树上的所有节点都小于根节点
    它的右子树上的所有节点都大于根节点
    它的左右子树都分别是二叉搜索树。

那么下面我们用图示来理解:
在这里插入图片描述

二、代码详解

1、定义节点结构

  • 定义根节点root。
  • 使用内部类定义节点,为了方便操作,我直接把val定义成public,本来应该是用set和get方法来操作的。
  • 注意:查找,新增,删除的方法都放在BinarySearchTree类里面。因为解决了查找相当于解决了修改了,所以修改就不再示例。
  • 注意:插入、删除操作都需要先查找。如果该数是平衡的,那查找效率为logn,如果恰好n个节点有n层,那效率为o(n)。AVL树和红黑树都是平衡二叉搜索树。
class BinarySearchTree{
	public TreeNode root = null;
    class TreeNode{
        public int val;
        TreeNode left;
        TreeNode right;
        public TreeNode(int val){
            this.val = val;
        }

        @Override
        public String toString() {
            return "delNode.val:" + this.val;
        }
   	}
}

2、查找

  • 如果root==null,表示树为空,那就查找不到。
  • 如果root!=null,那就把我要查找节点的值val和 root的val比较,比root的小,往左子树找;比root的大,往右子树找,如果和root的相等,就返回该节点。
  • 如果找到空了还没找到相等的,那就查找不到,返回null。
	// 查找
    public TreeNode search(int val){
        if (root == null){
            return null;
        }

        TreeNode cur = root;
        while (cur != null){
            if (val < cur.val) {
                cur = cur.left;
            } else if (val > cur.val) {
                cur = cur.right;
            }else {
                return cur;
            }
        }
        return null;
    }

2、新增

  • 如果树的根节点root==null,说明当前树为空,那么就把新增的节点挂在根节点上。
  • 如果root!=null,那就把我要新增节点的值val和 root的val比较,比root的小,往左子树找;比root的大,往右子树找,直到找到空,这个空的位置就是我的新增的val该放的位置。
  • 那么找到空了,我怎么实现把新增节点放进树里面呢?最简单的方式就是再定义一个标志,每次都标志上一个位置。那么最后找到空时,我用这个标志位的左(右)引用指向新节点即可。
	// 增加
    public boolean insert(int val){
        if (root == null){
            root = new TreeNode(val);
            return true;
        }
        
		// 注意:走到这里说明root!=null
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null){
            if(val < cur.val){
                parent = cur;
                cur = cur.left;
            }else if (val > cur.val){
                parent = cur;
                cur = cur.right;
            }else {  // 注意:val值已经有了就不需要再插入了
                return false;
            }
        }
        // 注意:出循环后cur=null,parent是val的父节点
        if(val < parent.val){
            parent.left = new TreeNode(val);
        }else {
            parent.right = new TreeNode(val);
        }
        return true;
    }

3、删除

  • 删除操作相对于查找和新增来说,难度上升了一个数量级。

@ 分析

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • cur.left == null
    cur 是 root,则 root = cur.right
    cur 不是 root,cur 是 parent.left,则 parent.left = cur.right
    cur 不是 root,cur 是 parent.right,则 parent.right = cur.right
  • cur.right == null
    cur 是 root,则 root = cur.left
    cur 不是 root,cur 是 parent.left,则 parent.left = cur.left
    cur 不是 root,cur 是 parent.right,则 parent.right = cur.left
  • cur.left != null && cur.right != null
    需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(右子树最大值),用它的值填补到被删除节点中,再来处理该结点的删除问题。大家可以自己画图试试。

@ 代码

// 删除,找出要删除的节点cur和它的父节点parent,交给parentCur方法去删除
    public void remove(int val){
        TreeNode cur = root;
        TreeNode parent = null;

        while (cur != null){
            if (val < cur.val) {
                parent = cur;
                cur = cur.left;
            }else if (val > cur.val){
                parent = cur;
                cur = cur.right;
            }else {
                removeCur(parent,cur);
                return;  //注意删除完后,直接return
            }
        }
    }
// 具体的删除节点操作交给这个方法来完成
	private void removeCur(TreeNode parent,TreeNode cur){
        if (cur.left == null){
            if (cur == root){
                root = cur.right;
            } else if (cur == parent.left){
                parent.left = cur.right;
            }else if (cur == parent.right){
                parent.right = cur.right;
            }
        }else if(cur.right == null){
            if (cur == root) {
                root = cur.left;
            }else if (cur == parent.left){
                parent.left = cur.left;
            } else if (cur == parent.right) {
                parent.right = cur.left;
            }
        }else {  // cur.left != null && cur.right != null
			
			//注意:我这里是使用找左子树最大值节点的方式,你也可以选择找右子树最小值节点
            TreeNode target = cur.left;
            TreeNode targetParent = cur;
            while (target.right != null){
                targetParent = target;
                target = target.right;
            }

            // 注意:走到这里,target就是以cur的左子树为根节点的最右边的节点,即cur左子树的最大值。
            // 当前操作:把target的val拷贝到cur节点,然后删除target
            cur.val = target.val;
            if (target == targetParent.left){
                targetParent.left = target.left;
            } else if (target == targetParent.right) {
                targetParent.right = target.left;
            }
        }
    }

4、如何简单判断代码是否正确?

  • 可以在Main类中写一个中序遍历,只要增删查都是有序的,那就证明二叉搜索树相关操作写对了。
	public static void inOrderTree(BinarySearchTree.TreeNode root){
        if(root == null){
            return;
        }
        inOrderTree(root.left);
        System.out.print(root.val + " ");
        inOrderTree(root.right);
    }
  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

学Java的冬瓜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值