代码随想录算法训练营|day18

513.找树左下角的值

(1)递归:复用求最大深度
先递归遍历左子树,后右子树,所以当取到最大深度时,返回对应的节点值

func findBottomLeftValue(root *TreeNode) int {
    if root == nil {
        return 0
    }
    height := 0
    leftVal := 0
    var getDepth func(root *TreeNode, depth int)
    getDepth = func(root *TreeNode, depth int) {
        if root == nil {
            return
        }
        depth++
        getDepth(root.Left, depth)
        getDepth(root.Right, depth)
        if depth > height {
            height = depth  
            leftVal = root.Val
        }
    }
    getDepth(root, 0)
    return  leftVal
}

(2)迭代:最后一层第一个值

func findBottomLeftValue(root *TreeNode) int {
    if root == nil {
        return 0
    }
    queue := []*TreeNode{root}
    res := 0
    for len(queue) > 0 {
        size := len(queue)
        for i:= 0; i < size; i++ {
            node := queue[0]
            if i == 0 {
                res = node.Val
            }
            queue = queue[1:]
            if node.Left != nil {
                queue = append(queue, node.Left)
            }
            if node.Right != nil {
                queue = append(queue,node.Right)
            }
        }
    }
    return res
}

112.路径总和

求二叉树所有路径的变体
(1)递归:递归判断当前节点叶子节点之和是否为targetSum

func hasPathSum(root *TreeNode, targetSum int) bool {
    if root == nil {
        return false
    }

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

(2)层序遍历nodeQueue存储了根节点到包括当前节点的所有节点,sumQueue记录了根节点到该节点之和

func hasPathSum(root *TreeNode, targetSum int) bool {
    if root == nil {
        return false
    }
    nodeQueue := []*TreeNode{root}
    sumQueue := []int{root.Val}
    for i := 0; i < len(nodeQueue); i++ {
        node, sum := nodeQueue[i], sumQueue[i]
        if node.Left == nil && node.Right == nil {
            if sum == targetSum  {
                return true
            }
            continue
        }
        if node.Left != nil {
            nodeQueue = append(nodeQueue, node.Left)
            sumQueue = append(sumQueue, sum + node.Left.Val)
        }
        if node.Right != nil {
            nodeQueue = append(nodeQueue, node.Right)
            sumQueue = append(sumQueue, sum + node.Right.Val)
        }
    }
    return false
}

113.路径总和ii

(1)递归初始化记录路径;终止条件;递归左右子树

func pathSum(root *TreeNode, targetSum int) [][]int {
    res := make([][]int, 0)
    path := []int{}
    var help func(root *TreeNode, targetSum int) 
    help = func(root *TreeNode, targetSum int) {
        if root == nil {
            return
        }
        path = append(path, root.Val)
        targetSum -= root.Val
        if root.Left == nil && root.Right == nil && targetSum == 0{
            res = append(res, path)
        }
        help(root.Left, targetSum)
        help(root.Right, targetSum)
    }
    help(root, targetSum)
    return res
}

(2)层序遍历:
遍历时对path变量使用append方法,会修改底层数组,进而会影响到pathQueue中的其它路径。需要为每个路径创建一个新的切片副本

type group struct {
	Node *TreeNode
	Val  []int
}

func pathSum(root *TreeNode, targetSum int) [][]int {
	var res [][]int
	if root == nil {
		return res
	}

	queue := []*group{{root, []int{root.Val}}}
	for len(queue) > 0 {
		node := queue[0].Node
		valArr := queue[0].Val
		queue = queue[1:]
		sum := 0
		for _, val := range valArr {
			sum += val
		}
		if node.Left == nil && node.Right == nil && sum == targetSum {
			res = append(res, valArr)
		}

		valArrCp := make([]int, len(valArr))
		copy(valArrCp, valArr)

		if node.Left != nil {
			valArr1 := valArrCp
			valArr1 = append(valArr1, node.Left.Val)
			queue = append(queue, &group{node.Left, valArr1})
		}
		if node.Right != nil {
			valArr2 := valArrCp
			valArr2 = append(valArr2, node.Right.Val)
			queue = append(queue, &group{node.Right, valArr2})
		}
	}
	return res
}

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

(1)递归:找到后序数组最后一个元素中序数组中的位置。递归右子树和左子树

func buildTree(inorder []int, postorder []int) *TreeNode {
    var build func(inorderLeft, inorderRight int) *TreeNode 
    build = func(inorderLeft, inorderRight int) *TreeNode {
        if inorderLeft > inorderRight {
            return nil
        }
        rootVal := postorder[len(postorder) - 1]
        postorder = postorder[:len(postorder) - 1]
        root := &TreeNode{Val:rootVal}
        i := 0
        for i = 0; i < len(inorder); i++ {
            if inorder[i] == rootVal {
                break
            }
        }
        root.Right = build(i + 1, inorderRight)
        root.Left = build(inorderLeft, i - 1)
        return root
    }
    return build(0, len(inorder) - 1)
}

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

(1)递归:找到前序数组第一个元素中序数组中的位置。递归左子树和右子树

func buildTree(preorder []int, inorder []int) *TreeNode {
    var build func(inorderLeft, inorderRight int) *TreeNode 
    build = func(inorderLeft, inorderRight int) *TreeNode{
        if inorderLeft > inorderRight {
            return nil
        }
        rootVal := preorder[0]
        root := &TreeNode{Val : rootVal}
        preorder = preorder[1:]

        i := 0
        for i = 0; i < len(inorder); i++ {
            if inorder[i] == rootVal {
                break
            }
        }
        root.Left = build(inorderLeft, i - 1)
        root.Right = build(i + 1, inorderRight)
        return root
    }
    return build(0, len(inorder) - 1)
}

代码随想录文章详解

513.找树左下角的值
112.路径总和 | 113.路径总和ii
106.从中序与后序遍历序列构造二叉树 | 105.从前序与中序遍历序列构造二叉树

总结

递归求解通常需要公共变量提取,创建无返回值函数,或者返回值为bool
注意切片扩容,导致结果出错,切片深拷贝、浅拷贝
感谢代码随想录练习营大佬们的帮助和解答