码迷,mamicode.com
首页 > 其他好文 > 详细

[leetcode刷题]—— 树(递归)

时间:2021-01-15 12:07:22      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:代码   最大   dia   函数返回   递归   情况   时间   简便   alt   

此篇博客主要记录使用递归求解的树相关的算法题。

 

一、二叉树的最大深度

104. 给定一个二叉树,找出其最大深度。 (easy) 2021-01-13

二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

说明: 叶子节点是指没有子节点的节点。

示例:
给定二叉树 [3,9,20,null,null,15,7],

         3
       / \
    9  20
   /      \
15      7
返回它的最大深度 3 。

  常规递归题目。

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        if(leftDepth > rightDepth){
            return leftDepth + 1;
        }else{
            return rightDepth + 1;
        }
    }
}

 

 二、平衡树

110.平衡二叉树   (easy) 2021-01-14

给定一个二叉树,判断它是否是高度平衡的二叉树。

本题中,一棵高度平衡二叉树定义为:

一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

   题解:这道easy题目难度大于很多medium。最大的难点在于返回值的选择。使用递归时,如果返回左右子树是否为平衡二叉树,但是左右都是平衡二叉树不能说明就是平衡二叉树,还要左右子树的高度差小于1。如果递归返回高度信息也不合适,左右子树的高度差小于也证明不了平衡二叉树。

  照着这个思路,可以编写一个返回结点的类,包含此结点的高度信息和是否平衡信息,递归函数返回此结点。

  分析到这儿,就能想出更简便的方法了。设置一个全局变量,布尔型。递归函数的返回值使用树的高度,只要左右子树的高度信息相差大于1,则变量成true。

     

class Solution {
    boolean flag = true;
    public boolean isBalanced(TreeNode root) {
        depth(root);
        return flag;
    }
    public int depth(TreeNode root){
        if(root == null){
            return 0;
        }
        int l = depth(root.left);
        int r = depth(root.right);
        if(Math.abs(l - r) > 1){   //多一个判断标志
            flag = false;
        }
        return Math.max(l , r) + 1;
    }
}

 

三、二叉树的直径

543.二叉树的直径   (easy)  2021-01-14

给定一棵二叉树,你需要计算它的直径长度。一棵二叉树的直径长度是任意两个结点路径长度中的最大值。这条路径可能穿过也可能不穿过根结点。

示例 :
给定二叉树

         1
        / \
      2    3
    /        \
  4          5
返回 3, 它的长度是路径 [4,2,1,3] 或者 [5,2,1,3]。

   题解:这一题与上一题及其相似。使用递归返回树的高度信息,定义一个全局变量记录当前结点的直径。每个结点的直径就是左子树长度加右子树长度,当然直径一直取较大值。

  

class Solution {
    int diameter;
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return diameter;
    }
    public int depth(TreeNode root){
        if(root == null){
            return 0;
        }
        int l = depth(root.left);
        int r = depth(root.right);
        int nowDiameter = l + r;
        diameter = (diameter > nowDiameter) ? diameter : nowDiameter;
        return Math.max(l, r) + 1;

    }
}

 

 

四、翻转树

226.翻转二叉树  (easy)2021-01-14

翻转一棵二叉树。

示例:

输入:

             4
            / \
        2     7
       / \    / \
     1 3    6 9
输出:

             4
          /     \
      7        2
    / \        /  \
 9   6      3   1

  常规递归求解的题目。

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root == null){
            return null;
        }
        TreeNode tempNode = invertTree(root.left);
        root.left = invertTree(root.right);
        root.right = tempNode;
        return root;
    }
}

 

五、合并二叉树

617.合并二叉树   (easy) 2021-01-14

给定两个二叉树,想象当你将它们中的一个覆盖到另一个上时,两个二叉树的一些节点便会重叠。

你需要将他们合并为一个新的二叉树。合并的规则是如果两个节点重叠,那么将他们的值相加作为节点合并后的新值,否则不为 NULL 的节点将直接作为新二叉树的节点。

示例 1:

输入:
Tree 1 Tree 2
       1                                 2
      / \                               / \
    3  2                           1    3
   / \                                       \
 5  4                                       7
