搜 索
1.简单概念
所谓图的遍历,也称为搜索,就是从图中某个顶点出发,沿着一些边访遍图中的所有的顶点,且使每个顶点仅被访问一次。
2.算法分类
2.1回溯算法: 常常不被认为是搜索,而被分类为基础算法题。
回溯算法是所有搜索算法中最为基本的一种算法,其采用了一种“走不通就掉头”思想作为其控制结构,其相当于采用了先根遍历的方法来构造解答树,可用于找解或所有解以及最优解。
评价:回溯算法对空间的消耗较少,当其与分枝定界法一起使用时,对于所求解在解答树中层较深的问题有较好的效果。但应避免在后继节点可能与前继节点相同的问题中使用,以免产生循环。 回溯算法可以从解决 八数码问题入手。
2.2遍历分为:
1.深度(Depth)优先搜索DFS:一个递归过程,有回退过程。尽可能“深”地搜索图。在深度优先搜索中,对于最新发现的顶点,如果它还有以此为起点而未探测到的边,就沿此边继续搜索下去。当结点V的所有边都已被探寻过,搜索将回溯到发现结点V有那条边的始结点,则选择其中一个作为源结点并重复以上过程,整个进程反复进行直到所有结点都被发现为止。
2.广度(Breath)优先搜索BFS:一个分层的搜索过程,没有回退过程,是非递归的。只是每次都尽可能地扩展当前节点的邻居节点,之后再向其子结点进行扩展。
2.3应用上的区别:
BFS 常用于找单一的最短路线,它的特点是 "搜到就是最优解",而 DFS 用于找所有解的问题,它的空间效率高,而且找到的不一定是最优解,必须记录并完成整个搜索,故一般情况下,深搜需要非常高效的剪枝
BFS:对于解决最短或最少问题特别有效,而且寻找深度小,但缺点是内存耗费量大(需要开大量的数组单元用来存储状态)。
DFS:对于解决遍历和求所有问题有效,对于问题搜索深度小的时候处理速度迅速,然而在深度很大的情况下效率不高。
2.4实现时用到的数据结构:
深度优先搜索用栈(stack)来实现,整个过程可以想象成一个倒立的树形:
1、把根节点压入栈中。
2、每次从栈中弹出一个元素,搜索所有在它下一级的元素,把这些元素压入栈中。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
当然DFS有时也直接利用函数进行递归。
广度优先搜索使用队列(queue)来实现,整个过程也可以看做一个倒立的树形:
1、把根节点放到队列的末尾。
2、每次从队列的头部取出一个元素,查看这个元素所有的下一级元素,把它们放到队列的末尾。并把这个元素记为它下一级元素的前驱。
3、找到所要找的元素时结束程序。
4、如果遍历整个树还没有找到,结束程序。
BFS根据实际情况可能要用到优先队列。
3.题目分类:
3.1坐标类型(地图)搜索 : 简单
这种类型的搜索题目通常来说简单的比较简单,复杂的通常在边界的处理和情况的讨论方面会比较复杂,分析这类问题,我们首先要抓住题目的意思,看具体是怎么建立坐标系(特别重要),然后仔细分析到搜索的每一个阶段是如何通过条件转移到下一个阶段的。确定每一次递归(对于DFS)的回溯和深入条件。
对于BFS,要注意每一次入队的条件同时注意判重。要牢牢把握目标状态是一个什么状态,在什么时候结束搜索。还有,DFS过程的参数如何设定,是带参数还是不带参数,带的话各个参数一定要保证能完全的表示一个状态,不会出现一个状态对应多个参数,而这一点对于BFS来说就稍简单些,只需要多设置些变量就可以了。
3.2数值类型(抽象状态)搜索: 困难
这种类型的搜索就需要仔细分析分析了,一般来说采用DFS,而且它的终止条件一般都是很明显的,难就难在对于过程的把握,过程的把握类似于坐标类型的搜索(判重、深入、枚举,注意这种类型的搜索通常还要用到剪枝优化,对于那些明显不符合要求的特殊(非法)状态我们一定要在处理前就判断去掉它。
典型题目如:素数环,变形课,非常可乐,组合数,整数划分等。
3.3 算法进阶
双向广度搜索、A*算法,最小耗散优先等。具体可参考算法篇文档 A※算法介绍及搜索算法的优化。
搜索基础打扎实后,如果还有时间与精力可以继续利用互联网学习上图中的细小分类。
4.基础知识
数据结构:栈,队列,优先队列。 具体可参考数据结构篇
5.模板整理
本模块没有多少模板需要整理。DFS和BFS可各找一经典题整理一个。具有典型意义的难题也可以整理一下。优先队列的定义不会手写的话也可以整理一下。
6.总结:搜索,据我认为,是当今ACM竞赛中最常规、也最能体现解题者水平的一类解题方法。 “纸上得来终觉浅,绝知此事要躬行。”要想真正领悟、理解各种搜索的思想,掌握搜索的解题技巧,还需要在实践中不断地挖掘、探索。实践得多了,也就能体会到渐入佳境之妙了。算法的优化是无穷尽的。
部分转载