深度优先搜索算法实现

Published: 05 Jul 2015 Category: 技术

前言:

在描述DFS实现原理之前首先需要搞清楚图的基本存储方式或结构,这里讲述两种:邻接矩阵和邻接表

邻接矩阵

存储方式采用两个数组来表示图:一个一维数组存储图中结点信息,一个二维数组即邻接矩阵存储图中的边或弧的信息 注:图分为有向图、无向图,如果每条边上带有权则称之为网 使用邻接矩阵存储图可以轻松实现查找顶点邻接点的操作,但是若是统计图中边或弧的数量,则需要按行和列对每个元素进行判断,同时使用*两个数组分别存储顶点信息和关系信息,花费比较大

邻接矩阵创建图

  typedef char VertexType;
  typedef int EdgeType;
  #define MAXVEX 100  //最大顶点数
  #define INFINITY 65535 //代表无穷

  typedef struct{
        VertexType vexs[MAXVEX];
        EdgeType arc[MAXVEX][MAXVEX];
        int numVertex, numEdges;
  }MGraph;

  void CreateGraph(MGraph *G){
        int i, j, k, w;
        printf("输入定点数和边数: \n");
        scanf("%d, %d", &G->numVertex, &G->numEdges);
        for(int i = 0; i < G->numVertex; i++){//输入顶点,建立顶点表
              scanf(&G->vexs[i]);
        }
        for(int i = 0; i < G->numVertex; i++){//邻接矩阵初始化
            for(int j = 0; j < G->numVertex; j++){
               G->aarc[i][j] = INFINTY; 
            }
        }

        for(k = 0; k < G->numEdges; k++){
              printf("输入边<Vi, Vj>上的下标 i和j以及权w: \n");
              scanf("%d, %d, %d", &i, &j, &w);
              G->arc[i][j] = w;
              G->arc[i][j] = G->arc[j][i];//无向图对称
        }
  }

邻接表

  • 图中的顶点由一个一维数组存储,每个数据元素还需存储指向第一个邻接顶点的指针,以便查找该顶点的边信息
  • 图中每个顶点v的所有邻接顶点构成一个线性表,由于邻接点个数不定,故而采用单链表 ## DFS基本思想描述: 首先访问某一个起始顶点v,然后有v出发,访问与v邻接且未被访问的任意结点w1,再访问与w1邻接且未被访问的任一顶点w2,...重复上述过程。当不能继续向下访问时,依次会退到最近被访问的顶点,若它还有邻接顶点未被访问过,则从该顶点继续上述搜索过程,直到途中所有顶点均被访问过为止。 ## 算法导论中的伪码描述:
  //u 为 v 的先辈或父母。
  DFS(G)
  1  for each vertex u  V [G]
  2       do color[u]  WHITE
  3          π[u]  NIL  
  //第1-3行,把所有顶点置为白色,所有π 域被初始化为NIL, π[u]即为u的前驱顶点,初始化
  4  time  0       //复位时间计数器
  5  for each vertex u  V [G]
  6       do if color[u] = WHITE
  7             then DFS-VISIT(u)  //调用DFS-VISIT访问u,u成为深度优先森林中一棵新的树
      //第5-7行,依次检索V中的顶点,发现白色顶点时,调用DFS-VISIT访问该顶点。
      //每个顶点u 都对应于一个发现时刻d[u]和一个完成时刻f[u]。
  DFS-VISIT(u)
  1  color[u]  GRAY            //u 开始时被发现,置为灰色
  2  time  time +1             //time 递增
  3  d[u]  time                   //记录u被发现的时间
  4  for each v  Adj[u]   //检查并访问 u 的每一个邻接点 v  Adj[u]b表示u所有的邻接点
  5       do if color[v] = WHITE            //如果v 为白色,则递归访问v。
  6             then π[v]  u                   //置u为 v的先辈
  7                         DFS-VISIT(v)        //递归深度,访问邻结点v
  8  color[u]  BLACK         //u 置为黑色,表示u及其邻接点都已访问完成
  9  f [u]  time  time +1  //访问完成时间记录在f[u]中。
  //完
  /*第1-3行,5-7行循环占用时间为O(V),此不包括调用DFS-VISIT的时间。
      对于每个顶点v(-V,过程DFS-VISIT仅被调用1次,因为只有对白色顶点才会调用此过程。
  第4-7行,执行时间为O(E)。
  因此,总的执行时间为O(V+E)。*/

