算法入门-深度优先搜索1

news2024/11/15 12:21:50

第六部分:深度优先搜索

144.二叉树的前序遍历(简单)

题目:给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

输入:root = [1,null,2,3]
输出:[1,2,3]

第一种思路:

通过递归的方式实现前序遍历,对于每个节点,先记录当前节点的值,然后递归调用左子树和右子树的方法,这种方式直观且易于理解。

  1. 创建结果列表:

    • preorderTraversal 方法接收一个树的根节点(root)作为参数,并创建一个 List 类型的列表 list 用于存储遍历结果。

  2. 处理空树的情况:

    • 方法首先检查传入的 root 是否为 null。如果是,说明树为空,直接返回空列表。

  3. 访问当前根节点:

    • 如果根节点不为空,首先将当前节点的值(root.val)添加到结果列表 list 中。

  4. 递归遍历左子树:

    • 接着,调用 preorderTraversal(root.left) 方法递归遍历当前节点的左子树,并将返回的结果(即左子树的遍历结果)添加到 list 中。这里使用了 list.addAll() 将子树的值合并到主列表中。

  5. 递归遍历右子树:

    • 然后,类似地,调用 preorderTraversal(root.right) 方法递归遍历当前节点的右子树,并将结果添加到 list 中。

  6. 返回结果列表:

    • 最后,返回合并后的结果列表 list,该列表包含了整个二叉树的前序遍历结果。

// 二叉树节点的定义  
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {
    }

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

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
} 

class Solution {  
    public List<Integer> preorderTraversal(TreeNode root) {  
        List<Integer> list = new ArrayList<>();  
        if (root == null) return list;  

        // 访问当前根节点  
        list.add(root.val);  
        
        // 递归遍历左子树并将结果添加到列表  
        list.addAll(preorderTraversal(root.left));  
        
        // 递归遍历右子树并将结果添加到列表  
        list.addAll(preorderTraversal(root.right));  
        
        return list; // 返回结果列表  
    }  
}

第二种思路:

使用迭代的方式,本质上是在模拟递归,因为在递归的过程中使用了系统栈,所以在迭代的解法中常用 Stack 来模拟系统栈。

!这里需要稍微注意的是Stack栈是先进后出(后进先出),所以先序是先进右子树,然后是左子树!

  1. 创建结果存储列表:

    • Solution 类中,定义了 preorderTraversal 方法用于实现前序遍历。首先,创建一个 List 列表 list 用于存储遍历结果。

  2. 处理空树的情况:

    • 若输入的根节点(root)为空(null),直接返回空列表,表示没有节点可供遍历。

  3. 初始化栈结构:

    • 创建一个 Stack 对象 stack,用以辅助实现树的遍历。将根节点压入栈中,开始遍历。

  4. 循环遍历栈:

    • 使用一个while循环,只要栈非空,就继续遍历。每次循环中,从栈中弹出一个节点(node):

      • 将弹出的节点值(node.val)添加到结果列表 list 中。

  5. 处理子节点:

    • 首先检查弹出节点的右子节点:

      • 若右子节点不为空,将其压入栈中。

    • 然后检查左子节点:

      • 若左子节点不为空,也将其压入栈中。

    • 由于栈是后进先出(LIFO)的特性,右子节点在栈中被压入后,会在左子节点之前被弹出和处理。

  6. 返回结果列表:

    • 当栈空时,表示所有可遍历的节点都已被处理,最后返回结果列表 list,其中包含了按照前序遍历顺序收集的所有节点值。

// 二叉树节点的定义  
public class TreeNode {
    int val;
    TreeNode left;
    TreeNode right;

    TreeNode() {
    }

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

    TreeNode(int val, TreeNode left, TreeNode right) {
        this.val = val;
        this.left = left;
        this.right = right;
    }
}

class Solution {
    // 前序遍历二叉树
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>(); // 用于存储遍历结果

        // 如果根节点为空,直接返回空列表
        if (root == null)
            return list;

