【数据结构-二叉搜索树的增删查改】

在这里插入图片描述

🌈个人主页:努力学编程’
个人推荐:基于java提供的ArrayList实现的扑克牌游戏 |C贪吃蛇详解
学好数据结构,刷题刻不容缓:点击一起刷题
🌙心灵鸡汤总有人要赢,为什么不能是我呢
在这里插入图片描述

🌈二叉搜索树

二叉搜索树又称二叉排序树,从节点的个数上又可分为两种,空树或者具有一定性质的树,这种具有特殊性质的树具有特点:

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

在这里插入图片描述

根节点27的左子树的所有的节点的值都小于27,右子树的所有节点的值都大于27,这也是为什么我们叫他二叉排序树的原因。当然他的每个子树也都满足这个性质。

⚡⚡⚡特别说明:当我们使用中序遍历搜索二叉树的时候,遍历所得的数组是一个有序数组。

🌈二叉搜索树-查找

如何在二叉搜索树中查找数值呢,他和普通二叉树有区别吗?

  • 当然我们在查找索索二叉树数值的时候,我们理应利用它结构上的特点进行查找:
  • 节点为空: 返回fasle;
  • 节点不为空:Key>root.val 在根节点的右侧进行查找
  • 节点不为空: Key<root.val 在根节点的左侧进行查找

在这里插入图片描述

🌈二叉搜索树-插入

在二叉搜索树当中,插入数据分为两步:找到要插入数据的位置,插入数据。

  • 如何找到插入数据的位置是最重要的一步,其实这步和查找有一定的相似度。我们将带插入数据与当前的根节点进行比较,根据大小关系,在树的左右子树进行查找。

  • 直到找到我们需要插入点的位置,但是只要你实际操作这个过程就会发现我们无法直接定位到需要插入数据的位置,只能定位到带插入位置的下个一位置,所以我们需要创建一个指针定位到插入数据指针的上一个位置,这样就找到了待插入的位置.

  • 若节点为空,直接插入节点!!!

在这里插入图片描述

  • 使用parent标记带插入的位置,直接进行插入:
    在这里插入图片描述

🌈二叉搜索树-删除

对于删除操作,比较麻烦,需要分不同的几种情况进行讨论,待删除的节点cur,待删除的数据的父节点parent:

  • cur.left == null:

  • cur 是 root: 则 root = cur.right

  • cur 不是 root,cur 是 parent.left: 则 parent.left = cur.right

  • cur 不是 root,cur 是 parent.right 则 parent.left = cur.left

  • 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:

  • 需要使用替换法进行删除,即在它的右子树中寻找中序下的第一个结点(关键码最小),用它的值填补到被
    删除节点中,再来处理该结点的删除问题

🌈搜索二叉树-模拟实现:

针对于搜索二叉树的增删查改的基础操作代码的实现:

public class BinarySearchTree {
    static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

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

    public TreeNode root = null;

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

    public void insert(int val) {
        TreeNode node = new TreeNode(val);
        if(root == null) {
            root = node;
            return;
        }
        TreeNode cur = root;
        TreeNode parent = null;
        while (cur != null) {
            if(cur.val < val) {
                parent = cur;
                cur = cur.right;
            }else if(cur.val > val) {
                parent = cur;
                cur = cur.left;
            }else {
                return;
            }
        }
        //parent 指向的节点 就是 需要插入的节点位置 的 父亲节点
        if(parent.val > val) {
            parent.left = node;
        }else {
            parent.right = node;
        }
    }
    //key = 10;
    public void remove(int key) {
        TreeNode parent = null;
        TreeNode cur = root;
        while (cur != null) {
            if(cur.val < key) {
                parent = cur;
                cur = cur.right;
            }else if(cur.val > key) {
                parent = cur ;
                cur = cur.left;
            }else {
                removeNode(parent,cur);
                return;
            }
        }
    }
    private void removeNode(TreeNode parent,TreeNode cur) {
        if(cur.left == null) {
            if(cur == root) {
                root = cur.right;
            }else if(cur == parent.left) {
                parent.left = cur.right;
            }else {
                parent.right = cur.right;
            }
        }else if(cur.right == null) {
            if(cur == root) {
                root = cur.left;
            }if(cur == parent.left) {
                parent.left = cur.left;
            }else {
                parent.right = cur.left;
            }
        }else {
            TreeNode target = cur.right;
            TreeNode targetP = cur;
            while (target.left != null) {
                targetP = target;
                target = target.left;
            }
            cur.val = target.val;
            if(target == targetP.left) {
                targetP.left = target.right;
            }else {
                targetP.right = target.right;
            }
        }
    }
}

🌈二叉搜索树-算法性能分析

针对我们上面的基础操作,插入和删除都需要查找操作,针对二叉树的查找,我们需要根据树的结构进行分析,不同的结构时间复杂度也会不同。

