1. 题目
考察点
这道题的考察点是二叉树的遍历和展平,
需要掌握前序遍历,中序遍历和后序遍历的顺序和方法,以及如何将二叉树转换为链表的技巧。
这道题可以用递归,迭代或者原地修改的方式来解决,需要注意展平后的链表要保持和二叉树的前序遍历相同的顺序。
这道题也可以扩展为按照中序遍历或者后序遍历的顺序来展平,需要改变一些细节。
2. 解法
有两种解法
- 递归
- 迭代
解法一:递归
思路
好的,我可以条理的讲一下递归代码的逻辑。这个递归的思路是这样的:
- 首先,如果根节点为空,或者根节点没有左右子节点,那么直接返回根节点,不需要展平。
- 然后,分别对左右子树进行递归展平,得到两个链表。
- 接着,将左子树的链表接到根节点的右子节点上,将原来的右子树的链表接到左子树的链表的末尾。
- 最后,将根节点的左子节点置为空,返回根节点作为整个链表的头节点。
代码逻辑
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // 定义一个二叉树节点的类 public 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 void flatten(TreeNode root) { // 基本情况:如果根节点为空,或者根节点没有左右子节点,那么直接返回根节点,不需要展平 if (root == null || (root.left == null && root.right == null )) { return ; } // 分别对左右子树进行递归展平,得到两个链表 flatten(root.left); flatten(root.right); // 将左子树的链表接到根节点的右子节点上,将原来的右子树的链表接到左子树的链表的末尾 TreeNode temp = root.right; // 保存原来的右子树 root.right = root.left; // 将展平的左子树移到右边 root.left = null ; // 将左子节点置为空 // 找到展平的左子树的末尾,然后将原来的右子树接上去 TreeNode curr = root; while (curr.right != null ) { curr = curr.right; } curr.right = temp; // 追加原来的右子树 // 返回展平后的根节点 return ; } } |
解法二:迭代
思路
好的,我可以再讲一下迭代的代码逻辑。这个迭代的思路是这样的:
- 首先,创建一个栈来存储右子树,初始化当前节点为根节点。
- 然后,循环直到当前节点为空且栈为空。
- 在循环中,如果当前节点有右子节点,将它压入栈中;如果当前节点有左子节点,将它移到右边,然后将左边置为空;否则,从栈中弹出一个右子树,将它移到右边;最后,移动到链表中的下一个节点。
- 最后,返回展平后的根节点。
具体实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | // 定义一个二叉树节点的类 public 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 void flatten(TreeNode root) { // 边界情况:如果根节点为空,直接返回 if (root == null ) { return ; } // 创建一个栈来存储右子树 Stack<TreeNode> stack = new Stack<>(); // 初始化当前节点为根节点 TreeNode curr = root; // 循环直到当前节点为空且栈为空 while (curr != null || !stack.isEmpty()) { // 如果当前节点有右子节点,将它压入栈中 if (curr.right != null ) { stack.push(curr.right); } // 如果当前节点有左子节点,将它移到右边,然后将左边置为空 if (curr.left != null ) { curr.right = curr.left; curr.left = null ; } else { // 否则,从栈中弹出一个右子树,将它移到右边 curr.right = stack.pop(); } // 移动到链表中的下一个节点 curr = curr.right; } // 返回展平后的根节点 return ; } } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2021-04-25 Dubbo源码分析(十)同步调用与异步调用
2021-04-25 Dubbo源码分析(九)负载均衡算法
2021-04-25 Dubbo源码分析(八)集群容错机制
2021-04-25 Dubbo源码分析(七)服务目录
2021-04-25 Dubbo源码分析(六)服务引用的具体流程
2021-04-25 Dubbo源码分析(五)服务暴露的具体流程(下)
2021-04-25 Dubbo源码分析(四)服务暴露的具体流程(上)