114. 二叉树展开为链表

114. 二叉树展开为链表

Difficulty: 中等

给你二叉树的根结点 root ,请你将它展开为一个单链表:

  • 展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null
  • 展开后的单链表应该与二叉树 顺序相同。

示例 1:

114. 二叉树展开为链表

输入:root = [1,2,5,3,4,null,6]
输出:[1,null,2,null,3,null,4,null,5,null,6]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [0]
输出:[0]

提示:

  • 树中结点数在范围 [0, 2000]
  • -100 <= Node.val <= 100

**进阶:**你可以使用原地算法(O(1) 额外空间)展开这棵树吗?

Solution

方法一:摘抄自 labuladong 大佬的解析

链接:https://labuladong.gitbook.io/algo/shu-ju-jie-gou-xi-lie/shou-ba-shou-shua-er-cha-shu-xun-lian-di-gui-si-wei/er-cha-shu-xi-lie-1

给 flatten 函数输入一个节点 root,那么以 root 为根的二叉树就会被拉平为一条链表。
我们再梳理一下,如何按题目要求把一棵树拉平成一条链表?很简单,以下流程:

1、将 root 的左子树和右子树拉平。

2、将 root 的右子树接到左子树下方,然后将整个左子树作为右子树。
114. 二叉树展开为链表

// 定义:将以 root 为根的树拉平为链表
void flatten(TreeNode root) {
    // base case
    if (root == null) return;

    flatten(root.left);
    flatten(root.right);

    /**** 后序遍历位置 ****/
    // 1、左右子树已经被拉平成一条链表
    TreeNode left = root.left;
    TreeNode right = root.right;

    // 2、将左子树作为右子树
    root.left = null;
    root.right = left;

    // 3、将原先的右子树接到当前右子树的末端
    TreeNode p = root;
    while (p.right != null) {
        p = p.right;
    }
    p.right = right;
}

你看,这就是递归的魅力,你说 flatten 函数是怎么把左右子树拉平的?说不清楚,但是只要知道 flatten 的定义如此,相信这个定义,让 root 做它该做的事情,然后 flatten 函数就会按照定义工作。另外注意递归框架是后序遍历,因为我们要先拉平左右子树才能进行后续操作。

方法二:leetcode讨论区感觉很牛逼的大佬分享,而且我竟然也看懂了
114. 二叉树展开为链表
其实是分为三步:

  • 首先将根节点的左子树变成链表
  • 其次将根节点的右子树变成链表
  • 最后将变成链表的右子树放在变成链表的左子树的最右边

这就是一个递归的过程,递归的一个非常重要的点就是:不去管函数的内部细节是如何处理的,我们只看其函数作用以及输入与输出。对于函数flatten来说:

  • 函数作用:将一个二叉树,原地将它展开为链表
  • 输入:树的根节点
  • 输出:无

那我们就直接根据三步来写程序就可以了

class Solution {
    public void flatten(TreeNode root) {
        if(root == null){
            return ;
        }
        //将根节点的左子树变成链表
        flatten(root.left);
        //将根节点的右子树变成链表
        flatten(root.right);
        TreeNode temp = root.right;
        //把树的右边换成左边的链表
        root.right = root.left;
        //记得要将左边置空
        root.left = null;
        //找到树的最右边的节点
        while(root.right != null) root = root.right;
        //把右边的链表接到刚才树的最右边的节点
        root.right = temp;
    }
}

作者:Geralt_U
链接:https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list/solution/114-er-cha-shu-zhan-kai-wei-lian-biao-by-ming-zhi-/
来源:力扣(LeetCode)著作权归作者所有.

总结(看了大佬自己的想法):这两种方法感觉很类似,其中最重要的一点是,不管二叉树的左右子树是怎样变成链表(直线),因为都是通过递归在函数flatten内部完成,只知道函数flatten执行完成之后,左右子树就都变成链表了;一开始看的时候,自己就很纠结,总是想深入通过执行函数,一步一步的去看看,内部是怎样完成的,结果,发现自己陷入内部递归,陷进去了。脑子转不出来了;没办法看看了大佬解析,自己瞬间就想通了。

自己一直有个误区:其实对待二叉树遍历的题目,无非就是遍历循环或者递归,那么方法可能就是:

  • 前序遍历: 根 左 右
  • 中序遍历: 左 根 右
  • 后序遍历: 左 右 根
  • 层次遍历

对于上面两种大佬解释,其实可以发现前序遍历的影子,先进行根节点的处理,在处理左右两个节点。

  • 前序遍历
    // 前序遍历
    if(root == null){
            return ;
    }
    flatten(root.left);
    flatten(root.right);
  • 剩下部分代码:可以理解为只是对当前节点的操作,对当前节点的左右子树的操作,因为是递归,对根节点的操作:将根节点的右子树拼接到根节点的左子树;同样的道理,由于递归,左子树同样可以看做是一棵二叉树,那么依然可以做上面类似的操作,将以左子树的根节点为首,这个节点左右子数拼接,这样一层层的递归拼接,最终完成了题目的要求
上一篇:114. 二叉树展开为链表


下一篇:歌斐资产目标策略产品价值解读