⚡⚡⚡完全二叉树
最优情况下,二叉搜索树为完全二叉树,其平均比较次数为: l o g N logN logN
在这里插入图片描述
💧💧💧右单支
最差情况下,二叉搜索树退化为单支树,其平均比较次数为:2/N
在这里插入图片描述

🌈二叉搜索树-力扣刷题

在这里插入图片描述

解题思路:我们解决二叉树问题,优先思考的是利用递归的套路解决问题,由于这是一颗搜索二叉树,我们也应当利用树的特点,解决这个问题:

递归的套路:

  • 方法的返回值和参数
  • 递归结束的条件
  • 单层遍历
  • 结合搜索二叉树的性质解决问题
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
         if(root==null||root.val==val) return root;
         TreeNode result=null;
         if(root.val>val) result=searchBST(root.left,val);
         if(root.val<val) result=searchBST(root.right,val);
         return result;
    }
}

在这里插入图片描述
在这里插入图片描述
解题思路:是否是一颗搜索二叉树,应当使用其定义进行判断,左子树必须小于根节点的值,右子树所有的节点必须大于根节点的值。当然他的每一棵子树都必须满足这个特性。

那么我们就可以利用递归实现这个过程,
所以递归的方法的返回值:首先是boolean ,参数是根节点,左右节点,
结束条件:root==null return true;
if(node.val<=lower||node.val>=upper) return false;
单层遍历:validBST(lower, root.val, root.left) && validBST(root.val, upper, root.right);

