算法导航之搜索(DFS & BFS)

前言

搜索是最常用的算法之一,更是在图算法中占有核心的位置,图算法最基本的就是图的遍历,而遍历图的两种最基本的方法就是 深度优先搜索(DFS)和广度优先搜索(BFS)。在搜索算法的编写过程中每个人都有自己的理解和习惯,但有一些框架和 场景是不变的,比如说DFS一般使用递归,而BFS使用队列,这里进行一下总结。

DFS

深度优先搜索算法适合于很多场景的问题的解决,很多能用动态规划解决的题目也能用DFS来解决,它的总体思想是 在条件满足的情况下直到搜索出一组解为止,搜索算法的复杂度与图的组成和表示有很大的关系,一般情况下DFS达不到 题目的时间要求的话,就需要对其进行剪枝,怎样去剪枝才是DFS算法的难点。我们来看题目:

  • Poj1011(一道比较经典的搜索题) 题目大意是:

     有若干根长度相同的木棍被折成了若干根短的木根(不一定相等),现已知一堆短木棍的长度,求原木棍的最短长度。
    

    这道题我们可以设:木棍长度x1,x2,x3....很容易确定原木棍的长度就在Max(xi)与Sum(xi)之间,设为d,可 以让d在两个最值之间循环,如果d能满足将所有的短木棍能拼成若干长度为d的长木棍,则d满足条件。怎么判断d 是否满足条件呢,一个好的解决方法是深搜。

      void dfs(int begin,int len,int num){
        if num显示已经搜索完成
           退出
        if 如果len满足长度条件了
           num+1,进行下一个d的搜索
        else{
           for(int i=begin;i<n;i++)
              改变begin和len的值进行dfs
        } 
       }
    

    上面就是一般dfs算法的模型,begin标记起始点,len表示当前的num的进展程度,如果num达到要求表示搜索完成。

还有一道题和这个题很像就是:给你若干木棍,问是否能将他们拼成一个正方形,与这题的思路几乎是一模一样的, 而且还更简单,只用考虑四个边。我们真正的目的是在这个题目中学会方法,这两道题都不需要怎么剪枝就能通过, 我们有时需要在算法中加一些条件限制,以获得更好的时间。

BFS

广度优先搜索最适合的场合就是求最短路径的问题,从根节点到目标节点的最短路径就是该图的广度优先遍历。BFS 的大致思路就是:首先将根节点放入队列中,每次取出队头的节点,然后依次访问与这个节点相邻的各个节点,并 将他们放入队尾中,这样离根节点越近的节点就会被先访问到。来看题目:

  • Poj1915(一道经典的广搜题) 题目大意如下:

    在一个棋盘中按照马字来行走,求两点之间到达的最短距离。
    

这个题目咋一看跟骑士漫游题目有点像,骑士漫游问题是问从棋盘的任一位置出发能否遍历整个棋盘(DFS)。而这道 题是为了求最短距离,我们不妨以出发点为根,找出从根能到达的各个点,并标记上与根的距离,这样按照BFS的 思想依次遍历下去遍历到终点便返回,因为由骑士漫游问题知:按照马字步,棋盘上的任意两点都是可达的。算法 描述:

    void bfs(){
    Q.offer(begin_x*n+begin_y);//将根加入到队列中

    while(!Q.isEmpty()
        int p=Q.poll(); //取出队列头
        for(int i=0;i<8;i++)
            if(该点没有被遍历到)
                board[x][y]=board[p/n][p%n]+1;  //记录与根的距离
                if(x==end_x&&y==end_y) is=false;//找到就返回
                Q.offer(x*n+y);                 //没找到就放入队列中

    }

这也是BFS的一般模型,在碰见类似求与根节点的最短距离的问题时,首先能反应用广度优先搜索时关键。



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories algorithm  tagged with algorithm