深度搜索与广度搜索

深度搜索

适用场景

输入数据:如果是递归数据结构,如单链表,二叉树,集合,则百分之百可以使用深度搜索;如果是非递归数据结构

状态转换图:树或者图

求解目标:必须要走到最深,比如二叉树中必须走到叶子结点,那么此方式适合深度搜索

思考的步骤

  1. 是求路径条数,还是路径本身(动作序列);常见要求,求满足条件的路径个数,求一个可行解,求所有可行解。
  1. 如果是路径条数,则不需要存储路径。
  2. 如果是求路径本身,则需要一个数组path[]存储路径。跟宽度搜索不同,宽搜虽然最终求的也是一条路径,但是需要存储扩展过程中的所有路径,在没有找到答案之前所有路径都不能放弃;深搜在搜索过程中只有一条路径,因此只使用一个数组就够了。
  1. 只要求一个解,还是要求所有解?如果只要求一个解,那么找到一个就可以返回;如果要求所有解,找到了一个后,还要继续扩展,直到遍历完。广搜一般只要求一个解,因而不需要考虑这个问题(广搜当然也可以求所有解,这时需要扩展到所有叶子结点,相当于在内存中存储整个状态转换图,非常消耗内存,因此广搜不适合这种问题)。
  2. 如何表示状态?即一个状态需要存储哪些必要的数据,才能够完整提供如何扩展到下一步状态的所有信息。跟广搜不同,深搜的惯用写法,不是把数据记录在状态struct中,而是添加函数参数(有时为了节省递归堆栈,用全局变量),struct里的字段与函数参数一一对应。
  3. 如何扩展状态?这一步和上一步相关,状态里记录的数据不同,扩展方法就不同,对于固定不变的数据结构(一般直接给出,作为输入数据),如二叉树,图等,扩展方法很简单,直接往下一层走,对于隐士图,要现在第一步想清楚状态所带的数据,想清楚这一点如何扩展就很容易了。

这里就是通常所说的,是本身就是递归结构。需要你去构造这种递归数据结构,那么在构造的过程中,你就要想清楚怎么个带参数最合适,能把该带的参数带全

  1. 关于判重
  1. 是否需要判断重复?如果状态转换是一棵树,则不需要判重,因为在遍历过程中不可能重复;如果状态转换图是一个DAG,则需要判重。这一点和BFS不同BFS的状态转换图总是一个DAG,因此必须要判断重复。
  2. 怎么判断重复,DAG说明存在子问题重叠,此时可以使用缓存加速。
  1. 终止条件是什么?终止条件是扩展到了不能扩展的末端节点。对于树,是叶子结点,对于图或者隐式图,是出度为0的节点。
  2. 收敛条件是什么?收敛条件是找到了一个合法解的时刻。如果是正向搜索(父状态处理完了才进行递归,即父状态不依赖子状态,递归语句一定是在最后,称为尾递归),则是指是否能够达到目标状态;如果是逆向深搜(处理父状态时需要先了解子状态的结果,此时递归语句不再最后),则是指是否达到初始状态。

由于很多时候终止条件和收敛条件是合二为一的,因此很多人不区分这两种条件,仔细区分这两种条件是十分必要的。

为了判断是否达到收敛条件,要在函数接口里用一个参数记录当前的位置(或者距离目标还有多远,)如果是求一个解,直接返回解,如果是求所有解,那么把第一步搜集到的解放到容器中去,然后回退去遍历下一个解。

  1. 如何加速
  1. 剪枝。深搜一定要好好考虑怎么剪枝,成本小收益大,加几行代码,就能大大加速。这里没有通用的方法,只能具体问题具体分析,要充分观察,充分利用各种信息来剪枝,在中间结点提前返回。
  2. 缓存
      1. 前提条件:状态转换图是一个DAG。DAG==》存在重叠子问题==》子问题的解会被重复利用,用缓存自然会有加速效果。如果依赖关系是树状的(例如树,单链表)没必要加缓存,因为子问题会一层层往下,用一次就再也不会用到了加了缓存也没什么加速效果

II.  具体实现:可以使用数组或者hashmap。维度简单的,用数组;复杂的用hashmap

 

深搜与回溯的区别

回溯 = 深搜 + 剪枝

深搜一般使用递归来完成的,深搜能够在进行到一半的时候就进行判断,抛弃掉不满足要求的答案。

深搜与递归的区别

深搜可以使用栈来实现,也可以使用递归来实现,递归一定是深搜,深搜不一定是递归

加速策略:

         剪枝:在递归的过程中进行判断,扔掉不满足条件的

         缓存:缓存中间结果,防止重复计算,用空间换时间。

 

标准的深度搜索加状态转换函数:加了if判断条件

广度搜索

适用场景:

输入数据:没什么特征,不像深搜,没有递归的性质,如果是树或者图

状态转换图:树或者图

求解目标:求最短

思考的步骤:

  1. 是求路径长度,还是路径本身(动作序列)
  1. 如果是求路劲长度,则状态里面要存储路径长度(双队列+全局变量)
  2. 如果是求路径本身或者动作序列
  1. 要用一棵树来存宽搜过程中的路径
  2. 是否可以预估状态个数的上限?能够预估状态总数,则开一个大数组,用树的双亲表示法:如果不能预估,那么就使用通用的树。

2.  如何表示状态?即一个状态需要存储哪些必要的信息,才能完整提供如何扩展到下一个结点的所有信息。

3.     如何扩展状态?这一步跟第二步相关。状态里记录的数据不同扩展方法不同。对于固定不变的数据结构,那么直接往下一层走就是了,但是如果需要你自己构造数据结构,那么你就要想清楚在下一个状态扩展的时候携带哪些数据才能完成扩展。

4.  关于判断重复

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值