输出:
合并后的树:
         3
        / \
    4       5
  /   \        \
5    4       7
注意: 合并必须从两个树的根节点开始

  常规递归的思路,就是内存占用较大,击败6%,诶。

  

class Solution {
    public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
        if(t1 == null) return t2;
        if(t2 == null) return t1;
     
        t1.left = mergeTrees(t1.left, t2.left);
        t1.right = mergeTrees(t1.right, t2.right);

        t1.val = t1.val + t2.val;   //以t1为主
        return t1;
    }
}

 

六、判断路径和是否等于一个数

112.路径总和   (easy)  2021-01-14

给定一个二叉树和一个目标和,判断该树中是否存在根节点到叶子节点的路径,这条路径上所有节点值相加等于目标和。

说明: 叶子节点是指没有子节点的节点。

示例: 
给定如下二叉树,以及目标和 sum = 22,

              5
            /   \
         4      8
      /          / \
   11       13   4
   / \                \
7   2               1
返回 true, 因为存在目标和为 22 的根节点到叶子节点的路径 5->4->11->2。

  这个测试用例绝了。

  技术图片

 

 

   

class Solution {
    public boolean hasPathSum(TreeNode root, int sum) {
        if(root == null)  return false;
        if(root.left == null && root.right == null && root.val == sum){
            return true; 
        }
        boolean l = hasPathSum(root.left, sum - root.val);
        boolean r = hasPathSum(root.right, sum - root.val);
        return l || r ;
    }
}

 

七、统计路径和等于一个数的路径数量

437.路径总和   (medium) 2021-01-14

给定一个二叉树,它的每个结点都存放着一个整数值。

找出路径和等于给定数值的路径总数。

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。

二叉树不超过1000个节点,且节点数值范围是 [-1000000,1000000] 的整数。

示例:

root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8

                 10
              /         \
             5         -3
           /  \           \
        3     2        11
       /  \      \
     3   -2   1

返回 3。和等于 8 的路径有:

1. 5 -> 3
2. 5 -> 2 -> 1
3. -3 -> 11

  题解:如果不是刚刚做了上面那个路径总和的题,这个题恐怕连思路都没。

    花了一个小时,结果时间击败10%, 空间击败12%,吐了吐了。

  class Solution {

int count = 0;
    public int pathSum(TreeNode root, int sum) {
        if(root == null) return 0;
        preOrder(root, sum);
        return count;
    }
    //前序遍历
    public void preOrder(TreeNode root, int sum){
        subtatal(root, sum);
        if(root.left != null){
            preOrder(root.left, sum);
        }
        if(root.right != null){
            preOrder(root.right, sum);
        }
    }
    //写一个函数判断以此结点为根结点时,是否有数值和满足的情况
    public int subtatal(TreeNode root, int sum){
        if(root == null){
            return count;
        }
        if(root != null && root.val == sum){
            count ++;
            //如果返回则忽略了后面正负数和为0的情况。
            //但是没有返回值,这个递归函数一直是false,虽然不需要用它的返回值
            //return true;                    
        }
        int l = subtatal(root.left, sum - root.val);
        int r = subtatal(root.right, sum - root.val);
        return count;
    }
}

 

八、子树

572.另一颗树的子树  (easy)     2021-01-14

给定两个非空二叉树 s 和 t,检验 s 中是否包含和 t 具有相同结构和节点值的子树。s 的一个子树包括 s 的一个节点和这个节点的所有子孙。s 也可以看做它自身的一棵子树。

示例 1:
给定的树 s:

       3
     /  \
   4   5
  /       \
1         2
给定的树 t:

     4 
   /    \
 1      2
返回 true,因为 t 与 s 的一个子树拥有相同的结构和节点值。

   这个easy题真心不easy。

class Solution {
    public boolean isSubtree(TreeNode s, TreeNode t) {        
        preOrder(s, t);
        return isSubtreeFlag;
    }