        // 创建一个栈来辅助遍历
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root); // 将根节点推入栈中

        // 当栈不为空时,继续遍历
        while (!stack.isEmpty()) {
            TreeNode node = stack.pop(); // 弹出栈顶节点
            list.add(node.val); // 将当前节点值添加到结果列表中

            // 先推入右子节点再推入左子节点,确保左子节点先被处理
            if (node.right != null)
                stack.push(node.right);
            if (node.left != null)
                stack.push(node.left);
        }
        return list; // 返回结果列表
    }
}

94.二叉树的中序遍历(简单)

题目:给定一个二叉树的根节点 root ,返回 它的 中序 遍历

示例 1:

输入:root = [1,null,2,3]
输出:[1,3,2]

第一种思路:

通过递归的方式实现中序遍历,对于每个节点,先递归调用左子树,然后记录当前节点的值,接着再递归调用右子树的方法,这种方式直观且易于理解。

  1. 中序遍历定义

    • 中序遍历的顺序是:先遍历左子树,再访问根节点,最后遍历右子树。

  2. 递归实现

    • inorderTraversal 方法中,首先创建一个结果列表 list 用以存放遍历的结果。

    • 如果当前传入的节点 rootnull,说明树已经遍历完毕,直接返回空列表。

    • 通过递归调用 inorderTraversal(root.left) 遍历左子树,返回的结果添加到 list 中。

    • 将当前节点的值 root.val 添加到 list

    • 最后,递归调用 inorderTraversal(root.right) 遍历右子树,将结果添加到 list 中。

  3. 返回结果:最终返回包含中序遍历节点值的列表。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>(); // 创建一个列表用于存储遍历结果
        if (root == null)
            return list; // 如果当前节点为空,返回空列表

        // 递归遍历左子树并将其结果添加到列表中
        list.addAll(inorderTraversal(root.left));

        // 将当前节点的值添加到列表中
        list.add(root.val);

        // 递归遍历右子树并将其结果添加到列表中
        list.addAll(inorderTraversal(root.right));

        return list; // 返回包含中序遍历结果的列表
    }
}

第二种思路:

  1. 初始化

    • 创建一个空的结果列表 list 来存储遍历结果。

    • 创建一个栈 stack 用于辅助遍历。

    • 当前节点 current 初始化为树的根节点。

  2. 循环遍历

    • 使用 while 循环检查当前节点是否为 null 或栈是否为空。这确保我们在有节点可处理的情况下继续遍历。

  3. 遍历左子树

    • 使用嵌套的 while 循环将所有左子节点依次推入栈中,直到 currentnull。这相当于深入到最左端节点。

  4. 访问根节点

    • 当到达最左端节点后,弹出栈顶的节点(即当前节点),将其值添加到结果列表中。

  5. 遍历右子树

    • 将当前节点更新为其右子节点(current = current.right),并循环回到外层的 while 继续进行中序遍历。

结束条件:

当栈为空且没有更多的节点可供处理时,整个树的中序遍历完成,此时返回结果列表 list

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    // 中序遍历二叉树
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>(); // 用于存储遍历结果
        Stack<TreeNode> stack = new Stack<>();
        TreeNode current = root; // 当前节点从根节点开始

        // 当栈不为空或当前节点不为空时,继续遍历
        while (current != null || !stack.isEmpty()) {
            // 将当前节点的所有左子节点和当前节点(最底下)推入栈中
            while (current != null) {
                stack.push(current);
                current = current.left;
            }
            // 弹出栈顶节点
            current = stack.pop();
            list.add(current.val); // 将当前节点值添加到结果列表中

            // 现在访问节点的右子树
            current = current.right;
        }

        return list; // 返回结果列表
    }
}

145.二叉树的后序遍历(简单)

使用场景:后序遍历主要用于需要在访问节点之前处理其子节点的场合,如删除树、计算树的权重等。

题目:给你一棵二叉树的根节点 root ,返回其节点值的 后序遍历

示例 1:

img

输入:root = [1,null,2,3]
输出:[3,2,1]

