图的邻接矩阵存储方法实现的深度优先遍历(DFS)

众所周知图的遍历有八种:有向、无向->邻接矩阵、邻接表->DFS、BFS,所以不可能都写一遍 如果有机会再补上吧

一、代码

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

#define MAXSIZE 10
#define MaxV  10 //顶点最大数量 

typedef struct stack{
    int data[MAXSIZE];
    int top;
}stack;

typedef struct Graph{
    char vertex[MaxV]; //顶点数组
    int edge[MaxV][MaxV]; //邻接矩阵 , 顶点的二维数组也就是 ,边表 
    int vertexNums , edgeNums ; // 图的当前顶点数 和 边数 
}Graph;

void initGraph(Graph *graph , int vertexNums){
    graph->vertexNums = vertexNums; //设置顶点数
    printf("自动设定顶点元素: ");

    for(int i = 0 ; i < vertexNums ; i++){
        graph->vertex[i] = 'a' + i; //自动设置 固定顶点 
        printf("%d:%c ",i,graph->vertex[i]);
        //手动输入顶点值 测试起来太费劲 并且没有意义 因为边是固定的 
//      scanf("%c",&graph->vertex[i]);
//      getchar(); //用getchar来消耗掉换行符 
    }
    graph->edgeNums = 0; //设置边数 但是边数只能由addEdge函数来自动增加 

    //边全部初始化为0 互不连通 
    for(int i = 0 ; i < vertexNums ; i++){
        for(int j = 0 ; j < vertexNums ; j++){
            graph->edge[i][j] = 0;
        }
    }
}

int getIndexOfV(Graph *graph , char v){
    //获取v在顶点数组中的位置 v的下标 
    for(int i = 0 ; i < graph->vertexNums ; i++){ 
        if(graph->vertex[i] == v){
            return i;
        }
    }
    return -1;

}

void addNoDirEdge(Graph *graph , char c1 , char c2){
    int i , j;
    i = getIndexOfV(graph,c1);
    j = getIndexOfV(graph,c2);
    if(i == -1 || j== -1){
        printf("未找到指定的顶点 %c or %c !",c1,c2);
        return;
    } 
    //无向图
    graph->edge[i][j] = 1;
    graph->edge[j][i] = 1; 
    graph->edgeNums += 1;
} 

int firstNeighbor(Graph *graph , int v){
    //顶点v的第一个邻接点的顶点号(下标) 
    //遍历v所在的行 第一个值为1的
    for(int j = 0 ; j < graph->vertexNums ; j++){ //第一个邻居要从头开始遍历 
        if(graph->edge[v][j] == 1){
            return j;
        }
    }

    return -1; 
}

int nextNeighbor(Graph *graph , int v , int t ,int visited[]){
    //假设图中顶点t是顶点v的一个邻接点,返回除了t之外顶点v的下一个邻接点的顶点号
    // v -> t -> 要求的那个点 

    //获取到x所在的行
    //然后从头遍历 但是如果遇到t所在的列 就跳过,因为要返回除了t的下一个邻居

    for(int j = 0 ; j < graph->vertexNums ; j++){
        if(graph->edge[v][j] == 1 && j != t && !visited[j]){
            return j;
        }
    }
//  printf("顶点: %c(第%d号顶点)没有下一个邻居!",graph->vertex[v],v);
    return -1; //用#表示没有邻居
}

void visit(Graph *graph , int v){
    //简单打印功能 
    printf("%c ",graph->vertex[v]);
}

void DFS(Graph *graph , int v , int visited[] ,int len){
    visit(graph,v); //对当前顶点号元素进行访问 
    visited[v] = 1; // 将当前顶点号元素设置为1 已访问 
//  for(int i = 0 ; i < len ; i++) printf("--vid{%d}:%d-- ",i,visited[i]);

    //t -- temp  || v -- vertex
    //从当前顶点开始  
    for(int t = firstNeighbor(graph,v) ; t>=0 ; t = nextNeighbor(graph,v,t,visited)){
//      printf("-%c-",graph->vertex[t]);
        if(!visited[t]){
            DFS(graph,t,visited,len); //确实是该递归调用 因为你的v也需要变到下一个元素
        }
    }
    return;
}

