【算法】二叉树中的 DFS

news2024/9/29 13:01:25

 【ps】本篇有 6 道 leetcode OJ。 

目录

一、算法简介

二、相关例题

1)计算布尔二叉树的值

.1- 题目解析

.2- 代码编写

2)求根节点到叶节点数字之和

.1- 题目解析

.2- 代码编写

3)二叉树剪枝

.1- 题目解析

.2- 代码编写

4)验证二叉搜索树

.1- 题目解析

.2- 代码编写

5)二叉搜索树中第K小的元素

.1- 题目解析

.2- 代码编写

6)二叉树的所有路径

.1- 题目解析

.2- 代码编写


一、算法简介

        二叉树是一种经典的树结构,是节点的一个有限集合(该集合为空,或由一个根节点加上两棵别称为左子树和右子树的二叉树组成)。

         二叉树中又有一些特别的类型,例如二叉搜索树、红黑树等。其中,二叉搜索树又称二叉排序树,是一种 key 搜索模型它或是一棵空树,亦或是具有以下性质的二叉树:

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

         二叉树中最常见的操作就是遍历整棵树,例如:

  • 前序遍历:根->左子树->右子树。
  • 中序遍历:左子树->根->右子树。
  • 后序遍历:左子树->右子树->根。
  • 层序遍历:根->下一层叶子节点->下下一层叶子节点...(一般用 BFS 实现)

        遍历整棵树的过程,一般通过 DFS 来实现,且二叉树的题型最能展现 DFS 的特性。

        DFS(深度优先搜索)是树或图这样的数据结构中常⽤的⼀种遍历算法,主要通过递归来实现。这个算法会尽可能深地搜索树或图中的分⽀,直到⼀条路径上的所有节点都被遍历完毕,然后再回溯到上⼀层,继续找另⼀条路遍历。

         在⼆叉树中,树的定义本⾝就是递归定义,因此采⽤ DFS 的⽅法去实现树的前序遍历、中序遍历、后序遍历,不仅容易理解⽽且代码很简洁。前中后序三种遍历的唯⼀区别就是访问根节点的时机不同,在做题的时候,选择⼀个适当的遍历顺序,对于算法的理解是⾮常有帮助的。

二、相关例题

1)计算布尔二叉树的值

2331. 计算布尔二叉树的值

.1- 题目解析

        整体地来看一棵树的话,题目就是要求根据根的运算符来求左孩子和右孩子的运算结果,而要求得根的运算结果,就要先知道整棵左子树的结果和整棵右子树的结果,由此,这个运算的过程其实是一个后序遍历,那么,我们对整棵树通过 DFS 进行一次后序遍历即可。

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    bool evaluateTree(TreeNode* root) {
        if(root->left==nullptr)return root->val==0 ? false : true;
        bool l=evaluateTree(root->left);
        bool r=evaluateTree(root->right);
        bool ret=false;
        if(root->val==2)ret=l|r;
        else ret=l&r;
        return ret;
        }
};

2)求根节点到叶节点数字之和

129. 求根节点到叶节点数字之和

.1- 题目解析

        与上道题不同的是,本题是前序遍历。

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    int sumNumbers(TreeNode* root) {
        return dfs(root,0);
    }
    int dfs(TreeNode* root,int presum)
    {
        presum=presum*10+root->val;
        if(root->left==nullptr && root->right==nullptr)return presum;
        int ret=0;
        if(root->left)ret+=dfs(root->left,presum);
        if(root->right)ret+=dfs(root->right,presum);
        return ret;
    }
};

3)二叉树剪枝

814. 二叉树剪枝

.1- 题目解析

        要删除二叉树中值为 0 的叶子节点,就要先删除左子树中的,再删除右子树,最后如果左子树和右子树都被删除了且根也是 0,那就把根也删除。也就是说,这其实是一个后序遍历的过程,而要删除值为 0 的叶子节点,只需将该节点置为空即可。

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    TreeNode* pruneTree(TreeNode* root) {
        if(root==nullptr)return nullptr;
        root->left=pruneTree(root->left);
        root->right=pruneTree(root->right);
        if(root->left==nullptr && root->right==nullptr && root->val==0)
        {
            root=nullptr;
        }
        return root;
    }
};

4)验证二叉搜索树

98. 验证二叉搜索树

.1- 题目解析

【Tips】二叉搜索树的中序遍历的结果,一定是一个升序序列。

        一颗二叉树要是二叉搜索树,那么根节点的左子树得是二叉搜索树,右子树也得是二叉搜索树。那么,我们可以通过中序遍历,先验证左子树是否是二叉搜索树,再将左孩子与根进行比较,然后再验证右子树是否是二叉搜索树,如果都满足二叉搜索树的性质,即左孩子 < 根 < 右孩子,那么整棵树就是一棵二叉搜索树。

        

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    long prev=LONG_MIN; //用全局变量存储上一个孩子节点的值
public:
    bool isValidBST(TreeNode* root) {
        if(root==nullptr)return true;

        bool left=isValidBST(root->left);//验证左子树
        if(!left)return false; //剪枝

        bool cur=false;
        if(root->val > prev)cur=true; //验证根与左孩子
        if(!cur)return false; //剪枝
        prev=root->val; //记录上一个孩子节点的值

        bool right=isValidBST(root->right);//验证右子树

        return left && right && cur;
    }
};

