图的深度广度优先遍历 (c语言实现)

目录

定义

邻接表存储图

深度优先遍历

广度优先遍历:

BFS部分

完整代码

定义
要实现该算法首先要知道邻接表的概念。

邻接表是一种常用的图的存储结构,它的结构特点是:

顶点由一个一维数组存储;

邻接点用链表存储

相比于单纯用数组实现的邻接矩阵,邻接表可以避免空间浪费

其图解如下:

firstedge指向边表第一个结点。

边表的adjvex的值代表与V0顶点有边的顶点下标,例:

A的firstedge指向边表的1而后指向2,3,意思是A与B,C,D各有一条共同的边。

算法的时间复杂度:对于n个顶点e条边的图,找邻接点所需的时间取决于顶点和边的数量,故为O(n+e)

邻接表存储图
 

邻接表结构代码实现如下:

/*邻接表结构*/
typedef char VertexType;     //顶点类型 
typedef int EdgeType;        //权值类型 
 
/*边表结点*/ 
typedef struct EdgeNode
{
    int adjvex;               //邻接点域,保存邻接点下标    
    EdgeType weight;        //存储权值,非网图则不需要 
    struct EdgeNode *next;    //链域,指向下一个邻接点 
}EdgeNode;
 
typedef struct VertexNode
{
    VertexType data;           //顶点域 
    EdegeNode *firstedge;    //边表头指针    
}VertexNode,AdList[MAX];
 
typedef struct
{
    AdjList adjList;
    int numVertexes,numEdges;    //顶点数量和边数量 
}GraphAdjList,*GraphAdj; 
 

创建邻接表:

/*邻接表创建*/
void create(GraphAdj G)
{
    int i,j,k;
    EdgeNode *e;
    printf("输入顶点数,边数:");
    scanf("%d%d",&G->numVertexes,&G->numEdges);
    for(i=0;i<G->numVertexes;i++)          //建立顶点表 
    {
        scanf("%c",&G->adjList[i].data);
        G->adjList[i].firstedge=NULL;         //注意将边表置空 
    }
    for(k=0;k<G->numEdges;k++)             //建立边表 
    {
        printf("输入边(Vi,Vj)上的顶点序号:");
        scanf("%d%d",&i,&j);
        /*使用头插法加入边表结点*/
        e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成边表结点 
        
        e->adjvex=j;
        e->next=G->adjList[i].firstedge;
        G->adjList[i].firstedge=e;
        
        e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成边表结点 
        
        e->adjvex=i;
        e->next=G->adjList[j].firstedge;
        G->adjList[j].firstedge=e;
                
    } 
     
 } 
 

深度优先遍历
邻接表的深度优先遍历同邻接矩阵的深度优先遍历大同小异,都需要创建一个标志数组,数组存放bool类型成员TRUE,FALSE。 其中TRUE表示已经访问过。

不同点:在递归函数中需要声明一个EdgeNode *类型的变量p来进行边表的遍历,也就是在边表的链表中遍历邻接点。

/*邻接表的深度优先递归*/
void DFS(GraphAdj G,int i)
{
    EdgeNode *p;
    visited[i]=TRUE;                 //访问过了该顶点,标记为TRUE 
    printf("%c",G->adjList[i].data);
    p=G->adjList[i].firstedge;     //让p指向边表第一个结点 
    while(p)                      //在边表内遍历 
    {
        if(!visited[p->adjvex])    //对未访问的邻接顶点递归调用 
            DFS(G,p->adjvex);    
        p=p->next;
    }
 } 
 
 //邻接表的深度遍历操作
 
void DFSTraverse(GraphAdj 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);    //对未访问的顶点调用DFS,若是连通图只会执行一次             

 

广度优先遍历:
使用循环队列实现:

typedef struct LoopQueue{ //定义循环队列结构体 
    int data[MAX];
    int front;
    int rear;   //注意每次队尾标记指向最后一个元素的下一个位置 
}Queue, *LQueue; 
需要手动实现队列的各种操作:

void InitQueue(LQueue &Q){  //初始化队列 
    Q->front = Q->rear = 0;
}
 
bool QueueisFull(LQueue &Q){ //判断队列是否满了 
    if((Q->rear + 1) % MAX == Q->front){
        return true;  //已满 
    }
    else{
        return false;
    }
}
 
