数据结构-图结构实验设计

1.前言

图是一种比线性结构和树结构更为复杂的数据结构。其中结点可以具有零个或多个相邻元素。两个结点之间的连接称为边。结点也可以称为顶点。至于图的定义以及图的基本术语多看多理解就懂了,我直接开始介绍图的一些存储结构吧。

2.邻接矩阵

用邻接矩阵表示法表示图,除了一个用于存储邻接矩阵的二维数组外,还需要用一个一维数组来存储顶点信息。话不多说,直接上代码部分。

#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#define MAX 100 // 矩阵最大容量
#define INF 9999 // 最大值
#define isLetter(a) ((((a)>='a')&&((a)<='z')) || (((a)>='A')&&((a)<='Z')))
typedef struct _graph
{
    char vexs[MAX];       // 顶点集合
    int vexnum;           // 顶点数
    int edgnum;           // 边数
    int matrix[MAX][MAX]; // 邻接矩阵
}Graph, * PGraph;

/* 返回名为ch的顶点在图G中的位置  */
static int get_position(Graph G, char ch)
{
    int i;
    for (i = 0; i < G.vexnum; i++)
        if (ch == G.vexs[i])//与顶点集合中的元素相比较
            return i;
    return -1;
}

/* 读取一个输入字符 */
static char read_char()
{
    char ch;
    do {
        ch = getchar();
    } while (!isLetter(ch));
    return ch;
}

Graph* create_graph()
{
    Graph* pG;
    char c1, c2;
    int i, j,w,p1,p2;
    int v, e;  //v为图G中的顶点个数,e为边的个数
    printf("请输入图G中顶点的个数: ");
    scanf_s("%d", &v);
    printf("请输入图G中边的个数: ");
    scanf_s("%d", &e);
    while (v < 1 || e < 1 || (e > (v * (v - 1))))  // 有向完全图有(v * (v-1))条边,超过次数则不符
    {
        printf("输入错误: 无效的参数!请核实!\n");
        printf("请输入图G中顶点的个数: ");
        scanf_s("%d", &v);
        printf("请输入图G中边的个数: ");
        scanf_s("%d", &e);
    }

    if ((pG = (Graph*)malloc(sizeof(Graph))) == NULL)
        return NULL;
    memset(pG,0,sizeof(Graph));  /* menset 算法*/
    // 初始化图的"顶点个数"和"边的条数"
    pG->vexnum = v;
    pG->edgnum = e;
    // 初始化"顶点"信息
    printf("\n请输入%d个顶点的字母标号:\n", v);
    for (i = 0; i < pG->vexnum; i++)
    {
        printf("顶点(%d): ", i + 1);
        pG->vexs[i] = read_char();  /* scanf("%c",&pG->vexs[i]);*/
    }
    // 1. 初始化"边"权值的初始值
    for (i = 0; i < pG->vexnum; i++)
    {
        for (j = 0; j < pG->vexnum; j++)
        {
            if (i == j)
                pG->matrix[i][j] = 0;      //对角线上的元素都为0
            else
                pG->matrix[i][j] = INF;  //非对角线上的元素都为无穷大
        }
    }

    // 2.  根据用户的输入信息,设置边的权值
    printf("\n请输入%d条边的信息(起点 终点 权值):\n", e);
    for (i = 0; i < pG->edgnum; i++)
    {
        // 读取边的起始顶点,结束顶点,权值
        printf("第%d条边:", i + 1);
        c1 = read_char();  //输入边上的一个字符
        c2 = read_char();  //输入边上的另一个字符
        scanf_s("%d", &w);   //输入边上的权值

        p1 = get_position(*pG, c1);  //查找c1字符在图顶点数组对应下标
        p2 = get_position(*pG, c2);  //查找c2字符在图顶点数组对应下标

        while (p1 == -1 || p2 == -1)
        {
            printf("输入错误: 无效的边!请重新输入:\n");
            printf("边(%d):", i + 1);
            c1 = read_char();
            c2 = read_char();
            scanf_s("%d", &w);

            p1 = get_position(*pG, c1);
            p2 = get_position(*pG, c2);
        }

        pG->matrix[p1][p2] = w;  //修改图邻接矩阵中对应边的权值
        pG->matrix[p2][p1] = w;
    }

    return pG;
}

首先我们从键盘输入我们想要图的顶点以及边的个数,如果输入不合法则重新输入,在这当中要注意memset函数。

memset函数定义于<string.h>头文件中。

函数原型:

extern void *memset(void *buffer, int c, int count)

buffer:为指针或是数组

c:是赋给buffer的值

count:赋值buffer中的位数

函数功能:为指针变量buffer所指的前n个字节的内存单元填充给定的int型数值c,它可以为任何数据进行初始化。换句话说,就是将数值c以单个字节逐个拷贝的方式放到指针变量buffer所指的内存中去。 注意:只将数值c的最低一个字节填充到内存。

