每日一题——力扣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;如树上的最长…

10秒教会你mysql的连接

连接MySQL数据库通常可以通过多种方法实现&#xff0c;以下是几种常见的方法&#xff0c;我将按照您的要求以清晰、分点的方式归纳说明&#xff1a; 1. 使用MySQL命令行客户端 打开终端或命令提示符&#xff1a;首先&#xff0c;打开您的计算机上的终端或命令提示符窗口。输入…

CSS中的display属性:布局控制的关键

CSS的display属性是控制元素在页面上如何显示的核心属性之一。它决定了元素的显示类型&#xff0c;以及它在页面布局中的行为。本文将详细介绍display属性的不同值及其使用场景&#xff0c;帮助你更好地掌握布局控制。 display属性的基本值 block 特点&#xff1a;块级元素&…

LeetCode每日一题 2734.子串操作后的字典序最小字符串|标志位遍历字符数组

问题描述 &#x1f4cb; 子串操作后的字典序最小字符串 给定一个仅包含小写字母的字符串&#xff0c;你可以执行如下操作任意次&#xff1a; 选择某个子串&#xff0c;将其中的每个字符都替换成其前一个字母&#xff08;比如 ‘b’ 变成 ‘a’&#xff0c;‘c’ 变成 ‘b’&…

未来数据中心智能运维的趋势

随着信息技术的飞速发展&#xff0c;数据中心作为支撑企业信息化建设的核心枢纽&#xff0c;其运维管理的重要性日益凸显。传统的运维模式已难以满足现代数据中心高效、安全、灵活的需求&#xff0c;而智能运维正成为行业发展的新趋势。本文将结合运维行业的资料和团队经验&…

【JavaScript 小工具】——如何判断当前页面是否在微信浏览器中打开

要判断用户是否通过微信浏览器打开网页&#xff0c;你可以检查用户代理&#xff08;User Agent&#xff09;字符串中是否包含微信浏览器的特定标识。微信浏览器通常会在User Agent中包含"MicroMessenger"这个关键词。 以下是一段JavaScript代码示例&#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;只可以匹配多次&…

电镀用开关电源技术详解

1 引言 在电镀行业里&#xff0c;一般要求工作电源的输出电压较低&#xff0c;而电流很大。电源的功率要求也比较高&#xff0c;一般都是几千瓦到几十千瓦。目前&#xff0c;如此大功率的电镀电源一般都采用晶闸管相控整流方式。其缺点是体积大、效率低、噪音高、功率因数低、…

[CocosCreator]CocosCreator网络通信:https + websocket + protobuf

环境 cocos creator版本&#xff1a;3.8.0 开发语言&#xff1a;ts 操作系统&#xff1a;windows http部分 直接使用 XMLHttpRequest 创建http请求 // _getHttpUrl 方法自己写字符串拼接public httpPostJsonRequest(uri: string, headData: any, data: any, cb: Function…

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

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