[模版总结] - 树的基本算法2 - BST

news2024/11/28 12:46:23

BST定义

BST - Binary Search Tree, 即二叉搜索树(有序二叉树)

特性

  • 中序遍历有序
  • 查找/插入/删除某个数值可以通过O(h) 即树的高度,最优logN,最坏 N.
    • 有多种改进BST可以动态维持插入删除后树结构能尽可能保持平衡

BST vs. 有序数组 vs. 普通数组 - 引用自 古城算法 https://www.youtube.com/watch?v=DpkTu2tU87o&list=PLbaIOC0vpjNVHmklf0nzPlPsNvJiqq1lZ&index=3

BST基本操作

查询 - 二分查找

  • 搜索数值 - 二分法
class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        while(root!=null) {
            if (root.val<val) {
                root = root.right;
            } else if (root.val>val) {
                root = root.left;
            } else {
                return root;
            }
        }
        return null;      
    }
}
  • 搜索临近数值
class Solution {
    double min = Double.MAX_VALUE;
    int res = -1;
    public int closestValue(TreeNode root, double target) {
        dfs(root, target);
        return res;
    }

    private void dfs(TreeNode root, double target) {
        if (root==null) return;

        if (Math.abs(root.val - target) < min) {
            min = Math.abs(root.val - target);
            res = root.val;
        } else if (Math.abs(root.val - target) == min) {
            res = root.val<res? root.val: res;
        }

        if (root.val>target) dfs(root.left, target);
        else dfs(root.right, target);

    }
}

插入

插入则是首先找到需要插入的位置,然后插入新结点

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        return dfs(root, val);
    }

    private TreeNode dfs(TreeNode root, int val) {
        if (root==null) {
            root = new TreeNode(val);
            return root;
        }

        if (root.val > val) root.left = dfs(root.left, val);
        else root.right = dfs(root.right, val);

        return root; 
    }
}

删除

删除操作较为复杂一点,我们在删除之后还需要维护当前二叉搜索树的性质,有三种情况:

  1. 删除叶子结点,不会影响BST性质,直接删除即可
  2. 删除结点没有右子树,也就是删除后左子树不会影响BST性质,将左子树root直接顶替删除结点的位置即可
  3. 删除结点有左右子树,为了保证BST性质,我们选择删除点的后继结点作为顶替结点,也就是删除结点右子树最左边的那个点,因为可以保证右子树所有点都大于该点,维持了BST性质
class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        /**
        删除三种情况
        1. 叶子结点
        2. 只存在一个子树
        3. 左右都存在子树
         */

        return dfs(root, key);

        

    }

    private TreeNode dfs(TreeNode root, int key) {
        if (root==null) return null;

        if (root.val==key) {
            if (root.left==null && root.right==null) return null;
            else if (root.left==null || root.right==null) {
                if (root.left!=null) return root.left;
                if (root.right!=null) return root.right;
            } else {
                // 找到root的后继结点,也就是右子树最左边的那个点
                TreeNode dum = root.right;
                while (dum.left!=null) {
                    dum = dum.left;
                }
                root.val = dum.val;
                root.right = dfs(root.right, root.val);
            }
        } else if (root.val>key) {
            root.left = dfs(root.left, key);
        } else {
            root.right = dfs(root.right, key);
        }

        return root;
    }
}

前驱/后继结点

Leetcode 285

求解某一个点的前驱结点思路存储一个变量prev来保存进行下一层递归前的结点信息,如果中序遍历递归遍历到目标结点,其实保存的prev就是该结点的前驱结点

private void preSuccessor(TreeNode root, TreeNode p) {
    if (node==null) return;

    preSuccessor(node.left, p);
    if (root==p) return prev;
    prev = root;

    preSuccessor(node.right, p);
}

求解后躯结点较为复杂,需要考虑到几种情况:

  1. 目标结点有右子树,那么后继结点则是右子树中leftmost结点
  2. 如果目标结点没有右子树,那么后继结点则可能是parent中的某个结点

求解上述第二类后继结点思路类似,前驱结点是当前递归层处理的结点是目标结点时,prev保存的值即为前驱结点;后继结点可以理解为当前递归层的前驱结点时目标结点时,那么当前结点就是目标结点的后继结点,有点逆向思维哈哈。

 

class Solution {
    // 需要前驱结点信息
    TreeNode prev;
    TreeNode insuccessor;
    public TreeNode inorderSuccessor(TreeNode root, TreeNode p) {
        if (p.right!=null) {
            TreeNode node = p.right;
            while (node.left!=null) {
                node = node.left;
            }
            return node;
        } else {
            helper(root, p);
        }

        return insuccessor;
    }

