【数据结构】深度优先搜索算法DFS

图的遍历

图的遍历为从图中某一顶点出发访遍图中其余顶点,且使每一个顶点仅被访问一次的过程。

对于图的遍历,不想树那么简单,需要在遍历的过程中把访问过的顶点打上标记,以避免访问多次。具体办法是设置一个访问数组visited[n]n是图中顶点的个数,初始值为0,访问后设置为1。

对于图的遍历来说,通常有两种遍历方案:深度优先遍历和广度优先遍历


深度优先遍历

深度优先遍历(Depth_First_Search),也称为深度优先搜索,简称DFS。

遍历方式:

  • ( 对于连通图)从图中某个顶点v出发,访问此顶点,然后从v的未被访问的邻接点出发深度优先遍历图,直到图中所有和v有路径相通的顶点都被访问到。

  • 对于非连通图,只需要对它的连通分量分别进行深度优先遍历,即在先前一个顶点进行一次深度优先遍历后,若图中尚有顶点未被访问,则另选图中一个未曾被访问的顶点作起始点,重复上述过程,直到图中所有的顶点都被访问过为止。

  • DFS其实就是一个递归的过程,就像一棵树的前序遍历

    深度优先遍历

邻接矩阵的DFS代码实现

/**
 * 邻接矩阵的DFS递归算法
 */
void DFS(MGraph G, int i){

    int j;
    visited[i] = TRUE;
    printf("%c", G.vexs[i]);  // 打印顶点(可以变成其他操作)
    
    for (j = 0; j < G.numVertexes; j++)
        if (G.arc[i][j] == 1 && !visited[j])
            DFS(G,j);
}

/**
 * 邻接矩阵的DFS遍历操作
 */
void DFSTraverse(MGraph G){
    
    int i;
    
    for (i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE;
    }
    
    for (i = 0; i < G.numVertexes; i++) {
        if (!visited[i]) {
            DFS(G, i);
        }
    }
}

附上邻接矩阵存储结构、图的创建和测试代码:

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

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0

#define MAXSIZE 9  // 存储空间初始分配量
#define MASEDGE 15
#define MAXVEX 9
#define INIFINITY 65535

typedef int  Status;
typedef int  Boolean;  // 布尔类型,值是TRUE或者FALSE
typedef char VertexType; // 顶点类型
typedef int  EdgeType;  // 边上的权值类型

typedef struct {

    VertexType vexs[MAXVEX]; // 顶点表
    EdgeType arc[MAXVEX][MAXVEX];  // 邻接矩阵
    int numVertexes, numEdges;  // 图中当前的顶点数和边数
    
}MGraph;

/**
 * 创建图
 */
void CreateMGRraph(MGraph * G){
    
    int i, j;
    
    G->numVertexes = 9;  // 9个顶点
    G->numEdges = 15;  // 15条边
    
    //读入顶点信息,建立顶点表
    G->vexs[0] = 'A';
    G->vexs[1] = 'B';
    G->vexs[2] = 'C';
    G->vexs[3] = 'D';
    G->vexs[4] = 'E';
    G->vexs[5] = 'F';
    G->vexs[6] = 'G';
    G->vexs[7] = 'H';
    G->vexs[8] = 'I';
    
    for (i = 0; i < G->numVertexes; i++)   // 初始化图
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = 0;
    
    G->arc[0][1] = 1;
    G->arc[0][5] = 1;
    
    G->arc[1][2] = 1;
    G->arc[1][8] = 1;
    G->arc[1][6] = 1;
    
    G->arc[2][3] = 1;
    G->arc[2][8] = 1;
    
    G->arc[3][4] = 1;
    G->arc[3][7] = 1;
    G->arc[3][6] = 1;
    G->arc[3][8] = 1;
    
    G->arc[4][5] = 1;
    G->arc[4][7] = 1;
    
    G->arc[5][6] = 1;
    
    G->arc[6][7] = 1;
    
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[j][i] = G->arc[i][j];
}

