系统学习算法: 专题八 二叉树中的深搜

news2025/3/12 22:08:31

深搜其实就是深度优先遍历(dfs),与此相对的还有宽度优先遍历(bfs)

如果学完数据结构有点忘记,如下图,左边是dfs,右边是bfs

而二叉树的前序,中序,后序遍历都可以使用递归来编写,而这三种遍历都属于dfs

所以本质还是递归,只是换到了二叉树这个数据结构而已

题目一:

思路:

题意很简单,根据示例1所演示的很容易理解

那就是用递归去遍历

还是按照我们递归专题的方法,构想递归函数的功能,这道我希望递归函数能够我传入一个结点, 返回以我传入的这个结点为根的布尔值,所以参数是一个结点,然后无条件信任

找结束条件,那就是当结点为叶子结点时,是1返回true,是0返回false

然后是子问题的操作步骤

先通过调用递归函数拿到左子树的布尔值和右子树的布尔值,然后再看当前根结点是2还是3,是2就返回或的条件判断后的布尔值,是3就返回与的条件判断后的布尔值

代码:

class Solution {
    //递归函数
    public boolean dfs(TreeNode root){
        //结束条件
        if(root.left==null&&root.right==null){
            return root.val==0?false:true;
        }
        //通过递归函数拿到左右子树的布尔值
        boolean ret1=dfs(root.left);
        boolean ret2=dfs(root.right);
        //返回对应的条件判断后的布尔值
        return root.val==2?ret1|ret2:ret1&ret2;
    }
    public boolean evaluateTree(TreeNode root) {
        //调用递归函数
        return dfs(root);
    }
}

所以这道题是一道后序遍历的dfs 

题目二:

思路:

根据题意,以示例2为例子,我们得知在4这个结点,左子树应该返回495+491,右子树应该返回40,往下走,9这个结点,左子树应该返回95+91,0这个结点则返回0,5和1返回5,1

如果按照上面这么想的话,那么就走弯路了

因为4和9结点的子问题操作流程不一样,4返回495+491没问题,但是9不能返回95+91,和题意的意思无关,而且返回值只有一个,又怎么返回95,91两个返回值呢

所以这种从下往上的顺序是不对的

由此要换成从上往下的顺序,构想递归函数的功能是返回该结点的整条分支的和,这里功能要想清楚,比如9这个结点,应该返回495+491而不是95+91,是包含该结点的全部分支,5这个结点就应该返回495,1这个结点就应该返回491,按照这个逻辑,4就应该返回(495+491)+40,这样就符合题意了

所以要将该节点的父亲结点的值传下来,即9这个结点就应该拿到4这个参数,然后往下传49,5就会拿到49.这是就会返回495

结束条件就是左右结点都为空,即叶子结点时,就直接返回这条分支的和

代码:

class Solution {
    public int dfs(TreeNode node,int presum){
        //拿到并更新当前分支的前位数
        presum=presum*10+node.val;
        //如果是叶子结点
        if(node.left==null&&node.right==null){
            //返回该分支的和
            return presum;
        }
        //左右分支的和
        int ret1=0,ret2=0;
        //如果有左分支
        if(node.left!=null){
            //返回左分支的和
            ret1 = dfs(node.left,presum);
        }
        //如果有右分支
        if(node.right!=null){
            //返回右分支的和
            ret2=dfs(node.right,presum);
        }
        //返回左右分支的和
        return ret1+ret2;
    }
    public int sumNumbers(TreeNode root) {
        //调用递归函数
        return dfs(root,0);
    }
}

算比较难的一道题,因为很容易一开始陷入错误思路,想不到传参要传之前的数,越绕越迷糊

  题目三:

思路:

 题意就是删除全为0的子树,结束条件会比较好想一点,就是当为叶子结点的时候,如果是0就删除,不是0就保留,这里删除就用null来代替,所以上面的父结点会接收到空结点,如果左右子节点都返回空,那么这个父结点又成为新的叶子结点,由此递归的特征就体现出来了

代码:

class Solution {
    public TreeNode dfs(TreeNode node){
        //如果有左子树
        if(node.left!=null){
            //返回不包含1的子树
            node.left=dfs(node.left);
        }
        //如果有右子树
        if(node.right!=null){
            //返回不包含1的子树
            node.right=dfs(node.right);
        }
        //结束条件,如果为叶子结点,且为0
        if(node.left==null&&node.right==null&&node.val==0){
            //把自己删除掉
            return null;
        }else{
            //不删除,返回自己
            return node;
        }
    }
    public TreeNode pruneTree(TreeNode root) {
        root=dfs(root);
        return root;
    }
}

题目四:

