104. 二叉树的最大深度

题目

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

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

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

一棵二叉树要么是空树,要么有一个或两个指针,指针指向一棵树,
用递归实现很简单, 当指针为空,即是递归出口,返回0, 求最大深度就是左右子树选出一个最大值。

代码

func maxDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    return int(math.Max(float64(maxDepth(root.Left)), float64(maxDepth(root.Right)))) + 1
}

110. 平衡二叉树

题目

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

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

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

示例 1:

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

    3
   / \
  9  20
    /  \
   15   7
返回 true 。

示例 2:

给定二叉树 [1,2,2,3,3,null,null,4,4]

       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
返回 false 。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/balanced-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

首先知道二叉平衡树的性质,其是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
分两步走:

  • 首先求左右子树的高度
  • 判断左右子树的差值是否符合要求

代码

// 求二叉树的高度
func Height(node *TreeNode) (h int)  {
    if node == nil {
        return 0
    }

    left := Height(node.Left)
    right := Height(node.Right)

    if left > right {
        return left + 1
    } else {
        return right + 1
    }
}

// 判断是否为二叉平衡树
func isBalanced(root *TreeNode) bool {
    if root == nil {
        return true
    }

    diff := Height(root.Left) - Height(root.Right)

    if diff < -1 || diff > 1 {
        return false
    }

    if isBalanced(root.Left) && isBalanced(root.Right) {
        return true
    }
    return false
}

543. 二叉树的直径

题目

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

示例 :
给定二叉树

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

注意:两结点之间的路径长度是以它们之间边的数目表示

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/diameter-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

  • 二叉树的直径等于从一个结点到另一个结点的最大长度,也就是左子树的最大深度+右子树的最大深度+1。
  • 采用分治法和递归进行求解。

代码

var max int = 0

func diameterOfBinaryTree(root *TreeNode) int {
    max = 0 // 运行样例前需要重置为0
    Depth(root)
    return max
}

// 最长路径
func Depth(node *TreeNode) int {
    if node == nil {
        return 0
    }

    left := Depth(node.Left)
    right := Depth(node.Right)

    if left + right > max {
        max = left + right
    }

    if left > right {
        return left + 1
    } else {
        return right + 1
    }
}

226.翻转二叉树

题目

翻转一棵二叉树。

示例:

输入:

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/invert-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

二叉树的翻转,其实就是递归的将左右子树进行调换。

代码

// 翻转二叉树
func invertTree(root *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }
    
    // go语言特性交换值
    root.Left, root.Right = root.Right, root.Left

    invertTree(root.Left)
    invertTree(root.Right)
    return root
}

617.合并二叉树

题目

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

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

示例 1:

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/merge-two-binary-trees
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

递归合并即可, 当两棵树都为空时即是递归出口,其中一棵树为空则用另一颗树填充,均不为空则将值相加作为新节点。

代码

// 合并二叉树
func mergeTrees(t1 *TreeNode, t2 *TreeNode) *TreeNode {
    if t1 == nil && t2 == nil {
        return nil
    }
    if t1 == nil {
        return t2
    }
    if t2 == nil {
        return t1
    }
    root := new(TreeNode)
    root.Val = t1.Val + t2.Val
    root.Left = mergeTrees(t1.Left, t2.Left) // 合并左子树
    root.Right = mergeTrees(t1.Right, t2.Right) // 合并右子树

    return root
}

112.路径总和

题目

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

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

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

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

使用目标值一步进行递减, 每个结点都有两条路径可走,判断结点没有左右子结点时该结点即为终止结点,只需判断最终值是否跟sum的剩余值相等即可。

代码

// 判断路径和是否等于一个数
func hasPathSum(root *TreeNode, sum int) bool {
    if root == nil {
        return false
    }

    if root.Left == nil && root.Right == nil && root.Val == sum {
        return true
    }
    return hasPathSum(root.Left, sum - root.Val) || hasPathSum(root.Right, sum - root.Val)
}

116. 路径总和III

题目

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

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

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

二叉树不超过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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/path-sum-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

进行双重递归, 根结点向下遍历, 根的左右结点再进行向下遍历。

代码

func pathSum(root *TreeNode, sum int) int {
    if root == nil{
        return 0
    }

    return pathSumRecursion(root, sum, 0) + pathSum(root.Left, sum) + pathSum(root.Right, sum)
}

func pathSumRecursion(root *TreeNode, sum int, count int)int{
    if root == nil {
        return count
    }

    sum -= root.Val
    if sum == 0{
        count++
    }
    count = pathSumRecursion(root.Left, sum, count)
    count = pathSumRecursion(root.Right, sum, count)

    return count
}

572.另一个树的子树

题目

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

示例 1:
给定的树 s:

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

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

示例 2:
给定的树 s:

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

   4
  / \
 1   2
返回 false。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/subtree-of-another-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

检测s中是否包含子树t,s自身拆分成多个树,与树t进行比较是否相等。

代码

// 判断是否有子树
func isSubtree(s *TreeNode, t *TreeNode) bool {
    if s == nil {
        return false
    }
    return isSame(s, t) || isSubtree(s.Left, t) || isSubtree(s.Right, t)
}


