每日一题——力扣144. 二叉树的前序遍历(举一反三+思想解读+逐步优化)五千字好文


一个认为一切根源都是“自己不够强”的INTJ

个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数

Python-3.12.0文档解读

目录

 我的写法:

代码结构

时间复杂度

空间复杂度

总结

我要更强

代码说明

时间复杂度

空间复杂度

哲学和编程思想

迭代与递归:

空间与时间的权衡:

抽象与具体化:

数据结构的选择:

内存管理:

算法优化:

举一反三


题目链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/

 我的写法:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
int TreeSize(struct TreeNode* root){return root == NULL ? 0 :1+TreeSize(root->left)+TreeSize(root->right);
}void perorder(struct TreeNode* root,int* a,int* pi){if(root==NULL)return;a[(*pi)++]=root->val;perorder(root->left,a,pi);perorder(root->right,a,pi);
}int* preorderTraversal(struct TreeNode* root, int* returnSize){*returnSize=TreeSize(root);int* a=(int*)malloc(*returnSize*sizeof(int));int i=0;perorder(root,a,&i);return a;
}

这段代码实现了二叉树的前序遍历(Preorder Traversal),并返回一个包含遍历结果的动态数组。下面是对这段代码的专业点评:

代码结构

  1. TreeSize 函数:
    • 该函数用于计算二叉树的节点数量。它通过递归地遍历左子树和右子树,并累加节点数来实现。如果根节点为空,则返回0;否则,返回1加上左子树和右子树的节点数。
  2. perorder 函数:
    • 这是一个递归函数,用于执行前序遍历。前序遍历的顺序是:根节点 -> 左子树 -> 右子树。该函数将遍历到的节点值存储到数组 a 中,并通过引用传递的 pi 指针来更新数组索引。
  3. preorderTraversal 函数:
  • 这是主函数,负责分配返回数组的空间,调用 perorder 函数进行遍历,并返回遍历结果。它首先通过 TreeSize 函数确定返回数组的大小,然后分配相应大小的内存空间,并调用 perorder 函数填充数组。

时间复杂度

  • TreeSize 函数:O(n),其中 n 是树中的节点数。每个节点都被访问一次。
  • perorder 函数:O(n),同样地,每个节点都被访问一次。
  • preorderTraversal 函数:O(n),因为它调用了 perorder 函数,而 perorder 函数的时间复杂度是 O(n)。

空间复杂度

  • TreeSize 函数:O(h),其中 h 是树的高度。在最坏的情况下,递归调用的栈深度等于树的高度。
  • perorder 函数:O(h),同样地,递归调用的栈深度等于树的高度。
  • preorderTraversal 函数:O(n),因为需要分配一个大小为 n 的数组来存储遍历结果。此外,递归调用的栈空间也是 O(h)。

总结

这段代码有效地实现了二叉树的前序遍历,并且时间复杂度和空间复杂度都是合理的。注意,空间复杂度中的 O(h) 部分是由于递归调用栈的使用,这在树的高度较大时可能会成为一个问题。在实际应用中,需要考虑树的平衡性以及可能的最大高度。


我要更强

为了优化时间复杂度和空间复杂度,我们可以考虑使用迭代方法而不是递归来实现前序遍历。这样可以避免递归调用栈的空间开销,并且时间复杂度仍然是 O(n),因为每个节点仍然只被访问一次。

下面是使用迭代方法实现的完整代码:

#include <stdlib.h>
#include <stdbool.h>struct TreeNode {int val;struct TreeNode *left;struct TreeNode *right;
};int* preorderTraversal(struct TreeNode* root, int* returnSize) {if (root == NULL) {*returnSize = 0;return NULL;}int* result = (int*)malloc(100 * sizeof(int)); // 假设最多100个节点,可根据实际情况调整struct TreeNode** stack = (struct TreeNode**)malloc(100 * sizeof(struct TreeNode*)); // 栈空间int top = -1; // 栈顶指针int index = 0; // 结果数组索引stack[++top] = root; // 根节点入栈while (top >= 0) {struct TreeNode* node = stack[top--]; // 出栈result[index++] = node->val; // 访问节点// 由于前序遍历是根-左-右,先右子树入栈,再左子树入栈if (node->right != NULL) {stack[++top] = node->right;}if (node->left != NULL) {stack[++top] = node->left;}}*returnSize = index; // 设置返回数组大小free(stack); // 释放栈空间return result;
}

代码说明

  1. 栈的使用:我们使用一个栈来模拟递归调用的过程。在迭代方法中,我们手动管理这个栈。
  2. 前序遍历的顺序:根节点首先被访问,然后是左子树,最后是右子树。因此,我们在栈中先压入右子树,然后是左子树,这样在出栈时,左子树会在右子树之前被访问。
  3. 内存管理:我们动态分配了栈和结果数组的空间。在函数结束前,我们释放了栈的空间,但结果数组的空间由调用者负责释放。