思路:

 在学习二叉搜索树的时候,我们知道它有一个特点:那就是中序遍历的话它会返回一串有序的数组,由此我们就可以利用这个特点来解决这道题

我们可以设置一个全局变量,初始化为最小值,然后中序遍历二叉搜索树,每遍历到一个就与全局变量进行比较,如果大于,则说明到现在为止还是二叉搜索树,如果小于或等于,那就不是二叉搜索树

那我们这个递归函数的功能就是传入一个根节点,判断是否是二叉搜索树

结束条件就是当结点为空,就直接返回true,因为定义上空树也是二叉搜索树

注:底下提示有说数据范围最小为整型的最小值,所以全局变量要用long的最小值,避免刚好出现极端情况

代码:

class Solution {
    //全局变量
    long prev = Long.MIN_VALUE;
  
    public boolean isValidBST(TreeNode root) {
        //空子树也是二叉搜索树
        if (root == null) {
            return true;
        }
        //先左
        boolean left = isValidBST(root.left);
        //再中
        boolean cur = false;
        //如果符合二叉搜索树的性质
        if (root.val > prev) {
            cur = true;
            //更新变量
            prev=root.val;
        }
        //后右
        boolean right=isValidBST(root.right);
        return left&&cur&&right;
    }
} 

其中如果判断完左子树如果已经不是二叉搜索树的话,我们就没必要继续了,就可以将剩下的“剪枝”掉,同理如果当前结点如果也不是二叉搜索树,右子树是不是二叉搜索树也无所谓了,也可以“剪枝”掉,而剪枝就是用if来判断一下就行 

代码(剪枝):

class Solution {
    //全局变量
    long prev = Long.MIN_VALUE;
  
    public boolean isValidBST(TreeNode root) {
        //空子树也是二叉搜索树
        if (root == null) {
            return true;
        }
        //先左(判断左子树是不是二叉搜索树)
        boolean left = isValidBST(root.left);
        //剪枝
        if (left == false) {
            return false;
        }
        //再中(判断当前结点是不是符合二叉搜索树)
        boolean cur = false;
        //如果符合二叉搜索树的性质
        if (root.val > prev) {
            cur = true;
            //更新变量
            prev=root.val;
        }
        //剪枝
        if(cur==false){
            return false;
        }
        //后右(判断右子树是不是二叉搜索树)
        boolean right=isValidBST(root.right);
        return right;
    }
} 

题目五:

思路:

这道题说了是二叉搜索树,所以还要利用中序遍历是有序的数组来解决,用一个全局变量count来记录遍历到第几个了,比如让count==k,每遍历一次count--,如果当count==0时,就说明当前结点的val就是第k小的元素,再用一个全局变量来保存该结点的值即可,然后剩下就全部return,可以用剪枝来优化,就判断一下保存答案的全局变量ret是否有值,如果有值就剪枝,没值就继续

代码:

class Solution {
    int count=0;
    int ret=0;
    public int kthSmallest(TreeNode root, int k) {
        count=k;
        dfs(root);
        return ret;
    }

    public void dfs(TreeNode root){
        //为空就无事发生,顺便剪枝
        if(root==null||ret!=0){
            return;
        }
        //先左
        dfs(root.left);
        //每遍历一个就--
        count--;
        //如果是第k小的值,记录答案
        if(count==0){
            ret=root.val;
        }
        //如果已经找到答案了,剪枝
        if(ret!=0){
            return;
        }
        dfs(root.right);
    }
}

题目六:

 思路:

这里就是前序遍历,因为是先处理完当前结点,再来考虑左右子树

一共有两种情况:叶子结点和非叶子节点

如果是叶子结点,就只用添加当前结点的值即可,并且将该字符串添加到list里

而如果是非叶子结点,那么不仅要添加当前结点的值,还要在后面多添加个“->”,但不添加到list

其中需要注意的是“恢复现场”

因为根据前序遍历的顺序,左结点之后才是右结点,所以字符串会先保留左结点的结果,比如上图会保留到1->2->4 ,但是回溯的时候,右边的结点应该是1->2->5->7,如果不恢复现场,就会是

1->2->45->7,导致左结点的结果影响了右结点

因此要注意恢复现场,如果是字符串是全局变量,那么操作就比较麻烦,要添加后再删除,我们可以利用函数参数来做到恢复现场,如果参数是String这种不可变对象就可以直接使用,因为是新创建一个String,但如果使用StringBuffer这种可变对象就不能直接使用,要自己在函数体中手动创建一个新StringBuffer

代码:

