Medium | LeetCode 114. 二叉树展开为链表 | 先序遍历 | 递归 | 迭代
114. 二叉树展开为链表
给定一个二叉树,原地将它展开为一个单链表。
例如,给定二叉树
1
/ \
2 5
/ \ \
3 4 6
将其展开为:
1
\
2
\
3
\
4
\
5
\
6
方法一:递归先序遍历保存进List
public void flatten(TreeNode root) {
List<TreeNode> list = new ArrayList<TreeNode>();
// 先使用递归先序遍历, 将其值保存进List
preorderTraversal(root, list);
// 然后遍历List, 将前一个节点和后一个节点串联起来
int size = list.size();
for (int i = 1; i < size; i++) {
TreeNode prev = list.get(i - 1), curr = list.get(i);
prev.left = null;
prev.right = curr;
}
}
public void preorderTraversal(TreeNode root, List<TreeNode> list) {
if (root != null) {
list.add(root);
preorderTraversal(root.left, list);
preorderTraversal(root.right, list);
}
}
方法二:递归子树, 返回链表头结点和尾节点
类似于 Medium | LeetCode 426 | 剑指 Offer 36. 二叉搜索树与双向链表 | 中序遍历(递归) 在递归的时候, 返回链表的头结点和尾节点。
public void flatten(TreeNode root) {
if (root == null) {
return;
}
preorderTraversal(root);
}
public TreeNode[] preorderTraversal(TreeNode root) {
if (root == null) {
return new TreeNode[2];
}
// 递归计算左右子树链表的头结点和尾节点
TreeNode[] left = preorderTraversal(root.left);
TreeNode[] right = preorderTraversal(root.right);
root.left = null;
if (left[0] != null) {
// 如果左子树不为空, 则将其插在根节点和右子树链表中间
root.right = left[0];
left[1].right = right[0];
} else {
// 没有左子树, 则将右子树链表直接插在根节点后面
root.right = right[0];
}
// 根据左右子树的返回值情况设置当前树的链表的头结点和尾节点
if (right[1] != null) {
return new TreeNode[]{root, right[1]};
} else if (left[1] != null) {
return new TreeNode[]{root, left[1]};
} else {
return new TreeNode[]{root, root};
}
}
方法三: 迭代算法
分为两步: 第一步, 将左孩子接在当前节点右指针上, 这个简单。第二步, 将右孩子也要接在某个节点的右指针上, 这个节点该怎么找呢?其实就是当前节点的左子树一路向右的节点。
public void flatten(TreeNode root) {
TreeNode curr = root;
while (curr != null) {
if (curr.left != null) {
// next表示当前节点的后继节点
TreeNode next = curr.left;
// predecessor表示当前节点右孩子的前驱节点
TreeNode predecessor = next;
while (predecessor.right != null) {
predecessor = predecessor.right;
}
// 将当前节点的右孩子, 连在其前驱节点上
predecessor.right = curr.right;
// 将当前节点的左孩子设为空
curr.left = null;
// 将当前节点的后继节点, 连在当前节点后面
curr.right = next;
}
// 当前节点位置走向下一个位置
curr = curr.right;
}
}