5)二叉搜索树中第K小的元素

230. 二叉搜索树中第K小的元素

.1- 题目解析

        我们可以仿照上一道题,在中序遍历时用两个全局变量来分别记录已经遍历的次数和相应节点的值。

 

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    int count;
    int ret;
public:
    int kthSmallest(TreeNode* root, int k) {
        count=k;
        dfs(root);
        return ret;
    }
    void dfs(TreeNode* root)
    {
        if(root==nullptr || count==0)return; //剪枝
        dfs(root->left);
        count--;
        if(count==0)ret=root->val;
        dfs(root->right);
    }
};

6)二叉树的所有路径

257. 二叉树的所有路径

.1- 题目解析

        路径以字符串形式存储,从根节点开始前序遍历,每次遍历时将当前节点的值加⼊到路径中,如果该节点为叶子节点,将路径存储到结果中。否则,将 "->" 加⼊到路径中并递归遍历该节点的左右子树。 

 

.2- 代码编写

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
    vector<string> ret;//存储路径
public:
    vector<string> binaryTreePaths(TreeNode* root) {
        dfs(root,"");
        return ret;
    }
    void dfs(TreeNode* root,string path)//便于回溯时恢复现场
    {
        path+=to_string(root->val);
        if(!root->left && !root->right) //遍历到叶子节点,就停止添加节点值并记录当前结果
        {
            ret.push_back(path);
            return;
        }

        path+="->";

        if(root->left)dfs(root->left,path);
        if(root->right)dfs(root->right,path);
    }
};

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

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

相关文章

手机浏览器如何切换IP‌?多种方法任你选

在数字化时代&#xff0c;手机已成为我们日常生活中不可或缺的一部分&#xff0c;而手机浏览器则是我们访问互联网的重要工具。然而&#xff0c;有时出于隐私保护、访问限制规避或其他需求&#xff0c;我们可能需要切换手机浏览器的IP地址。本文将详细介绍多种在手机上切换浏览…

主流数据库与最佳备份工具选择

文章目录 前言选择备份工具的考虑因素1、数据库兼容性&#xff1a;2、备份性能&#xff1a;3、恢复能力&#xff1a;4、备份策略与灵活性&#xff1a;5、数据安全性&#xff1a; 主流数据库概述1、Oracle2、PostgreSQL3、Microsoft SQL Server4、MongoDB 备份工具推荐1、Oracle…

C++手动实现栈、和队列

栈的实现 头文件 #ifndef MY_STACK_H #define MY_STACK_H #include <iostream>using namespace std;class my_stack { private:int *base;//动态栈指针int top;//栈顶元素int size;//栈大小 public:my_stack();//无参构造my_stack(int size):top(-1),size(size){base …

机器学习——Bagging

Bagging&#xff1a; 方法&#xff1a;集成n个base learner模型&#xff0c;每个模型都对原始数据集进行有放回的随机采样获得随机数据集&#xff0c;然后并行训练。 回归问题&#xff1a;n个base模型进行预测&#xff0c;将得到的预测值取平均得到最终结果。 分类问题&#xf…

每日 GitHub 探索|构建强大 LLM 管道和优化 AI 聊天框架

探索 DSPy&#xff0c;一款用于优化 LLM 提示和权重的框架。了解 Anthropic Cookbook&#xff0c;该代码库提供使用 Anthropic 强大 AI 模型的可复制代码片段。此外&#xff0c;还有 LobeChat&#xff0c;一个开源、现代设计的 AI 聊天框架&#xff0c;支持多种 AI 供应商和功能…

AI大模型全套学习资料

前言 如果您也对AI大模型感兴趣想学习却苦于没有方向&#x1f440; 小编给自己收藏整理好的学习资料分享出来给大家&#x1f496; &#x1f449;获取方式&#xff1a; &#x1f61d;有需要的小伙伴&#xff0c;可以保存图片到wx扫描二v码关注免费领取【保证100%免费】&#x1…

C盘太满了 用这五个步骤彻底的清理C盘垃圾 真干净!

C盘太满了 用这五个步骤彻底的清理C盘垃圾 真干净&#xff01;C盘忽然满了&#xff0c;这个问题长期困扰着大家&#xff0c;因为太多的人不知道怎么去清理C盘了&#xff0c;毕竟C盘的结构确实是太复杂了&#xff0c;也不是一般人能够充分了解的&#xff0c;所以就间接的导致了大…

代码随想录算法训练营第55天 | 寻找存在的路径

