二叉树的遍历与构造

唉,好想回家,我想回家跟馒头酱玩,想老爸老妈。如果上天再给我一次选择的机会,我会选择当一只小动物,或者当棵大树也好,或者我希望自己不要有那么多多余的情绪,不要太被别人影响,开心点,想睡就睡,想玩就玩,不要为难自己。老爸每次都和我说累了就回家,但越是这样我就越希望自己变得更强大一点。希望明天是个好天气。

目录

一、遍历

1 前序遍历

(1)递归

(2)非递归(先右后左哈)

2 中序遍历

(1)递归

(2)非递归

3 后序遍历

(1)递归

(2)非递归(中右左,反转列表后变成左右中)

4 层序遍历

(1)bfs(借助队列)

(2)dfs

二、构造

1 构建最大二叉树

2 由 前 中 构造

3 由 前 后 构造

4 由 中 后 构造


一、遍历

1 前序遍历

(1)递归

   private static void preorderHelper(TreeNode node, List<Integer> result) {if (node == null) return;result.add(node.val);preorderHelper(node.left, result);preorderHelper(node.right, result);}

(2)非递归(先右后左哈)

// 前序遍历 - 非递归public static List<Integer> preorderTraversalIterative(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) return result;Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();result.add(node.val);if (node.right != null) stack.push(node.right);if (node.left != null) stack.push(node.left);}return result;}

2 中序遍历

(1)递归

 private static void inorderHelper(TreeNode node, List<Integer> result) {if (node == null) return;inorderHelper(node.left, result);result.add(node.val);inorderHelper(node.right, result);}

(2)非递归

核心思路是先将当前节点及其所有左子节点依次入栈,直到左子节点为空,接着从栈中弹出节点进行访问,然后将当前节点更新为弹出节点的右子节点,继续重复上述操作。

// 中序遍历 - 非递归public static List<Integer> inorderTraversalIterative(TreeNode root) {List<Integer> result = 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();result.add(current.val);current = current.right;}return result;}

3 后序遍历

(1)递归

private static void postorderHelper(TreeNode node, List<Integer> result) {if (node == null) return;postorderHelper(node.left, result);postorderHelper(node.right, result);result.add(node.val);}

(2)非递归(中右左,反转列表后变成左右中)

 // 后序遍历 - 非递归public static List<Integer> postorderTraversalIterative(TreeNode root) {List<Integer> result = new ArrayList<>();if (root == null) return result;Stack<TreeNode> stack = new Stack<>();stack.push(root);while (!stack.isEmpty()) {TreeNode node = stack.pop();result.add(node.val);if (node.left != null) stack.add(node.left);if (node.right != null) stack.add(node.right);}Collections.reverse(result);return result;}

4 层序遍历

(1)bfs(借助队列)

 // 层序遍历 - BFS(借助队列)public static List<List<Integer>> levelOrderTraversalBFS(TreeNode root) {List<List<Integer>> result = new ArrayList<>();if (root == null) return result;Queue<TreeNode> queue = new LinkedList<>();queue.offer(root);while (!queue.isEmpty()) {int levelSize = queue.size();List<Integer> level = new ArrayList<>();for (int i = 0; i < levelSize; i++) {TreeNode node = queue.poll();level.add(node.val);if (node.left != null) queue.offer(node.left);if (node.right != null) queue.offer(node.right);}result.add(level);}return result;}

(2)dfs

 // 层序遍历 - DFSpublic static List<List<Integer>> levelOrderTraversalDFS(TreeNode root) {List<List<Integer>> result = new ArrayList<>();dfs(root, 0, result);return result;}private static void dfs(TreeNode node, int level, List<List<Integer>> result) {if (node == null) return;if (level >= result.size()) {result.add(new ArrayList<>());}result.get(level).add(node.val);dfs(node.left, level + 1, result);dfs(node.right, level + 1, result);}

二、构造

1 构建最大二叉树

给定一个不重复的整数数组 nums 。 最大二叉树 可以用下面的算法从 nums 递归地构建:

创建一个根节点,其值为 nums 中的最大值。
递归地在最大值 左边 的 子数组前缀上 构建左子树。
递归地在最大值 右边 的 子数组后缀上 构建右子树。
返回 nums 构建的 最大二叉树

输入:nums = [3,2,1,6,0,5]
输出:[6,3,5,null,2,0,null,null,1]
解释:递归调用如下所示:

