二叉树它上线查找功能了?

简介: 二叉树它上线查找功能了?

二叉树它上线查找功能了?

多日不见,朋友们还记得什么是二叉树吗?

什么是二叉查找树

在树的任意一个节点,左子树中每个节点的值都要小于这个节点,右子树中每个节点的值都要大于这个节点,这就是二叉查找树。

image.png

查找操作

从根节点开始,如果比根节点的值小,就查询它的左子树;

如果比根节点的值大,就查询它的右子树;

如果等于我们要查找的数据,就返回这个节点元素;

按照这种逻辑,依次往下递归查询,最后到叶子节点都没有查到类似的数据,则表示数据不存在。

查找一个节点可以使用递归查询,也可以使用循环查询,一般建议使用循环操作,假设树变成一个链表,递归过深的时候,很容易出现stackoverflow 的异常。

public class BinarySearchTree {
    // 根节点
    private Node root;
    /**
     * 循环实现方法
     * 
     * @param value 要查找的值
     * @return 查找结果
     */
    public Node find(int value) {
        Node node = root;
        while (root != null) {
            if (node.value == value) {
                return node;
            }
            if (node.value > value) {
                node = node.right;
            } else {
                node = node.left;
            }
        }
        return null;
    }
    /**
     * 递归实现方法
     * 
     * @param node 起始节点,一般为根节点
     * @param value 要查找的值
     * @return 查找结果
     */
    public Node find2(Node node, int value) {
        if (node == null) {
            return null;
        }
        if (node.value > value) {
            return find2(node.right, value);
        } else if (node.value < value) {
            return find2(node.left, value);
        } else {
            return node;
        }
    }
    public class Node {
        private int value;
        private Node left;
        private Node right;
        public Node(int value) {
            this.value = value;
        }
    }
}

插入操作

插入和查询的操作很类似,一般把新插入的节点放到叶子节点。类似查询的操作方式,查找元素应该插入的位置。

  1. 需要判断根节点是否为空,为空,则添加为根节点
  2. 如果根节点不为空,则与根节点对比大小
  3. 如果比根节点大,则从右子树继续查找
  4. 如果比根节点小,则从左子树继续查找
  5. 插入的条件:判断当前节点是否还有左/右节点,如果没有,则插入当前节点的左/右节点位置。
