【数据结构】【C语言】图

前言

本文为个人笔记。作者会不定时修改文章内容。

图存储结构用来存储具有“多对多”逻辑关系的数据。

1. 应知应会

1.1 顶点

图中存储的各个数据元素被称为顶点(不是节点)。

习惯用V[i]表示图中的顶点,且所有的顶点构成的集合通常用V表示,例如V={V1, V2, V3, V4}。

1.2 边或弧

  • 连接顶点的没有方向的线叫做,也叫做无向边
  • 用(V1, V2)表示连通顶点V1和顶点V2的边
  • 连接顶点的有方向的线叫做,也叫做有向边
  • 用<V1, V2>表示连通顶点V1和顶点V2的弧
  • 指向的顶点被称为终端点弧头
  • 远离的顶点被称为初始点弧尾

1.3 图的组成

  • 由一些顶点和一些连接顶点的线组成的

1.4 无向图和有向图

1.4.1 无向图 Undirected Graph

  • 由一些顶点和一些连接顶点的无向边组成的

1.4.2 有向图 Directed Graph

  • 由一些顶点和一些连接顶点的组成的。
入度

对于有向图中的一个顶点来说,弧头指向该顶点的弧的数量为该顶点的入度(InDegree,记为ID(V))

出度

对于有向图中的一个顶点来说,弧头远离该顶点的弧的数量为该顶点的出度(OutDegree,记为OD(V))

1.5 集合VR的含义

用VR表示图中所有顶点之间关系的集合
VR={V1, V2, V3, V4, V5}

1.6 路径和回路

无论是有向图还是无向图,从一个顶点到另一个顶点途径的所有顶点组成的序列(包含这两个顶点),称为一条路径

如果路径中第一个顶点和最后一个顶点相同,则此路径称为回路,或称为

路径中各顶点都不重复,此路径又被称为简单路径
回路中的顶点互不重复,此回路被称为简单回路

图中一部分顶点和边构成的图,称为原图的子图

1.7 权和网

在某些场景中,图中的每条边(弧)会被赋予一个实数来表示一定的含义,这种与边(或弧)向匹配的实数被称为,而带权的图常称为

1.8 图存储结构的分类

1.8.1 完全图

若图中各个顶点都与除自身外的其他顶点有关系,这样的无向图称为完全图
满足此条件的有向图则称为有向完全图

  • 具有n个顶点的完全图,图中的数量为n(n-1)/2
  • 对于具有n个顶点的有向完全图,图中的数量为n(n-1)

1.8.2 稀疏图和稠密图

  • 稀疏图和稠密图是相对存在的
  • 如果图中具有很少的(或),此图就称为”稀疏图“,反之,此图称为”稠密图“
  • 稀疏和稠密的判断条件是:e<nlogn,其中e表示(或)的数量,n表示顶点的数量。

1.8.3 连通图

1.8.3.1 连通图

无向图中,如果任意两个顶点之间都能够连通,则称此无向图为连通图

无向图不是连通图,但图中存储的某个子图符合连通图的性质,则称为该子图为连通分量

1.8.3.2 强连通图

有向图中,如果任意两个顶点之间,双向都连通,也就是至少含有一个通路,则称此有向图为强连通图

有向图不是强连通图,但其包含的连通子图具有强连通图的性质,则称该连通子图为强连通分量

1.8.3.3 生成树

连通图进行遍历,过程中所经历的顶点的组合可看作是一颗普通树,通常称为生成树

连通图中生成树的必需满足的条件
  • 包含连通图中所有的顶点
  • 任意两顶点之间有且只有一条通路

连通图的生成树具有这样的特性,即生成树中边的数量 = 顶点数 - 1

1.8.3.4 生成森林

生成树是对连通图来说的,而生成森林是对应非连通图来说

2. 图的顺序存储结构

使用数组存储图时,需要使用两个数组。

  • 一个数组存放图中顶点本身的数据(一维数组)
  • 另外一个数组用于存储各顶点之间的关系(二维数组)

2.1 图的结构

#define MAX_VERtEX_NUM 20   //顶点的最大个数
#define VRType int  //顶点之间关系的变量类型,无权图,用0和1表示,有权图直接为权值
#define InfoType char   //存储弧或者边的额外信息的指针
#define VertexType infoType   //图中顶点的数据类型

typedef enum{DG, DN, UDG, UDN}GrapKind;   //枚举图的4中类型,有向图,有向网、图,网