DFS的c代码实现(采用的是邻接表结构建的图)

  #include<stdio.h>
  #include<stdlib.h>
  #include<conio.h>

  #define MaxNum 10

  struct enode                  /*定义表结点结构*/
  {
    int adjvex;      //对应的是结点的序号0,1,2。。。
    struct enode* next;
  }enode;

  struct vnode                  /*定义顶点结点结构*/
  {
    char vertex;
    struct enode* firstedge;
  }vnode;

  struct ALGraph                    /*定义图结构*/
  {
    struct vnode adjlist[MaxNum];//存储的是结点的名称如v1,v2。。。
    int n, e;

  }ALGraph;

  int locate_vex(struct ALGraph*G, char vex)//定位定点vex在有向图中G中的序号位置
  {
    int i;
    for(i = 0; i < G->n; i++)
    {
        if(G->adjlist[i].vertex == vex)
            return i;
    }

  }

  void createGraph(struct ALGraph* G)
  {
    int i, j, k;
    char vex1, vex2;
    struct enode * s;

    printf("\n请输入有向图的顶点名称:");

    for(i = 0; i < G->n; i++)
    {
        scanf_s("\n%c", &G->adjlist[i].vertex);
        G->adjlist[i].firstedge = NULL;
    }

    printf("\n请输入有向图的每条弧的弧尾顶点和弧头顶点名称:");
    for (k = 0; k < G->e; k++)
    {
        printf("\n请输入第%d条弧的弧尾顶点名称:", k+1);
        scanf_s("\n%c", &vex1);
        printf("\n请输入第%d条弧的弧头顶点名称:", k+1);
        scanf_s("\n%c", &vex2);

        i = locate_vex(G, vex1);
        j = locate_vex(G, vex2);

        s = (struct enode*)malloc(sizeof(struct enode));
        s->adjvex = j;
        s->next = G->adjlist[i].firstedge;
        G->adjlist[i].firstedge = s;
    }
  }


  void outGraph(struct ALGraph* G)//输出图结构
  {
    int i, j;
    struct enode* s;

    printf("\n\n有向图有%d个顶点", G->n);
    for (int i = 0; i < G->n; i++)
    {
        printf("%c", G->adjlist[i].vertex);
    }
    printf("\n\n有向图有%d个弧", G->e);
    for (int i = 0; i < G->n; i++)
    {
        printf("\n%c->", G->adjlist[i].vertex);
        s = G->adjlist[i].firstedge;
        while (s)
        {
            printf("%c ", G->adjlist[s->adjvex].vertex);
            s = s->next;
        }
    }
  }

  int visited[MaxNum];

  void DFS(struct ALGraph *G, int i)//从n个结点中任意选取一个开始遍历
  {
    struct enode *p;
    printf("访问顶点%c", G->adjlist[i].vertex);
    visited[i] = 1;
    p = G->adjlist[i].firstedge;
    while(p)
    {
        if(!visited[p->adjvex])
        {
            DFS(G, p->adjvex);
        }
        p = p->next;
    }
  }

  void DFSTraverse(struct ALGraph *G)
  {
    int i;
    for(i = 0; i < G->n; i++)
    {
        visited[i] = 0;
    }
    printf("\n");

    for(i = 0; i < G->n; i++)
    {
        if(!visited[i])
        {
            DFS(G, i);
        }
    }
  }

  void main()
  {
    struct ALGraph alg;
    printf("\n有向图存储结构。。。");
    printf("\n请输入有向图的顶点数目:");
    scanf_s("%d",&alg.n);
    printf("\n请输入有向图的弧数目:");
    scanf_s("%d",&alg.e);
    createGraph(&alg);
    outGraph(&alg);
    DFSTraverse(&alg);
    getch();
  }

BFS

BFS(G, s)
for each vertex u  V [G] - {s}
  do color[u]  WHITE
  d[u]  
  π[u]  NIL
  //除了源顶点s之外,第1-4行置每个顶点为白色,置每个顶点u的d[u]为无穷大,
  //置每个顶点的父母为NIL。
  color[s]  GRAY
  //第5行,将源顶点s置为灰色,这是因为在过程开始时,源顶点已被发现。
  d[s]  0       //将d[s]初始化为0。
  π[s]  NIL     //将源顶点的父顶点置为NIL。
  Q  Ø          //队列为空,初始化
  ENQUEUE(Q, s)                  //入队
  //第8、9行,初始化队列Q,使其仅含源顶点s。
  while Q  Ø
     do u  DEQUEUE(Q)    //出队
  //第11行,确定队列头部Q头部的灰色顶点u,并将其从Q中去掉。
        for each v  Adj[u]        //for循环考察u的邻接表中的每个顶点v
            do if color[v] = WHITE
                  then color[v]  GRAY     //置为灰色
                      d[v]  d[u] + 1     //距离被置为d[u]+1
                      π[v]  u            //u记为该顶点的父母
                      ENQUEUE(Q, v)        //插入队列中
 color[u]  BLACK      //u 置为黑色
//12-18行就是检查完顶点u的邻接链表中的所有顶点后,将u置为灰色

下面我将以更为直观的图示来分析: 图片后面补充。。

About me

面朝大海,春暖花开。哈喽,大家好,我是Randy!