二叉树 回溯算法

509 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第1天,点击查看活动详情

前言

提到树的回溯算法,就不得不提树的深度优先遍历。说到树的深度优先遍历,就离不开讲树的前、中、后序遍历方法。

我们浅浅回顾一下二叉树的三种遍历方法:

  • 前序遍历:根左右。先打印根节点,再遍历左子树,再遍历右子树
  • 中序遍历:左根右。先遍历左子树,再打印根节点,再遍历右子树
  • 后序遍历:左右根。先遍历左子树,再遍历右子树,再打印根节点
type TreeNode struct{
    Val   int
    Left  *TreeNode
    Right *TreeNode
}

// 前序遍历
func preOrder(root *TreeNode) (res []int) {
	if root == nil {
		return
	}
	res = append(res, root.Val)
	res = append(res, preOrder(root.Left)...)
	res = append(res, preOrder(root.Right)...)
	return res
}
// 中序遍历
func MidOrder(root *TreeNode) (res []int) {
	if root == nil {
		return
	}
	res = append(res, MidOrder(root.Left)...)
	res = append(res, root.Val)
	res = append(res, MidOrder(root.Right)...)
	return res
}
// 后序遍历
func PostOrder(root *TreeNode) (res []int) {
	if root == nil {
		return
	}
	res = append(res, PostOrder(root.Left)...)
	res = append(res, PostOrder(root.Right)...)
	res = append(res, root.Val)
	return res
}

深度优先遍历和回溯法其主要的区别是:回溯法在求解过程中不保留完整的树结构,而深度优先搜索则记下完整的搜索树

正文

初学者可能会纳闷,回溯到底是什么?下面这张图很好的模拟了回溯的实现步骤(图片来自:代码随想录)。从根节点出发,一直向下直到叶子结点,然后回溯到其父节点,继续向下到另一个叶子结点...这是一个递归的过程

image.png

只靠脑补过程不能很好的理解回溯的实现,我们直接在实战中加强理解

image.png

根据题目,显然我们要使用到二叉树的前序遍历以及回溯的思想,话不多说,我们直接上码

var path = []int{}    //用来接收路径上的值

func pathSum(root *TreeNode, target int) [][]int {
	if root == nil {
            return [][]int{}
	}
        temp := [][]int{}   //我们最终要返回满足条件的路径集
	traveral(root, &temp, target)
	return temp
}

func traveral(root *TreeNode, temp *[][]int, target int) {
    path = append(path, root.Val)
    //当递归到叶子结点时,进行判断
    if root.Left == nil && root.Right == nil {
           if target - root.Val == 0 {
               tmp := make([]int, len(path))
               copy(tmp, path)       //这里注意!!!我们不能直接将path加入到temp中,因为后序回溯时还要对path进行修改,会修改掉path原来的值,因此应该使用深拷贝将path的值加入temp
               *temp = append(*temp, tmp)
           }
           return
    }
    if root.Left != nil {
        traveral(root.Left, temp, target-root.Val)
        path = path[:len(path)-1]   //回溯的实现
    }
    if root.Right != nil {
        traveral(root.Right, temp, target-root.Val)
        path = path[:len(path)-1]   //回溯的实现,回溯到上一个节点位置
    }
}