包含力扣226-二叉树上左右子节点交换,114--二叉树展开为链表,116--填充每个节点的下一个右侧节点指针,654--最大二叉树,105--前序和中序遍历结果构造二叉树

1、

链表遍历分两种结构,迭代和递归。

同样根据链表可以 推导出二叉树和N叉树的遍历框架。

再由n叉树的遍历扩展到图的遍历。

手把手带你刷二叉树(第一期)

/* 二叉树遍历框架 */ void traverse(TreeNode root) { // 前序遍历 traverse(root.left) // 中序遍历 traverse(root.right) // 后序遍历 }

快速排序可以看作 二叉树的前序遍历,归并可以看作后序遍历

写递归算法的秘诀

写树相关的算法,简单说就是,先搞清楚当前 root 节点「该做什么」以及「什么时候做」,然后根据函数定义递归调用子节点,递归调用会让孩子节点做相同的事情。

所谓「该做什么」就是让你想清楚写什么代码能够实现题目想要的效果,所谓「什么时候做」,就是让你思考这段代码到底应该写在前序、中序还是后序遍历的代码位置上。

226 题-二叉树上的每一个节点的左右子节点进行交换

二叉树上的每一个节点的左右子节点进行交换

递归实现: --深度优先

转换左右孩子的时候,左右孩子的孩子已经到位了。/或者先转换左右孩子,再转换子孙

可以放在前序遍历和后续遍历的位置。

错误1:没有return 错误2,左右孩子搞反了 错误3:没有判断为空

/** * Definition for a binary tree node. * 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 TreeNode invertTree(TreeNode root) { if (root==null){ return null; } TreeNode tmp= root.right; root.right=root.left; root.left=tmp; invertTree(root.left); invertTree(root.right); return root; } }

迭代实现: --广度优先

class Solution { public TreeNode invertTree(TreeNode root) { if(root==null) { return null; } //将二叉树中的节点逐层放入队列中,再迭代处理队列中的元素 LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); queue.add(root); while(!queue.isEmpty()) { //每次都从队列中拿一个节点,并交换这个节点的左右子树 TreeNode tmp = queue.poll(); TreeNode left = tmp.left; tmp.left = tmp.right; tmp.right = left; //如果当前节点的左子树不为空,则放入队列等待后续处理 if(tmp.left!=null) { queue.add(tmp.left); } //如果当前节点的右子树不为空,则放入队列等待后续处理 if(tmp.right!=null) { queue.add(tmp.right); } } //返回处理完的根节点 return root; } }

LinkedList创建: LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); ( )别忘了

poll 删除返回第一个元素,add加入一个元素。

解析: 当换完左右子树后,再对孙子节点交换,也就是队列中推入孩子节点

注意: 循环退出的条件,然后循环里面要干什么:用临时变量交换左右孩子。 tmp是某一个根节点

错误:要用while不能用 if

116. 填充每个节点的下一个右侧节点指针

tips: 转换为节点要做的事情,这里设置为两个节点。

方法1:递归函数法

从根节点开始,定义函数conect_two 。 如果connect的两个节点不为空,就连接。

left.left - left.right /left.right --right.left /right.left-right.right

class Solution { public Node connect(Node root) { if(root==null){ return null; } connectTwo(root.left,root.right); return root; } public void connectTwo(Node first,Node second){ if(first==null || second==null ){ return ; //不是null } first.next=second; connectTwo(first.left,first.right); connectTwo(first.right,second.left); connectTwo(second.left,second.right); return; } }

错误: 忘记了 first.next=second; 即本层忘了连接了。 函数可以不加public。

方法2递归

两个指针,从根节点开始往中间靠拢,直到最后一层。

7.gif
class Solution { public Node connect(Node root) { dfs(root); return root; } void dfs(Node root) { if(root==null) { return; } Node left = root.left; Node right = root.right; //配合动画演示理解这段,以root为起点,将整个纵深这段串联起来 while(left!=null) { left.next = right; left = left.right; right = right.left; } //递归的调用左右节点,完成同样的纵深串联 dfs(root.left); dfs(root.right); } }

方法3 迭代

一种是有共同父节点,可以直接填充下一个。一种是跨了父节点,可以通过上一层的串联来获得next即为root.right.next => root.next.left

class Solution { public Node connect(Node root) { if(root==null) { return root; } Node pre = root; //循环条件是当前节点的left不为空,当只有根节点 //或所有叶子节点都出串联完后循环就退出了 while(pre.left!=null) { Node tmp = pre; while(tmp!=null) { //将tmp的左右节点都串联起来 //注:外层循环已经判断了当前节点的left不为空 tmp.left.next = tmp.right; //下一个不为空说明上一层已经帮我们完成串联了 if(tmp.next!=null) { tmp.right.next = tmp.next.left; } //继续右边遍历 tmp = tmp.next; } //从下一层的最左边开始遍历 pre = pre.left; } return root; } }

114. 二叉树展开为链表

假设左右子树已经拉直了

同样用递归的方法,假设左右子树已经拉直了。

/** * Definition for a binary tree node. * 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 ; } flatten(root.left); flatten(root.right); //错了 // root.left.right=root.right; // root.right=root.left; // root.left=null; 这里把p 设置为根节点比较好,否则p会为空 TreeNode left_=root.left; TreeNode right_=root.right; root.right=left_; TreeNode p=root; while(p.right!=null){ p=p.right; } p.right=right_; root.left=null; }

错误: 把左子树放到右孩子后, 原来的右子树要 放在 最下节点(要一直遍历到下面) 的右边

左子树要置空

迭代的方法

将左子树放到右子树的位置,右子树放到左子树的最右下。

public void flatten(TreeNode root) { while (root != null) { //左子树为 null,直接考虑下一个节点 if (root.left == null) { root = root.right; } else { // 找左子树最右边的节点 TreeNode pre = root.left; while (pre.right != null) { pre = pre.right; } //将原来的右子树接到左子树的最右边节点 pre.right = root.right; // 将左子树插入到右子树的地方 root.right = root.left; root.left = null; // 考虑下一个节点 root = root.right; } } }

关键: 找到右子树的前驱节点 然后再考虑 下一个节点root = root.right;

类后序遍历

其实要做的是前序遍历的结果倒过来,1-2-3-4-5-6 到6-5-4-3-2-1

遍历顺序 右 左 中。

https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/solution/xiang-xi-tong-su-de-si-lu-fen-xi-duo-jie-fa-by--26/

--- 还有一种解,先略过。

手把手带你刷二叉树(第二期)

654. 最大二叉树

class Solution { public TreeNode constructMaximumBinaryTree(int[] nums) { return construct(nums,0,nums.length-1); //可以直接return } public TreeNode construct(int[] nums,int left,int right){ TreeNode root; // if (left>right || left==right){ // root =new TreeNode(nums[left],null,null); // //这里不能用left 或者right , 直接改成 low>high返回null // return root; // } if (left>right){ return null; } if(left<right || left==right){ int index = findMax(nums,left,right); root =new TreeNode(nums[index]); root.left=construct(nums,left,index-1); root.right=construct(nums,index+1,right); return root; } return null; } //不是 0改成 最小值,其次要改成小于等于,index是-1 public int findMax (int[] nums,int l,int r){ if (l==r){ return l; } int max =Integer.MIN_VALUE; int index =0; for (int i=l;i<=r;i++){ if (nums[i]>max){ max=nums[i]; index=i; } } return index; } }

数组参数: .length 可以获得长度 最小值:Integer.MIN_VALUE

问题:

判断节点条件,可以把子右孩子看作节点单独判断,若left>right则返回空,这样比较好处理

其次,索引i<=r,因为传入的就是length-1

105通过前序和中序遍历结果构造二叉树

递归版本

/** * Definition for a binary tree node. * 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 TreeNode buildTree(int[] preorder, int[] inorder) { return construct(preorder,inorder,0,preorder.length-1,0,inorder.length-1); } public TreeNode construct(int[] pre,int[] ino,int left_1,int right_1,int left_2,int right_2) { int index=0; TreeNode root=new TreeNode(); //注意要初始化 if (left_1 >right_1){ return null; } int root_val=pre[left_1]; for(int i=left_2;i<=right_2;i++){ if (ino[i]==root_val){ index=i; break; //加个break } //错误2:index-left2 =size,不用+1 int size =index-left_2; root.val=root_val; root.left=construct(pre,ino,left_1+1,left_1+size,left_2,index-1); root.right=construct(pre,ino,left_1+1+size,right_1,index+1,right_2 ); return root; } }

问题:

找index时,可以加个break加快速度。 其次是算size时不用+1。 最后变量注意要初始化

迭代版本

补充

给一个随机数组,生成相应的二叉搜索树

https://leetcode-cn.com/problems/convert-sorted-array-to-binary-search-tree/

根据前序和中序,输出后序


__EOF__

本文作者userName
本文链接https://www.cnblogs.com/Blunt-Raz0r/p/15500303.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   Blunt-Razor  阅读(39)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 从HTTP原因短语缺失研究HTTP/2和HTTP/3的设计差异
· 三行代码完成国际化适配,妙~啊~
点击右上角即可分享
微信分享提示