typedef struct
{
    VRType adj;  //无权图,用0和1表示是否相邻;对于带权图,直接为权值;
    InfoType *info;  //图或者边额外含有的信息指针
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

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

2.2 图的顺序存储结构实现

#include<stdio.h>

#define MAX_VERtEX_NUM 20
#define VRType int
#define InfoType char
#define VertexType int

typedef enum{DG, DN, UDG, UDN}GraphKind;

typedef struct
{
    VRType adj;
    InfoType *info;
}ArcCell, AdjMatrix[MAX_VERtEX_NUM][MAX_VERtEX_NUM];

typedef struct
{
    VertexType vexs[MAX_VERtEX_NUM];
    AdjMatrix arcs;
    int vexnum, arcnum;
    GrapKind kind;
}MGraph;

int LocatVex(MGraph *G, VertexType v)
{
    int i = 0;
    
    for(; i < G->vexnum; i++)
    {
        if(G->vexs[i] == v)
        {
            break;
        }
    }

    if(i >= G->vexnum)
    {
        printf("No such vertex.\n");
        return -1;
    }

    return i;
}

//构造有向图
void CreatDG(MGraph *G)
{
    scanf("%d, %d", &(G->vexnum), &(G->arcnum));

    for(int i = 0; i <= G->vexnum; i++)
    {
        G->vexnum = i + 1;  //输入顶点数据
    }

    //初始化二维矩阵,全部归0,指针指向NULL
    for(int i = 0; i < G->vexnum; i++)
    {
        for(int j = 0; j < G->vexnum; j++)
        {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }

    for(int i = 0; i <G->arcnum; i++)
    {
        int v1, v2;

        //输入弧头和弧尾
        scanf("%d, %d", &v1, &v2);

        //确定顶点位置
        int n = LocateVex(G, v1);
        int m = LocateVex(G, v2);

        //排除错误数据
        if(m == -1 || n == -1)
        {
            printf("No this vertex\n");
            return;
        }

        G->arcs[n][m].adj = 1;
    }
}

//构造无向图
void CreatUDM(MGraph *G)
{
    scanf("%d, %d", &(G->vexnum), &(G->arcnum));

    for(int i = 0; i < G->vexnum; i++)
    {
        scanf("%d", &(G->vexs[i]));
    }

    for(int i = 0; i < G->vexnum; i++)
    {
        for(int j = 0; j < G->vexnum; j++)
        {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }

    for(int i = 0; i < G->arcnum; i++)
    {
        int v1, v2;
        scanf("%d, %d", &v1, &v2);

        int n = LocateVex(G, v1);
        int 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 CreatDN(MGraph *G)
{
    //输入顶点vexnum, 弧(或边)vexnum
    scanf("%d, %d", &(G->vexnum), &(G->vexnum));

    //输入各顶点的数据
    for(int i = 0; i <= G->vexnum; i++)
    {
        //手动输入
        //scanf("%d", &(G->vexs[i]));

        //这里赋值i+1
        G->vexs[i] = i + 1;
    }

    //arcs[i][j].adj赋初值0,也就是将权值置为0
    for(int i = 0; i <= G->vexnum; i++)
    {
        for(int j = 0; j <= G->vexnum; j++)
        {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }

    //给每个弧(或边)输入权值
    for(int i = 0; i < G->arcnum; i++)
    {
        int v1, v2, w;

        scanf("%d, %d, %d", &v1, &v2, &w);

        int n = LocateVex(G, v1);
        int m = LocateVex(G, v2);

        if(m == -1 || n == -1)
        {
            printf("No this vertex\n");

            return;
        }

        G->arcs[n][m].adj = w;
    }
}

//构造无向网
void CreatUDN(MGraph *G)
{
    //输入顶点vexnum,弧或边arcnum
    scanf("%d, %d", &(G->vexnum), &(G->arcnum));

    //输入顶点的值
    for(int i = 0; i < G->vexnum; i++)
    {
        scanf("%d", &(G->vexs[i]));
    }

    for(int i = 0; i < G->vexnum; i++)
    {
        for(int j = 0; j < G->vexnum; j++)
        {
            G->arcs[i][j].adj = 0;
            G->arcs[i][j].info = NULL;
        }
    }

    for(int i = 0; i < G->arcnum; i++)
    {
        int v1, v2, w;
        scanf("%d, %d, %d", &v1, &v2, &w);

        int m = LocateVex(G, v1);
        int 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 GreateUDG(G);
            break;
        case UDM:
            return CreateUDM(G);
            break;
        default:
            break;
    }
}

void PrintGrapth(MGrap G)
{
    for(int i = 0; i < G.vexnum; i++)
    {
        for(int j = 0; j < G.vexnum; j++)
        {
            printf("%d", G.arcs[i][j].adj);
        }
        printf("\n");
    }
}

int main()
{
    MGraph G;  //建立一个图的变量

    CreateGraph(&G);  //调用创建函数,传入地址参数
    PrintGraph(G);  //输出图的二阶矩阵;

    return 0;
}

3. 图的链表存储

图更多的是采用链表存储,具体的实现方法分别是邻接表邻接多重表十字链表

3.1 图的邻接表存储结构

即适用存储无向图,也适用于存储有向图

  • 邻接点,在图中,如果两个点相互连通,即通过其中一个顶点,可以直接找到另一个顶点,则称它们互为邻接点
  • 邻接,指图中顶点之间有边或者弧的存在

实现方式

给图中各个顶点独自建立一个链表,用链表中的节点存储该顶点,用链表中其他节点存储各自的邻接点。

3.1.1 结构

#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;

3.1.2 实现

3.2 图的十字链表存储结构

3.2.1 结构

3.2.2 实现

3.3 图的邻接多重表存储结构

3.3.1 结构

3.3.2 实现

结语

感谢阅读,欢迎各位读者在评论区留言。如果文章对你有帮助,欢迎点赞收藏。

猜你喜欢

转载自blog.csdn.net/weixin_45117176/article/details/132080914