    private void helper(TreeNode node, TreeNode p) {
        if (node==null) return;

        helper(node.left, p);
        // check 当前结点
        if (prev!=null && prev==p) {
            insuccessor = node;
        }
        prev = node; //如果 当前结点前驱结点==p那么这个结点就是p的后驱结点
        helper(node.right, p);
    }
}

验证是否为BST

Leetcode 98. Validate BST

基本思路就是确保左结点 < 根结点 < 右结点,但是还需要保证局部正确的同时,左子树全部结点 < 根结点 < 右子树全部结点。所以每一次向下递归左子树时要以当前结点值作为上限值,遍历右子树时以当前结点值作为下限值

class Solution {
    public boolean isValidBST(TreeNode root) {
        return helper(root, null, null);
    }

    private boolean helper(TreeNode root, Integer low, Integer high) {
        if (root==null) return true;

        if ((low!=null && root.val<=low) || (high!=null && root.val>=high)) {
            return false;
        }

        return helper(root.left, low, root.val) && helper(root.right, root.val, high);
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JTS: 25 Index 索引

文章目录 版本代码 版本 org.locationtech.jts:jts-core:1.19.0 链接: github 代码 package pers.stu.index;import org.locationtech.jts.geom.*; import org.locationtech.jts.index.hprtree.HPRtree; import org.locationtech.jts.index.quadtree.Quadtree; import org.lo…

【MySQL8】1130 - Host ‘***‘ is not allowed to connect to this MySOL server

问题描述 使用 Navicat 连接 MySQL8 报错&#xff1a; 1130 - Host *** is not allowed to connect to this MySOL server解决方案 use mysql;select host ,user from user; -- 将 root 用户的主机&#xff08;host&#xff09;值修改为 %&#xff0c;即允许从任何主机连接 …

电脑集中管理软件有哪些?电脑集中管控怎么做

电脑集中管理软件有哪些&#xff1f;电脑集中管控怎么做 电脑集中管理软件是指通过一种软件或系统&#xff0c;对多台电脑进行集中管理和控制的工具。这种软件通常具备远程控制、软硬件监控、自动部署等功能。可以方便企业或组织统一管理电脑资源&#xff0c;提高工作效率。今…

新版软考高项试题分析精选(四)

请点击↑关注、收藏&#xff0c;本博客免费为你获取精彩知识分享&#xff01;有惊喜哟&#xff01;&#xff01; 1、一般而言&#xff0c;大型软件系统中实现数据压缩功能&#xff0c;工作在OSI参考模型的&#xff08; &#xff09;。 A.应用层 B.表示层 C.会话层 D.网络层…

Odoo 15开发手册第七章 记录集 - 使用模型数据

在前面的章节中&#xff0c;我们概览了模型创建以及如何向模型加载数据。现在我们已有数据模型和相关数据&#xff0c;是时候学习如何编程与其进行交互了。 业务应用需要业务逻辑来计算数据、执行验证或自动化操作。Odoo框架API为开发者提供了工具用于实现这种业务逻辑。大多数…

有Mac或无Mac电脑通用的获取安卓公钥的方案

从2023年9月开始&#xff0c;所有上架应用市场的app都需要进行APP备案。 其中后端服务器在阿里云的可以在阿里云备案&#xff0c;后端服务器在腾讯云的可以在腾讯云备案。但无论你是在什么云厂商里做备案&#xff0c;无一例外的是&#xff0c;无论是上架安卓应用还是上架IOS应…

如何将图片转为excel或word?(客户端)

演示软件&#xff1a;金鸣表格文字识别大师3.6.1&#xff08;新版本界面可能会略有不同&#xff09; 第一部分 将图片转为excel或文表混合的word 一般的软件要将图片转为可编辑的excel&#xff0c;都需要待识别的图片要有明显清晰的表格线&#xff0c;但我们程序现已克服了这…

多线程概述及创建

什么是线程&#xff1f; 线程(thread)是一个程序内部的一条执行路径。 我们之前启动程序执行后&#xff0c;main方法的执行其实就是一条单独的执行路径。 程序中如果只有一条执行路径&#xff0c;那么这个程序就是单线程的程序。 多线程是什么&#xff1f; 多线程是指从软…

把GPT知识库当成记事本,非常有趣的玩法,很欢乐!

1. 笔者创建了一个“每天碎碎念”知识库&#xff0c;把重要的事情保存成文件记录&#xff0c;并进行训练。 2. 这样每当我记不清楚的时候 就开始灵魂发问~ 3. GPT最擅长胡编乱造&#xff0c;万一他忽悠我怎么办&#xff0c;别着急。查看“知识原文”就知道他是否忽悠你了。 这…

C# 实时监控双门双向门禁控制板源码

本示例使用设备&#xff1a;实时网络双门双向门禁控制板可二次编程控制网络继电器远程开关-淘宝网 (taobao.com) using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.…

通信原理板块——脉冲编码调制(PCM)

微信公众号上线&#xff0c;搜索公众号小灰灰的FPGA,关注可获取相关源码&#xff0c;定期更新有关FPGA的项目以及开源项目源码&#xff0c;包括但不限于各类检测芯片驱动、低速接口驱动、高速接口驱动、数据信号处理、图像处理以及AXI总线等 1、脉冲编码调制PCM原理 将模拟信号…

[Jenkins] Docker 安装Jenkins及迁移流程

系统要求 最低推荐配置: 256MB可用内存1GB可用磁盘空间(作为一个Docker容器运行jenkins的话推荐10GB) 为小团队推荐的硬件配置: 1GB可用内存50 GB 可用磁盘空间 软件配置: Java 8—无论是Java运行时环境&#xff08;JRE&#xff09;还是Java开发工具包&#xff08;JDK&#xff…

算法-链表-简单-相交、反转、回文、环形、合并

记录一下算法题的学习5 在写关于链表的题目之前&#xff0c;我们应该熟悉回忆一下链表的具体内容 什么是链表&#xff1a; 链表&#xff08;Linked list&#xff09;是一种常见的基础数据结构&#xff0c;是一种线性表&#xff0c;但是并不会按线性的顺序存储数据&#xff0c…

力扣第695题 岛屿的最大面积 C++ DFS BFS 附Java代码

题目 695. 岛屿的最大面积 中等 相关标签 深度优先搜索 广度优先搜索 并查集 数组 矩阵 给你一个大小为 m x n 的二进制矩阵 grid 。 岛屿 是由一些相邻的 1 (代表土地) 构成的组合&#xff0c;这里的「相邻」要求两个 1 必须在 水平或者竖直的四个方向上 相邻。你…

系列四、JVM的内存结构【本地接口(Native Interface)】

一、组成 本地接口由本地方法栈&#xff08;Native Method Stack&#xff09;、本地方法接口&#xff08;Native Interface&#xff09;、本地方法库组成。 二、本地接口的作用 本地接口的作用是融合不同的编程语言为Java所用&#xff0c;它的初衷是融合C/C程序&#xff0c;Jav…

CTFhub-RCE-过滤运算符

检查网页源代码发现如果用户输入 || &#xff08;逻辑或操作符&#xff09; 或者是 & &#xff08;按位与操作符&#xff09;&#xff0c;就不会执行。直接进行测试&#xff0c;在 URL 后输入本地 IP 进行 ping 命令测试&#xff0c;发现可以正常回显。检查网页源代码发现如…

C++基础从0到1入门编程(一)

系统学习C 方便自己日后复习&#xff0c;错误的地方希望积极指正 参考视频&#xff1a;黑马程序员匠心之作|C教程从0到1入门编程,学习编程不再难 1 第一个C程序-HelloWorld 编写一个C程序分为四个步骤&#xff1a; &#xff08;1&#xff09;创建项目 &#xff08;2&#xff…

Android开发APP显示头部Bar

Android开发显示头部Bar 需求&#xff1a; 显示如下图&#xff1a; 显示头部Bar&#xff0c;颜色也能自定义。 解决方案 这个修改是在如下三个文件里进行修改&#xff1a; 按顺序修改&#xff1a; themes.xml(night): <resources xmlns:tools"http://schemas.andr…

LeetCode(22)N 字形变换【数组/字符串】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; N 字形变换 1.题目 将一个给定字符串 s 根据给定的行数 numRows &#xff0c;以从上往下、从左到右进行 Z 字形排列。 比如输入字符串为 "PAYPALISHIRING" 行数为 3 时&#xff0c;排列如下&#xff1a; P A …

3.6 Windows驱动开发:内核进程汇编与反汇编

在笔者上一篇文章《内核MDL读写进程内存》简单介绍了如何通过MDL映射的方式实现进程读写操作&#xff0c;本章将通过如上案例实现远程进程反汇编功能&#xff0c;此类功能也是ARK工具中最常见的功能之一&#xff0c;通常此类功能的实现分为两部分&#xff0c;内核部分只负责读写…