数据结构 图的存储结构(邻接矩阵,邻接表,十字链表,邻接多重表)的实现

数据结构 图的存储结构(邻接矩阵,邻接表,十字链表,邻接多重表)的实现,第1张

图的逻辑结构是多对多,所以图没有顺序存储结构,但可以借助二维数组来表示元素间的关系,这就是数组表示法(邻接矩阵) 链式存储结构:可以用多重链表(邻接表,邻接多重表,十字链表)来表示

重点:邻接矩阵(数组)表示法 和 邻接表(链式)表示法

一:数组(邻接矩阵)表示法

1,先建立一个顶点表(记录各个顶点的信息)和一个邻接矩阵(表示各个顶点之间的关系).

顶点表是一个一维数组Vexs[n]

无向图邻接矩阵是一个二维数组arcs[n][n],如果两个顶点间有边相连,那么arcs[n][n] = 1,否则arcs[n][n] = 0(n为两个顶点的下标)n取决于顶点个数

有向图邻接矩阵是一个二维数组arcs[i][j],如果存在着由i顶点发出的,到j顶点弧,那么arcs[i][j] = 1,别的位置都为0

无向图的邻接矩阵特点:

1,无向图的邻接矩阵是对称的;

2,顶点i的度 = 第i行(列)中1的个数;

3,全完图的邻接矩阵中,对角元素为0,其余都是1;

 在有向图的邻接矩阵中

第i行含义:以结点vi为尾的弧(即出度边)

第j列含义:以结点vj为头的弧(即入度边)

有向图的邻接矩阵特点:

1,有向图的邻接矩阵可能是不对称的.

2,顶点的出度 = 第i行元素之和;顶点的入度 = 第j列元素之和.

3,顶点的度 = 第i行的元素之和 + 第j列的元素之和

网(即有权图)的邻接矩阵表示法

无向网的邻接矩阵,就是把无向图的邻接矩阵中的1,变成权值,0变成无穷大

有向网的邻接矩阵,就是把有向图的邻接矩阵中的1,变成权值,0变成无穷大

 二,邻接矩阵代码实现

邻接矩阵的存储表示:用两个数组分别存储顶点表和邻接矩阵

#include 
#include 
#define MAXSIZE 100 //最大顶点数
typedef struct{
    char vexs[MAXSIZE];//这里的数据类型根据实际情况而定
    int arcs[MAXSIZE][MAXSIZE];//这里的数据类型根据实际情况而定
    int vexnum, arcnum;//图的当前顶点数和边数
}G;
int main()
{
    printf("Hello world!\n");
    return 0;
}
采用邻接矩阵表示法创建无向网

算法思想:1,输入总顶点数和总边数

                   2,依次输入顶点的信息存入顶点表中.

                 3,初始化邻接矩阵,使每个权值初始化为极大值.

                 4,构造邻接矩阵.

#include 
#include 
#define MAXSIZE 100 //最大顶点数
#define MAX_INT 32767//设置最大值
typedef struct{
    char vexs[MAXSIZE];//这里的数据类型根据实际情况而定
    int arcs[MAXSIZE][MAXSIZE];//这里的数据类型根据实际情况而定
    int vexnum, arcnum;//图的当前顶点数和边数
}Graph;
int get_index(char* arr,char m)
{
    int i = 0;
    for(i = 0; i < MAXSIZE; i++)
    {
        if(arr[i] == m)return i;
    }
    return -1;
}