Boolean visited[MAXVEX];  // 访问标志数组

/**
 * 邻接矩阵的DFS递归算法
 */
void DFS(MGraph G, int i){

    int j;
    visited[i] = TRUE;
    printf("%c", G.vexs[i]);  // 打印顶点(可以变成其他操作)
    
    for (j = 0; j < G.numVertexes; j++)
        if (G.arc[i][j] == 1 && !visited[j])
            DFS(G,j);
}

/**
 * 邻接矩阵的DFS遍历操作
 */
void DFSTraverse(MGraph G){
    
    int i;
    
    for (i = 0; i < G.numVertexes; i++) {
        visited[i] = FALSE;
    }
    
    for (i = 0; i < G.numVertexes; i++) {
        if (!visited[i]) {
            DFS(G, i);
        }
    }
}

int main(int argc, const char * argv[]) {
    
    MGraph G;
    CreateMGRraph(&G);
    printf("\n邻接矩阵的深度优先遍历:\n");
    DFSTraverse(G);
    printf("\n");
    return 0;
}
邻接矩阵的DFS运行结果

邻接表的DFS代码实现

对于邻接表的DFS,和邻接矩阵差不多,只不过在递归函数中将数组换成了链表。

核心代码:

/**
 * 邻接表的深度优先递归算法
 */
void DFS(GraphAdjList GL, int i){
    
    EdgeNode *p;
    visited[i] = TRUE;
    printf("%c", GL->adjList[i].data);  // 打印结点
    
    p = GL->adjList[i].firstedge;
    
    while (p) {
        if (!visited[p->adjvex]) {
            DFS(GL, p->adjvex);
        }
        p = p->next;
    }
}

/**
 * 邻接表的深度优先遍历操作
 */
void DFSTraverse(GraphAdjList GL){
    
    int i;
    
    for (i = 0; i < GL->numVertexes; i++)
        visited[i] = FALSE;       // 初始化所有顶点状态都是未访问过状态
    
    for (i = 0; i < GL->numVertexes; i++) {
        if (!visited[i]) {
            DFS(GL, i);
        }
    }
}

附上队列操作和测试代码:

#include <stdlib.h>

#define TRUE 1
#define FALSE 0
#define OK 1
#define ERROR 0

#define MAXSIZE 9  // 存储空间初始分配量
#define MASEDGE 15
#define MAXVEX 9
#define INIFINITY 65535

typedef int  Status;
typedef int  Boolean;  // 布尔类型,值是TRUE或者FALSE

typedef char VertexType; // 顶点类型
typedef int  EdgeType;  // 边上的权值类型

typedef struct {//邻接矩阵结构
    
    VertexType vexs[MAXVEX]; // 顶点表
    EdgeType arc[MAXVEX][MAXVEX];  // 邻接矩阵
    int numVertexes, numEdges;  // 图中当前的顶点数和边数
    
}MGraph;

#pragma - 邻接表结构
typedef struct EdgeNode{  // 边表结点
    
    int adjvex;  // 邻接点域,存储该顶点对应的下标
    int weight;
    struct EdgeNode * next;  // 链域,指向下一个邻接点
    
}EdgeNode;

typedef struct VertexNode{ // 顶点表结点
    
    int in;  // 顶点入度
    char data; // 顶点域
    EdgeNode * firstedge; // 边表头指针
    
}VertexNode, AdjList[MAXSIZE];

typedef struct {
    
    AdjList adjList;
    int numVertexes, numEdges;  // 图中当前的顶点数和边数
    
}graphAdjList, *GraphAdjList;


#pragma DFS
/**
 * 创建图
 */