第一种思路:

  1. 初始化结果列表:创建一个空的列表 list,该列表用于存储遍历过程中遇到的节点值。

  2. 检查节点是否为空

    • 如果 root(当前节点)为 null,这说明在递归中某个节点没有子节点,此时直接返回空列表 list。这也是递归的终止条件。

  3. 递归遍历左子树

    • 调用 postorderTraversal(root.left) 递归遍历左子树,返回结果并加入 list 中。此时,遍历到左子树的所有节点,并将它们的值按照后序顺序添加到 list 中。

  4. 递归遍历右子树

    • 之后,调用 postorderTraversal(root.right) 递归遍历右子树,同样返回结果并加入 list 中。此时,遍历到右子树的所有节点,并将它们的值按照后序顺序添加到 list 中。

  5. 添加当前节点的值

    • 最后,将当前节点的值 root.val 添加到 list 中。根据后序遍历的规则,当前节点的值在所有子节点值之后添加。

  6. 返回结果列表

    • 返回 list,即当前子树的后序遍历结果。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    // 方法:后序遍历二叉树
    public List<Integer> postorderTraversal(TreeNode root) {
        // 创建一个空列表,用于存储遍历结果
        List<Integer> list = new ArrayList<>();

        // 如果根节点为空,直接返回空列表
        if (root == null)
            return list;

        // 递归遍历左子树并将结果添加到列表中
        list.addAll(postorderTraversal(root.left));
        // 递归遍历右子树并将结果添加到列表中
        list.addAll(postorderTraversal(root.right));
        // 添加根节点的值到列表中,表示后序遍历的顺序
        list.add(root.val);

        // 返回包含后序遍历结果的列表
        return list;
    }
}

第二种思路:

  1. 栈的使用:使用栈来保存尚未访问的节点,以便在后续操作中能够逐个访问它们。这样可以模拟递归调用的过程。

  2. 初始遍历

    • 首先,从根节点开始,一直向左子树移动,并将路径上的所有节点压入栈中。这部分保证了我们可以在后续的操作中首先处理左子树。

  3. 处理栈顶节点

    • 一旦到达最左下的节点(rootnull),进入处理栈顶节点的阶段。

    • 获取栈顶节点(

      peekNode

      ),然后看看它的右子节点:

      • 如果 当前节点的右子树是null,或者它的右子节点已经在之前的遍历中访问过(通过lastVisited来判断),那么我们就可以访问这个节点(添加它的值到结果列表中)并将其从栈中弹出。

      • 否则,如果当前节点有右子节点且尚未访问,则将root指向该右子节点,以便接下来访问右子树。

  4. 更新lastVisited:在访问完一个节点后,将其赋值给lastVisited,以便在后续判断中使用。

  5. 循环终止条件:循环继续,直到栈为空且当前节点为null,此时遍历完成。

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 * int val;
 * TreeNode left;
 * TreeNode right;
 * TreeNode() {}
 * TreeNode(int val) { this.val = val; }
 * TreeNode(int val, TreeNode left, TreeNode right) {
 * this.val = val;
 * this.left = left;
 * this.right = right;
 * }
 * }
 */
class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode lastVisited = null; // 用于记录上一个访问的节点

        while (root != null || !stack.isEmpty()) {
            while (root != null) {
                stack.push(root);
                root = root.left; // 优先访问左子树
            }

            TreeNode peekNode = stack.peek(); // 获取栈顶节点
            // 如果右子树为null或已经访问过
            if (peekNode.right == null || peekNode.right == lastVisited) {
                list.add(peekNode.val); // 添加当前节点的值
                lastVisited = stack.pop(); // 将当前节点标记为已访问
            } else {
                root = peekNode.right; // 如果有右子树,访问右子树
            }
        }

        return list;
    }
}

迭代的后序遍历方法充分利用了栈的特性,模拟递归中的函数调用栈,确保在访问节点时能够保持后序遍历的顺序。通过lastVisited的引入,有效地管理了节点的访问状态,避免了重复访问的问题。

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

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

