在 二叉树--纲领篇 中,介绍了解决二叉树问题无非就是「遍历」➕「分解」两种方法
对于遇到的题目,我们的思路就是:是否可以通过遍历一遍二叉树得到答案?如果不能的话,是否可以定义一个递归函数,通过子问题(子树)的答案推导出原问题的答案? 如果需要设计到子树信息, 建议使用后续遍历
下面本篇文章,将按照这个思路,手把手的带你解决三个实战题目

对于这个题目的要求就是「翻转整棵树」
首先我们思考一下,是否可以通过遍历一遍二叉树得到答案呢??
xxxxxxxxxxpublic void traversal(TreeNode root) { if (root == null) return ; // 前序阶段:交换左右子树 TreeNode tmp = root.left; root.left = root.right; root.right = tmp; traversal(root.left); traversal(root.right);}那我们现在继续思考是否可以使用分解的思路来解决问题呢??
xxxxxxxxxx// 定义:将以 root 为根的这棵二叉树翻转,返回翻转后的二叉树的根节点public TreeNode invertTree(TreeNode root) { if (root == null) return null; TreeNode left = invertTree(root.left); TreeNode right = invertTree(root.right); // 后序阶段:交换左右子树 root.left = right; root.right = left; // 和定义逻辑自恰:以 root 为根的这棵二叉树已经被翻转,返回 root return root;}在使用分解的思路时,我们需要明确「函数返回值」代表的含义,即:给递归函数一个合适的定义,然后用函数的定义来解释你的代码

对于这个题目的要求就是「拉平左子树,接到右子树上」
首先我们思考一下,是否可以通过遍历一遍二叉树得到答案呢??
x// 虚拟头节点,dummy.right 就是结果TreeNode dummy = new TreeNode(-1);// 用来构建链表的指针TreeNode p = dummy;
public void traverse(TreeNode root) { if (root == null) { return; } // 前序位置 p.right = new TreeNode(root.val); p = p.right;
traverse(root.left); traverse(root.right);}但是注意flatten函数的签名,返回类型为void,也就是说题目希望我们在原地把二叉树拉平成链表
这样一来,没办法通过简单的二叉树遍历来解决这道题了
那我们现在继续思考是否可以使用分解的思路来解决问题呢??
xxxxxxxxxx// 定义:将以 root 为根的这棵二叉树拉直,返回拉直后的二叉树根节点private TreeNode traversal(TreeNode root) { if (root == null) return root; TreeNode left = traversal(root.left); TreeNode right = traversal(root.right); // 后序阶段 root.left = null; root.right = left; TreeNode p = root; while (p.right != null) { p = p.right; } p.right = right; // 和定义逻辑自恰:以 root 为根的这棵二叉树已经被拉直,返回 root return root;}
对于这个题目的要求就是「node.next = leftNode」
首先我们思考一下,是否可以通过遍历一遍二叉树得到答案呢??
root.left.next = root.right,而对于节点5和6是无法连接起来的但是如果我们在二叉树的遍历基础上抽象成「三叉树遍历」呢???直接看图!!!
OK!直接看代码!!
public void traversal(TreeNode node1, TreeNode node2) { if (node1 == null || node2 == null) return ; // 前序:执行连接操作 node1.next = node2; traversal(node1.left, node1.right); traversal(node1.right, node2.left); traversal(node2.left, node2.right);}