void CreateMGRraph(MGraph * G){
    
    int i, j;
    
    G->numVertexes = 9;  // 9个顶点
    G->numEdges = 15;  // 15条边
    
    //读入顶点信息,建立顶点表
    G->vexs[0] = 'A';
    G->vexs[1] = 'B';
    G->vexs[2] = 'C';
    G->vexs[3] = 'D';
    G->vexs[4] = 'E';
    G->vexs[5] = 'F';
    G->vexs[6] = 'G';
    G->vexs[7] = 'H';
    G->vexs[8] = 'I';
    
    for (i = 0; i < G->numVertexes; i++)   // 初始化图
        for (j = 0; j < G->numVertexes; j++)
            G->arc[i][j] = 0;
    
    G->arc[0][1] = 1;
    G->arc[0][5] = 1;
    
    G->arc[1][2] = 1;
    G->arc[1][8] = 1;
    G->arc[1][6] = 1;
    
    G->arc[2][3] = 1;
    G->arc[2][8] = 1;
    
    G->arc[3][4] = 1;
    G->arc[3][7] = 1;
    G->arc[3][6] = 1;
    G->arc[3][8] = 1;
    
    G->arc[4][5] = 1;
    G->arc[4][7] = 1;
    
    G->arc[5][6] = 1;
    
    G->arc[6][7] = 1;
    
    for (i = 0; i < G->numVertexes; i++)
        for (j = 0; j < G->numVertexes; j++)
            G->arc[j][i] = G->arc[i][j];
}

/**
 * 利用邻接矩阵构建邻接表
 */
void CreatALGraph(MGraph G, GraphAdjList * GL){
    
    int i, j;
    
    EdgeNode * e;
    
    *GL = (GraphAdjList)malloc(sizeof(graphAdjList));
    
    (*GL)->numVertexes = G.numVertexes;
    (*GL)->numEdges = G.numEdges;
    
    for (i = 0; i < G.numVertexes; i++) {  // 读入顶点信息,建立顶点表
        (*GL)->adjList[i].in = 0;
        (*GL)->adjList[i].data = G.vexs[i];
        (*GL)->adjList[i].firstedge = NULL;  // 将边表置为空
    }
    
    for (i = 0; i < G.numVertexes; i++) {  // 建立边表
        for (j = 0; j < G.numVertexes; j++) {
            if (G.arc[i][j] == 1) {
                
                e = (EdgeNode *)malloc(sizeof(EdgeNode));
                e->adjvex = j;                          // 邻接序号为j
                e->next = (*GL)->adjList[i].firstedge;   // 将当前顶点上的指向的结点指针赋值给e
                (*GL)->adjList[i].firstedge = e;         // 将当前顶点的指针指向e
                (*GL)->adjList[j].in++;
            }
        }
    }
}

Boolean visited[MAXSIZE];  // 访问标志数组

/**
 * 邻接表的深度优先递归算法
 */
void DFS(GraphAdjList GL, int i){
    
    EdgeNode *p;
    visited[i] = TRUE;
    printf("%c", GL->adjList[i].data);  // 打印结点
    
    p = GL->adjList[i].firstedge;
    
    while (p) {
        if (!visited[p->adjvex]) {
            DFS(GL, p->adjvex);
        }
        p = p->next;
    }
}

/**
 * 邻接表的深度优先遍历操作
 */
void DFSTraverse(GraphAdjList GL){
    
    int i;
    
    for (i = 0; i < GL->numVertexes; i++)
        visited[i] = FALSE;       // 初始化所有顶点状态都是未访问过状态
    
    for (i = 0; i < GL->numVertexes; i++) {
        if (!visited[i]) {
            DFS(GL, i);
        }
    }
}

int main(int argc, const char * argv[]) {
    
    MGraph G;
    
    GraphAdjList GL;
    CreateMGRraph(&G);
    CreatALGraph(G, &GL);
    
    printf("\n深度优先遍历:");
    DFSTraverse(GL);
    printf("\n");    
    return 0;
}
邻接表的DFS结果
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,716评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,558评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,431评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,127评论 0 209
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,511评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,692评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,915评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,664评论 0 202
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,412评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,616评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,105评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,424评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,098评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,096评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,869评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,748评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,641评论 2 271

推荐阅读更多精彩内容