树和二分查找树

这篇博客介绍了树的基本概念,包括节点的高度、深度、度等,并详细讲解了二分查找树(Binary Search Tree)的特性。文章阐述了二分查找树的插入、删除、搜索操作,并探讨了树的遍历方法,如前序、中序和后序遍历。此外,还提到了完全二叉树、平衡二叉树等特殊类型的二叉树。
摘要由CSDN通过智能技术生成

树的基本定义

在树中,每个节点都含有自己的数值,以及与之相连的子节点,连接子节点的线叫相连线(edge)。如下图所示,A是根节点(root),也是B和C的父节点(parent node),也就是说B、C都是A的子节点(child node)。在树中,没有子节点的节点叫做叶子节点(leaf node),下图中的H、I、J、F、G都是叶子节点。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCoGhmPo-1651577441360)(C:\Users\so long\AppData\Roaming\Typora\typora-user-images\image-20220502230110058.png)]

节点的高度(height)和深度(depth)

节点的高度和深度是相反的表示,深度是从上到下数的,而高度是从下往上数。若规定根节点的深度和叶子节点的高度都是0:

  • 节点的高度:此节点到与之相连的叶子节点之间edge的数量,以上图为例,B的高度为2(B到叶子节点H和I的edge数量都是2),C的高度为1。
  • 节点的深度:此节点到根节点的edge的数量,以上图为例,B和C的深度都是1。

节点的度

  • 度的定义:节点所拥有的子树的数目称为该节点的度。
  • 注意: 叶子节点的度为0。
  • 二叉树中度为0的节点=度为2的节点+1。也就是说,二叉树中叶子节点的数量总是等于拥有两个孩子的节点的数量+1。

树的种类

  • 二叉树(Binary Tree):每个节点最多含有两个子节点,上面图示中的树就是二叉树。
  • 完全二叉树(Complete Binary Tree):假设一个二叉树深度(depth)为d(d > 1),除了第d层外,其它各层的节点数量均已达到最大值,且第d层所有节点从左向右紧密排列,这样的二叉树就是完全二叉树。上图的树就是一个完全二叉树。
  • 满二叉树(Full Binary Tee):
    • 国内:除最后一层无任何子节点,每一层上的所有结点都有两个子结点的二叉树。
    • 国外:二叉树的结点要么是叶子结点,要么有两个子结点。
  • 二分查找树(Binary Search Tree):在此树中,每个节点的数值比左子树上的每个节点都大,比所有右子树上的节点都小。
  • 平衡二叉树(AVL Tree):
    • 它是一棵空树或它的左右两个子树的高度差的绝对值不超过1;
    • 平衡二叉树的左右两个子树都是一棵平衡二叉树。
  • B树(B-Tree):B树和平衡二叉树一样,只不过它是一种多叉树(一个节点的子节点数量可以超过二)。
  • 红黑树(Red—Black Tree):是一种自平衡二叉寻找树。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bOJVOCE-1651577441361)(C:\Users\so long\AppData\Roaming\Typora\typora-user-images\image-20220503000834264.png)]

二叉树的性质

  1. 二叉树的第i层至多有2^(i-1) 个节点(i>=1)。满二叉树的第i层为2^(i-1)个节点;
  2. 深度为k的二叉树至多有2^k-1个节点(k>=1);
  3. 对任何一颗二叉树T,如果其叶子节点数量为n,则度为2的节点数量为n-1;
  4. 具有n个节点的完全二叉树的深度为[logn]+1([x]表示不大于x的最大整数)。
  5. 对于n个节点的完全二叉树,对任意节点i(0<=i<n)具有如下性质:
    • 如果i存在父节点,则父节点为 (i-1)/2;
    • 如果i存在左孩子,则左孩子为 2*i+1;
    • 如果i存在右孩子,则右孩子为 2*i+2。

二分查找树(Binary Search Tree)的实现

在这里插入图片描述
在二分查找树(很多地方也叫二叉搜索树)中,每个节点的数值比它的左子树的数值都大,比它的右子树的数值都小,因此如果我们要查找特定的数值,只需要从根节点出发,根据二分查找树的特性,顺着特定的路径就能找到目标。

在二分查找树中,插入、删除、搜索的复杂度都等于树高,平均复杂度为O(logn)