public void insert(int value) {
    Node node = root;
    if (node == null) {
        node = new Node(value);
        return;
    }
    while (node != null) {
        if (node.value > value) {
            if (node.right == null) {
                node.right = new Node(value);
                return;
            }
            node = node.right;
        } else {
            if (node.left == null) {
                node.left = new Node(value);
                return;
            }
            node = node.left;
        }
    }

删除操作

标记删除法:使用类似散列表中添加标记的删除方法,当一个数据删除以后,我们把它标记为已删除,数据并不会真的从内存中清理掉,这时候比较浪费内存。

还可以在查找的基础上进行改造,先查询节点所在位置,然后对其进行删除。

  • 待删除节点为叶子节点,直接设置节点为null即可。
  • 待删除节点只有一个子节点,让待删除节点的父节点指向待删除节点的子节点即可。
  • 待删除节点有两个节点,删除节点以后,我们需要一个子节点填在删除的位置上,可以在它的左子树查找一个最大值,或者在右子树查找一个最小值。这个值一般在叶子节点,我们移动到删除节点位置即可。

编码思路

  1. 查询待删除的节点以及其父节点
  2. 判断节点是否为空,为空表示不存在
  3. 假设有两个节点,找出右子树中最小节点,替换待删除节点,然后把要删除的情况转化成叶子节点
  4. 假设删除的节点是叶子节点或者仅有一个子节点,或者待删除节点的子节点
  5. 使待删除节点的父节点指向待删除节点的子节点。
public void delete(int value) {
    Node delNode = tree;//要删除的节点
    Node pDelNode = null;// 要删除节点的父节点
    // 1.查找要删除节点,以及其父节点
    while (delNode != null && delNode.value != value) {
        pDelNode = delNode;
        if (delNode.value > value) {
            delNode = delNode.left;
        } else {
            delNode = delNode.right;
        }
    }
    // 要删除的节点不存在时
    if (delNode == null) {
        return;
    }
    // 2.假设待删除节点有两个子节点,查询出右子树最小节点
    if (delNode.left != null && delNode.right != null) {
        Node miniRight = delNode.right;
        Node pMiniRight = miniRight;
        while (miniRight.left != null) {
            pMiniRight = miniRight;
            miniRight = miniRight.left;
        }
        // 两个节点的情况转化成 待删除节点为叶子节点,父节点为叶子节点的父节点
        delNode.value = miniRight.value;
        delNode = miniRight;
        pDelNode = pMiniRight;
    }
    // 3. 假设待删除节点只有一个子节点,获取删除节点的子节点
    Node child;
    if (delNode.left != null) {
        child = delNode.left;
    } else if (delNode.right != null) {
        child = delNode.right;
    } else {
        // 如果两个子节点都是空,表示要删除的节点是叶子节点
        child = null;
    }
    // 设置父节点的后继节点,删除节点
    if (pDelNode == null) {
        // 删除根节点,只有一个根节点的情况
        tree = null;
    } else if (pDelNode.right == delNode) {
        pDelNode.right = child;
    } else {
        pDelNode.left = child;
    }
}

其他

中序遍历二叉树,可以输出有序的数据序列,时间复杂度为O(n)

包含重复数据的二叉树

一、使用链表或者数组存储相同的数据,然后存放到节点中

二、把和某节点相同的值当做大于这个节点的值来处理。数据插入到这个节点的右子树。查找数据的时候,遇到值相同的节点,接着去右子树中查找数据,停止查找的条件变为遇到叶子节点。删除的时候也需要遍历右子树节点处理。

时间复杂度

二叉查找树退化成链表时,查找的时间复杂度为O(n)

如果二叉树一个完全二叉树时,时间复杂度跟输的高度成正比,O(height),使用层数做计算,假设层数为 k 。第一层包含1个节点,第二层包含2个节点,第三层包含4个节点,下面一层节点个数是上一层的2倍,第 k 层包含的节点个数是 2^(k-1)

假设节点个数为n,最大层数为k
n >= 1+2+4+8+……+2^(k-2)
n <= 1+2+4+8+……+2^(k-2)+2^(k-1)

所以 k 的范围是 [log2(n+1), log2(n)+1],完全二叉树的层数小于等于log2(n)+1,高度小于等于log2(n),最好的时间复杂度为O(logn)。平衡二叉树的时间复杂度可以做到稳定的O(logn)

目录
相关文章
|
机器学习/深度学习 JSON 数据格式
CatBoost模型部署与在线预测教程
CatBoost模型部署与在线预测教程【2月更文挑战第16天】
578 2
|
存储 Shell Linux
【Shell 命令集合 文档编辑】Linux 文本统计 wc命令使用指南
【Shell 命令集合 文档编辑】Linux 文本统计 wc命令使用指南
396 0
使用PostMan上传文件,有图易懂
使用PostMan上传文件,有图易懂
8478 0
使用PostMan上传文件,有图易懂
|
Linux Python
如何在服务器上跑python程序
购买服务器 首先你需要一个服务器,阿里云云翼计划有一个9.9云服务器ECS服务。你怎么买我不管,反正你最后给我搞到一个云服务器。 购买的配置界面 由于阿里云现在限量购买,所以这里只是截个图说明而已,主要说明一点公共镜像选择ubuntu14.04 64位,还有root密码别忘了。
11330 1
|
8月前
|
存储 算法
【赵渝强老师】Memcached的路由算法
Memcached支持两种客户端路由算法:求余数Hash算法和一致性Hash算法。求余数Hash算法通过键值对服务器数量取模分配数据,虽分布均匀但扩容缩容时易丢失数据。一致性Hash算法则通过哈希环减少数据丢失,仅影响故障节点相关数据,在集群扩容或节点宕机时表现更优。
193 10
|
安全 关系型数据库 MySQL
CentOS7仅安装部署MySQL80客户端
通过上述步骤,你可以在CentOS 7上成功安装并配置MySQL 8.0客户端。这个过程确保你能够使用MySQL客户端工具连接和管理远程的MySQL数据库,而不需要在本地安装MySQL服务器。定期更新MySQL客户端可以确保你使用的是最新的功能和安全修复。
1095 16
|
存储 安全 网络安全
|
传感器 安全 物联网
5G车联网技术:智能交通的未来
【10月更文挑战第26天】
618 1
|
存储 数据可视化 数据挖掘
揭秘!Matplotlib与Seaborn联手,如何让Python数据分析结果一目了然,惊艳全场?
在数据驱动时代,高效直观地展示分析结果至关重要。Python中的Matplotlib与Seaborn是两大可视化工具,结合使用可生成美观且具洞察力的图表。本文通过分析某电商平台的商品销量数据集,展示了如何利用这两个库揭示商品类别与月份间的销售关系及价格对销量的影响。首先使用Matplotlib绘制月份销量分布直方图,再借助Seaborn的箱线图进一步探索不同类别和价格区间下的销量稳定性。
242 10
|
存储 数据库 Android开发
🔥Android Jetpack全解析!拥抱Google官方库,让你的开发之旅更加顺畅无阻!🚀
【7月更文挑战第28天】在Android开发中追求高效稳定的路径?Android Jetpack作为Google官方库集合,是你的理想选择。它包含多个独立又协同工作的库,覆盖UI到安全性等多个领域,旨在减少样板代码,提高开发效率与应用质量。Jetpack核心组件如LiveData、ViewModel、Room等简化了数据绑定、状态保存及数据库操作。引入Jetpack只需在`build.gradle`中添加依赖。例如,使用Room进行数据库操作变得异常简单,从定义实体到实现CRUD操作,一切尽在掌握之中。拥抱Jetpack,提升开发效率,构建高质量应用!
536 4