void CreatGraph(Graph* G)
{
    int i,j = 0;
    printf("请输入顶点和边的数量:>");
    scanf("%d%d",&G->vexnum,&G->arcnum);//把输入的值保存到图结构体中
    for(i = 0; i < G->vexnum; i++)//初始化邻接矩阵
    {
        for(j = 0; j < G->vexnum; j++)
            {
                G->arcs[i][j] = MAX_INT;//边的权值设置为极大值
            }

    }

    for(i = 0; i < G->vexnum; i++)
    {
        printf("请输入每个顶点的值:>");
        getchar();//清除键盘缓存区的回车键
        scanf("%c", &G->vexs[i]);//把输入的值保存到顶点数组当中
    }
    for(i = 0; i < G->arcnum; i++)//有几条边,循环几次
    {
        char m,n;//用来接收两个顶点
        int w;//用来接收权值
        int j,k;//接收顶点的下标
        printf("请输入哪两个顶点之间有边,以及边的权值,格式为:ab 10:>");
        getchar();
        scanf("%c%c%d",&m,&n,&w);
        j = get_index(G->vexs,m);//得到输入的m的值,在顶点数组中的下标
        k = get_index(G->vexs,n);//得到输入的n的值,在顶点数组中的下标
        G->arcs[j][k] = w;//给邻接矩阵对应的位置赋权值
        G->arcs[k][j] = w;//因为是无向网,所以是对称的,给对称点赋相同值
    }


}
int main()
{
    Graph G;
    CreatGraph(&G);
    return 0;
}

会创建无向网了,无向图就只是初始化的时候,全部初始化为零,然后权值都为1

有向网就更容易了,别的都不变,只要把G->arcs[k][j] = w去掉,因为有向网不对称

数组(邻接矩阵)表示法的优点是:

1,直观,简单,好理解

2,方便检查任意一对顶点间是否存在边

3,方便找任一顶点的所有"邻接点"(有边直接相连的顶点)

4,方便计算任一顶点的度(从该点发出的边数为"出度",指向该点的边数为"入度")

        无向图:对应行(或列)非0元素的个数

        有向图:对应行非0元素的个数是"出度",对应列非0元素的个数是"入度"

数组(邻接矩阵)表示法的缺点是:

1,不便于增加和删除顶点

2,浪费空间--存稀疏图(点很多而边很少)有大量无效元素,空间复杂度为O(n*n)

3,浪费时间--统计稀疏图中一共有多少条边,时间复杂度也为O(n*n)

三,邻接表表示法(链式)

1,顶点

        按编号将顶点数据存储在一维数组中;

2,关联同一顶点的边(以顶点为尾的弧)

        用线性链表存储

                            无  向  图

 特点:

1邻接表不唯一(上面表结点中1和3可以换位置)

2若无向图中有n个顶点,e条边,则其邻接表需要n个头结点和2e个表结点.适宜存储稀疏图

3无向图中顶点vi的度为第i个单链表中的结点数

          有  向  图

 特点:

1顶点vi的出度为第i个单链表中的结点个数

2顶点vi的入度为整个单链表中邻接点域值是i-1的结点个数

这种邻接表,找出度容易,找入度难,所以就有另外一种逆邻接表

特点:

1顶点vi的入度为第i个单链表中的结点个数

2顶点vi的出度为整个单链表中邻接点域值是i-1的结点个数

 这种邻接表,找入度容易,找出度难,所以我们可以根据实际情况选择合适的邻接表

四,图(网)的邻接表存储代码实现

        用邻接表创建一个无向网:

        表结点的结构体:

#include 
#include 
typedef struct TableNode{
    int index;//结点在数组中的下标
    TableNode* nextarc;
    int info;//权值
    
}TNode;
int main()
{
    return 0;
}

          头结点和整个无向网结构体:

        

#include 
#include 
typedef struct TableNode{//表结点
    int index;//结点在数组中的下标
    TableNode* nextarc;
    int info;//权值
    
}TNode;

typedef struct{//头结点
    char data;
    TNode* firstarc;
}HNode;

typedef struct{//整个无向网的结构体
    HNode* Gragh;
    int vexnum,arc;
}
int main()
{
    return 0;
}

算法思想:1,输入顶点和边的数量,并保存在网结构体中

                2,通过输入的顶点个数,创建头结点的数组

                3,输入顶点的值,并把顶点的firstarc值赋空值

                4,输入每条边的两个顶点值和这条边的权值

                5,利用输入的值创建表结点,并用头插法加入到对应的头结点

 具体的代码如下:       

