题目:二叉树展开为链表 middle
给定一个二叉树,原地将它展开为一个单链表。
package leetCode.DFS;
public class lc_dfs_114_flatten {
/*
题目:二叉树展开为链表 middle
给定一个二叉树,原地将它展开为一个单链表。
例如,给定二叉树
1
/ \
2 5
/ \ \
3 4 6
将其展开为:
1
\
2
\
3
\
4
\
5
\
6
*/
/*
思路:两种都简单,都要会
解法一(简单)
可以发现展开的顺序其实就是二叉树的先序遍历。算法和 94 题中序遍历的 Morris 算法有些神似,
我们需要两步完成这道题。
1.左子树插入到右子树的地方
2.将原来的右子树接到左子树的最右边节点
3.考虑新的右子树的根节点,一直重复上边的过程,直到新的右子树为 null
可以看图理解下这个过程。
1
/ \
2 5
/ \ \
3 4 6
//将 1 的左子树插入到右子树的地方
1
\
2 5
/ \ \
3 4 6
//将原来的右子树接到左子树的最右边节点
1
\
2
/ \
3 4
\
5
\
6
//将 2 的左子树插入到右子树的地方
1
\
2
\
3 4
\
5
\
6
//将原来的右子树接到左子树的最右边节点
1
\
2
\
3
\
4
\
5
\
6
......
解法二:我其实就是用了前序遍历的方式失败的
题目中,要求说是 in-place, in-place 的意思可能更多说的是直接在原来的节点上改变指向,
空间复杂度并没有要求。所以这道题也可以用递归解一下。
1
/ \
2 5
/ \ \
3 4 6
利用递归的话,可能比解法一难理解一些。
题目其实就是将二叉树通过右指针,组成一个链表。
1 -> 2 -> 3 -> 4 -> 5 -> 6
先序遍历的顺序是 1 2 3 4 5 6。
遍历到 2,把 1 的右指针指向 2。1 -> 2 3 4 5 6。
遍历到 3,把 2 的右指针指向 3。1 -> 2 -> 3 4 5 6。
... ...
一直进行下去似乎就解决了这个问题。但现实是残酷的,原因就是我们把 1 的右指针指向 2,
那么 1 的原本的右孩子就丢失了,也就是 5 就找不到了。
解决方法的话,我们可以逆过来进行。
我们依次遍历 6 5 4 3 2 1,然后每遍历一个节点就将当前节点的右指针更新为上一个节点。
遍历到 5,把 5 的右指针指向 6。6 <- 5 4 3 2 1。
遍历到 4,把 4 的右指针指向 5。6 <- 5 <- 4 3 2 1。
... ...
1
/ \
2 5
/ \ \
3 4 6
这样就不会有丢失孩子的问题了,因为更新当前的右指针的时候,当前节点的右孩子已经访问过了。
而 6 5 4 3 2 1 的遍历顺序其实变形的后序遍历,遍历顺序是右子树->左子树->根节点。
这里的话,我们不再是打印根节点,而是利用一个全局变量 pre,
更新当前根节点的右指针为 pre,左指针为 null。
相应的左孩子也要置为 null,同样的也不用担心左孩子丢失,因为是后序遍历,左孩子已经遍历过了。
*/
public static void main(String[] args) {
TreeNode node1 = new TreeNode(1);
TreeNode node2 = new TreeNode(2);
TreeNode node3 = new TreeNode(5);
TreeNode node4 = new TreeNode(3);
TreeNode node5 = new TreeNode(4);
TreeNode node6 = new TreeNode(6);
node1.left = node2;
node1.right = node3;
node2.left = node4;
node2.right = node5;
node3.right = node6;
lc_dfs_114_flatten m = new lc_dfs_114_flatten();
m.flatten(node1);
}
public void flatten(TreeNode root) {
while (root != null) {
//左子树为 null,直接考虑下一个节点
if (root.left == null)
root = root.right;
else {
// 找左子树最右边的节点
TreeNode mostRightOfLeft = root.left;
while (mostRightOfLeft.right != null) {
mostRightOfLeft = mostRightOfLeft.right;
}
//将原来的右子树接到左子树的最右边节点
mostRightOfLeft.right = root.right;
// 将左子树插入到右子树的地方
root.right = root.left;
root.left = null;//!!!注意左子树要置空
// 考虑下一个节点
root = root.right;
}
}
}
TreeNode pre;
public void flatten2(TreeNode root) {
if (root == null)
return;
flatten2(root.right);
flatten2(root.left);
root.right = pre;
root.left = null;//左指针同样要置空
pre = root;
}
static 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;
}
}
}