时间复杂度

  • O(n):每个节点被访问一次。

空间复杂度

  • O(h):在最坏情况下,栈的大小等于树的高度。在最好情况下(平衡树),空间复杂度为 O(log n)。

这种方法在空间效率上优于递归方法,因为它避免了递归调用栈的空间开销。然而,它仍然需要一个栈来模拟递归过程,因此在最坏情况下的空间复杂度仍然是 O(h)。如果树非常不平衡,这可能会导致较高的空间使用。


哲学和编程思想

这些方法体现了以下哲学和编程思想:

  1. 迭代与递归:

    • 递归是一种自我调用的方法,它依赖于调用栈来保存状态。递归通常使得代码更简洁、易于理解,但在处理大量数据时可能会导致栈溢出。
    • 迭代是一种循环结构,它通过显式地管理状态来避免递归的缺点。迭代通常更节省空间,因为它不需要额外的栈空间来保存中间状态。
    • 在上述代码中,我们使用了迭代方法来实现前序遍历,这体现了在空间效率和性能之间的权衡。
  2. 空间与时间的权衡:

    • 在编程中,我们经常需要在空间复杂度和时间复杂度之间做出权衡。例如,使用额外的空间(如栈)来存储中间结果,可以减少时间复杂度。
    • 在迭代方法中,我们牺牲了一定的空间(栈空间)来减少递归调用栈的空间开销,从而优化了整体的空间复杂度。
  3. 抽象与具体化:

    • 抽象是指忽略问题的某些细节,专注于核心概念。在二叉树遍历中,我们抽象出了遍历的顺序(根-左-右),而不关心具体的实现细节。
    • 具体化是指将抽象的概念转化为具体的实现。在迭代方法中,我们将抽象的遍历顺序具体化为栈的操作,通过入栈和出栈来实现遍历。
  4. 数据结构的选择:

    • 选择合适的数据结构是编程中的一个重要决策。在二叉树遍历中,我们选择了栈作为辅助数据结构,因为它可以有效地存储和访问节点的顺序。
    • 栈的先进后出(LIFO)特性非常适合模拟递归调用的过程,使得我们可以在迭代中实现与递归相同的前序遍历顺序。
  5. 内存管理:

    • 在C语言中,内存管理是一个重要的编程实践。我们手动分配和释放内存,以避免内存泄漏和悬挂指针。
    • 在代码中,我们动态分配了栈和结果数组的空间,并在不再需要时释放了栈的空间,这体现了良好的内存管理习惯。
  6. 算法优化:

  • 算法优化是提高程序效率的关键。在迭代方法中,我们通过优化栈的使用顺序(先右后左)来确保前序遍历的正确性,这是一种算法层面的优化。

这些哲学和编程思想是解决复杂问题的基石,它们帮助我们在设计和实现代码时做出明智的决策,以达到高效、可维护和可扩展的解决方案。


举一反三

根据上述哲学和编程思想,以下是一些技巧和策略,可以帮助在面对类似问题时举一反三:

  1. 理解问题本质:
    • 在解决任何问题之前,首先要深入理解问题的本质。例如,在二叉树遍历问题中,理解前序遍历的定义(根-左-右)是至关重要的。
    • 技巧:将问题抽象为基本概念,然后从这些概念出发寻找解决方案。
  2. 选择合适的数据结构:
    • 根据问题的特点选择合适的数据结构。例如,栈在处理需要逆序操作的问题时非常有用。
    • 技巧:熟悉各种数据结构的特性,并能够根据问题的需求灵活选择。
  3. 迭代与递归的转换:
    • 学会将递归算法转换为迭代算法,或者反之。这种转换通常涉及到使用栈或队列来模拟递归调用栈。
    • 技巧:练习将递归算法重写为迭代算法,以提高对这两种方法的理解和应用能力。
  4. 空间与时间的权衡:
    • 在设计算法时,考虑时间和空间的权衡。有时候,牺牲一些空间可以显著提高时间效率。
    • 技巧:评估不同解决方案的时间和空间复杂度,选择最合适的平衡点。
  5. 内存管理:
    • 在需要手动管理内存的编程语言中,如C语言,注意内存的分配和释放。
    • 技巧:养成良好的内存管理习惯,确保在不再需要内存时及时释放。
  6. 算法优化:
    • 不断寻找算法优化的可能性。例如,通过改变数据结构的使用顺序或方式来提高效率。
    • 技巧:分析算法的瓶颈,尝试不同的优化策略,如减少不必要的计算或改进数据访问模式。
  7. 抽象与具体化:
    • 学会将复杂问题抽象为简单的模型,然后将这些模型具体化为可执行的代码。
    • 技巧:练习将问题分解为更小、更易于管理的部分,然后逐步构建解决方案。
  8. 代码复用与模块化:
    • 在编写代码时,考虑代码的复用性和模块化。这有助于提高代码的可维护性和可扩展性。
    • 技巧:设计可重用的函数和类,将代码分解为独立的模块。
  9. 测试与调试:
  • 编写测试用例来验证代码的正确性,并使用调试工具来定位和修复错误。
  • 技巧:编写全面的测试用例,使用调试工具逐步跟踪代码执行过程。