#include 
#include 
typedef struct TableNode{//表结点
    int index;//结点在数组中的下标
    struct TableNode* nextarc;
    int info;//权值

}TNode;

typedef struct{//头结点
    char data;
    TNode* firstarc;
}HNode;

typedef struct{//整个无向网的结构体
    HNode* Gragh;
    int vexnum,arc;
}UnGragh;

int get_index(HNode* arr, char m, int nums)
{
    int i = 0;
    for(i = 0; i < nums; i++)
    {
        if(arr[i].data == m)
        {
            return i;
        }
    }
    return -1;
}

void CreatUndigraghNet(UnGragh* G)
{
    printf("请输入顶点和边的数量:>");
    scanf("%d%d",&G->vexnum,&G->arc);
    G->Gragh = malloc(sizeof(HNode)*G->vexnum);
    int i = 0;
    for(i = 0;i < G->vexnum; i++)
    {
        printf("请输入顶点的值:>");
        getchar();
        scanf("%c",&G->Gragh[i].data);
        G->Gragh[i].firstarc = NULL;
    }
    for(i = 0;i < G->arc; i++)
    {
        char m,n;//用来接收两个顶点
        int w;//用来接收权值
        int j,k;//接收顶点的下标
        printf("请输入哪两个顶点之间有边,以及边的权值,格式为:ab 10:>");
        getchar();
        scanf("%c%c%d",&m,&n,&w);
        j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
        k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
        TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
        P1->index = k;//给表结点赋值
        P1->info = w;
        P1->nextarc = G->Gragh[j].firstarc;//利用头插法把表结点插入到头结点后面
        G->Gragh[j].firstarc = P1;
        //因为是无向的,所以下面还要再建立一个反向的边
        TNode* P2 = (TNode*)malloc(sizeof(TNode));
        P2->index = j;
        P2->info = w;
        P2->nextarc = G->Gragh[k].firstarc;
        G->Gragh[k].firstarc = P2;


    }
}
int main()
{
    UnGragh G;
    CreatUndigraghNet(&G);
    return 0;
}

五,图的 十字链表 表示法

十字链表,就是集中了 邻结表 和 逆邻结表 的优势,找出度和找入度都容易的一种链表,适用于有向图或网的查找

 firstout可以理解为邻结表的表头,方便查出度(指向出度)

firstin 可以理解为逆邻结表的表头,方便查入度(指向入度)

headvex 作为出度时对应头结点数组中的下标(弧头位置)

tailvex 作为入度时对应头结点数组中的下标(弧尾位置)

Tlink 弧尾相同的下一条弧

Hlink 弧头相同的下一条弧

继续用上面的例子:

把两个表揉合在一起,组成十字链表为: 

 用C语言,生成十字链表有向图,代码为:

#include 
#include 
typedef struct TableNode{//表结点
    int headvex;//弧头位置(下标)
    int tailvex;//弧尾位置(下标)
    struct TableNode* Hlink;//弧头相同的下一条弧
    struct TableNode* Tlink;//弧尾相同的下一条弧

}TNode;

typedef struct{//头结点
    char data;
    TNode* firstin;//指向入度
    TNode* firstout;//指向出度
}HNode;

typedef struct{//整个有向图的结构体
    HNode* Gragh;
    int vexnum,arc;
}UnGragh;

int get_index(HNode* arr, char m, int nums)
{
    int i = 0;
    for(i = 0; i < nums; i++)
    {
        if(arr[i].data == m)
        {
            return i;
        }
    }
    return -1;
}