相关文章

AtCoder Beginner Contest 366(D~E题解)

闲来无事去vp了一下之前放假没打的比赛&#xff0c;感觉需要总结的也就这两题吧&#xff0c;a&#xff0c;c都是水题&#xff0c;b只不过是实现有一点难&#xff0c;并不是很难写&#xff0c;d是一个需要自己推的三维前缀和&#xff0c;e也是一种前缀和&#xff0c;我当时没想到…

WEB渗透Win提权篇-白名单提权

提权工具合集包&#xff08;免费分享&#xff09;&#xff1a; 夸克网盘分享 往期文章 WEB渗透Win提权篇-提权工具合集-CSDN博客 WEB渗透Win提权篇-RDP&Firewall-CSDN博客 WEB渗透Win提权篇-MSSQL-CSDN博客 WEB渗透Win提权篇-MYSQL-udf-CSDN博客 WEB渗透Win提权篇-Acc…

什么是代码审查(Code Review)?它有什么好处?

代码审查&#xff08;Code Review&#xff09;是软件开发过程中一个至关重要的环节&#xff0c;它指的是团队成员之间相互检查、评估代码的过程。这一过程不仅涉及对代码质量的把控&#xff0c;更是提升团队整体编程能力、确保软件安全性的重要手段。在本文中&#xff0c;我们将…

CSRF 概念及防护机制

概述 CSRF&#xff08;Cross-Site Request Forgery&#xff09;&#xff0c;即跨站请求伪造&#xff0c;是一种网络攻击方式。在这种攻击中&#xff0c;恶意用户诱导受害者在不知情的情况下执行某些操作&#xff0c;通常是利用受害者已经登录的身份&#xff0c;向受害者信任的…

我是如何在一分钟之内,不用多次交互AI,就完成了指定任务

本文背景 为什么我的AI不听话&#xff1f; 为什么我用AI写知乎文、视频文案、豆瓣影评、工作日报、论文、商业策划案、标书、代码都一直得不到想要的效果&#xff1f; 为什么我的AI生成的都是没有价值的口水文&#xff1f; 大象经过大量的AI实战经验&#xff0c;给出了这些问题…

ESP32-C3在MQTT访问时出现“Last error code reported from esp-tls: 0x8001”和问题的分析(3)

接前一篇文章:ESP32-C3在MQTT访问时出现“Last error code reported from esp-tls: 0x8001”和问题的分析(2) 上一回讲解了所遇问题log中的“esp-tls: couldnt get hostname for :iot-emqx-pre.nanshe-tech.com: getaddrinfo() returns 202, addrinfo=0x0”,再来回顾一下。…

USB:物理接口

USB&#xff1a;物理接口 物理接口 从高级概述角度来看&#xff0c;USB 的物理接口具有两个组件&#xff1a;线缆和连接器。这些连接器将设备连接到主机上。 一个 USB 线缆包含由一个绝缘套保护的多个组件。该绝缘套下面是一个包含了一个带有铜面的外部扩展板。 外部扩展板内包…

为什么现在人工智能大部分都用python而不是其他软件呢?

大部分人都选择使用Python而不是其他软件&#xff0c;主要是因为Python具有多方面的优势&#xff0c;这些优势使其在众多编程语言中脱颖而出&#xff0c;成为许多领域&#xff0c;特别是IT和人工智能领域的首选。以下是几个主要原因&#xff1a; 1. 简单易学 Python的语法简洁…

PMP备考3A的心得分享

首先&#xff0c;每一位报考的都会收到一份学习计划表&#xff0c;一定要仔细阅读这张表&#xff0c;并与自己的时间结合起来&#xff0c;看是否会有很大的冲突&#xff0c;如果有&#xff0c;那么可以找老师帮忙解决。一般来说&#xff0c;学习计划表的时间安排是非常恰当的&a…

下载淘宝的短视频(通过第三方插件)