[3,2,1,6,0,5] 中的最大值是 6 ,左边部分是 [3,2,1] ,右边部分是 [0,5] 。
[3,2,1] 中的最大值是 3 ,左边部分是 [] ,右边部分是 [2,1] 。
空数组,无子节点。
[2,1] 中的最大值是 2 ,左边部分是 [] ,右边部分是 [1] 。
空数组,无子节点。
只有一个元素,所以子节点是一个值为 1 的节点。
[0,5] 中的最大值是 5 ,左边部分是 [0] ,右边部分是 [] 。
只有一个元素,所以子节点是一个值为 0 的节点。
空数组,无子节点。


解法&思路

首先要解决的是找到每次递归传入数组的最大值做为根节点
该最大值下标左边的元素递归构造为左子树,右边的元素递归构造为右子树

// 定义二叉树节点类
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 TreeNode constructMaximumBinaryTree(int[] nums) {return build(nums, 0, nums.length);}private TreeNode build(int[] nums, int start, int end) {// 如果数组为空,返回 nullif (start == end) {return null;}// 找到最大值和最大值的索引int maxIndex = start;for (int i = start + 1; i < end; i++) {if (nums[i] > nums[maxIndex]) {maxIndex = i;}}// 用最大值创建根节点TreeNode rootNode = new TreeNode(nums[maxIndex]);// 最大值左侧为左子树rootNode.left = build(nums, start, maxIndex);// 最大值右侧为右子树rootNode.right = build(nums, maxIndex + 1, end);return rootNode;}
}

2 由 前 中 构造

// 定义二叉树节点类
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 TreeNode buildTree(int[] preorder, int[] inorder) {return build(preorder, inorder, 0, 0, inorder.length - 1);}private TreeNode build(int[] preorder, int[] inorder, int preStart, int inStart, int inEnd) {// 如果中序遍历的起始下标大于结束下标,说明当前子树为空if (inStart > inEnd) {return null;}// 从前序遍历中找到根节点int rootVal = preorder[preStart];TreeNode rootNode = new TreeNode(rootVal);// 在中序遍历中找到根节点的位置int rootIndex = -1;for (int i = inStart; i <= inEnd; i++) {if (inorder[i] == rootVal) {rootIndex = i;break;}}// 计算左子树的大小int leftSize = rootIndex - inStart;// 递归构造左子树// 左子树的前序遍历范围:preStart + 1 到 preStart + leftSize// 左子树的中序遍历范围:inStart 到 rootIndex - 1rootNode.left = build(preorder, inorder, preStart + 1, inStart, rootIndex - 1);// 递归构造右子树// 右子树的前序遍历范围:preStart + leftSize + 1 到 preStart + leftSize + 右子树大小// 右子树的中序遍历范围:rootIndex + 1 到 inEndrootNode.right = build(preorder, inorder, preStart + leftSize + 1, rootIndex + 1, inEnd);return rootNode;}
}

至于为什么只需要中序遍历的终点,而不需要前序遍历的终点?因为在我们的思路中其实可以发现只需要前序遍历的起点确认根节点的值,并不需要终点值。我们可以随便选择一个遍历的终点值用来确认边界值,即这部分代码。

if(inStart > inEnd){return null
}

3 由 前 后 构造

// 定义二叉树节点类
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 TreeNode constructFromPrePost(int[] preorder, int[] postorder) {return build(preorder, 0, preorder.length - 1, postorder, 0, postorder.length - 1);}private TreeNode build(int[] preorder, int preStart, int preEnd, int[] postorder, int postStart, int postEnd) {// 如果前序遍历的起始下标大于结束下标,说明当前子树为空if (preStart > preEnd) {return null;}// 当前子树只有一个节点时,直接返回该节点if (preStart == preEnd) {return new TreeNode(preorder[preStart]);}// 找到根节点int rootVal = preorder[preStart];TreeNode root = new TreeNode(rootVal);// 找到左子树起点在后序遍历中的位置int leftStartIndex = -1;for (int i = postStart; i <= postEnd; i++) {if (postorder[i] == preorder[preStart + 1]) {leftStartIndex = i;break;}}// 计算左子树的长度int size = leftStartIndex - postStart + 1;// 递归构造左子树// 左子树的前序范围:preStart + 1 到 preStart + size// 左子树的后序范围:postStart 到 leftStartIndexroot.left = build(preorder, preStart + 1, preStart + size, postorder, postStart, leftStartIndex);// 递归构造右子树// 右子树的前序范围:preStart + size + 1 到 preEnd// 右子树的后序范围:leftStartIndex + 1 到 postEnd - 1root.right = build(preorder, preStart + size + 1, preEnd, postorder, leftStartIndex + 1, postEnd - 1);return root;}
}

4 由 中 后 构造

