114. 二叉树展开为链表

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> 114. 二叉树展开为链表

本题来自 LeetCode:114. 二叉树展开为链表[1]

题目描述

给定一个二叉树,原地将它展开为链表。
例如,给定二叉树


    1
   / 
  2   5
 /    
3   4   6

将其展开为:


1
 
  2
   
    3
     
      4
       
        5
         
          6

递归版

题目分析

本题是二叉树的 前序遍历的变种,主要处理逻辑为:

  • 将当前节点的右子树暂存,然后当前节点的左子树链接到右子树(`链表`)上;
  • 继续处理左子树,重复`步骤1`;
  • 将右子树链接到`链表`上,继续处理右左子树,重复`步骤1`;
  • 示例分析

  • 当前处理节点`current`为`1`,先将`1`的右子树`5`暂存,将左子树`2`移到`1`的右子树上。当前链表为`1 - 2`,链表尾节点为`2`;下一步处理节点`2`;
  • 
    1
                    
      2               5
     /                
    3   4               6
    
  • 当前处理节点`current`为`2`,先将`2`的右子树`4`暂存,将左子树`3`移到`2`的右子树上。当前链表为`1 - 2 - 3`,链表尾节点为`3`, 下一步处理节点`3`;
  • 
    1
                    
      2               5
                     
        3       4       6
    
  • 当前处理节点`current`为`3`,先将`3`的右子树`4`暂存,由于`3`无左子树,故直接将右子树`4`链接到链表上。当前链表为`1 - 2 - 3 - 4`,链表尾节点为`4`;下一步处理节点`4`;
  • 
    1
                    
      2               5
                      
        3               6
         
          4
    
  • 当前处理节点`current`为`4`,由于`4`无左右子树,故返回处理`步骤1`的右子树。将`5`链接到链表上,当前链表为`1 - 2 - 3 - 4 - 5`,链表尾节点为`5`;下一步处理节点`5`;
  • 
    1
     
      2
       
        3
         
          4
           
            5
             
              6
    
  • 当前处理节点`current`为`5`,先将`5`的右子树`6`暂存,由于`5`无左子树,故直接将右子树`6`链接到链表上。当前链表为`1 - 2 - 3 - 4 - 5 - 6`,链表尾节点为`6`;下一步处理节点`6`;
  • 
    1
     
      2
       
        3
         
          4
           
            5
             
              6
    

    至此,整个过程就完成了。

    题目解答

    
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public void flatten(TreeNode root) {
            preOrder(root);
        }
    
        /**
         * @param  current 当前处理的节点
         * @return 返回链表的尾节点
         */
        public TreeNode preOrder(TreeNode current) {
            // root = null的情况
            if(current == null) {
                return current;
            }
            TreeNode right = current.right;
            TreeNode left = current.left;
    
            if(left != null) {
                //注意需要将左子树置null
                current.left = null;
                // 链表链接左子树
                current.right = left;
                // 获得链接的尾节点
                current = preOrder(left);
            }
    
            if(right != null) {
                    // 链表链接右子树
                current.right = right;
                // 获得链接的尾节点
                current = preOrder(right);
            }
            // 返回链接的尾节点
            return current;
        }
    }
    

    复杂度分析:
    时间复杂度: O(n)
    空间复杂度: O(n)

    迭代版

    题目分析

    迭代的逻辑是将当前节点的右子树,链接到当前节点的左子树的最右节点的右子树上。如果不好理解这句话,直接看下面的示例过程。

  • 将当前节点的右子树暂存,然后当前节点的左子树链接到右子树(`链表`)上;
  • 继续处理左子树,重复`步骤1`;
  • 将右子树链接到`链表`上,继续处理右左子树,重复`步骤1`;
  • 示例分析

  • 当前处理节点`current`为`1`,先将`1`的右子树`5`移到左子树`2`的最右节点的右子树上,
  • 
        1
       /
      2
     / 
    3   4
         
          5
           
            6
    
  • 将左子树`2`移到`1`的右子树上。当前链表为`1 - 2`,链表尾节点为`2`;下一步处理节点`2`;
  • 
    1
     
      2
     / 
    3   4
         
          5
           
            6
    
  • 当前处理节点`current`为`2`,先将`2`的右子树`4`移到左子树`3`的最右节点的右子树上,
  • 
     1
      
       2
      /
     3
      
       4
        
         5
          
           6
    
  • 将左子树`3`移到`2`的右子树上。当前链表为`1 - 2 - 3`,链表尾节点为`3`, 下一步处理节点`3`;
  • 
    1
     
      2
       
        3
         
          4
           
            5
             
              6
    
  • 当前处理节点`current`为`3`,由于`3`无左子树,故直接处理右子树`4`,同理处理`5`、`6`;
  • 
    1
     
      2
       
        3
         
          4
           
            5
             
              6
    
  • 当前处理节点`current`为`4`,由于`4`无左右子树,故返回处理`步骤1`的右子树。将`5`链接到链表上,当前链表为`1 - 2 - 3 - 4 - 5`,链表尾节点为`5`;下一步处理节点`5`;
  • 
    1
     
      2
       
        3
         
          4
           
            5
             
              6
    

    至此,整个过程就完成了。

    题目解答

    
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public void flatten(TreeNode root) {
            TreeNode node = null;
    
            while(root != null) {
                // 左子树为null,下一个迭代直接处理右子树
                if(root.left == null) {
                    root = root.right;
                    continue;
                }
                // 左右子树都不为null
                if(root.right != null) {
                    // 找到左子树的最右节点
                    node = root.left;
                    while(node.right != null) {
                        node = node.right;
                    }
                    // 将右子树链接到最右节点的右子树上
                    node.right = root.right;
                }
                // 将子子树链接到右子树上
                root.right = root.left;
                // 将左子树置null
                root.left = null;
                // 下一个迭代处理右子树
                root = root.right;
            }
        }
    }
    

    复杂度分析:
    时间复杂度: O(n)
    空间复杂度: O(1)

    其他递归版

    这是我第一次写的解法,贴上来,记录下。

    
    /**
     * Definition for a binary tree node.
     * public class TreeNode {
     *     int val;
     *     TreeNode left;
     *     TreeNode right;
     *     TreeNode(int x) { val = x; }
     * }
     */
    class Solution {
        public void flatten(TreeNode root) {
            flatten(root, null);
        }
    
        public TreeNode flatten(TreeNode current, TreeNode preNode) {
            if(current == null) {
                return preNode;
            }
            if(preNode == null) {
                preNode = current;
            } else {
                preNode.right = current;
                preNode = preNode.right;
            }
    
            TreeNode left = current.left;
            TreeNode right = current.right;
    
            current.left = null;
            current.right = null;
    
            if(left != null) {
                preNode = flatten(left, preNode);
            }
            if(right != null) {
               preNode = flatten(right, preNode);
            }
            return preNode;
        }
    }
    

    复杂度分析:
    时间复杂度: O(n)
    空间复杂度: O(n)

    参考资料

    1. 二叉树展开为链表: https://leetcode-cn.com/problems/flatten-binary-tree-to-linked-list

    原文始发于微信公众号(xiaogan的技术博客):

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> 114. 二叉树展开为链表


     上一篇
    二叉树遍历专题 二叉树遍历专题
    今天来全面学习下二叉树的几种遍历方式,包括 前序遍历、 中序遍历、 后序遍历、 层次遍历。定义以下数据结构为二叉树的节点。 public class TreeNode { int val; TreeNode left;
    2021-04-05
    下一篇 
    78&90. 子集&子集 II(回溯法) 78&90. 子集&子集 II(回溯法)
    本题来自 LeetCode:78. 子集[1] 78. 子集题目描述给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。说明:解集不能包含重复的子集。示例: 输入: nums = [1,2,3] 输出: [ [3
    2021-04-05