目录&#xff1a; 1、通过插件插件下载短视频 1&#xff09;获取“Microsoft Edge扩展” 2&#xff09;搜索“aix智能下载器” 3&#xff09;将插件钉在浏览器上 4&#xff09;嗅控并下载视频 2、从其他来源安装插件 1、通过插件插件下载短视频 1&#xff09;获取“M…

(二)Kafka离线安装 - Zookeeper下载及安装

一、下载 下载ZooKeeper之前&#xff0c;需要根据下载的Kafka版本&#xff0c;来确定ZooKeeper的版本。 Kafka官方地址&#xff1a;Apache Kafka 1、先下载源码版本&#xff0c;下载完后解压&#xff0c;在kafka-3.8.0-src\gradle目录下找到dependencies.gradle文件&#xff…

从 0 到 1 的Prompt 教程,来自Claude 官方,不会写 prompt的看这个足够

Claude 的强大&#xff0c;最近得到了很多网友的验证&#xff0c;甚至效果上面大有超越 GPT的许多声音。 所以从优秀的 Claude 中学习&#xff0c;将会是一个很好的起点。 这里&#xff0c;Claude 的开发者们提供了一个相当详细的 Prompt Engineering 教程。 这个教程能够全面…

USB3.2 摘录(13)

系列文章目录 USB3.2 摘录&#xff08;一&#xff09; USB3.2 摘录&#xff08;二&#xff09; USB3.2 摘录&#xff08;三&#xff09; USB3.2 摘录&#xff08;四&#xff09; USB3.2 摘录&#xff08;五&#xff09; USB3.2 摘录&#xff08;六&#xff09; USB3.2 摘录&…

SpringBoot JPA 语法大全

Keyword:为关键词 Sample:为直接用在方法名上的例子 JPQLsnipper:为JPQL,Query的写法的例子

redhat7.9安装zsh以及常用插件

1 安装zsh并更改默认终端 #1.安装软件包 yum -y install zsh git#2.更改默认终端 chsh -s /bin/zsh然后再退出下终端&#xff0c;重新登录用echo $SHELL 查看环境是否是/bin/zsh 2 配置oh-my-zsh #1.从git仓库中拉取oh-my-zsh git clone https://gitee.com/mirrors/oh-my-z…

【私有云场景案例分享②】批量装包与冒烟测试的自动化实现

一、前言 在软件开发和测试过程中&#xff0c;批量装包和冒烟测试是两个关键环节。随着项目规模的扩大和测试需求的增加&#xff0c;传统的手动操作方式已经无法满足效率和质量的要求。通过AirtestIDE企业版与DeviceKeeper结合的批量装包和冒烟测试解决方案&#xff0c;可以提…

【科研新手】如何判断自己找到的创新点是否已被发表呢?

很多人在自己的实验结果出来后再开始写论文&#xff0c;才发现自己的论文创新点已经被他人抢先发表了&#xff0c;因此我们需要做好预防措施&#xff0c;那么如何判断自己所找到的创新点是否已被发表了呢。 1、在纬度学术检索最新的文献并下载 2、将文献投喂到Kimi智能助手&am…

IOS逆向助手-无需越狱就能逆向IOS软件的强大工具

前言 晓杰自己就是JAVA开发程序员&#xff0c;工作之余也会学习下逆向&#xff0c;研究一下各个软件的漏洞用于提交漏洞赚取微薄的漏洞奖励来补贴家用&#xff0c;下面晓杰就将自用的IOS逆向助手分享给大家&#xff01; 软件简介 IOS逆向助手支持越狱和未越狱设备安装&#…

储能电池仓电池热失控监测系统技术分析

在当今能源转型的大背景下&#xff0c;储能技术的发展至关重要。而储能电池仓作为储能系统的核心组成部分&#xff0c;其安全性问题备受关注。电池热失控是储能电池仓面临的重大风险之一&#xff0c;一旦电池发生热失控&#xff0c;可能引发火灾、爆炸等严重后果。为了有效防范…

2024洗衣机选择(个人笔录)

总结 比较 说明&#xff1a;素材引自小红书博主郎朗Waves