    //前序遍历第一颗树
    boolean isSubtreeFlag = false;
    public void preOrder(TreeNode n1, TreeNode n2){
        if(isEqual(n1, n2)){
            isSubtreeFlag = true;
        }
        if(n1.left != null){
            preOrder(n1.left, n2);     
        }
        if(n1.right != null){
            preOrder(n1.right, n2); 
        }
    }
    //判断两个树是否完全相等
    public boolean isEqual(TreeNode n1, TreeNode n2){
        if(n1 == null && n2 == null){
            return true;
        }else if(n1 ==null || n2 ==null){
            return false;
        }else if(n1.val != n2.val){
            return false;
        }
        boolean l = isEqual(n1.left, n2.left);
        boolean r = isEqual(n1.right, n2.right);
        return l && r;
    }
}

 九、对称二叉树

101、给定一个二叉树,检查它是否是镜像对称的。 (easy) 2021-01-14

例如,二叉树 [1,2,2,3,4,4,3] 是对称的。

          1
      /       \
     2        2
    /  \      /  \
  3   4     4  3
 

但是下面这个 [1,2,2,null,3,null,3] 则不是镜像对称的:

      1
     /  \
   2     2
     \      \
       3     3
 

进阶:

你可以运用递归和迭代两种方法解决这个问题吗?

   使用递归方式。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root == null){
            return true;
        }else{
            return leftEqualRight(root.left, root.right);
        }   
    }
    public boolean leftEqualRight(TreeNode node1, TreeNode node2){
        if(node1 == null && node2 == null){
            return true;
        }else if(node1 == null || node2 == null){
            return false;
        }else if(node1.val != node2.val ){
            return false;
        }
        boolean l = leftEqualRight(node1.left, node2.right);
        boolean r = leftEqualRight(node1.right, node2.left);
        return l && r;
    }
}

 

十、二叉树的最小深度

111、二叉树的最小深度  (easy)2021-01-14

给定一个二叉树,找出其最小深度。

最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

说明:叶子节点是指没有子节点的节点。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:2

  常规思路,与最大深度不同的是,不能直接取左右两子树中小的那个,要判断空的存在。

class Solution {
    public int minDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        int l = minDepth(root.left);
        int r = minDepth(root.right);
        if(l == 0){
            return r + 1;
        }else if(r == 0){
            return l + 1;
        }else{
            return Math.min(l, r) + 1;
        }
    }
}

 

十一、统计左叶子节点的和

404.左字节之和  (easy) 2021-01-14

计算给定二叉树的所有左叶子之和。

示例:

          3
        /  \
      9  20
     /       \
   15       7

在这个二叉树中,有两个左叶子,分别是 9 和 15,所以返回 24。

  常规解法。

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if(root == null){
            return 0;
        }else{
        preOrder(root);
        return sum;
        }
    }

    int sum = 0;
    public void preOrder(TreeNode root){
        if(root.left != null && root.left.left == null && root.left.right == null){
            sum = sum + root.left.val;
        }
        if(root.left != null){
            preOrder(root.left);
        }
        if(root.right != null){
            preOrder(root.right);
        }
    }
}

 

十二、相同结点值的最大路径长度

687、最长同值路径 (medium) 2021-01-14

给定一个二叉树,找到最长的路径,这个路径中的每个节点具有相同值。 这条路径可以经过也可以不经过根节点。

注意:两个节点之间的路径长度由它们之间的边数表示。

示例 1:

输入:

       5
    /     \
   4       5
  / \         \
1  1         5
输出:

2


我的思路,先写个函数返回当某个结点为子树根结点时最长同值路径,然后遍历整棵树,取最大值。

遇到的问题是,使用递归的方法最好函数返回一侧的同值最长路径。那么在遍历的时候就需要判断是否左右子树的最长路径是否是取大值还是相加。

这个easy题目可太不简单了,此题大约花了我快两小时了,最后时间超过9%,内存超过7%,诶。

class Solution {
    public int longestUnivaluePath(TreeNode root) {
        if(root == null){
            return 0;
        }else{
        preOrder(root);
        return max;
        }
    }
   