bool QueueisEmpty(LQueue &Q){//判断队列是否为空 
    if(Q->front == Q->rear){
        return true;
    }
    return false;
}
 
 
void EnQueue(LQueue &Q, int i){ //入队列 
    if(!QueueisFull(Q)){
        Q->data[Q->rear] = i;
        Q->rear = (Q->rear + 1) % MAX;  //队尾指针后移 
    }
}
 
void DeQueue(LQueue &Q, int *k){ //出队列 
    if(!QueueisEmpty(Q)){
        *k = Q->data[Q->front];
        Q->front = (Q->front + 1) % MAX; 
    }
}
 

BFS部分
该算法的思路就是从初始位置顶点开始,遍历所有的邻接顶点并将其加入队列,当一个顶点的所有邻接点都遍历完了之后就把该顶点出队列,拿出下一个顶点,然后不断的重复该过程

/*广度优先遍历*/
void BFS(GraphAdj G){
    
    Queue *Q =(LQueue)malloc(sizeof(Queue));
    for(int i = 0; i < G->numVertexes; i++){
        visited[i] = FALSE;
    }
    InitQueue(Q);  //初始化队列 
    for(int i = 0; i < G->numVertexes; i++){
        visited[i] = TRUE;
        printf("\t%c", G->adjList[i].data);
        EnQueue(Q, i);
        
        while(!QueueisEmpty(Q)){
            DeQueue(Q, &i);  //这里不断的修改i的值!! 
            EdgeNode *e = G->adjList[i].firstedge;  //i顶点的邻接链表的第一个结点
            while(e){//e存在时,将e的所有邻接点加入队列,也就是遍历i的所有邻接点 
                if(!visited[e->adjvex]){ // adjvex是e所表示的结点下标 
                    visited[e->adjvex] = TRUE;
                    printf("\t%c", G->adjList[e->adjvex].data);
                    EnQueue(Q, e->adjvex); //将该结点入队 
                }
                e = e->next; //遍历i的下一个邻接点 
            }
        } 
    }

 

完整代码
 

#include<stdio.h>
#include<stdlib.h>
#define MAX 10
#define INIFINITY 65535
#define TRUE 1
#define FALSE 0
typedef int Boole;  //布尔类型 存储TRUE FALSE
Boole visited[MAX];    //访问标志数组 
 
//邻接表结点定义
typedef char VertexType;  //顶点数据类型     
typedef int EdgeType;    //边上的权值类型 
 
typedef struct EdgeNode  //边表结点   存储边表信息 
{
    int adjvex;            //邻接点域,存储该顶点对应的下标 
    EdgeType weight;    //权值 
    struct EdgeNode *next;    //链域,指向下一个邻接点 
}EdgeNode;
 
typedef struct VertexNode   //顶点表结点
{
    VertexType data;      //顶点域,存储顶点信息 
    EdgeNode *firstedge;    //边表头指针,指向此顶点的第一个邻接点 
}VertexNode,AdjList[MAX]; 
 
 
typedef struct
{
    AdjList adjList;     
    int numVertexes,numEdges;   //图中当前顶点数和边数 
}GraphAdjList,*GraphAdj;
 
 
typedef struct LoopQueue{ //定义循环队列结构体 
    int data[MAX];
    int front;
    int rear;   //注意每次队尾标记指向最后一个元素的下一个位置 
}Queue, *LQueue; 
 
void InitQueue(LQueue &Q){  //初始化队列 
    Q->front = Q->rear = 0;
}
 
bool QueueisFull(LQueue &Q){ //判断队列是否满了 
    if((Q->rear + 1) % MAX == Q->front){
        return true;  //已满 
    }
    else{
        return false;
    }
}
 
bool QueueisEmpty(LQueue &Q){//判断队列是否为空 
    if(Q->front == Q->rear){
        return true;
    }
    return false;
}
 
 
void EnQueue(LQueue &Q, int i){ //入队列 
    if(!QueueisFull(Q)){
        Q->data[Q->rear] = i;
        Q->rear = (Q->rear + 1) % MAX;  //队尾指针后移 
    }
}
 
void DeQueue(LQueue &Q, int *k){ //出队列 
    if(!QueueisEmpty(Q)){
        *k = Q->data[Q->front];
        Q->front = (Q->front + 1) % MAX; 
    }
}
 
 
/*邻接表创建*/
void create(GraphAdj G)
{
    int i,j,k;
    EdgeNode *e;
    printf("输入顶点数和边数:");
    scanf("%d%d",&G->numVertexes,&G->numEdges);
    getchar();                          //注意要清除缓冲 
    for(i=0;i<G->numVertexes;i++)          //建立顶点表 
    {
        scanf("%c",&G->adjList[i].data);    //输入顶点的符号 
        G->adjList[i].firstedge=NULL;         //将边表置空 
        getchar();
    }
    for(k=0;k<G->numEdges;k++)             //建立边表 
    {
        printf("输入边(Vi,Vj)上的顶点序号:");
        scanf("%d%d",&i,&j);
        /*使用头插法加入边表结点*/
        e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 
 
        e->adjvex=j;
        e->next=G->adjList[i].firstedge;
        G->adjList[i].firstedge=e;
        
        e=(EdgeNode *)malloc(sizeof(EdgeNode));   //生成结点 
        
        e->adjvex=i;
        e->next=G->adjList[j].firstedge;
        G->adjList[j].firstedge=e;            
    } 
    printf("\n");

 
 
/*邻接表的深度优先递归*/
void DFS(GraphAdj G,int i)
{
    EdgeNode *p;
    visited[i]=TRUE;                 //访问过了该顶点,标记为TRUE 
    printf("\t%c",G->adjList[i].data);
    p=G->adjList[i].firstedge;     //让p指向边表第一个结点 
    while(p)                      //在边表内遍历 
    {
        if(!visited[p->adjvex])    //对未访问的邻接顶点递归调用 
            DFS(G,p->adjvex);    
        p=p->next;
    }
 } 
 
 //邻接表的深度遍历操作
 
void DFSTraverse(GraphAdj G)
{
    int i;
    for(i=0;i<G->numVertexes;i++)
        visited[i]=FALSE;       //初始设置为未访问 
    for(i=0;i<G->numVertexes;i++)
        if(!visited[i])       //对未访问的顶点调用DFS,若是连通图只会执行一次 
            DFS(G,i);                

 
/*广度优先遍历*/
void BFS(GraphAdj G){
    
    Queue *Q =(LQueue)malloc(sizeof(Queue));
    for(int i = 0; i < G->numVertexes; i++){
        visited[i] = FALSE;
    }
    InitQueue(Q);  //初始化队列 
    for(int i = 0; i < G->numVertexes; i++){
        visited[i] = TRUE;
        printf("\t%c", G->adjList[i].data);
        EnQueue(Q, i);
        
        while(!QueueisEmpty(Q)){
            DeQueue(Q, &i);  //这里不断的修改i的值!! 
            EdgeNode *e = G->adjList[i].firstedge;  //i顶点的邻接链表的第一个结点
            while(e){//e存在时,将e的所有邻接点加入队列,也就是遍历i的所有邻接点 
                if(!visited[e->adjvex]){ // adjvex是e所表示的结点下标 
                    visited[e->adjvex] = TRUE;
                    printf("\t%c", G->adjList[e->adjvex].data);
                    EnQueue(Q, e->adjvex); //将该结点入队 
                }
                e = e->next; //遍历i的下一个邻接点 
            }
        } 
    }

 
int main()
{
    GraphAdjList G;
    create(&G);
    printf("深度优先遍历为:"); 
    DFSTraverse(&G);
    printf("\n");
    printf("广度优先遍历为:"); 
    BFS(&G);
    printf("\n图遍历完毕");
    return 0;     
 } 
 

错误警示:

一开始我在main函数中写的是

int main()
{
    GraphAdj G;
    create(G);
    printf("\n");
    DFSTraverse(G);
    printf("\n图遍历完毕");
    return 0;     
 } 
 

可是运行时会出现问题,后来我才知道,GraphAdj G需要初始化,指针变量在分配了内存空间,即先要有指向之后才可以引用其值

但若是在cpp中可以把create函数的形参改成:

GraphAdj &G
 

&的意思是传进来节点指针的引用,括号内等价于 GraphAdj* &G,目的是让传递进来的指针发生改变
 

使用引用后,main函数就可以写成上述的形式了。
————————————————
版权声明:本文为CSDN博主「大芝士球」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_40663810/article/details/79292769

发布了37 篇原创文章 · 获赞 20 · 访问量 4966

猜你喜欢

转载自blog.csdn.net/qq_24436765/article/details/100172565