LeetCode:经典题之144、94、145、102题解及延伸|二叉树的遍历|前中后层序遍历|Morris算法

系列目录

88.合并两个有序数组
52.螺旋数组
567.字符串的排列
643.子数组最大平均数
150.逆波兰表达式
61.旋转链表
160.相交链表
83.删除排序链表中的重复元素
389.找不同
1491.去掉最低工资和最高工资后的工资平均值
896.单调序列
206.反转链表
92.反转链表II
141.环形链表
142.环型链表
21.合并两个有序列表
24.两辆交换链表中的节点
876.链表的中间节点
143. 重排链表
2.两数相加
445.两数相加II


目录

  • 系列目录
  • 前言
  • 144.二叉树的前序遍历
  • 94.二叉树的中序遍历
  • 145.二叉树的后序遍历
    • Morris遍历
  • 102.二叉树的层序遍历


前言

方法都是类似的~
递归是隐式调用栈(递归栈,无需手动实现),迭代是显式调用栈



144.二叉树的前序遍历

🌟递归

原题链接


C++
若未特殊标明,以下题解均写用C++

/*** 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:// 注意引用符void preorder(TreeNode* root, vector<int> &res) {if (root == nullptr)// preorder 函数被定义为返回 void 类型——不返回任何值// return; 仅仅是结束函数的执行,并不返回任何值return;// 存入向量的末尾res.push_back(root->val);// 前序——根左右preorder(root->left, res);preorder(root->right, res);}vector<int> preorderTraversal(TreeNode* root) {// 定义一个 矢量/向量——更准确地说是一个动态数组vector<int> res;preorder(root, res);return res;}
};





94.二叉树的中序遍历

🌟递归+迭代

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 递归 (DFS )
/*** 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:void inorder(TreeNode* root, vector<int>& res) {//可以另写成if (!root)if (!root) return;// 中序——左根右——先插入最左边的左孩子inorder(root->left, res);res.push_back(root->val);inorder(root->right, res);}vector<int> inorderTraversal(TreeNode* root) {vector<int> res;inorder(root, res);return res;}
};





145.二叉树的后序遍历

🌟递归+迭代

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 递归 (DFS )
/*** 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:void postorder(TreeNode *root, vector<int> &res) {if (!root)return;// 后序——左右根postorder(root->left, res);postorder(root->right, res);// 左右都不存在才先插入根节点的值res.push_back(root->val);}vector<int> postorderTraversal(TreeNode* root) {vector<int> res;postorder(root, res);return res;}
};



方法二 迭代
/*** 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:vector<int> postorderTraversal(TreeNode* root) {vector<int> res;// 空树if (!root) return res;// 定义一个 树节点栈stack<TreeNode* > stk;TreeNode *prev = nullptr;// 树没遍历完 或 栈非空while (root != nullptr || !stk.empty()) {// 树没遍历完while (root != nullptr) {// 遍历所有左节点 入栈// 将这个树节点 入栈stk.emplace(root);root = root->left;}// 若此时 root 为空// 更新 root root = stk.top();// 栈顶元素用完 弹出栈stk.pop();// 无右子节点 或 这个右子节点被遍历过if (root->right == nullptr || root->right == prev) {res.emplace_back(root->val);// 标记这个节点prev = root;root = nullptr;} // 有右子节点else { stk.emplace(root);root = root->right;}}return res;}
};



方法三 Morris遍历
class Solution {
public:void addPath(vector<int> &vec, TreeNode *node) {int count = 0;while (node != nullptr) {++count;vec.emplace_back(node->val);node = node->right;}reverse(vec.end() - count, vec.end());}vector<int> postorderTraversal(TreeNode *root) {vector<int> res;if (root == nullptr) {return res;}TreeNode *p1 = root, *p2 = nullptr;while (p1 != nullptr) {p2 = p1->left;if (p2 != nullptr) {while (p2->right != nullptr && p2->right != p1) {p2 = p2->right;}if (p2->right == nullptr) {p2->right = p1;p1 = p1->left;continue;} else {p2->right = nullptr;addPath(res, p1->left);}}p1 = p1->right;}addPath(res, root);return res;}
};

Morris遍历

Morris遍历是一种高效的二叉树遍历算法,其显著特点是能在不使用栈或队列的情况下实现中序遍历,且空间复杂度为O(1)

1. 基本思想
Morris遍历的基本思想是利用叶子节点的空指针来存储临时信息,从而达到节省空间的目的
在遍历过程中,Morris遍历会修改树中某些节点的指针指向,以便在遍历时能够方便地回溯到上一个节点 遍历完成后,需要还原树的结构

2. 实现过程
Morris遍历的实现过程可以分为以下几个步骤:

  • 对于当前遍历到的节点,如果它有左子节点,就找到左子树中最右边的节点 (记为mostRight ),将其右子节点指向当前节点
  • 将当前节点更新为其左子节点,继续遍历
  • 如果当前节点没有左子节点,就输出当前节点的值 (或进行其他操作),并将当前节点更新为其右子节点
  • 重复以上步骤,直到遍历完整棵树

3. 遍历类型
Morris遍历可以实现前序、中序和后序遍历
具体实现时,需要根据遍历的顺序调整指针的指向和节点的访问顺序

  • 前序遍历:在第一次访问节点时,输出节点的值,然后进行左子树的遍历
  • 中序遍历:在第二次访问节点时(即mostRight的右指针指向当前节点时),输出节点的值,然后进行右子树的遍历
  • 后序遍历:后序遍历的实现相对复杂,需要在遍历过程中记录左子树最右分支的路径,并在回溯时逆序输出

4. 优缺点

  • 优点:Morris遍历的空间复杂度为O(1),比使用栈或递归的遍历算法更高效
    此外,Morris遍历不会占用额外的内存空间,对于内存受限的环境非常友好
  • 缺点:Morris遍历会修改原始树的结构,需要在遍历完成后还原 此外,Morris遍历的实现相对复杂,需要理解其背后的思想和原理

5. 应用场景
Morris遍历适用于需要高效遍历二叉树且内存受限的场景
例如,在处理大规模数据时,使用Morris遍历可以节省内存空间并提高遍历效率
同时,Morris遍历也可以用于实现一些特殊的算法和数据结构,如线索二叉树等





102.二叉树的层序遍历

🌟BFS+队列

原题链接


C++
若未特殊标明,以下题解均写用C++

方法一 BFS
/*** 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:vector<vector<int>> levelOrder(TreeNode* root) {vector <vector <int>> ret;if (!root) {return ret;}queue <TreeNode*> q;q.push(root);while (!q.empty()) {int currentLevelSize = q.size();ret.push_back(vector <int> ());for (int i = 1; i <= currentLevelSize; ++i) {auto node = q.front(); q.pop();ret.back().push_back(node->val);if (node->left) q.push(node->left);if (node->right) q.push(node->right);}}return ret;}
};



方法二 队列
/*** 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) {}* };*/
// #include <queue>  
// #include <vector>  
class Solution {  
public:  vector<vector<int>> levelOrder(TreeNode* root) {  vector<vector<int>> ans;  if (root == nullptr )return ans;  queue<TreeNode*> cur;  cur.push(root);  while (!cur.empty()) {// 当前层的节点数int size = cur.size();// 存储当前层的节点值  vector<int> vals; for (int i = 0; i < size; ++i) {  TreeNode* node = cur.front();  cur.pop();vals.push_back(node->val);if (node->left)cur.push(node->left); if (node->right)cur.push(node->right);  }  ans.push_back(vals); // 将当前层的节点值添加到答案中  }  return ans;  }
};

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

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

相关文章

Linux 常用命令之 RZ和SZ 简介

一、引言 在Linux系统管理中&#xff0c;尤其是在远程操作时&#xff0c;文件的上传与下载是常见的需求。对于CentOS用户而言&#xff0c;rz和sz这两个命令提供了简单而高效的文件传输方式&#xff0c;尤其在SSH终端环境中更为便利。本文将详细介绍rz和sz命令的基本概念、如何…

【鸿蒙学习笔记】@Styles装饰器:定义组件重用样式

原文链接&#xff1a;Styles装饰器&#xff1a;定义组件重用样式 目录标题 [Q&A] Styles装饰器作用 [Q&A] Styles装饰器特点使用场景 [Q&A] Styles装饰器作用 Styles 表示公共样式&#xff0c;每个组件都可以复用。如果相同样式在各个组件里复制一遍&#xff0c;…

高效管理:在Postman中处理大型响应数据的策略与技巧

Postman是一款功能强大的API开发和测试工具&#xff0c;但在处理大型响应数据时&#xff0c;用户可能会遇到性能瓶颈或数据管理上的挑战。本文将详细探讨在Postman中处理大型响应数据的策略和技巧&#xff0c;包括数据查看、分析、预处理和性能优化等。 一、大型响应数据的挑战…

基于SpringBoot漫画网站系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f; 感兴趣的可以先收藏起来&#xff0c;…

Nettyの粘包、半包问题框架解决方案自定义协议

1、Netty框架是如何解决粘包、半包问题 关于粘包&#xff0c;半包问题&#xff0c;在前面几篇中都有提及&#xff0c;我们简单的复习一下。 粘包指的是客户端发出的多条消息&#xff0c;被服务端当做一条进行接收。半包指的是客户端发出一条完整的消息&#xff0c;在传输的过程…

数据安全和隐私保护在大数据领域的重要性

如何理解数据安全和隐私保护在大数据领域的重要性&#xff1f; 在大数据领域&#xff0c;数据安全和隐私保护至关重要。随着技术的发展&#xff0c;海量的数据被收集、存储和分析&#xff0c;这些数据包含了用户的个人信息和敏感信息。其重要性体现在以下几个方面&#xff1a;…

Java IO: 使用 `BufferedReader` 和 `BufferedWriter` 进行高效文件操作

在 Java 中&#xff0c;文件 I/O 操作是常见的任务。BufferedReader 和 BufferedWriter 是用于读取和写入文件的高效类&#xff0c;它们通过缓冲机制显著提高了 I/O 操作的性能。本文将介绍如何使用 BufferedReader 和 BufferedWriter&#xff0c;并提供具体的示例代码。 Buff…

Redisson框架

1. Redisson锁与Redis订阅与发布模式的联系&#xff1a; Redisson锁中&#xff0c;使用订阅发布模式去通知等待锁的客户端&#xff1a;锁已经释放&#xff0c;可以进行抢锁。 publish channel_name message&#xff1a;将消息发送到指定频道 解锁时&#xff0c;在Lua解锁脚本…

如何把项目文文件/文件夹)上传到Gitee(全网最细)

目录 1、首先必须要有一个Gitee官网的账号 2、点击右上角的号&#xff0c;点击新建仓库 3、按照下图步骤&#xff0c;自己起仓库名字&#xff0c;开发语言 4、点击初始化readme文件 5、在自己的电脑上选择姚上传的文件夹&#xff0c;或者文件&#xff0c;这里都是一样的&a…

内网渗透:端口转发(SSH隧道)

SSH&#xff1a;两台设备之间进行远程登录的协议&#xff08;SSH本身就是一个隧道协议&#xff09; 远程文件传输scp命令&#xff08;scp是基于SSH的&#xff09; 拓扑&#xff1a; SSH隧道搭建的条件 1.获取到跳板机权限 2.跳板机中SSH服务启动 SSH端口转发分类&#xff1…

C语言 实现链表的各种功能

C语言实现链表的各种功能 链表的定义 链表是一种数据结构&#xff0c;它是由一系列节点组成的线性结构。每个节点包含两个部分&#xff1a;数据和指针。数据部分存储着实际的数据&#xff0c;指针部分指向下一个节点。 链表的特点是&#xff1a; 每个节点都可以自由地插入或…

python爬虫:实现程序模拟点击搜索等任务,爬取动态网页

引言: 爬虫也被称为网络蜘蛛(Spider),是一种自动化的软件程序,能够在互联网上漫游,按照一定的规则和算法抓取数据。 爬虫技术广泛应用于搜索引擎、 数据挖掘 、信息提取等领域,是互联网技术的重要组成部分。 摘要: 作为爬虫的初学者,网页越简单越好,因为网页的结构…

正点原子rk3588烧录linux和安卓镜像

1、烧录 Linux buildroot 系统镜像 1.1 进入 Loader 模式&#xff1a; 按住开发板上的 V&#xff08;音量&#xff09;按键不松&#xff0c;给开发板 上电或复位&#xff0c;此时烧录工具会提示&#xff1a;发现一个 LOADER 设备&#xff0c;表示开发板此时已经处于 Loader 模…

【爆肝34万字】从零开始学Python第2天: 判断语句【入门到放弃】

目录 前言判断语句True、False简单使用作用 比较运算符引入比较运算符的分类比较运算符的结果示例代码总结 逻辑运算符引入逻辑运算符的简单使用逻辑运算符与比较运算符一起使用特殊情况下的逻辑运算符 if 判断语句引入基本使用案例演示案例补充随堂练习 else 判断子句引入else…

Node.js 事件循环的工作流程二

在每个tick的过程中&#xff0c;如何判断是否有事件需要处理 在 Node.js 的事件循环中&#xff0c;每个循环迭代称为一个 "tick"。在每个 tick 过程中&#xff0c;事件循环需要判断是否有事件需要处理。这个过程主要依赖于检查各个阶段的队列是否有待处理的回调函数…

第3章:Electron的核心概念(2)

3.4 预加载脚本 预加载脚本在渲染进程加载前执行&#xff0c;允许在渲染器上下文中暴露自定义 API&#xff0c;并提供与主进程安全通信的桥梁。使用预加载脚本可以提高应用的安全性&#xff0c;尤其是在启用了 contextIsolation 的情况下。 3.4.1 创建预加载脚本 预加载脚本…

【Docker Compose】掌握容器资源管理:高效限制CPU与内存使用

【Docker Compose】掌握容器资源管理:高效限制CPU与内存使用 一、Docker Compose 介绍1.1 Docker Compose简介1.2 Docker Compose V2简介1.3 Docker Compose V1与V2版本区别1.4 docker-compose.yaml部署文件介绍二、检查本地docker环境2.1 本地环境规划2.2 检查docker版本2.3 …

43.三倍游戏

上海市计算机学会竞赛平台 | YACSYACS 是由上海市计算机学会于2019年发起的活动,旨在激发青少年对学习人工智能与算法设计的热情与兴趣,提升青少年科学素养,引导青少年投身创新发现和科研实践活动。https://www.iai.sh.cn/problem/390 题目描述 三倍游戏是一种单人游戏。玩…

基于51单片机心形LED流水灯电路原理图、PCB和源程序(SCH、PCB源文件)

资料下载地址&#xff1a;基于51单片机心形LED流水灯电路原理图、PCB和源程序&#xff08;SCH、PCB源文件&#xff09; 1、单片机心形LED流水灯功能说明&#xff1a; 单片机&#xff1a;无论是散件还是成品&#xff0c;单片机里面都烧录有LED 流水灯的程序&#xff0c;装上单片…

TCP重传机制

TCP重传机制是为了确保数据可靠传输而设计的&#xff0c;当传输的数据包丢失或损坏时&#xff0c;TCP会重新发送这些数据包。TCP重传主要有以下几种方式&#xff1a; 超时重传&#xff08;Timeout Retransmission&#xff09;&#xff1a; 当发送方发送一个数据包后&#xff…