Loading

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;
    }
}
posted @ 2021-01-17 23:13  反身而诚、  阅读(92)  评论(0编辑  收藏  举报