Leetcode114. Flatten Binary Tree to Linked List
Given a binary tree, flatten it to a linked list in-place.
For example, given the following tree:
1
/ \
2 5
/ \ \
3 4 6
The flattened tree should look like:
1
\
2
\
3
\
4
\
5
\
6
解法一
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
......
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;
}
}
}
解法二 变形的后序排序
基本思路是直接在原来的节点上改变指向
题意是将二叉树通过右指针,组成一个链表。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。
... ...
这样就不会有丢失孩子的问题了,因为更新当前的右指针的时候,当前节点的右孩子已经访问过了。
而6 5 4 3 2 1的遍历顺序其实变形的后序遍历,遍历顺序是右子树->左子树->根节点。
变形的后序遍历的代码
public void postorder(TreeNode<T> root){
if (root == null)
return;
postorder(root.right);
postorder(root.left);
System.out.print(root.data);
}
我们不再是打印根节点,而是利用一个全局变量pre
,更新当前根节点的右指针为pre
,左指针为null
。
private TreeNode pre = null;
public void flatten(TreeNode root) {
if (root == null)
return;
flatten(root.right);
flatten(root.left);
root.right = pre;
root.left = null;
pre = root;
}
相应的左孩子也要置为null,同样的也不用担心左孩子丢失,因为是后序遍历,左孩子已经遍历过了。
迭代形式:
public void flatten(TreeNode root) {
Stack<TreeNode> toVisit = new Stack<>();
TreeNode cur = root;
TreeNode pre = null;
while (cur != null || !toVisit.isEmpty()) {
while (cur != null) {
toVisit.push(cur); // 添加根节点
cur = cur.right; // 递归添加右节点
}
cur = toVisit.peek(); // 已经访问到最右的节点了
// 在不存在左节点或者右节点已经访问过的情况下,访问根节点
if (cur.left == null || cur.left == pre) {
toVisit.pop();
/**************修改的地方***************/
cur.right = pre;
cur.left = null;
/*************************************/
pre = cur;
cur = null;
} else {
cur = cur.left; // 左节点还没有访问过就先访问左节点
}
}
}