class Solution {
    //保存所有路径的顺序表
    List<String> list=new ArrayList<String>();
    //使用StringBuffer作为参数
    public void dfs(TreeNode node,StringBuffer s){
        //需要手动创建一个新StringBuffer
        StringBuffer str=new StringBuffer(s);
        //先自身,如果为叶子结点
        if(node.left==null&&node.right==null){
            //就只加该结点的值
            list.add((str.append(node.val)).toString());
            //剪枝
            return;
        }
        //不是叶子结点就加值和箭头
        str.append(node.val+"->");
        //再左
        if(node.left!=null){
            dfs(node.left,str);
        }
        //后右
        if(node.right!=null){
            dfs(node.right,str);
        }
    }
    public List<String> binaryTreePaths(TreeNode root) {
        StringBuffer str=new StringBuffer("");
        dfs(root,str);
        return list;
    }
}

总结:

如果遇到二叉树,那么大部分都要用到递归,也就是深搜(dfs),其中包括全局变量的使用,剪枝,回溯,而如果出现回溯,那么就会出现恢复现场,而出现递归就会出现回溯,也就是说递归就会涉及到恢复现场,其中二叉搜索树的特性是中序遍历后是有序的数组,操作顺序要结合题目来选择,是前序还是中序还是后序,接下来一些较难的题主要都会涉及到路径,路径最重要的也是恢复现场,所以要理解恢复现场

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

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

相关文章

进程、线程、内存和IO模型的概念详解

进程、线程、内存和IO模型的概念详解 1 进程与线程1.1 进程1.1.1 进程分类1.1.2 进程的状态和转换1.1.3 僵尸进程和孤儿进程的区别1.1.4 进程之间的通信1.1.5 用户态和内核态1.1.6 用户空间和内核空间 1.2 线程1.2.1 线程的状态和转换1.2.2 进程与线程的区别 1.3 多进程和多线程…

Labelme转Voc、Coco

Q&#xff1a;在github找的cv代码基本都是根据现有且流行的公共数据集格式组织的训练数据集&#xff0c;这导致我使用labelme标注好之后需要我们重新组织数据集 labelme2coco #!/usr/bin/env pythonimport argparse import collections import datetime import glob import j…

JVM方法区

一、栈、堆、方法区的交互关系 二、方法区的理解: 尽管所有的方法区在逻辑上属于堆的一部分&#xff0c;但是一些简单的实现可能不会去进行垃圾收集或者进行压缩&#xff0c;方法区可以看作是一块独立于Java堆的内存空间。 方法区(Method Area)与Java堆一样&#xff0c;是各个…

【Python】第七弹---Python基础进阶:深入字典操作与文件处理技巧

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【MySQL】【Python】 目录 1、字典 1.1、字典是什么 1.2、创建字典 1.3、查找 key 1.4、新增/修改元素 1.5、删除元素 1.6、遍历…

在实际开发中,如何正确使用 INT(1) 和 INT(10)

在实际开发中&#xff0c;如何正确使用 INT(1) 和 INT(10) 前言 在数据库设计和开发过程中&#xff0c;数据类型的选择至关重要。 最近&#xff0c;我在工作中遇到了一个关于MySQL中INT类型的误解问题&#xff0c;这让我意识到很多开发者对INT类型的理解存在误区。 本文将深…

像接口契约文档 这种工件,在需求 分析 设计 工作流里面 属于哪一个工作流

οゞ浪漫心情ゞο(20***328) 2016/2/18 10:26:47 请教一下&#xff0c;像接口契约文档 这种工件&#xff0c;在需求 分析 设计 工作流里面 属于哪一个工作流&#xff1f; 潘加宇(35***47) 17:17:28 你这相当于问用例图、序列图属于哪个工作流&#xff0c;看内容。 如果你的&quo…

GAMES101学习笔记(六):Geometry 几何(基本表示方法、曲线与曲面、网格处理)

