深度优先搜索算法以及应用

当处于一个巨大的迷宫中,你只有第一人称视角,应该如何以最快速度走出这个迷宫呢?

依据深度优先算法,这种策略一定有效:以当前位置为起点,沿着一条路向前走。当遇到一个岔路口时,就选择一个岔路前进,如果这个岔路是死胡同,就退回上一个岔路口;如果这个岔路又有新的岔路口,就依据之前的做法,再选择一条岔路口继续前进。

这样就出现了一个问题,当经历了众多岔路口之后,你的面前出现了死胡同,你的记忆力已经不支持你退回一开始的岔路口,那岂不是又要在迷宫里打转转了?其实解决这个问题的方法很简单,无论是选择岔路口或者是返回上一个岔路口,只要保证在走的过程中你的右手一直贴着右边的墙壁,你就一定会走出迷宫。

这种不撞南墙不回头的策略,形象地从深度上彻底挖掘了这个迷宫,因此这种搜索路径的方法被称作深度优先搜索,也称为DFS(Depth-First-Search)

 

以上图迷宫为例,将所有岔路口看作节点,深度优先搜索策略具体实现过程如下:

  • 当遇见A节点,根据右手前进的策略,会依次访问B、D和H节点,此时H节点已经走到了死胡同,于是原路返回到D节点。再到I,而I也是死胡同,退回D后再到J,依然是死胡同,回到D时只能再次原路返回,退回至B节点。
  • 从B到E后,依旧有三条岔路,按照前进策略,将分别访问K、L和M后返回E,再返回B。此时B的岔路也全部枚举完毕,只能退回A。
  • 从A到达C后,仍然有岔路口,先访问F再访问G,最终走出迷宫

由上述过程可以看出来,深度优先搜索访问节点顺序为:ABDHIJEKLMCFG。

从数据结构的角度来看,深度优先搜索离不开一种数据结构,那就是堆栈,访问节点过程也就是入栈出栈的过程,只多了两个约束条件:①栈不空就执行出栈操作②对于每个出栈元素,必须将所有邻接点依次入栈(①的约束优先级大于②)

既然提到了堆栈,在算法上自然容易联想到递归,实际上如果使用递归,深度优先搜索的代码将简洁很多。先来看一个典型的递归例子——Fibonacci数列,数列规定:F(0)=1,F(1)=1,F(n)=F(n-1)+F(n-2)(n不小于2)。每当F(n)被分为F(n-1)和F(n-2)时,就可以看做时一个岔路口的两条路,紧接着递归处理F(n-1)和F(n-2)。由此启发可以得到搜索优先搜索的递归代码:

#define MaxSize = 100;
bool visited[MaxSize];

void DFS(Graph G, int v){
    Node *p;    //工作指针
    visit(v);
    visited[v] = true;
    p = G->adjlist[v].firstarc;    //指针p指向该顶点的第一条边
    while(p != NULL){
        if(!visited[p->adjvex]){
            DFS(G, p->adjvex);
        }
        p = p->adjvex;
    }
}

void DFSGraph(Graph G){
    int i;
    for(i = 0; i < G->vexnum; i++){
        visited[i] = false;
    }
    for(i = 0; i < G->vexnum; i++){
        if(visited[i]){
            DFS(G, i);
        }
    }
}

深度优先搜索不仅仅局限于寻找路径,在实际问题上也有重要的思想启发作用,如以下的0-1背包问题,就可以利用深度优先搜索思想来解决。

有N件物品和一个容量为V的背包。第i件物品的价格(即体积,下同)是w[i],价值是c[i]。求解将哪些物品装入背包可使这些物品的费用总和不超过背包容量,且价值总和最大。

  对于背包中的每一个物品,都可以看作一个节点,都有“选择”和“不选择”两条“岔路”可以走,而两种选择会产生不同的背包目前价格总和、背包目前价值总和以及当前物品编号,于是DFS函数参数为:

void DFS(int price, int value, int index){...}

根据题意可以轻易得出两个约束条件,即index<N,price<V,在满足两个条件的情况下,选择index号物品放入背包继而去处理index+1号物品的写法为:

DFS(price + w[index+1], value + c[index+1], index+1);

  不选择index号物品而直接去处理index+1号物品的写法为:

DFS(price, value, index+1);

于是整个代码如下:

#include <stdio.h>

const int maxn = 30;
int n, V, max = 0;    //物品数量n,背包容量V,最大价值max
int w[maxn], c[maxn];    //每件物品的价格价值

void DFS(int price, int value, int index){
    if(index == n) return;     
    DFS(price, value, index + 1);
    if(price + w[index] <= V){
    
if(value + c[index] > max) max = value + c[index];
    DFS(price
+ w[index], value + c[index], index + 1);
  } }
int main(){ while(scanf("%d%d", &n, &V) != EOF){ for(int i = 0; i < n; i++){ scanf("%d", &w[i]); } for(int i = 0; i < n; i++){ scanf("%d", &c[i]); } DFS(0, 0, 0); printf("%d", max); } return 0; }

猜你喜欢

转载自www.cnblogs.com/three-year-old/p/10564598.html