// 定义二叉树节点类
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 TreeNode buildTree(int[] inorder, int[] postorder) {return build(inorder, 0, inorder.length - 1, postorder, 0, postorder.length - 1);}private TreeNode build(int[] inorder, int inStart, int inEnd, int[] postorder, int postStart, int postEnd) {// 如果中序遍历的起始下标大于结束下标,说明当前子树为空if (inStart > inEnd) {return null;}// 后序遍历的最后一个节点是根节点int rootVal = postorder[postEnd];TreeNode rootNode = new TreeNode(rootVal);// 找到根节点在中序遍历中的位置int rootIndex = -1;for (int i = inStart; i <= inEnd; i++) {if (inorder[i] == rootVal) {rootIndex = i;break;}}// 计算左子树的大小int leftSize = rootIndex - inStart;// 递归构造左子树// 左子树的中序范围:inStart 到 rootIndex - 1// 左子树的后序范围:postStart 到 postStart + leftSize - 1rootNode.left = build(inorder, inStart, rootIndex - 1, postorder, postStart, postStart + leftSize - 1);// 递归构造右子树// 右子树的中序范围:rootIndex + 1 到 inEnd// 右子树的后序范围:postStart + leftSize 到 postEnd - 1rootNode.right = build(inorder, rootIndex + 1, inEnd, postorder, postStart + leftSize, postEnd - 1);return rootNode;}
}

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

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

相关文章

leetcode 141. Linked List Cycle

题目描述&#xff1a; 代码&#xff1a; 用哈希表也可以解决&#xff0c;但真正考察的是用快慢指针法。 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode(int x) : val(x), next(NULL) {}* };*/ class Soluti…

AI辅助DevOps与自动化测试:重构软件工程效率边界

随着AI技术渗透至软件开发生命周期&#xff0c;DevOps与自动化测试领域正经历颠覆性变革。本文系统性解析AI在需求分析、测试用例生成、部署决策、异常检测等环节的技术实现路径&#xff0c;结合微软Azure DevOps、Tesla自动驾驶测试等典型场景&#xff0c;探讨AI如何突破传统效…

5月7号.

flex布局: 表单标签: 表单标签-表单项:

【AI面试准备】中文分词与实体抽取技术详解

分词&#xff0c;词性标准 目录 一、分词与词性标注1. **分词&#xff08;Word Segmentation&#xff09;**2. **词性标注&#xff08;Part-of-Speech Tagging&#xff09;** 二、实体抽取&#xff08;Named Entity Recognition, NER&#xff09;1. **实体类型示例**2. **输出…

【AI落地应用实战】Amazon Bedrock 零门槛使用 DeepSeek-R1:在 Amazon Bedrock 上部署与调用的完整实践指南

随着大语言模型&#xff08;LLM&#xff09;技术的快速发展&#xff0c;企业和开发者对具备更强理解与生成能力的模型需求也愈加旺盛。DeepSeek-R1 作为 DeepSeek 公司推出的一款强大开源模型&#xff0c;不仅在多项评测中表现优异&#xff0c;更具备出色的推理能力和长文本处理…

阿里云平台与STM32的物联网设计

基于阿里云平台与STM32的物联网设计方案可结合硬件选型、通信协议、云端配置及功能实现等多个维度进行设计。以下是综合多个参考案例的详细设计方案&#xff1a; 一、硬件选型与架构设计 主控芯片选择 STM32系列&#xff1a;推荐使用STM32F103&#xff08;如STM32F103ZET6、STM…

IBM BAW(原BPM升级版)使用教程Toolkit介绍

本部分为“IBM BAW&#xff08;原BPM升级版&#xff09;使用教程系列”内容的补充。 一、系统Toolkit 在 IBM Business Automation Workflow (BAW) 中&#xff0c;System Toolkit 是一组预先定义和配置好的工具、功能和组件&#xff0c;旨在帮助流程设计者和开发人员快速构建…

力扣-hot100 (矩阵置零)

73. 矩阵置零 中等 给定一个 *m* x *n* 的矩阵&#xff0c;如果一个元素为 0 &#xff0c;则将其所在行和列的所有元素都设为 0 。请使用 原地 算法。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,1,1],[1,0,1],[1,1,1]] 输出&#xff1a;[[1,0,1],[0,0,0],[1,0,1]] 示…

安装并运行第一个Spark程序

安装并运行第一个Spark程序需要完成以下步骤&#xff1a;安装Java和Spark&#xff0c;配置环境变量&#xff0c;编写并运行Spark程序。以下是详细的教程&#xff1a; 1. 安装Java Spark需要Java运行环境&#xff08;JRE&#xff09;或Java开发工具包&#xff08;JDK&#xff…