注意:本文不讨论树的平衡,插入和删除时不做平衡处理。

定义节点的结构体

    /**
     * 定义节点
     */
    static class TreeNode {
   
        int value;
        // 左子树
        TreeNode left;
        // 右子树
        TreeNode right;

        public TreeNode(int value) {
   
            this.value = value;
        }
    }

    /**
     * 定义二叉树的根节点
     */
    TreeNode root = null;

insert方法

在insert方法中,我们首先判断树的根节点是否为空,如果为空,就把当前插入的节点设为根节点。如果根节点不为空,我们需要根据当前节点的value值,从根节点出发,从左或者向右遍历,找到一个符合当前节点插入的位置。

    /**
     * 核心方法 insert:插入节点到二分查找树
     *
     * @param value
     */
    public void insert(int value) {
   
        TreeNode newNode = new TreeNode(value);
        // 插入到根节点
        if (root == null) {
   
            root = newNode;
            return;
        }
        // 遍历,找到符合插入的位置
        TreeNode current = root;
        TreeNode currentParent = null;
        while (current != null) {
   
            // parent要在current走之前走
            currentParent = current;
            // 插入的节点已经存在的情况下,直接返回
            if (value == current.value) {
   
                return;
            } else if (value < current.value) {
   
                current = current.left;
            } else {
   
                current = current.right;
            }
        }
        if (value < currentParent.value) {
   
            currentParent.left = newNode;
        } else if (value > currentParent.value) {
   
            currentParent.right = newNode;
        }
    }

get方法

get方法用于根据节点值返回节点,如果找到了就返回节点信息,没找到就返回null。

    /**
     * 核心方法 get:根据节点值返回节点
     *
     * @param value
     * @return
     */
    public TreeNode get(int value) {
   
        TreeNode current = root;
        while (current != null) {
   
            if (value < current.value) {
   
                current = current.left;
            } else if (value > current.value) {
   
                current = current.right;
            } else {
   
                return current;
            }
        }
        return null;
    }

delete方法

删除节点的方法比较复杂,假设我们找到的删除节点为current,我们需要从以下三种情况去讨论如何删除:

  • current是叶子节点:直接删除就好了;
  • current只有一个孩子:直接把节点删除,然后被删除节点的孩子替代删除节点;
  • current有两个孩子:需要从左子树找到一个最大的节点,或者从右子树找到一个最小的节点替代删除节点的位置,本文采用的是从左子树找到一个最大的节点(取名为successor)代替被删除节点。

current有两个孩子的情况,我们获取的successor可能会有以下三种情况:

  1. 是叶子节点;
  2. 是被删除节点的直接左孩子;
  3. 只有左孩子(不可能有右孩子,如果有右孩子就不可能是successor)。

对于情况1和情况3,successor替代current要走以下三步:

  1. current的左子树挂载到successor的左子树;
  2. current的右子树挂载到successor的右子树;
  3. successor代替current位置挂到parent的对应位置。

而对于情况2,只需要走上面的步骤2和步骤三。

情况3的删除步骤如下所示:
successor有左孩子
情况2删除步骤如下所示:
successor是被删除节点的直接左孩子
情况1删除步骤如下所示:
successor是叶子节点

    /**
     * 核心方法 delete:将节点从二分查找树中删除。这里需要分三种情况去删除:
     * 1.删除的节点是叶子节点,那么直接删除就好了;
     * 2.删除的节点只有一个孩子,那么直接把节点删除,然后删除节点的孩子替代删除节点;
     * 3.删除的节点有两个孩子,那么就需要从左子树找到一个最大的节点,或者从右子树找到一个最小的节点替代删除节点的位置。
     *
     * @param value
     */
    public boolean delete(int value) {
   
        if (root == null) {
   
            return false;
        }
        // 要删除的节点
        TreeNode current = root;
        // 要删除节点的父节点
        TreeNode currentParent = null;
        // 要删除节点是否是左孩子
        boolean isLeftChild = false;
        while (current != null && current.value != value) {
   
            currentParent = current;
            if (value < current.value) {
   
                current = current.left;
                isLeftChild = true;
            } else if (value > current.value) {
   
                current = current.right;
                isLeftChild = false
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值