// 判断两树是否相同
func isSame(s *TreeNode, t *TreeNode) bool {
    if s == nil && t == nil {  
        return true
    }

    if s == nil || t == nil {
        return false
    }
    
    return s.Val == t.Val && isSame(s.Left, t.Left) && isSame(s.Right, t.Right)
}

101.对称二叉树

题目

给定一个二叉树,检查它是否是镜像对称的。

例如,二叉树 [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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/symmetric-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

判断是否为对称二叉树, 跟判断两个树是否相同差不多, 只不过是将二叉树1的左子树跟二叉树2的右子树进行比较。

代码

// 判断是否为镜像树
func isSymmetric(root *TreeNode) bool {
    if root == nil {
        return true
    }
    return isSmt(root.Left, root.Right)
}


// 判断是否为镜像树
func isSmt(t1 *TreeNode, t2 *TreeNode) bool {
    if t1 == nil && t2 == nil {
        return true
    }
    if t1 == nil ||  t2 == nil  {
        return false
    }

    // t1的左子树跟t2的右子树进行对比
    return  (t1.Val == t2.Val) && isSmt(t1.Left, t2.Right) && isSmt(t1.Right, t2.Left)
}

111. 二叉树的最小深度

题目

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

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

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

示例:

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

    3
   / \
  9  20
    /  \
   15   7
返回它的最小深度  2.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/minimum-depth-of-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

思想跟求二叉树的最大深度类似, 区别在于数值的比较。

代码

func minDepth(root *TreeNode) int {
    if root == nil {
        return 0
    }
    left := minDepth(root.Left)
    right := minDepth(root.Right)

    if left == 0 || right == 0 {
        return left + right + 1
    }
    if left < right {
        return left + 1
    }else {
        return right + 1
    }
}

404.左叶子之和

题目

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

示例:

    3
   / \
  9  20
    /  \
   15   7

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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/sum-of-left-leaves
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

递归遍历结点,只添加左叶子结点的值。

代码

func sumOfLeftLeaves(root *TreeNode) int {
    if root == nil {
        return 0
    }
    if isLeaf(root.Left) { // 剪枝
        return root.Left.Val + sumOfLeftLeaves(root.Right);
    }
    return sumOfLeftLeaves(root.Left) + sumOfLeftLeaves(root.Right)
}


// 判断结点是否有子节点, 用于剪枝
func isLeaf(node *TreeNode) bool {
    if node == nil {
        return false
    }
    return node.Left == nil && node.Right == nil
}

337.打家劫舍III

题目

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

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

示例 1:

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

     3
    / \
   2   3
    \   \ 
     3   1

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

输入: [3,4,5,1,3,null,1]

     3
    / \
   4   5
  / \   \ 
 1   3   1

输出: 9
解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9.

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/house-robber-iii
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

其实就是间隔遍历节点, 可分为两种情况递归求解:

  • 打劫根节点,那么就不能打劫左右子树了,要打劫子孙辈的所有节点。
  • 不打劫根节点, 那么打劫左右子树。
  • 判断两种情况的哪个价值高。

代码

// 间隔遍历
func rob(root *TreeNode) int {
    if root == nil {
        return 0
    }
    val1 := root.Val
    
    // 打劫子孙辈节点
    if root.Left != nil {
        val1 += rob(root.Left.Left) + rob(root.Left.Right)
    }
    if root.Right != nil {
        val1 += rob(root.Right.Left) + rob(root.Right.Right)
    }
    
    // 打劫左右子树
    val2 := rob(root.Left) + rob(root.Right)

    if val1 > val2 {
         return val1
    }
    return val2
}

671.二叉树中第二小节点

题目

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

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

示例 1:

输入: 
    2
   / \
  2   5
     / \
    5   7

输出: 5
说明: 最小的值是 2 ,第二小的值是 5 。
示例 2:

输入: 
    2
   / \
  2   2

输出: -1
说明: 最小的值是 2, 但是不存在第二小的值。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/second-minimum-node-in-a-binary-tree
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解

  • 由题可知,二叉树的子节点大于或等于父节点, 如果相等的情况下,则需要继续往子节点查找,
    否则进行左右节点的比较,返回较小的一个,如果其中一个节点为空,那么另一个节点即为要找的值。

代码

func findSecondMinimumValue(root *TreeNode) int {
    if root == nil { //根节点为空,找不到
        return -1
    }
    // 左右子树为空找不到
    if root.Left == nil && root.Right == nil {
        return -1
    }
    left := root.Left.Val
    right := root.Right.Val
    if left == root.Val { // 左子树与根节点值相同继续往下找
        left = findSecondMinimumValue(root.Left)
    }
    if right == root.Val { // 右子树与根节点值相同继续往下找
        right = findSecondMinimumValue(root.Right)
    }
    // 左右子树进行比较,返回值小的
    if left != -1 && right != -1 {
        return min(left, right)
    }
    // 左子树不为空,则返回左子树,否则为右子树
    if left != -1 {
        return left
    }
    return right
}

func min(a, b int) int {
    if a > b {
        return b
    }
    return a
}