LeetCode深度优先算法之树(树转换)

熬夜冠军
• 阅读 1654

以下5个习题是用dfs解决数组or链表和二叉树相互转换的问题,进行统一一起处理可以有效地增强学习,巩固记忆。

105. 从前序与中序遍历序列构造二叉树

题目描述

根据一棵树的前序遍历与中序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

前序遍历 preorder = [3,9,20,15,7]
中序遍历 inorder = [9,3,15,20,7]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路

首先如果想解决此题,首先知道前序和中序遍历的特征。前序遍历的特点是中左右,也就是说数组第一个元素也就是整个树根节点的值,而中序遍历的特点是左中右,也就是说左子树的节点都在根节点的左侧,右子树在根节点的右侧。

根据上面的特征,我们就可以根据前序+中序的遍历序列进行构建二叉树。

  • 在前序遍历序列中找到树的根节点
  • 在中序序列中找到这个根节点
  • 然后递归构建子树

既然我们使用递归构建子树,就需要明确递归的几个条件

  • 递归的结束条件
  • 递归的顺序结构

首先是递归的结束条件,我们是根据树遍历结果来构建树,所以可以根据遍历的数组确定递归条件

if (instart == inEnd || preStart == preEnd) return;

其次是递归的顺序结构,由于我们可以确定根节点的位置,然后才能找出其对应的左子树和右子树,所以这种情况就是先确定节点然后进行递归,类似于先序遍历。

ensure the root node;
recursion left;
recursion right;

另外我们可以使用map将中序序列的遍历结果进行缓存,避免重复遍历,使用空间换时间。

代码实现

class Solution {
    
    private Map<Integer, Integer> map = new HashMap<>();
    
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        
        int index = 0;
        for (int cur : inorder) {
            map.put(cur, index++);
        }

        return build(preorder, inorder, 0, preorder.length, 0, inorder.length);
    }

    TreeNode build(int[] pre, int[] in, int pStart, int pEnd, int inStart, int inEnd) {
        if (pStart == pEnd) return null;
        
        int rootVal = pre[pStart];
        int rootIndex = map.get(rootVal);

        TreeNode root = new TreeNode(rootVal);
        int leftNum = rootIndex - inStart;

        root.left = build(pre, in, pStart + 1, pStart + 1 + leftNum, inStart, rootIndex);
        root.right = build(pre, in, pStart + 1 + leftNum, pEnd, rootIndex + 1, inEnd);

        return root;

    }
}

106.从中序与后序遍历序列构造二叉树

题目描述

根据一棵树的中序遍历与后序遍历构造二叉树。

注意:
你可以假设树中没有重复的元素。

例如,给出

中序遍历 inorder = [9,3,15,20,7]
后序遍历 postorder = [9,15,7,20,3]
返回如下的二叉树:

    3
   / \
  9  20
    /  \
   15   7

思路

这道题和上面的105题基本类似,没有太多区别。后序遍历的根节点是数组的最后一个元素。数组的边界是左开右闭。

代码

class Solution {

    Map<Integer, Integer> map = new HashMap<>();
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        
        int index = 0;
        for (int cur : inorder) {
            map.put(cur, index++);
        }

        return build(inorder, postorder, 0, inorder.length, 0, postorder.length);
    }

    TreeNode build(int[] in, int[] post,int iStart, int iEnd, int pStart, int pEnd) {
        if (iStart == iEnd || pStart == pEnd) return null;

        int rootVal = post[pEnd - 1];
        TreeNode root = new TreeNode(rootVal);

        int rootIndex = map.get(rootVal);
        int leftNum = rootIndex - iStart;

        root.left = build(in, post, iStart, rootIndex, pStart, pStart + leftNum);
        root.right = build(in, post, rootIndex + 1, iEnd, pStart + leftNum, pEnd - 1);

        return root;
    }
}

108. 将有序数组转换为二叉搜索树

题目描述

将一个按照升序排列的有序数组,转换为一棵高度平衡二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定有序数组: [-10,-3,0,5,9],

一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:

      0
     / \
   -3   9
   /   /
 -10  5

思路

首先根据题目描述,这道题提供的是一个升序的数组。我们都知道二叉搜索树的特征是中序遍历是有序的。所以,我们可以把这道题当做是将二叉搜索树中序遍历的结果进行还原。同时,题目给出一个条件就是高度差不超过1,也就是左右是平衡的。这样就可以使用二分搜索做了,mid索引其实就是对应根节点的位置,mid左边的就是根节点的左子树,反之就是右子树,依次递归就可以解决本题。

代码

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if (nums == null || nums.length == 0) return null;

        return build(nums, 0, nums.length - 1);
    }

    TreeNode build(int[] nums, int left, int right) {
        if (left > right) return null;

        int mid = (right - left) / 2 + left;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = build(nums, left, mid - 1);
        root.right = build(nums, mid + 1, right);

        return root;
    }
}

109.有序链表转换二叉搜索树

题目描述

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。

本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

示例:

给定的有序链表: [-10, -3, 0, 5, 9],

一个可能的答案是:[0, -3, 9, -10, null, 5], 它可以表示下面这个高度平衡二叉搜索树:


      0
     / \
   -3   9
   /   /
 -10  5
 

