这道题,一开始毫无思路,甚至走错思路,然后经过自己画图,一点一点做出来。一次就 ac,爽到起飞。原来思路对了是这么样的感觉。
这次不像往常,先贴一下结果。
好了,开心完了,进入正题。
题目描述
看了题目,接下来复现一下心路历程。
心路历程 1
这个单链表,仔细一看顺序,是一个先序遍历。那我要做的就是,按先序遍历的顺序去遍历树,然后,当遍历到右节点时,将右节点接到左节点的尾巴上。(细心的人可能已经发现了,我的思路完全没考虑左右:
但是当时的我没意识到这个问题,一心想的是,如何在递归过程中知道,我已经可以进行链接了。(由于是记录心路历程,所以我把错的思路也往下走,没耐心的朋友直接跳到 2 部分)
那么如何分辨呢,我想到的是,使用一个栈维护遍历结果,当到达叶子节点时,推入一个额外的 null 节点作为标记。只有遇到 null 节点,才进行拼接操作。
这时候我发现了,我虽然能够按顺序进行链接,但是左右顺序完全乱了。
心路历程2
我一直思考如何从先序遍历入手解决,因为毕竟输出就是先序的。但始终想不出来,无奈我只能完全推翻了思路 1 的思考,从新进行画图。以下是我在思考过程中的图:
如①,我想的是,要完成变成右链表的话,那么我就得把左子树搬到根与右子树之间。并且,这个操作过程必须得从孩子开始,不能先操作父节点,否则就会乱掉。
然后我就写了一段伪代码(如 ② )。
然后我就开始愉快的敲代码了:
public class Solution1 {
public void flatten(TreeNode root) {
if (root == null) return;
flatten(root.left);
flatten(root.right);
// if (root.left == null && root.right == null) return; // 左 null,右 null
// else if (root.left == null) return; // 左 null,右 not null
// if (root.right == null && root.left != null) { // 左 not null,右 null
// root.right = root.left;
// root.left = null;
// }
if (root.left != null){
TreeNode temp = root.left;
while (temp.right != null) {
temp = temp.right;
}
temp.right = root.right;
root.right = root.left;
root.left = null;
}
}
}
我为了方便,先列出了 root 的所有情况:
- 为 null
- 左空右不空
- 左不空右空
- 左不空右不空
然后发现,所有的情况都可以归结为一个:
当左节点不为空时,才需要操作。
另外,为了保证先操作子树,我采用的是后序遍历的方式。
事情进行的很顺利,但我马上又发现了问题:
当我要把 1 的左子树插到 1 与右树中间时,我发现按② 的伪代码实现,插入又发生了错误。
节点 5 应该接到节点 4,而不是节点 2。
所以说,在将左子树插入间隔中时,应该寻找左子树的最右子节点。(如③)
于是有了这一段代码:
TreeNode temp = root.left;
while (temp.right != null) {
temp = temp.right;
}
temp.right = root.right;
root.right = root.left;
root.left = null;