【11.6力扣打卡】DFS深度优先和BFS广度优先(二叉树)

DFS深度优先搜索

深度优先通俗理解:从树的根节点开始,一直往下遍历,访问其左孩子结点,直到遇到叶子结点,则退回到上一个结点,访问这个节点的另一个孩子(右)结点,这样便可以遍历完整个树。这一个访问的过程,是否觉得很像栈的递归调用?如果是子节点则取上一个结点进行递归。

应用场景:一般题目为待求解找祖先?找路径?

最近刷题遇到的DFS(力扣257)遍历二叉树的所有路径,废话少说,上题目:

在这里插入图片描述
代码如下:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func binaryTreePaths(root *TreeNode) []string {
	var res []string
	dfs(root,"",&res)
	return res

}
func dfs(root *TreeNode,value string,result *[]string){
	if root!=nil{
		value+=strconv.Itoa(root.Val)
		if root.Left==nil&&root.Right==nil{//如果是叶子结点
			*result=append(*result,value)
		}else{//如果不是叶子结点
			value+="->"
			dfs(root.Left,value,result)
			dfs(root.Right,value,result)
		}
	}
}

有几个地方要注意

  1. dfs函数的第三个参数要传递的是[]string的指针,因为dfs是无返回值的函数,必须将形参设为指针类型,否则值传递是没办法改变内容的
  2. dfs函数需要自己设计,但是第一个参数一般都是root.Left或者root.Right,第二个参数可能是深度,或者题目待求解的val等
  3. 关于指针的append,这里需要
*result=append(*result,value)

一定要取* !!!

再来一题(力扣1123)最深叶节点的公共祖先

上题目:
在这里插入图片描述
在这里插入图片描述
上代码:

/**
 * Definition for a binary tree node.
 * type TreeNode struct {
 *     Val int
 *     Left *TreeNode
 *     Right *TreeNode
 * }
 */
func lcaDeepestLeaves(root *TreeNode) *TreeNode {
    res,_:=dfs(root,0)//第一层深度为0
    return res
}
func dfs(root *TreeNode,deepth int)(nextNode *TreeNode,nextDeepth int){//深度优先,找左右子树的关系
    if root==nil{
        return root,deepth
    }

    leftNode,lDeepth:=dfs(root.Left,deepth+1)
    rightNode,rDeepth:=dfs(root.Right,deepth+1)
    if lDeepth==rDeepth{//如果叶子高度都一样
        return root,lDeepth  //返回公共父节点,给后面遍历
    }
    if lDeepth>rDeepth{//左边高,右边没有孩子
        return leftNode,lDeepth
    }
    return rightNode,rDeepth  //最后一种情况,右边高
}

其实我目前对递归也不是很熟悉,因为刷题还是太少了,需要加强。但是有递归的地方你就一定要设置一个出口(如n=1?root==nil?),写好递归的返回值之后,不要去想里面递归的过程,否则你会把自己绕晕,我们只要操作递归的返回值就行,你可以把这些返回值当成已知条件来进行后续的判断、操作等。

BFS广度优先搜索(层次遍历)

广度优先通俗理解:对于一棵树,无论是几叉树,都是从根节点开始,沿着树的宽度遍历树的节点。如果所有结点均被访问,则算法中止。很容易联想到队列吧,因为队列先进先出,那么当我访问了这个节点的时候,我就直接把他的孩子结点也放入队列中,直到队列为空即是终止。

应用场景:按层次遍历N叉树的所有结点

(力扣429)N叉树的层次遍历

上题目:
在这里插入图片描述
上代码:

/**
 * Definition for a Node.
 * type Node struct {
 *     Val int
 *     Children []*Node
 * }
 */

func levelOrder(root *Node) [][]int {	//返回值为2维,则先创一个一维,在append进二维
	result:=make([][]int,0,500)
    if root==nil{
        return result
    }
	q:=[] *Node{root}//用切片充当队列
	for len(q)!=0{//只要队列里面还有东西
		long:=len(q)
		res:=make([]int,0)
		for i:=0;i<long;i++{//遍历队列中的结点
            //fmt.Println(q[i].Val)
			res=append(res,q[i].Val)
			for _,value:=range q[i].Children{//遍历孩子结点,放入队列中
				q=append(q,value)
			}
		}
		result=append(result,res)
		q=q[long:]//往后取
	}
	return result
}

BFS过程讲解:

  1. 先讲一个二维切片的细节,这也是经常被忽略的地方。题目要求返回的是一个二维整型切片,那么我们需要先创建一个二维整型切片[][]int,append二维切片的内容,一定是一个一维整型切片[]int,那么我们又需要设置一个一维来存结果,然后append到二维里面,没毛病!

  2. 我们需要一个队列,那么我们可以直接把Node指针切片直接当成队列,为什么呢?你可以想象把所有每一个结点都是*Node,刚好放入切片,遍历完我直接修改他的下标(改为上一次长度开始)重新赋值不就可以了吗?

  3. 循环条件为队列不为空,即len(q)!=0,不为空时,我们循环遍历队列中的每一个元素,添加到res一维切片,这个就是该层的结果,然后顺便将这个节点的所有孩子结点都添加到队列中,这是我们下一层循环要访问的结点

  4. 之后把一维切片append进二维切片中,就是我们最终要的结果了

  5. 还有一个地方要注意,那就是len(q)不能写在for循环的判断条件中,否则你for循环的时候q长度是一直变的,也就意味着你这个长度一直在变化,用来做for循环的判断条件明显是不对的,因此应该先赋值给一个变量,将其固定,当做for循环的条件即可,我之前也在这里踩了一次坑,以后会留意。

层次遍历结构很清晰,个人感觉比DFS遍历好理解很多,因为层次遍历只需要从队列中取元素,然后再把他的孩子放入队列中就行了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值