数据结构简记--6

第 6 章 图

6.1 图的顺序存储

        图的顺序存储结构代码如下:

#include <stdio.h>
#define MAX_VERtEX_NUM 20                   //顶点的最大个数
#define VRType int                          //表示顶点之间的关系的变量类型
#define InfoType char                       //存储弧或者边额外信息的指针变量类型
#define VertexType int                      //图中顶点的数据类型
typedef enum { DG, DN, UDG, UDN }GraphKind;       //枚举图的 4 种类型
typedef struct {
    VRType adj;                             //对于无权图,用 1 或 0 表示是否相邻;对于带权图,直接为权值。
    InfoType* info;                         //弧或边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];        //存储图中顶点数据
    AdjMatrix arcs;                         //二维数组,记录顶点之间的关系
    int vexnum, arcnum;                     //记录图的顶点数和弧(边)数
    GraphKind kind;                         //记录图的种类
}MGraph;
//根据顶点本身数据,判断出顶点在二维数组中的位置
int LocateVex(MGraph* G, VertexType v) {
    int i = 0;
    //遍历一维数组,找到变量v
    for (; i < G->vexnum; i++) {
        if (G->vexs[i] == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i == G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}
//构造有向图
void CreateDG(MGraph* G) {
    int i, j;
    //输入图含有的顶点数和弧的个数
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    //依次输入顶点本身的数据
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    //初始化二维矩阵,全部归0,指针指向NULL
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    //在二维数组中添加弧的数据
    for (i = 0; i < G->arcnum; i++) {
        int v1, v2;
        int n, m;
        //输入弧头和弧尾
        scanf("%d,%d", &v1, &v2);
        //确定顶点位置
        n = LocateVex(G, v1);
        m = LocateVex(G, v2);
        //排除错误数据
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        //将正确的弧的数据加入二维数组
        G->arcs[n][m].adj = 1;
    }
}

//构造无向图
void CreateDN(MGraph* G) {
    int i, j;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (i = 0; i < G->arcnum; i++) {
        int v1, v2;
        int n, m;
        scanf("%d,%d", &v1, &v2);
        n = LocateVex(G, v1);
        m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = 1;
        G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称
    }
}
//构造有向网,和有向图不同的是二阶矩阵中存储的是权值。
void CreateUDG(MGraph* G) {
    int i, j;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (i = 0; i < G->arcnum; i++) {
        int v1, v2, w;
        int n, m;
        scanf("%d,%d,%d", &v1, &v2, &w);
        n = LocateVex(G, v1);
        m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = w;
    }
}
//构造无向网。和无向图唯一的区别就是二阶矩阵中存储的是权值
void CreateUDN(MGraph* G) {
    int i, j;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }
    for (i = 0; i < G->arcnum; i++) {
        int v1, v2, w;
        int m, n;
        scanf("%d,%d,%d", &v1, &v2, &w);
        m = LocateVex(G, v1);
        n = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = w;
        G->arcs[m][n].adj = w;//矩阵对称
    }
}
void CreateGraph(MGraph* G) {
    //选择图的类型
    scanf("%d", &(G->kind));
    //根据所选类型,调用不同的函数实现构造图的功能
    switch (G->kind) {
    case DG:
        return CreateDG(G);
        break;
    case DN:
        return CreateDN(G);
        break;
    case UDG:
        return CreateUDG(G);
        break;
    case UDN:
        return CreateUDN(G);
        break;
    default:
        break;
    }
}
//输出函数
void PrintGrapth(MGraph G)
{
    int i, j;
    for (i = 0; i < G.vexnum; i++)
    {
        for (j = 0; j < G.vexnum; j++)
        {
            printf("%d ", G.arcs[i][j].adj);
        }
        printf("\n");
    }
}
int main() {
    MGraph G;//建立一个图的变量
    CreateGraph(&G);//调用创建函数,传入地址参数
    PrintGrapth(G);//输出图的二阶矩阵
    return 0;
}

 6.2 邻接表

        邻接表(Adjacency List)是图的一种链式存储结构,既可以存储无向图(网),也可以存储有向图(网)。

        邻接表存储图的核心思想是:将图中的所有顶点存储到顺序表中(也可以是链表),同时为各个顶点配备一个单链表,用来存储和当前顶点有直接关联的边或者弧(边的一端是该顶点或者弧的弧尾是该顶点)。

        邻接表的实现代码如下:

#define  MAX_VERTEX_NUM 20//图中顶点的最大数量
#define  VertexType int//图中顶点的类型
#define  InfoType int*//图中弧或者边包含的信息的类型
typedef struct ArcNode{
    int adjvex;//存储边或弧,即另一端顶点在数组中的下标
    struct ArcNode * nextarc;//指向下一个结点
    InfoType info;//记录边或弧的其它信息
}ArcNode;
typedef struct VNode{
    VertexType data;//顶点的数据域
    ArcNode * firstarc;//指向下一个结点
}VNode,AdjList[MAX_VERTEX_NUM];//存储各链表首元结点的数组
typedef struct {
    AdjList vertices;//存储图的邻接表
    int vexnum,arcnum;//记录图中顶点数以及边或弧数
    int kind;//记录图的种类
}ALGraph;

         使用邻接表存储有向图的代码如下:

#include<stdio.h>
#include<stdlib.h>
#define  MAX_VERTEX_NUM 20//最大顶点个数
#define  VertexType char//图中顶点的类型

typedef struct ArcNode {
    int adjvex;//存储弧,即另一端顶点在数组中的下标
    struct ArcNode* nextarc;//指向下一个结点
}ArcNode;

typedef struct VNode {
    VertexType data;//顶点的数据域
    ArcNode* firstarc;//指向下一个结点
}VNode, AdjList[MAX_VERTEX_NUM];//存储各链表首元结点的数组

typedef struct {
    AdjList vertices;  //存储图的邻接表
    int vexnum, arcnum;//图中顶点数以及弧数
}ALGraph;

void CreateGraph(ALGraph * graph) {
    int i, j;
    char VA, VB;
    ArcNode* node = NULL;
    printf("输入顶点的数目:\n");
    scanf("%d", &(graph->vexnum));
    printf("输入弧的数目:\n");
    scanf("%d", &(graph->arcnum));
    scanf("%*[^\n]"); scanf("%*c");
    printf("输入各个顶点的值:\n");
    for (i = 0; i < graph->vexnum; i++) {
        scanf("%c", &(graph->vertices[i].data));
        getchar();
        graph->vertices[i].firstarc = NULL;
    }
    //输入弧的信息,并为弧建立结点,链接到对应的链表上
    for (i = 0; i < graph->arcnum; i++) {
        printf("输入弧(a b 表示弧 a->b):\n");
        scanf("%c %c", &VA, &VB);
        getchar();
        node = (ArcNode*)malloc(sizeof(ArcNode));
        node->adjvex = '#';
        node->nextarc = NULL;
        //存储弧另一端顶点所在顺序表中的下标
        for (j = 0; j < graph->vexnum; j++) {
            if (VB == graph->vertices[j].data) {
                node->adjvex = j;
                break;
            }
        }
        //如果未在顺序表中找到另一端顶点,则构建图失败
        if (node->adjvex == '#') {
            printf("弧信息输入有误\n");
            exit(0);
        }
        //将结点添加到对应的链表中
        for (j = 0; j < graph->vexnum; j++) {
            if (VA == graph->vertices[j].data) {
                //将 node 结点以头插法的方式添加到相应链表中
                node->nextarc = graph->vertices[j].firstarc;
                graph->vertices[j].firstarc = node;
                break;
            }
        }
        if (j == graph->vexnum) {
            printf("弧信息输入有误\n");
            exit(0);
        }
    }
}

//计算某个顶点的入度
int InDegree(ALGraph graph, char V) {
    int i, j, index = -1;
    int count = 0;
    //找到 V 在顺序表中的下标
    for (j = 0; j < graph.vexnum; j++) {
        if (V == graph.vertices[j].data) {
            index = j;
            break;
        }
    }
    if (index == -1) {
        return -1;
    }
    //遍历每个单链表,找到存储 V 下标的结点,并计数
    for (j = 0; j < graph.vexnum; j++) {
        ArcNode* p = graph.vertices[j].firstarc;
        while (p) {
            if (p->adjvex == index) {
                count++;
            }
            p = p->nextarc;
        }
    }
    return count;
}

//计算某个顶点的出度
int OutDegree(ALGraph graph, char V) {
    int j;
    int count = 0;
    for (j = 0; j < graph.vexnum; j++) {
        if (V == graph.vertices[j].data) {
            ArcNode* p = graph.vertices[j].firstarc;
            while (p) {
                count++;
                p = p->nextarc;
            }
            break;
        }
    }
    //如果查找失败,返回 -1 表示计算失败
    if (j == graph.vexnum) {
        return -1;
    }
    return count;
}

int main(void) {
    ALGraph graph;
    CreateGraph(&graph);
    if (OutDegree(graph, 'A') != -1) {
        printf("%c 顶点的出度为 %d\n", 'A', OutDegree(graph, 'A'));
    }
    if (InDegree(graph, 'A') != -1) {
        printf("%c 顶点的入度为 %d", 'A', InDegree(graph, 'A'));
    }
    return 0;
}

6.3 十字链表

        十字链表(Orthogonal List)是一种专门存储有向图(网)的结构,它的核心思想是:将图中的所有顶点存储到顺序表(也可以是链表)中,同时为每个顶点配备两个链表,一个链表记录以当前顶点为弧头的弧,另一个链表记录以当前顶点为弧尾的弧。 

        构建图的十字链表结构的代码如下:

#define  MAX_VERTEX_NUM 20 //图中顶点的最大数量
#define  InfoType int*     //表示弧额外信息的数据类型
#define  VertexType char    //图中顶点的数据类型
//表示链表中存储弧的结点
typedef struct ArcBox {
    int tailvex, headvex;          //弧尾、弧头对应顶点在顺序表中的位置下标
    struct ArcBox* hlik, * tlink;  //hlik指向下一个以当前顶点为弧头的弧结点;
                                   //tlink 指向下一个以当前顶点为弧尾的弧结点;
    //InfoType info;               //存储弧相关信息的指针
}ArcBox;

//表示顺序表中的各个顶点
typedef struct VexNode {
    VertexType data;              //顶点的数据域
    ArcBox* firstin, * firstout;  //指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;

//表示十字链表存储结构
typedef struct {
    VexNode xlist[MAX_VERTEX_NUM];  //存储顶点的顺序表
    int vexnum, arcnum;             //记录图的顶点数和弧数
}OLGraph;

         使用十字链表结构存储此图的代码如下:

#include<stdio.h>
#define  MAX_VERTEX_NUM 20 //图中顶点的最大数量
#define  InfoType int*     //表示弧额外信息的数据类型
#define  VertexType char    //图中顶点的数据类型
//表示链表中存储弧的结点
typedef struct ArcBox {
    int tailvex, headvex;          //弧尾、弧头对应顶点在顺序表中的位置下标
    struct ArcBox* hlik, * tlink;  //hlik指向下一个以当前顶点为弧头的弧结点;
                                   //tlink 指向下一个以当前顶点为弧尾的弧结点;
    //InfoType info;               //存储弧相关信息的指针
}ArcBox;

//表示顺序表中的各个顶点
typedef struct VexNode {
    VertexType data;              //顶点的数据域
    ArcBox* firstin, * firstout;  //指向以该顶点为弧头和弧尾的链表首个结点
}VexNode;

//表示十字链表存储结构
typedef struct {
    VexNode xlist[MAX_VERTEX_NUM];  //存储顶点的顺序表
    int vexnum, arcnum;             //记录图的顶点数和弧数
}OLGraph;

int LocateVex(OLGraph* G, VertexType v) {
    int i;
    //遍历一维数组,找到变量v
    for (i = 0; i < G->vexnum; i++) {
        if (G->xlist[i].data == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回 -1
    if (i > G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}

//构建十字链表存储结构
void CreateDG(OLGraph* G) {
    int i, j, k;
    VertexType v1, v2;
    ArcBox* p = NULL;
    //输入有向图的顶点数和弧数
    scanf("%d %d", &(G->vexnum), &(G->arcnum));
    getchar();
    //使用一维数组存储顶点数据,初始化指针域为NULL
    for (i = 0; i < G->vexnum; i++) {
        scanf("%c", &(G->xlist[i].data));
        getchar();
        G->xlist[i].firstin = NULL;
        G->xlist[i].firstout = NULL;
    }
    //存储图中的所有弧
    for (k = 0; k < G->arcnum; k++) {
        scanf("%c %c", &v1, &v2);
        getchar();
        //确定v1、v2在数组中的位置下标
        i = LocateVex(G, v1);
        j = LocateVex(G, v2);
        //建立弧的结点
        p = (ArcBox*)malloc(sizeof(ArcBox));
        p->tailvex = i;
        p->headvex = j;
        //采用头插法插入新的p结点
        p->hlik = G->xlist[j].firstin;
        p->tlink = G->xlist[i].firstout;
        G->xlist[j].firstin = G->xlist[i].firstout = p;
    }
}

//计算某顶点的入度
int indegree(OLGraph* G, VertexType x) {
    int i;
    int num = 0;
    //遍历整个顺序表
    for (i = 0; i < G->vexnum; i++) {
        //找到目标顶点
        if (x == G->xlist[i].data) {
            //从该顶点的 firstin 指针所指的结点开始遍历
            ArcBox* p = G->xlist[i].firstin;
            while (p)
            {
                num++;
                //遍历 hlink 指针指向的下一个结点
                p = p->hlik;
            }
            break;
        }
    }
    if (i == G->vexnum) {
        printf("图中没有指定顶点\n");
        return -1;
    }
    return num;
}

//计算某顶点的出度
int outdegree(OLGraph* G, VertexType x) {
    int i;
    int num = 0;
    //遍历整个顺序表
    for (i = 0; i < G->vexnum; i++) {
        //找到目标顶点
        if (x == G->xlist[i].data) {
            //从该顶点的 firstout 指针所指的结点开始遍历
            ArcBox* p = G->xlist[i].firstout;
            while (p)
            {
                num++;
                //遍历 tlink 指针指向的下一个结点
                p = p->tlink;
            }
            break;
        }
    }
    if (i == G->vexnum) {
        printf("图中没有指定顶点\n");
        return -1;
    }
    return num;
}

//删除十字链表结构
//每个顶点配备两个链表,选定一个链表(比如 firstout 所指链表),删除每个顶点中 firstout 所指链表上的结点
void DeleteDG(OLGraph* G) {
    int i;
    ArcBox* p = NULL, * del = NULL;
    for (i = 0; i < G->vexnum; i++) {
        p = G->xlist[i].firstout;
        while (p) {
            del = p;
            p = p->tlink;
            free(del);
        }
        //将第 i 个位置的两个指针全部置为 NULL,能有效避免出现野指针
        G->xlist[i].firstout = NULL;
        G->xlist[i].firstin = NULL;
    }
}
int main() {
    OLGraph G;
    CreateDG(&G);
    printf("A 顶点的入度为 %d\n", indegree(&G, 'A'));
    printf("A 顶点的出度为 %d\n", outdegree(&G, 'A'));
    DeleteDG(&G);
    return 0;
}

6.4  深度优先搜索

        深度优先搜索(Depth First Search)简称深搜或者 DFS,是遍历图存储结构的一种算法,既适用于无向图(网),也适用于有向图(网)。

        深度优先搜索算法的具体实现,所谓深度优先搜索,就是从图中的某个顶点出发,不停的寻找相邻的、尚未访问的顶点:
(1)如果找到多个,则任选一个顶点,然后继续从该顶点出发。
(2)如果一个都没有找到,则回退到之前访问过的顶点,看看是否有漏掉的。

        实现图的顺序存储结构的深度优先搜索算法的代码如下 :

#include <stdio.h>
#define MAX_VERtEX_NUM 20                   //顶点的最大个数
#define VRType int                          //表示顶点之间关系的类型, 0 表示不相邻,1 表示相邻
#define VertexType int                      //图中顶点的数据类型
#define States int
typedef enum { false, true }bool;           //定义bool型常量
bool visited[MAX_VERtEX_NUM];               //设置全局数组,标记图中的各个顶点是否被访问过

typedef struct {
    VRType adj;                             //用 1 或 0 表示是否相邻;
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];        //存储图中的顶点
    AdjMatrix arcs;                         //二维数组,记录顶点之间的关系
    int vexnum, arcnum;                      //记录图的顶点数和弧(边)数
}MGraph;

//根据顶点数据,返回顶点在二维数组中的位置下标
int LocateVex(MGraph* G, VertexType v) {
    int i = 0;
    //遍历一维数组,找到变量v
    for (; i < G->vexnum; i++) {
        if (G->vexs[i] == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i > G->vexnum) {
        printf("no such vertex.\n");
        return -1;
    }
    return i;
}

//构造无向图
States CreateDN(MGraph* G) {
    int i, j, n, m;
    int v1, v2;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
        }
    }
    for (i = 0; i < G->arcnum; i++) {
        scanf("%d,%d", &v1, &v2);
        n = LocateVex(G, v1);
        m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return -1;
        }
        G->arcs[n][m].adj = 1;
        G->arcs[m][n].adj = 1;//无向图的二阶矩阵沿主对角线对称
    }
    return 1;
}

int FirstAdjVex(MGraph G, int v)
{
    int i;
    //对于数组下标 v 处的顶点,找到第一个和它相邻的顶点,并返回该顶点的数组下标
    for (i = 0; i < G.vexnum; i++) {
        if (G.arcs[v][i].adj) {
            return i;
        }
    }
    return -1;
}

int NextAdjVex(MGraph G, int v, int w)
{
    int i;
    //对于数组下标 v 处的顶点,从 w 位置开始继续查找和它相邻的顶点,并返回该顶点的数组下标
    for (i = w + 1; i < G.vexnum; i++) {
        if (G.arcs[v][i].adj) {
            return i;
        }
    }
    return -1;
}

void DFS(MGraph G, int v) {
    int w;
    printf("%d ", G.vexs[v]);  //访问第 v 个顶点
    visited[v] = true;         //将第 v 个顶点的标记设置为true
    //对于与第 v 个顶点相邻的其它顶点,逐个调用深度优先搜索算法
    for (w = FirstAdjVex(G, v); w >= 0; w = NextAdjVex(G, v, w)) {
        //如果该顶点的标记为false,证明尚未被访问,就调用深度优先搜索算法
        if (!visited[w]) {
            DFS(G, w);
        }
    }
}

//深度优先搜索
void DFSTraverse(MGraph G) {
    int v;
    //visit数组记录各个顶点是否已经访问过,全部初始化为 false
    for (v = 0; v < G.vexnum; ++v) {
        visited[v] = false;
    }
    //对于每个标记为false的顶点,都调用一次深度优先搜索算法
    for (v = 0; v < G.vexnum; v++) {
        //如果该顶点的标记位为false,就调用深度优先搜索算法
        if (!visited[v]) {
            DFS(G, v);
        }
    }
}

int main() {
    MGraph G;      //建立一个图
    CreateDN(&G);  //初始化图
    DFSTraverse(G);//深度优先搜索图
    return 0;
}

6.5  广度优先搜索

        广度优先搜索(Breadth First Search)简称广搜或者 BFS,是遍历图存储结构的一种算法,既适用于无向图(网),也适用于有向图(网)。 

        广度优先搜索算法的具体实现如下:
(1)所谓广度优先搜索,就是从图中的某个顶点出发,寻找紧邻的、尚未访问的顶点,找到多少就访问多少,然后分别从找到的这些顶点出发,继续寻找紧邻的、尚未访问的顶点。

(2)当从某个顶点出发,所有和它连通的顶点都访问完之后,广度优先搜索算法会重新选择一个尚未访问的顶点(非连通图中就存在这样的顶点),继续以同样的思路寻找未访问的其它顶点。直到图中所有顶点都被访问,广度优先搜索算法才会结束执行。

        实现图的顺序存储结构的广度优先搜索算法的代码如下 :

#include <stdio.h>
#include <stdlib.h>
#define MAX_VERtEX_NUM 20                   //顶点的最大数量
#define VRType int                          //表示顶点之间关系的数据类型
#define VertexType int                      //顶点的数据类型
typedef enum { false, true }bool;           //定义bool型常量
bool visited[MAX_VERtEX_NUM];               //设置全局数组,记录每个顶点是否被访问过

//队列链表中的结点类型
typedef struct Queue {
    VertexType data;
    struct Queue* next;
}Queue;

typedef struct {
    VRType adj;                             //用 0 表示不相邻,用 1 表示相邻
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct {
    VertexType vexs[MAX_VERtEX_NUM];        //存储图中的顶点
    AdjMatrix arcs;                         //二维数组,记录顶点之间的关系
    int vexnum, arcnum;                     //记录图的顶点数和弧(边)数
}MGraph;

//判断 v 顶点在二维数组中的位置
int LocateVex(MGraph* G, VertexType v) {
    int i;
    //遍历一维数组,找到变量v
    for (i = 0; i < G->vexnum; i++) {
        if (G->vexs[i] == v) {
            break;
        }
    }
    //如果找不到,输出提示语句,返回-1
    if (i > G->vexnum) {
        printf("no this vertex\n");
        return -1;
    }
    return i;
}

//构造无向图
void CreateDN(MGraph* G) {
    int i, j, n, m;
    int v1, v2;
    scanf("%d,%d", &(G->vexnum), &(G->arcnum));
    for (i = 0; i < G->vexnum; i++) {
        scanf("%d", &(G->vexs[i]));
    }
    for (i = 0; i < G->vexnum; i++) {
        for (j = 0; j < G->vexnum; j++) {
            G->arcs[i][j].adj = 0;
        }
    }
    for (i = 0; i < G->arcnum; i++) {
        scanf("%d,%d", &v1, &v2);
        n = LocateVex(G, v1);
        m = LocateVex(G, v2);
        if (m == -1 || n == -1) {
            printf("no this vertex\n");
            return;
        }
        G->arcs[n][m].adj = 1;
        G->arcs[m][n].adj = 1;
    }
}

int FirstAdjVex(MGraph G, int v)
{
    int i;
    //对于数组下标 v 处的顶点,找到第一个和它相邻的顶点,并返回该顶点的数组下标
    for (i = 0; i < G.vexnum; i++) {
        if (G.arcs[v][i].adj) {
            return i;
        }
    }
    return -1;
}

int NextAdjVex(MGraph G, int v, int w)
{
    int i;
    //对于数组下标 v 处的顶点,从 w 位置开始继续查找和它相邻的顶点,并返回该顶点的数组下标
    for (i = w + 1; i < G.vexnum; i++) {
        if (G.arcs[v][i].adj) {
            return i;
        }
    }
    return -1;
}

//初始化队列,这是一个有头结点的队列链表
void InitQueue(Queue** Q) {
    (*Q) = (Queue*)malloc(sizeof(Queue));
    (*Q)->next = NULL;
}

//顶点元素v进队列
void EnQueue(Queue** Q, VertexType v) {
    Queue* temp = (*Q);
    //创建一个存储 v 的结点
    Queue* element = (Queue*)malloc(sizeof(Queue));
    element->data = v;
    element->next = NULL;
    //将 v 添加到队列链表的尾部
    while (temp->next != NULL) {
        temp = temp->next;
    }
    temp->next = element;
}

//队头元素出队列
void DeQueue(Queue** Q, int* u) {
    Queue* del = (*Q)->next;
    (*u) = (*Q)->next->data;
    (*Q)->next = (*Q)->next->next;
    free(del);
}

//判断队列是否为空
bool QueueEmpty(Queue* Q) {
    if (Q->next == NULL) {
        return true;
    }
    return false;
}

//释放队列占用的堆空间
void DelQueue(Queue* Q) {
    Queue* del = NULL;
    while (Q->next) {
        del = Q->next;
        Q->next = Q->next->next;
        free(del);
    }
    free(Q);
}

//广度优先搜索
void BFSTraverse(MGraph G) {
    int v, u, w;
    Queue* Q = NULL;
    InitQueue(&Q);
    //将用做标记的visit数组初始化为false
    for (v = 0; v < G.vexnum; ++v) {
        visited[v] = false;
    }
    //遍历图中的各个顶点
    for (v = 0; v < G.vexnum; v++) {
        //若当前顶点尚未访问,从此顶点出发,找到并访问和它连通的所有顶点
        if (!visited[v]) {
            //访问顶点,并更新它的访问状态
            printf("%d ", G.vexs[v]);
            visited[v] = true;
            //将顶点入队
            EnQueue(&Q, G.vexs[v]);
            //遍历队列中的所有顶点
            while (!QueueEmpty(Q)) {
                //从队列中的一个顶点出发
                DeQueue(&Q, &u);
                //找到顶点对应的数组下标
                u = LocateVex(&G, u);
                //遍历紧邻 u 的所有顶点
                for (w = FirstAdjVex(G, u); w >= 0; w = NextAdjVex(G, u, w)) {
                    //将紧邻 u 且尚未访问的顶点,访问后入队
                    if (!visited[w]) {
                        printf("%d ", G.vexs[w]);
                        visited[w] = true;
                        EnQueue(&Q, G.vexs[w]);
                    }
                }
            }
        }
    }
    DelQueue(Q);
}
int main() {
    MGraph G;
    //构建图
    CreateDN(&G);
    //对图进行广度优先搜索
    BFSTraverse(G);
    return 0;
}

6.6 Dijkstra 算法

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

#define V 6 //图的顶点数

int minDistance(int dist[], bool sptSet[]) {
    int min = INT_MAX, min_index;

    for (int v = 0; v < V; v++) {
        if (sptSet[v] == false && dist[v] <= min) {
            min = dist[v];
            min_index = v;
        }
    }

    return min_index;
}

void dijkstra(int graph[V][V], int src) {
    int dist[V];        //存储源节点到各个节点的最短距离
    bool sptSet[V];     //标记是否包含在最短路径树中

    for (int i = 0; i < V; i++) {
        dist[i] = INT_MAX;
        sptSet[i] = false;
    }

    dist[src] = 0;

    for (int count = 0; count < V-1; count++) {
        int u = minDistance(dist, sptSet);

        sptSet[u] = true;

        for (int v = 0; v < V; v++) {
            if (!sptSet[v] && graph[u][v] && dist[u] != INT_MAX
                && dist[u] + graph[u][v] < dist[v]) {
                dist[v] = dist[u] + graph[u][v];
            }
        }
    }

    //打印最短路径
    printf("节点\t\t距离\n");
    for (int i = 0; i < V; i++) {
        printf("%d\t\t%d\n", i, dist[i]);
    }
}

int main() {
    int graph[V][V] = {
        {0, 2, 0, 3, 0, 0},
        {2, 0, 4, 4, 5, 0},
        {0, 4, 0, 1, 0, 0},
        {3, 4, 1, 0, 7, 4},
        {0, 5, 0, 7, 0, 3},
        {0, 0, 0, 4, 3, 0}
    };
    dijkstra(graph, 0);

    return 0;
}

 6.7 Bellman-Ford 算法

#include <stdio.h>
#include <stdbool.h>
#include <limits.h>

#define V 5 //图的顶点数
#define E 8 //图的边数

struct Edge {
    int src, dest, weight;
};

void bellmanFord(struct Edge edges[], int dist[], int src) {
    int i, j;

    //初始化距离数组
    for (i = 0; i < V; i++) {
        dist[i] = INT_MAX;
    }
    dist[src] = 0;

    //执行 V-1 次迭代
    for (i = 0; i < V-1; i++) {
        for (j = 0; j < E; j++) {
            int u = edges[j].src;
            int v = edges[j].dest;
            int weight = edges[j].weight;

            if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
            }
        }
    }

    //检测负环
    for (j = 0; j < E; j++) {
        int u = edges[j].src;
        int v = edges[j].dest;
        int weight = edges[j].weight;

        if (dist[u] != INT_MAX && dist[u] + weight < dist[v]) {
            printf("图中存在负环\n");
            return;
        }
    }

    //打印最短路径
    printf("节点\t\t距离\n");
    for (i = 0; i < V; i++) {
        printf("%d\t\t%d\n", i, dist[i]);
    }
}

int main() {
    struct Edge edges[E] = {
        {0, 1, -1}, {0, 2, 4}, {1, 2, 3},
        {1, 3, 2}, {1, 4, 2}, {3, 2, 5},
        {3, 1, 1}, {4, 3, -3}
    };
    int dist[V];
    int src = 0;

    bellmanFord(edges, dist, src);

    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值