void DFSTraverse(Graph *graph){
    int len = graph->vertexNums; //获取图中顶点数量 也就是visited数组的长度 
    int visited[len] = {0};  

    for(int i = 0 ; i < len ; i++){
        visited[i] = 0; //遍历前全为0 
    }

    for(int i = 0 ; i < len ; i++){
        if(!visited[i]){ // 没有遍历到的 这是因为可能图是非连通图,那么会从数组中继续遍历 
            DFS(graph,i,visited,len);
        }
    }

} 

void printGraph(Graph *graph){
    int n = graph->vertexNums;
    printf("\n即将打印图,图中共有%d个顶点\n----------图----------\n",n);   

    printf("\t");
    for(int j = 0 ; j < n ; j++){
        printf("%c\t",graph->vertex[j]);
    }
    printf("\n\n");

    for(int i = 0; i < n ;i++){
        printf("%c\t",graph->vertex[i]);
        for(int j = 0 ; j < n ; j++){
            printf("%d\t",graph->edge[i][j]);
        }
        printf("\n\n");
    }
    printf("----------------------\n",n); 

}

int main(){

    Graph *ndgraph; //no direct 这是一个无向图 
    ndgraph = (Graph*)malloc(sizeof(Graph)); //务必要分配内存  但不清楚底层原因 待以后再探索吧 
    initGraph(ndgraph,7);   //顶点数组 01234 -> abcde

    // for + scanf 可以自己手动连线 
    addNoDirEdge(ndgraph,'a','c'); //a,c之间添加一条边
    addNoDirEdge(ndgraph,'c','d');
    addNoDirEdge(ndgraph,'b','d');
    addNoDirEdge(ndgraph,'d','e');
    addNoDirEdge(ndgraph,'f','g');

    /**
        a  ——   c      e      f —— g 
                |      |
                |      |
                b———— d 

    */

    printGraph(ndgraph);

    printf("DFS: ");
    DFSTraverse(ndgraph);

    return 0;   
}

二、效果图

file

三、代码实现中思考过的问题

  1. 其实在代码中 有向图和无向图的区别就在于addEdge()函数中,给某两个顶点号增加边的时候是否是双向的
  2. 关于设置参数的问题,最开始我是使用的顶点值来进行传递,后来发现这样在进行便利的时候会造成代码冗长,比如在获取第一个邻接点和下一个邻接点的时候的传递问题。
  3. 关于nextNeighbor()函数,这里有一个坑,写代码的时候较易忽略。
    nextNeighbor函数中寻找下一节点应该有三个条件:
    (0)首先看一下关于nextNeighbor的解释:
    int nextNeighbor(Graph *graph , int v , int t ,int visited[])
    假设图中顶点t是顶点v的一个邻接点,返回除了t之外顶点v的下一个邻接点的顶点号
    然后从头遍历 但是如果遇到t所在的列 就跳过,因为要返回除了t的下一个邻居
    (1)这一顶点在二维数组中应为1,这个自然不必多说,因为需要两个顶点有边才能遍历
    (2)应该跳过刚才遍历的点,这也就是函数本身的含义,即(0)的解释
    (3)在遍历的时候应该跳过已经遍历过的点,坑就在这里,
    如果我们不对这里加以判断,那么当某一结点有两个边的时候,就会陷入死循环:
    a-b-c,从a走到b的时候,b根据(2)判断应走向c,
    但是c再次走回b的时候,如果没有跳过已经遍历过的边(a,b,c都已遍历),
    就会只根据函数跳过c再次走向a。从而陷入死循环
    但其实 (!visited[j])这个条件相当于已经包括了(2) 所以(2)显得可有可无
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值