Python Selenium爬虫功能使用介绍

本文介绍python selenium 爬虫的功能以及使用 1. 基础核心功能 浏览器控制 from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager# 自动管理浏览器驱动 driver webdriver.Chro…

Cloudera CDP 7.1.3 主机异常关机导致元数据丢失,node不能与CM通信

问题描述 plaintext ERROR Could not load post-deployment data from /var/run/cloudera-scm-agent/process/ccdeploy_hadoop-conf_etchadoopconf.cloudera.yarn_-8903374259073700469 IOError: [Errno 2] No such file or directory: /var/run/cloudera-scm-agent/proce…

Nginx安全防护与HTTPS部署

目录 Nginx 隐藏版本号 限制危险请求方法 请求限制&#xff08;CC攻击防御&#xff09; 压力测试 防盗链 防止防盗链 动态黑名单 自动添加黑名单 HTTPS配置 HTTPS 概念 安全通信的四大原则 HTTPS的几种加密方式 nginx https的作用 Nginx 隐藏版本号 &#xff01;&#xff01;&a…

C++类对象的隐式类型转换和编译器返回值优化

文章目录 前言1. 隐式类型转换1.1 单参数的隐式类型转换1.2 多参数的隐式类型转换1.3 explicit关键字 2. 编译器的优化2.1 普通构造优化2.2 函数传参优化2.3 函数返回优化 前言 在类与对象的学习过程中&#xff0c;一定会对隐式类型转换这个词不陌生。对于内置类型而言&#x…

领麦微红外温度传感器,摇奶器测温应用

在育儿领域&#xff0c;精准控制奶液温度是守护宝宝健康的重要环节。领麦微作为MEMS传感器领域的创新先锋&#xff0c;通过其红外测温传感器的非接触式测量、高精度测温、实时反馈以及智能温控节能等核心优势&#xff0c;为摇奶器注入了全新的智能化解决方案。这一技术不仅提升…

第十一届蓝桥杯 2020 C/C++组 蛇形填数

目录 题目&#xff1a; 题目描述: 题目链接&#xff1a; 思路&#xff1a; 思路详解&#xff1a; 代码&#xff1a; 代码详解&#xff1a; 题目&#xff1a; 题目描述: 题目链接&#xff1a; 蛇形填数 - 蓝桥云课 思路&#xff1a; 思路详解&#xff1a; 看图找规律…

如何检查 Watchtower 是否正常工作及更新未生效的排查方法【日常排错】

文章目录 前言一、验证 Watchtower 是否正在运行1. 检查 Watchtower 容器状态2. 查看 Watchtower 日志 二、检查5分钟间隔设置是否正确1. 确认启动命令2. 验证环境变量 三、排查更新未生效的原因1. 检查是否有镜像更新2. 检查容器标签3. 检查监控范围 四、测试 Watchtower 功能…

宝塔面板,删除项目后还能通过域名进行访问

场景&#xff1a;在阿里云宝塔面板中&#xff0c;删除了之前建立的html项目&#xff0c;通过之前绑定的域名还是可以访问&#xff0c;又把项目的目录文件删除&#xff0c;发现还是不行 又清理了浏览器缓存&#xff0c;但还是有这个问题通过该域名重新创建一个html项目&#xff…

多层PCB SMT贴装全流程指南:从物料准备到回流焊工艺控制

在电子制造领域&#xff0c;多层PCB板元器件贴片是一项重要的技术操作。本文将详细介绍多层PCB板元器件贴片的操作流程和注意事项&#xff0c;帮助您更好地理解和掌握这项技术。 一、准备阶段 在进行多层PCB板元器件贴片操作前&#xff0c;需要做好以下准备工作&#xff1a; 1.…

PAT(最近)

1022 D进制的AB - PAT (Basic Level) Practice &#xff08;中文&#xff09; 加减位置调换 本来以为就是简单的 十进制转换为一个长的字符串 没想到在那个拼接字符串的时候 只需要简单的 加减位置调换就可以 避免使用麻烦的翻转函数 import java.util.Scanner; public clas…

【Harbor v2.13.0 详细安装步骤 安装证书启用 HTTPS】

Harbor v2.13.0 详细安装步骤&#xff08;启用 HTTPS&#xff09; 1. 环境准备 系统要求&#xff1a;至少 4GB 内存&#xff0c;100GB 磁盘空间。 已安装组件&#xff1a; Docker&#xff08;版本 ≥ 20.10&#xff09;Docker Compose&#xff08;版本 ≥ v2.0&#xff09; 域…