文章目录 几何的表示方法隐式几何 Implicit Geometry代数曲面(Algebraic surface)构造实体几何CSG(Constructive Solid Geometry)距离函数(Distance Function)水平集方法(Level Set Methods)分型几何(Fractal) 显式几何 Explicit Geometry点云(Point Cloud)多边形网格(Polygon …

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】1.24 随机宇宙:生成现实世界数据的艺术

1.24 随机宇宙&#xff1a;生成现实世界数据的艺术 目录 #mermaid-svg-vN1An9qZ6t4JUcGa {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-vN1An9qZ6t4JUcGa .error-icon{fill:#552222;}#mermaid-svg-vN1An9qZ6t4JUc…

爬虫基础(三)Session和Cookie讲解

目录 一、前备知识点 &#xff08;1&#xff09;静态网页 &#xff08;2&#xff09;动态网页 &#xff08;3&#xff09;无状态HTTP 二、Session和Cookie 三、Session 四、Cookie &#xff08;1&#xff09;维持过程 &#xff08;2&#xff09;结构 正式开始说 Sessi…

HTMLCSS :下雪了

这段代码创建了一个动态的雪花飘落加载动画&#xff0c;通过 CSS 技术实现了雪花的下落和消失效果&#xff0c;为页面添加了视觉吸引力和动态感。 大家复制代码时&#xff0c;可能会因格式转换出现错乱&#xff0c;导致样式失效。建议先少量复制代码进行测试&#xff0c;若未能…

【Windows Server实战】生产环境云和NPS快速搭建

前置条件 本文假定你已达成以下前提条件&#xff1a; 有域控DC。有证书服务器&#xff08;AD CS&#xff09;。已使用Microsoft Intune或者GPO为客户机申请证书。服务器上至少有两张网卡&#xff08;如果用虚拟机做的测试环境&#xff0c;可以用一张HostOnly网卡做测试&#…

RHCSA——搭建FTP文件共享服务器

一、实验目的 1、掌握vsftpd服务器的配置方法 2、熟悉FTP客户端工具的使用 3、掌握常见的FTP服务器的故障排除 二、实验项目背景 某企业像架构一台FTP服务器&#xff0c;为企业局域网中的计算机提供文件传送的任务&#xff0c;为财务部门、销售部门和OA系统提供异地数据备…

IM 即时通讯系统-50-[特殊字符]cim(cross IM) 适用于开发者的分布式即时通讯系统

IM 开源系列 IM 即时通讯系统-41-开源 野火IM 专注于即时通讯实时音视频技术&#xff0c;提供优质可控的IMRTC能力 IM 即时通讯系统-42-基于netty实现的IM服务端,提供客户端jar包,可集成自己的登录系统 IM 即时通讯系统-43-简单的仿QQ聊天安卓APP IM 即时通讯系统-44-仿QQ即…

Python在线编辑器

from flask import Flask, render_template, request, jsonify import sys from io import StringIO import contextlib import subprocess import importlib import threading import time import ast import reapp Flask(__name__)RESTRICTED_PACKAGES {tkinter: 抱歉&…

ZZNUOJ(C/C++)基础练习1041——1050(详解版)

1041 : 数列求和2 题目描述 输入一个整数n&#xff0c;输出数列1-1/31/5-……前n项的和。 输入 输入只有一个整数n。 输出 结果保留2为小数,单独占一行。 样例输入 3 样例输出 0.87注意sum 1相当于sumsum1 注意sum * 1相当于sumsum*1 C语言版 #include<stdio.h> // 包含…

浅析DDOS攻击及防御策略

DDoS&#xff08;分布式拒绝服务&#xff09;攻击是一种通过大量计算机或网络僵尸主机对目标服务器发起大量无效或高流量请求&#xff0c;耗尽其资源&#xff0c;从而导致服务中断的网络攻击方式。这种攻击方式利用了分布式系统的特性&#xff0c;使攻击规模更大、影响范围更广…

深度学习 Pytorch 神经网络的学习

本节将从梯度下降法向外拓展&#xff0c;介绍更常用的优化算法&#xff0c;实现神经网络的学习和迭代。在本节课结束将完整实现一个神经网络训练的全流程。 对于像神经网络这样的复杂模型&#xff0c;可能会有数百个 w w w的存在&#xff0c;同时如果我们使用的是像交叉熵这样…

【回溯】目标和 字母大小全排列

文章目录 494. 目标和解题思路&#xff1a;回溯784. 字母大小写全排列解题思路&#xff1a;回溯 494. 目标和 494. 目标和 给你一个非负整数数组 nums 和一个整数 target 。 向数组中的每个整数前添加 或 - &#xff0c;然后串联起所有整数&#xff0c;可以构造一个 表达式…

Linux系统上安装与配置 MySQL( CentOS 7 )

目录 1. 下载并安装 MySQL 官方 Yum Repository 2. 启动 MySQL 并查看运行状态 3. 找到 root 用户的初始密码 4. 修改 root 用户密码 5. 设置允许远程登录 6. 在云服务器配置 MySQL 端口 7. 关闭防火墙 8. 解决密码错误的问题 前言 在 Linux 服务器上安装并配置 MySQL …

记录一次,PyQT的报错,多线程Udp失效,使用工具如netstat来检查端口使用情况。

1.问题 报错Exception in thread Thread-1: Traceback (most recent call last): File "threading.py", line 932, in _bootstrap_inner File "threading.py", line 870, in run File "main.py", line 456, in udp_recv IndexError: list…