void CreatUndigraghNet(UnGragh* G)
{
    printf("请输入顶点和边的数量:>");
    scanf("%d%d",&G->vexnum,&G->arc);
    G->Gragh = malloc(sizeof(HNode)*G->vexnum);
    int i = 0;
    for(i = 0;i < G->vexnum; i++)
    {
        printf("请输入顶点的值:>");
        getchar();
        scanf("%c",&G->Gragh[i].data);
        G->Gragh[i].firstin = NULL;
        G->Gragh[i].firstout = NULL;
    }
    for(i = 0;i < G->arc; i++)
    {
        char m,n;//用来接收两个顶点
        int j,k;//接收顶点的下标
        printf("请按方向输入每一条边的两个顶点值,格式为:ab>");
        getchar();
        scanf("%c%c",&m,&n);
        j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
        k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
        TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
        //利用头插法把出度边插入到G->Gragh[j]后面
        P1->tailvex = j;
        P1->headvex = k;
        P1->Tlink = G->Gragh[j].firstout;
        G->Gragh[j].firstout = P1;
        //利用头插法把入度边插入到G->Gragh[k]后面
        P1->Hlink = G->Gragh[k].firstin;
        G->Gragh[k].firstin = P1;



    }
}
int main()
{
    UnGragh G;
    CreatUndigraghNet(&G);
    return 0;
}
六,邻接多重表

邻接多重表主要是用在无向图中,解决无向图中每条边都要存储两遍,造成浪费的问题

邻接多重表的头结点和表结点的存储结构为:

data:顶点的值

firstedge:连接该顶点的第一条边

ivex, jvex:为一条边的两个顶点的下标 

ilink:关联   ivex下标代表的头结点的下一条边

jlink:关联   jvex下标代表的头结点的下一条边

例:

 具体的生成代码为:

#include 
#include 
typedef struct TableNode{//表结点
    int ivex;
    int jvex;//一条边的两个顶点
    struct TableNode* ilink;//关联ivex的下一条边指针
    struct TableNode* jlink;//关联jvex的下一条边指针
}TNode;

typedef struct{//头结点
    char data;
    TNode* firstedge;//指向第一条边
}HNode;

typedef struct{//无向图的结构体
    HNode* Gragh;
    int vexnum,arc;
}UnGragh;

int get_index(HNode* arr, char m, int nums)//根据输入得到下标
{
    int i = 0;
    for(i = 0; i < nums; i++)
    {
        if(arr[i].data == m)
        {
            return i;
        }
    }
    return -1;
}

void CreatUndigraghNet(UnGragh* G)
{
    printf("请输入顶点和边的数量:>");
    scanf("%d%d",&G->vexnum,&G->arc);
    G->Gragh = malloc(sizeof(HNode)*G->vexnum);
    int i = 0;
    for(i = 0;i < G->vexnum; i++)
    {
        printf("请输入顶点的值:>");
        getchar();
        scanf("%c",&G->Gragh[i].data);
        G->Gragh[i].firstedge = NULL;
    }
    for(i = 0;i < G->arc; i++)
    {
        char m,n;//用来接收两个顶点
        int j,k;//接收顶点的下标
        printf("请按方向输入每一条边的两个顶点值,格式为:ab>");
        getchar();
        scanf("%c%c",&m,&n);
        j = get_index(G->Gragh,m,G->vexnum);//得到输入的m的值,在顶点数组中的下标
        k = get_index(G->Gragh,n,G->vexnum);//得到输入的n的值,在顶点数组中的下标
        TNode* P1 = (TNode*)malloc(sizeof(TNode));//创建一个表结点
        //利用头插法把输入的第一条边插入到头结点G->Gragh[j]后
        P1->ivex = j;
        P1->jvex = k;
        P1->ilink = G->Gragh[j].firstedge;
        G->Gragh[j].firstedge = P1;
        //利用头插法把输入的第一条边插入到头结点G->Gragh[k]后面
        P1->jlink = G->Gragh[k].firstedge;
        G->Gragh[k].firstedge = P1;



    }
}
int main()
{
    UnGragh G;
    CreatUndigraghNet(&G);
    return 0;
}

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/789659.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-05
下一篇 2022-05-05

发表评论

登录后才能评论

评论列表(0条)

保存