通过实践这些技巧和策略,将能够更好地理解和解决各种编程问题,提高你的编程能力和问题解决能力。记住,编程是一个不断学习和实践的过程,通过不断的练习和挑战,将能够举一反三,解决更复杂的问题。


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

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

相关文章

C语言力扣刷题7——删除排序链表中的重复元素 II——[快慢双指针法]

力扣刷题7——删除排序链表中的重复元素 II——[快慢双指针法] 一、博客声明二、题目描述三、解题思路1、思路说明 四、解题代码&#xff08;附注释&#xff09; 一、博客声明 找工作逃不过刷题&#xff0c;为了更好的督促自己学习以及理解力扣大佬们的解题思路&#xff0c;开辟…

好书安利 | LangChain入门指南:构建高可复用、可扩展的LLM应用程序(送PDF)轻松入门LangChain

《LangChain入门指南》 LangChain作为大模型集成框架鼎鼎大名&#xff0c;这本《LangChain入门指南》是一本很及时的书&#xff0c;值得推荐&#xff5e; 01 为什么需要LangChain 首先想象一个开发者在构建一个LLM应用时的常见场景。 当你开始构建一个新项目时&#xff0c;…

不使用canvs也能创建出点状背景

div{ height: 100%; touch-action: none; background: radial-gradient(circle, #e6e6e6 1px, transparent 1px); /* 创建一个点状背景 */ background-size: 15px 15px; /* 控制点的大小和间距 */ padding: 20px; /* 添加内边距使内容不靠边 */ position: relative; /* 让内部内…

树形DP——AcWing 323. 战略游戏

树形DP 定义 树形动态规划&#xff08;Tree Dynamic Programming&#xff0c;简称树形DP&#xff09;是一种在树形结构上应用动态规划算法的技术。它利用树的递归结构&#xff0c;通过定义状态和状态转移方程&#xff0c;来求解与树相关的最优化问题&#xff0c;如树上的最长…

不使用cmake,如何在vs2019对cpp项目进行文件夹分类?

不使用cmake&#xff0c;如何在vs2019对cpp项目进行文件夹分类&#xff1f; 1.不使用cmake的根目录指的是哪里&#xff1f;2.什么时候进行项目管理&#xff1f;3.应该分成什么样的文件夹&#xff1f;4.如何分类&#xff1f; 1.不使用cmake的根目录指的是哪里&#xff1f; 查看项…

最新AI智能聊天对话问答系统源码(图文搭建部署教程)+AI绘画,文生图,TTS语音识别输入,文档分析

一、人工智能语言模型和AI绘画在多个领域广泛应用 人工智能语言模型和AI绘画在多个领域都有广泛的应用。以下是一些它们的主要用处&#xff1a; 人工智能语言模型 内容生成 写作辅助&#xff1a;帮助撰写文章、博客、报告、剧本等。 代码生成&#xff1a;自动生成或补全代码&…

sudo: /etc/init.d/ssh: command not found

在 WSL 中尝试启动 SSH 服务时遇到 sudo: /etc/init.d/ssh: command not found 错误 安装 OpenSSH 服务器 更新软件包列表 sudo apt update安装 OpenSSH 服务器 sudo apt install openssh-server启动 SSH 服务 在 WSL 2 上,服务管理与传统 Linux 系统有所不同。你可以手动启动…

C++之STL(十)

1、适配器 2、函数适配器 #include <iostream> using namespace std;#include <algorithm> #include <vector> #include <functional>bool isOdd(int n) {return n % 2 1; } int main() {int a[] {1, 2, 3, 4, 5};vector <int> v(a, a 5);cou…

ONLYOFFICE 8.1版本桌面编辑器测评:重塑办公效率的巅峰之作

在数字化办公日益普及的今天&#xff0c;一款高效、便捷且功能强大的桌面编辑器成为了职场人士不可或缺的工具。ONLYOFFICE 8.1版本桌面编辑器凭借其卓越的性能和丰富的功能&#xff0c;成功吸引了众多用户的目光。今天&#xff0c;我们将对ONLYOFFICE 8.1版本桌面编辑器进行全…

使用el-amap-info-window遇到的问题

使用的这个库https://github.com/yangyanggu/vue-amap 想要滚动amapInfoWindow里的内容&#xff0c;但不触发地图缩放 默认滚动amapInfoWindow里的内容&#xff0c;会触发地图缩放。看了C站一个大佬的文章解决了。 amapInfoWindow会自动滚动到顶部 我的amapInfoWindow里面用了…

【智能制造-4】机器人控制器

机器人控制器中分哪几个模块&#xff1f; 机器人控制器通常由以下几个主要模块组成: 运动控制模块: 负责机器人各轴电机的位置、速度、加速度等控制 实现机器人末端执行器的精确定位和运动控制传感器接口模块: 负责机器人各种传感器信号的采集和处理 为运动控制、环境感知等提…

Python-正则表达式

目录 一、打开正则表达式 二、正则表达式的使用 1、限定符 &#xff08;1&#xff09;x*&#xff1a;*表示它前面的字符y 可以有0个或多个&#xff1b; &#xff08;2&#xff09;x&#xff1a;表示它前面的字符可以出现一次以上&#xff1b;&#xff08;只可以匹配多次&…

2024年6月大众点评深圳餐饮店铺POI分析18万家

2024年6月大众点评深圳餐饮店铺POI共有178720家 店铺POI点位示例&#xff1a; 店铺id G9TSD2JvdLtA7fdm 店铺名称 江味龙虾馆(南山店) 十分制服务评分 8.8 十分制环境评分 8.8 十分制划算评分 8.6 人均价格 128 评价数量 12840 店铺地址 南山大道与桂庙路交叉口西北角…

RAG开发中常见的12个痛点及解决方案

受到 Barnett 等人论文《构建检索增强生成系统的七大挑战》启发&#xff0c;本文将探讨论文中提及的七大挑战及在开发 RAG&#xff08;检索增强生成&#xff09;流程中常遇到的五个额外难题。更为重要的是&#xff0c;我们将深入讨论解决这些 RAG 难题的策略&#xff0c;以便我…

使用 WebGL 创建 3D 对象

WebGL Demohttps://mdn.github.io/dom-examples/webgl-examples/tutorial/sample5/index.html 现在让我们给之前的正方形添加五个面从而可以创建一个三维的立方体。最简单的方式就是通过调用方法 gl.drawElements() 使用顶点数组列表来替换之前的通过方法gl.drawArrays() 直接…

检测SD NAND文件系统异常和修复的方法

目录 1、打开命令提示符&#xff1a; 2、运行chkdsk命令&#xff1a; 3、命令参数说明&#xff1a; chkdsk是Windows中的一个命令行工具&#xff0c;用于检查磁盘上的文件系统错误和修复坏块。MK米客方德为您提供指导&#xff0c;以下是使用chkdsk的步骤&#xff1a; 1、打开…

综合IT运维管理解决方案

综合IT运维管理解决方案 在信息化和数字化高速发展的时代&#xff0c;企业的IT运维管理已经成为保障业务连续性和提升运营效率的关键环节。高效的IT运维管理不仅能够降低运维成本&#xff0c;还能提升服务质量和用户满意度。本文将详细介绍综合IT运维管理解决方案&#xff0c;…

eBPF技术揭秘:DeepFlow如何引领故障排查,提升运维效率

DeepFlow 实战&#xff1a;eBPF 技术如何提升故障排查效率 目录 DeepFlow 实战&#xff1a;eBPF 技术如何提升故障排查效率 微服务架构系统中各个服务、组件及其相互关系的全景 零侵扰分布式追踪&#xff08;Distributed Tracing&#xff09;的架构和工作流程 关于零侵扰持…

华为od 2024 | 什么是华为od,od 薪资待遇,od机试题清单

目录 专栏导读华为OD机试算法题太多了&#xff0c;知识点繁杂&#xff0c;如何刷题更有效率呢&#xff1f; 一、逻辑分析二、数据结构1、线性表① 数组② 双指针 2、map与list3、队列4、链表5、栈6、滑动窗口7、二叉树8、并查集9、矩阵 三、算法1、基础算法① 贪心思维② 二分查…

Spring Session将HttpSession保存到Redis中,实现重启应用会话不丢失

这篇文章介绍一下在springboot项目中整合Spring Session&#xff0c;将session会话信息保存到Redis中&#xff0c;防止重启应用导致会话丢失。 第一步 创建一个springboot项目&#xff0c;添加spring-session-redis的依赖&#xff0c;因为要用到reids&#xff0c;所以要把redi…