    int max = 0 ;  //累计最长同值路径
    int temp = 0;  //当前最长
    public void preOrder(TreeNode root){  
        if(root.left == null && root.right == null){
            temp = 0;
        }else if(root.left == null || root.right == null){
            temp = longest(root);
        }else if(root.val != root.left.val && root.val == root.right.val){
            temp = 1 + longest(root.right);
        }else if(root.val == root.left.val && root.val != root.right.val){
            temp = 1 + longest(root.left);
        }else if(root.val == root.left.val && root.val == root.right.val){
            temp = longest(root.left) + 2 + longest(root.right);
        }
        max = (max > temp) ? max : temp;
        if(root.left != null){
            preOrder(root.left);
        }
        if(root.right != null){
            preOrder(root.right);
        }
    }
    

    //当某个结点为子树根结点时,返回最长单侧同值路径
    public int longest(TreeNode root){
        int l = 0; int r = 0;
        if(root.left != null && root.val == root.left.val){
            l = 1 + longest(root.left);
        }
        if(root.right != null && root.val == root.right.val){
            r = 1 + longest(root.right);
        }
        return Math.max(l, r);
    }
}

 

  大佬的代码如此简洁。一次递归返回单侧路径的最大值,和我的一样。不同的是我想着再次遍历,然后去分类左右两边是否合并,而更方便的做法如下。直接定义一个全局变量,在一次递归的时候就可以

判断并记录下最长的同值路径。

    下次看到此题时再重新提交试试,该进算法。

class Solution {
    private int path = 0;

public int longestUnivaluePath(TreeNode root) {
    dfs(root);
    return path;
}

private int dfs(TreeNode root){
    if (root == null) return 0;
    int left = dfs(root.left);
    int right = dfs(root.right);
    int leftPath = root.left != null && root.left.val == root.val ? left + 1 : 0;
    int rightPath = root.right != null && root.right.val == root.val ? right + 1 : 0;
    path = Math.max(path, leftPath + rightPath);
    return Math.max(leftPath, rightPath);
}
}

 

十三、间隔遍历

337、打家劫舍  (medium)2021-01-14

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。

计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

示例 1:

输入: [3,2,3,null,3,null,1]

     3
 /      \
2       3
 \         \
 3         1

输出: 7
解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7.

    这个题我开始完全没有思路。需要注意的是,大多数没有思路的树相关的题目是可以使用递归解决的。

  此题解法:一棵树的间隔遍历只有两种情况,一个是计算此结点的值,加上左子结点的左右子树和右边结点的左右子树的返回值;另一种情况是,不计算此结点,直接计算左右子节点的返回值。

  

class Solution {
    public int rob(TreeNode root) {
    if (root == null) return 0;
    int val1 = root.val;
    if (root.left != null) val1 += rob(root.left.left) + rob(root.left.right);
    if (root.right != null) val1 += rob(root.right.left) + rob(root.right.right);
    int val2 = rob(root.left) + rob(root.right);
    return Math.max(val1, val2);
    }
}

 

十四、二叉树中最小的结点

671、二叉树中最小的结点   (easy)  2021-01-14

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个。

更正式地说,root.val = min(root.left.val, root.right.val) 总成立。

给出这样的一个二叉树,你需要输出所有节点中的第二小的值。如果第二小的值不存在的话,输出 -1 。

示例 1:

输入:root = [2,2,5,null,null,5,7]
输出:5
解释:最小的值是 2 ,第二小的值是 5 。

  常规思路。

class Solution {
    int min = -1; int secondMin = -1;
    public int findSecondMinimumValue(TreeNode root) {
        preOrder(root);
        return secondMin;
    }
    public void preOrder(TreeNode root){
        if(min == -1 || root.val < min){
            secondMin = min;
            min = root.val;
        }else if(min < root.val && (secondMin == -1 ||root.val < secondMin)){
            secondMin = root.val;
        }
        if(root.left != null){
            preOrder(root.left);
        }
        if(root.right != null){
            preOrder(root.right);
        }
    }
}

 

一天刷了13道递归题,头发要掉光了,睡了睡了。

[leetcode刷题]—— 树(递归)

标签:代码   最大   dia   函数返回   递归   情况   时间   简便   alt   

原文地址:https://www.cnblogs.com/nobita/p/14276387.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!