寻找存在的路径 题目描述 给定一个包含 n 个节点的无向图中&#xff0c;节点编号从 1 到 n &#xff08;含 1 和 n &#xff09;。 你的任务是判断是否有一条从节点 source 出发到节点 destination 的路径存在。 输入描述 第一行包含两个正整数 N 和 M&#xff0c;N 代表节点…

openpnp - 为了防止物料操作混乱,做一张物料分布位置图清晰一些

文章目录 openpnp - 为了防止物料操作混乱&#xff0c;做一张物料分布位置图清晰一些概述笔记做子装配图做总装配图备注END openpnp - 为了防止物料操作混乱&#xff0c;做一张物料分布位置图清晰一些 概述 看网上资料&#xff0c;当openpnp贴片机上料后&#xff0c;为了防止…

海山数据库(He3DB)+AI(四):一种基于迁移学习的启发式数据库旋钮调优方法

文章目录 0 前言1 OpAdviser1.1 主要工作1.2 总体流程 2 确定搜索空间2.1 相似任务识别2.2 有效区域提取2.3 多数加权投票 3确定优化器3.1 元特征提取3.2 离线数据生成3.3 Meta-Ranker构建 4 参考文献 0 前言 在海山数据库(He3DB)AI&#xff08;三&#xff09;中&#xff0c;介…

华润置地基于Apache SeaTunnel构建统一数据集成框架

作者&#xff1a;田力、陈允德 编辑整理&#xff1a;曾辉 引言 随着数字化转型的深入&#xff0c;企业对数据集成与处理的需求不断提升&#xff0c;如何高效、灵活地处理多系统、多数据源的同步&#xff0c;成为企业数据系统建设中的关键挑战。 在这篇文章中&#xff0c;来自…

display:flex;和margin的妙用

想要实现这样的效果&#xff1a; 第一个想法就是使用display:flex;justify-content: space-between; 所以想要得到效果&#xff0c;一般来说还需要在盒子外面再套一层盒子才行&#xff0c;但是也可以使用margin来实现。 <!DOCTYPE html> <html lang"en"&g…

makefile和CMakeLists/C++包管理器

make 大家可能会很奇怪&#xff0c;都什么年代了&#xff0c;还学makefile&#xff0c;cmake都有些过时了&#xff0c;为什么还要再学这个呢&#xff1f; 我是这么看待这个问题的&#xff0c;cmake跨平台性还是很有有优势的&#xff0c;有着多年积累的底蕴&#xff0c;借助大模…

c++反汇编逆向还原——do while循环(笔记)

c反汇编逆向还原代码do while循环的实现 涉及到的汇编指令mov、lea、cmp、jle、push 一、汇编 汇编代码 涉及到的指令 mov &#xff1a;将源操作数复制到目的操作数 lea &#xff1a;与mov类似 mov a&#xff0c;b 表示将b赋值给a 若是 mov a&#xff0c;[b] 这是将b的…

OpenAI首席技术官Mira Murati宣布她将离开公司

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

解决VsCode不显示环境名称

在VSCODE终端中激活运行的conda环境&#xff0c;但是只显示PS&#xff0c;并不显示环境名称 PS D:\Code\Pro\003_pro> 解决方法&#xff1a;以管理员权限打开PowerShell 方式1&#xff1a;在Cortana搜索栏中打开带管理员权限的PowerShell 在Windows 10的任务栏搜索框输入p…

Ansible-template模块动态生成特定文件

文章目录 一、Jinja2介绍什么是主要特性安装基本用法进阶特性总结 Jinja2与Ansible关系1. 模板引擎2. Ansible 的依赖3. 变量和模板4. 动态生成配置5. 社区和生态系统总结 二、Ansible如何使用Jinja2使用template模块Jinja2文件中使用判断和循环Jinja2文件中使用判断语法 Jinja…

一文读懂Service以及实践攻略

一文读懂Service以及实践攻略 目录 1 一文读懂 Kubernetes Service 以及实践攻略 1.1 1. 什么是 Service&#xff1f; 1.1.1 为什么需要 Service&#xff1f; 1.2 2. Service 的工作原理 1.2.1 核心概念1.2.2 流量转发过程 1.3 3. Service 的几种类型及应用场景 2 实践&#…

基于SpringBoot校园失物招领系统设计与实现

文未可获取一份本项目的java源码和数据库参考。 本课题的作用、意义&#xff0c;在国内外的研究现状和发展趋势&#xff0c;尚待研究的问题 作用&#xff1a;本课题的目的是使失物招领信息管理清晰化&#xff0c;透明化&#xff0c;便于操作&#xff0c;易于管理。通过功能模…

【JavaSE】-- 类和对象(2)

文章目录 6. 封装6.1 封装的概念6.2 访问限定符6.3 封装拓展之包6.3.1 包的概念6.3.2 导入包中的类6.3.3 自定义包 7. static成员7.1 再谈学生类7.2 static修饰成员变量7.3 static修饰成员方法7.4 static成员变量初始化 8. 代码块8.1 代码块概念以及分类8.2 普通代码块8.3 构造…