class Solution {
    public boolean isValidBST(TreeNode root) {
        return validBST(Long.MIN_VALUE, Long.MAX_VALUE, root);
    }
    boolean validBST(long lower, long upper, TreeNode root) {
        if (root == null) return true;
        if (root.val <= lower || root.val >= upper) return false;
        return validBST(lower, root.val, root.left) && validBST(root.val, upper, root.right);
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/608414.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python-类和对象

1、设计一个 Circle类来表示圆,这个类包含圆的半径以及求面积和周长的函数。再使用这个类创建半径为1~10的圆,并计算出相应的面积和周长。 &#xff08;1&#xff09;源代码&#xff1a; import math class Circle: def __init__(self, r): self.r r #面积 def area(self): r…

最佳实践 | 八爪鱼采集器如何用PartnerShare做全民分销?

在数字化时代&#xff0c;数据采集和分析已经成为企业运营和决策的重要一环。八爪鱼采集器作为一款领先的SaaS产品&#xff0c;凭借其强大的数据采集和处理能力&#xff0c;成为了众多企业和个人用户的心头好。为了进一步拓展市场份额&#xff0c;提升品牌影响力&#xff0c;八…

TCP通信并发:

上次的程序只能保持&#xff0c;单线程或者进程 多进程并发服务器 进程的特点&#xff08;有血缘关系&#xff09; 创建子进程&#xff1a;fork&#xff08;&#xff09;&#xff1b; 虚拟地址空间被复制 &#xff0c;从一份变成两份&#xff08;用户区和内核区&#xff09…

国内如何访问 OpenAI 的 api

这个问题甚至我的一些大厂的朋友也不太清楚&#xff0c;所以我觉得有必备写一篇文章来简单盘盘它&#xff0c;希望能帮助到有需要的人 众所周知&#xff0c;由于大陆与 OpenAI 双方互相封锁&#xff0c;大陆是无法直接访问 OpenAI api 的 不过由于 GPT 4 的统治地位&#xff0c…

下一代自动化,国外厂商如何通过生成性AI重塑RPA?

企业自动化的未来趋势是什么&#xff1f;科技巨头们普遍认为&#xff0c;由生成性AI驱动的AI Agent将成为下一个重大发展方向。尽管“AI Agent”这一术语尚无统一定义&#xff0c;但它通常指的是那些能够根据指令通过模拟人类互动&#xff0c;在软件和网络平台上执行复杂任务的…

[C++核心编程-05]----C++类和对象之对象的初始化和清理

目录 引言 正文 01-构造函数和析构函数 ​02-构造函数的分类及调用 03-拷贝构造函数调用时机 04-构造函数调用规则 05-深拷贝与浅拷贝 06-初始化列表 07-静态成员变量 08-静态成员函数 …

Eigen求解线性方程组

1、线性方程组的应用 线性方程组可以用来解决各种涉及线性关系的问题。以下是一些通常可以用线性方程组来解决的问题&#xff1a; 在实际工程和科学计算中&#xff0c;求解多项式方程的根有着广泛的应用。 在控制系统的设计中&#xff0c;我们经常需要求解特征方程的根来分析…

【训练与预测】02 - 完整的模型验证套路

02 - 完整的模型验证套路 模型图 验证一个模型就是指使用已经训练好的模型&#xff0c;然后给它提供输入。 test.py import torch import torchvision from PIL import Imagedevice torch.device("cuda" if torch.cuda.is_available() else "cpu") ima…

C++学习笔记——仿函数

文章目录 仿函数——思维导图仿函数是什么仿函数的优势理解仿函数仿函数的原理举例 仿函数——思维导图 仿函数是什么 使用对象名调用operator&#xff08;&#xff09;函数看起来像是在使用函数一样&#xff0c;因此便有了仿函数的称呼&#xff1b;仿函数存在的意义是&#x…

Burp Suite 抓包,浏览器提示有软件正在阻止Firefox安全地连接到此网站

问题现象 有软件正在阻止Firefox安全地连接到此网站 解决办法 没有安装证书&#xff0c;在浏览器里面安装bp的证书就可以了 参考&#xff1a;教程合集 《H01-启动和激活Burp.docx》——第5步

如何防止源代码泄露?彻底解决源代码防泄密的方法

SDC沙盒系统&#xff1a;数据安全的守护者 SDC沙盒系统&#xff0c;为研发型企业设计&#xff0c;实现了对数据的代码级保护&#xff0c;同时不影响工作效率和正常使用。系统通过自动加密敏感数据&#xff0c;并配合多种管控机制&#xff0c;有效防止了数据的泄露。 涉密可信…

代码随想录算法训练营第四十二天| 01背包问题(二维、一维)、416.分割等和子集

系列文章目录 目录 系列文章目录动态规划&#xff1a;01背包理论基础①二维数组②一维数组&#xff08;滚动数组&#xff09; 416. 分割等和子集①回溯法&#xff08;超时&#xff09;②动态规划&#xff08;01背包&#xff09;未剪枝版剪枝版 动态规划&#xff1a;01背包理论基…

渗透之sql注入----二次注入

目录 二次注入的原理&#xff1a; 实战&#xff1a; 第一步&#xff1a;找注入点 找漏洞&#xff1a; 注入大概过程&#xff1a; 第二步&#xff1a;开始注入 二次注入的原理&#xff1a; 二次注入是由于对用户输入的数据过滤不严谨&#xff0c;导致存在异常的数据被出入…

FreeRTOS的任务详解、创建与删除

目录 1、任务详解 1.1 什么是任务&#xff1f; 1.2 任务的特点 1.3 任务的状态 1.4 任务的优先级 1.5 任务的堆和栈 2、任务的创建与删除 2.1 相关API 2.2 函数解析 2.2.1 xTaxkCreate() 2.2.2 xTaskCreateStatic() 2.2.3 vTaskDelete() 3、实战案例 3.1 创建两个…

达梦数据刷盘测试

达梦数据库为了保证数据故障恢复的一致性&#xff0c;REDO 日志的刷盘必须在数据页刷盘之前进行。 下面我们通过测试来验证是不是这样 执行我们事先准备的SHELL脚本 可以看到第一次strings文件没有输出&#xff0c;说明刚写的数据在数据库的BUFFER缓冲区内&#xff0c;还没有刷…

RN阴影组件使用

yarn add react-native-shadow yarn add react-native-svg // 这个是必须的,阴影依赖这个包四周都有阴影,如下设置 import React from react; import {StyleSheet, View, Text} from react-native; import {BoxShadow} from react-native-shadow;const App () > {const …

3GBJ5016A-ASEMI电焊机专用3GBJ5016A

编辑&#xff1a;ll 3GBJ5016A-ASEMI电焊机专用3GBJ5016A 型号&#xff1a;3GBJ5016A 品牌&#xff1a;ASEMI 封装&#xff1a;3GBJ-5 正向电流&#xff08;Id&#xff09;&#xff1a;50A 反向耐压&#xff08;VRRM&#xff09;&#xff1a;1600V 正向浪涌电流&#xf…

“找不到mfcm80u.dll”错误怎么办?一文了解原因和解决办法!

在使用Windows操作系统时&#xff0c;许多用户可能会遇到各种DLL文件缺失或损坏的问题。其中&#xff0c;“找不到mfc80u.dll”错误就是比较常见的一种。 下面小编就给大家分享出现“找不到mfc80u.dll”错误的原因和解决办法&#xff0c;帮助您快速解决此问题。 一、mfc80u.dl…

漏洞管理是如何在攻击者之前识别漏洞从而帮助人们阻止攻击的

漏洞管理 是主动查找、评估和缓解组织 IT 环境中的安全漏洞、弱点、差距、错误配置和错误的过程。该过程通常扩展到整个 IT 环境&#xff0c;包括网络、应用程序、系统、基础设施、软件和第三方服务等。鉴于所涉及的高成本&#xff0c;组织根本无法承受网络攻击和数据泄露。如果…

mysql数据库调优篇章1

目录 1.认识数据库中日志的作用2.增加mysql数据库中my.ini 基本配置3.增加my.ini中参数配置4.查看已经执行过的sql语句过去执行时间5.找出慢查询的sql6. SHOW VARIABLES LIKE ‘innodb_read_io_threads’; SHOW VARIABLES LIKE ‘innodb_write_io_threads’; SHOW VARIABLES LI…