思路

本题和108基本如出一辙,不同的地方在于本题是链表转换为树结构。链表计算中间节点的方式不像数组那么简单,链表是依靠快慢指针实现

slow = node;
fast = node;
while (fast.next != boarder && != fast.next.next != null) {
    fast = fast.next.next;
    slow = slow.next;
}

return slow;

只要知道如何计算链表的中间节点,我们就可以继续使用二分构建树结构了。

代码

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if (head == null) return null;

        return build(head, null);
    }

    TreeNode build(ListNode left, ListNode right) {
        if (left == right) return null;
        ListNode mid = getMid(left, right);
        
        TreeNode root = new TreeNode(mid.val);
        root.left = build(left, mid);
        root.right = build(mid.next, right);

        return root;
    }

    ListNode getMid(ListNode left, ListNode right){
        ListNode slow = left;
        ListNode fast = left;
        
        while (fast.next != right && fast.next.next != right) {
            slow = slow.next;
            fast = fast.next.next;
        }

        return slow;
    }

114.二叉树展开为链表

题目描述

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

    1
   / \
  2   5
 / \   \
3   4   6
将其展开为:

1
 \
  2
   \
    3
     \
      4
       \
        5
         \
          6

思路

展开的顺序仔细观察实际就是前序遍历的顺序,所以我们可以先将链表进行前序遍历,遍历的结果存储在List中。

展开的时候按照顺序进行构造关系,并且将左节点置为null就可以了。

当前节点的父亲节点是上一个节点的右子树

代码

class Solution {
    
    List<TreeNode> list = new ArrayList<>();
    public void flatten(TreeNode root) {
        if (root == null) return ;
        
        pre(root);
        TreeNode pre = null;
        TreeNode cur = null;
        for (int i = 1; i < list.size(); i++) {
            pre = list.get(i - 1);
            cur = list.get(i);

            pre.left = null;
            pre.right = cur;
        }
    }

    void pre(TreeNode root) {
        if (root != null) {
            list.add(root);
            
            pre(root.left);
            pre(root.right);
        }
    }
}
点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
白茶清欢 白茶清欢
4年前
js实现二叉树、二叉查找树
树是一种数据结构,该章节讨论二叉树(二叉树的每个节点的子节点不允许超过两个),二叉树中有又分为完全二叉树和不完全二叉树.....不在本章节赘述相关概念,感兴趣可以去查阅《数据结构》。你将会获得:1.如何使用js实现二叉查找树。2.学会前、中、后序遍历。3.了解相关实现原理阅读时长5min,可选择直接调试代码特点    二叉查找树中序遍历后
九路 九路
4年前
前端学数据结构与算法:二叉树的四种遍历方式及其应用
前言上一章我们从0到1的实现了一颗二叉搜索树,以及理解了二叉搜索树的特性与基本操作,这一章介绍关于二叉树的更多操作,也就是树的遍历,对树的每个节点进行访问。主要包括前序遍历、中序遍历、后序遍历、层序遍历,前面三种也叫深度优先遍历(DFS),最后的层序遍历也叫广度优先遍历(BFS),理解这四种遍历方式的不同,再遇到树相关的算法问题时,也就能更加游刃有余。这
Wesley13 Wesley13
3年前
JAVA递归实现线索化二叉树
JAVA递归实现线索化二叉树基础理论首先,二叉树递归遍历分为先序遍历、中序遍历和后序遍历。先序遍历为:根节点左子树右子树中序遍历为:左子树根节点右子树后序遍历为:左子树右子树根节点(只要记住根节点在哪里就是什么遍历,且都是先左再右)线索化现在有这么一棵二叉树,它的数据结
Wesley13 Wesley13
3年前
Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)
Java实现二叉树的前序、中序、后序、层序遍历(非递归方法)(https://www.oschina.net/action/GoToLink?urlhttp%3A%2F%2Fwww.cnblogs.com%2Fliuyang0%2Fp%2F6271331.html)
Stella981 Stella981
3年前
LeetCode(94):二叉树的中序遍历
Medium!题目描述:给定一个二叉树,返回它的_中序_遍历。示例:输入:1,null,2,31\2/3输出:1,3,2进阶: 递归算法很简单,你可以通过迭代算法完成吗?解题思路:
Wesley13 Wesley13
3年前
04.重建二叉树 (Java)
题目描述输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建二叉树并返回。(https://www.oschina.net/action/GoToLink?urlht
Wesley13 Wesley13
3年前
6.重建二叉树(代码未完成)
 题目:输入某二叉树的前序遍历和中序遍历的结果,请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6},则重建出图2.6所示的二叉树并输出它的头结点。!(https://static.oschina.net/uploads
菜园前端 菜园前端
2年前
什么是二叉树?
原文链接:什么是二叉树?树中每个节点最多只能有两个子节点,在JavaScript中一般都是通过Object来模拟二叉树。常用操作前序遍历中序遍历后序遍历前序遍历根左右。口诀:1.访问根节点2.对根节点的左子树进行前序遍历3.对根节点的右子树进行前序遍历通过
Python进阶者 Python进阶者
1年前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
美凌格栋栋酱 美凌格栋栋酱
5个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(