void print_graph(Graph G)
{
    int i, j;
    printf("\n图G的权值矩阵:\n");
    for (i = 0; i < G.vexnum; i++)
    {
        for (j = 0; j < G.vexnum; j++)
            printf("%10d ", G.matrix[i][j]);
        printf("\n");
    }
}

我们写一个函数将图用二维矩阵打印出来。

3.图的遍历

和树的遍历类似,图的遍历也是从图中某一顶点出发,按照某种方法对图中所有顶点进行访问且仅访问一次。图的遍历算法是求解图的连通性问题、拓扑排序和关键路径等算法的基础。

根据搜索路径的方向,通常有两条遍历图的路径:深度优先搜索和广度优先搜索。

深度优先搜索

深度优先搜索(Depth First Search, DFS)遍历类似于树的先序遍历,是树的先序遍历的推广。

/* 返回顶点v的第一个邻接顶点的索引(下标),失败则返回-1 */
static int first_vertex(Graph G, int v)   //v是顶点在图中顶点数组的下标
{
    int i;

    if (v<0 || v>(G.vexnum - 1))
        return -1;

    for (i = 0; i < G.vexnum; i++)
        if (G.matrix[v][i] != 0 && G.matrix[v][i] != INF) //邻接矩阵中不在对角线上和不是无穷大
            return i;
    return -1;
}

/* 返回顶点v相对于w的下一个邻接顶点的索引,失败则返回-1 */
static int next_vertix(Graph G, int v, int w)
{
    int i;

    if (v<0 || v>(G.vexnum - 1) || w<0 || w>(G.vexnum - 1))
        return -1;

    for (i = w + 1; i < G.vexnum; i++)
        if (G.matrix[v][i] != 0 && G.matrix[v][i] != INF)
            return i;

    return -1;
}

/* 深度优先搜索遍历图的递归实现 */
void DFS(Graph G, int i, int* visited) {  //从下标为i的顶点出发,遍历该顶点所有邻接点  
    int w;
    visited[i] = 1;
    printf("%c ", G.vexs[i]);
    // 遍历该顶点的所有邻接顶点。若是没有访问过,那么继续往下走
    for (w = first_vertex(G, i); w >= 0; w = next_vertix(G, i, w))
    {
        if (visited[w] == 0)
            DFS(G, w, visited);
    }

}

/*  深度优先搜索遍历图 */
void DFSTraverse(Graph G)
{
    int i;
    int visited[MAX];       // 顶点访问标记

    // 初始化所有顶点都没有被访问
    for (i = 0; i < G.vexnum; i++)
        visited[i] = 0;

    printf("\n深度优先搜索遍历(DFS): ");
    for (i = 0; i < G.vexnum; i++)//按照下标次序和顶点关系,深度遍历图中顶点
    {
        if (visited[i] == 0)
            DFS(G, i, visited);
    }
    printf("\n");
}

广度优先搜索

广度优先搜索(Breadth First Search,BFS)遍历类似于树的按层次遍历的过程。

/* * 广度优先搜索(类似于树的层次遍历) */
void BFS(Graph G)
{
    int head = 0;
    int rear = 0;
    int queue[MAX];     // 辅组队列,用来存放读取的顶点下标
    int visited[MAX];     // 顶点访问标记
    int i, j, k;

    for (i = 0; i < G.vexnum; i++)  //设置所有顶点状态均未访问
        visited[i] = 0;

    printf("\n广度优先搜索遍历(BFS): ");
    for (i = 0; i < G.vexnum; i++)
    {
        if (visited[i]==0)
        {
            visited[i] = 1;
            printf("%c ", G.vexs[i]);
            queue[rear++] = i;  // 入队列
        }
        while (head != rear) 
        {
            j = queue[head++];  // 出队列
            for (k = first_vertex(G, j); k >= 0; k = next_vertix(G, j, k)) //k是为访问的邻接顶点
            {
                if (visited[k]==0)
                {
                    visited[k] = 1;
                    printf("%c ", G.vexs[k]);
                    queue[rear++]=k;
                }
            }
        }
    }
    printf("\n");
}

和深度优先算法搜索类似,广度优先搜索在遍历的过程中也需要一个访问标志数组。

int main()
{
    int prev[MAX] = {0};
    int dist[MAX] = {0};
    Graph* pG;

    printf("       **************************************************\n");
    printf("                      图 的 常 用 算 法\n");
    printf("       **************************************************\n\n");

    //自定义"图"(输入矩阵队列)
    pG = create_graph();
    print_graph(*pG);       // 打印图
    DFSTraverse(*pG);
    BFS(*pG);
    return 0;
}

现实生活中的许多问题都可以利用图来解决,对于图的最小生成树和克鲁斯卡尔算法,下次有机会再分享吧

4.总结

代码部分如果有错误或者可以优化希望大佬们提出来,我再加以改进。最后我们一起进步!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿陈爱编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值