lc_dfs_114_flatten

题目:二叉树展开为链表 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;
        }
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值