数据结构之自建算法库——图及其存储结构(邻接矩阵、邻接表)

简介: 本文是[数据结构基础系列(7):图]中第4课时[图的邻接矩阵存储结构及算法]和第5课时[图的邻接表存储结构及算法],并为后续内容的实践提供支持。  图的存储结构主要包括邻接矩阵和邻接表,本算法库提供存储结构的定义,以及用于构造图存储结构、不同结构的转换及显示的代码。算法库采用程序的多文件组织形式,包括两个文件:      1.头文件:graph.h,包含定义图数据

本文是[数据结构基础系列(7):图]中第4课时[图的邻接矩阵存储结构及算法]和第5课时[图的邻接表存储结构及算法],并为后续内容的实践提供支持。

  图的存储结构主要包括邻接矩阵和邻接表,本算法库提供存储结构的定义,以及用于构造图存储结构、不同结构的转换及显示的代码。算法库采用程序的多文件组织形式,包括两个文件:
  
  1.头文件:graph.h,包含定义图数据结构的代码、宏定义、要实现算法的函数的声明;

#ifndef GRAPH_H_INCLUDED
#define GRAPH_H_INCLUDED

#define MAXV 100                //最大顶点个数
#define INF 32767       //INF表示∞
typedef int InfoType;

//以下定义邻接矩阵类型
typedef struct
{
    int no;                     //顶点编号
    InfoType info;              //顶点其他信息,在此存放带权图权值
} VertexType;                   //顶点类型

typedef struct                  //图的定义
{
    int edges[MAXV][MAXV];      //邻接矩阵
    int n,e;                    //顶点数,弧数
    VertexType vexs[MAXV];      //存放顶点信息
} MGraph;                       //图的邻接矩阵类型

//以下定义邻接表类型
typedef struct ANode            //弧的结点结构类型
{
    int adjvex;                 //该弧的终点位置
    struct ANode *nextarc;      //指向下一条弧的指针
    InfoType info;              //该弧的相关信息,这里用于存放权值
} ArcNode;

typedef int Vertex;

typedef struct Vnode            //邻接表头结点的类型
{
    Vertex data;                //顶点信息
    int count;                  //存放顶点入度,只在拓扑排序中用
    ArcNode *firstarc;          //指向第一条弧
} VNode;

typedef VNode AdjList[MAXV];    //AdjList是邻接表类型

typedef struct
{
    AdjList adjlist;            //邻接表
    int n,e;                    //图中顶点数n和边数e
} ALGraph;                      //图的邻接表类型

//功能:由一个反映图中顶点邻接关系的二维数组,构造出用邻接矩阵存储的图
//参数:Arr - 数组名,由于形式参数为二维数组时必须给出每行的元素个数,在此将参数Arr声明为一维数组名(指向int的指针)
//      n - 矩阵的阶数
//      g - 要构造出来的邻接矩阵数据结构
void ArrayToMat(int *Arr, int n, MGraph &g); //用普通数组构造图的邻接矩阵
void ArrayToList(int *Arr, int n, ALGraph *&); //用普通数组构造图的邻接表
void MatToList(MGraph g,ALGraph *&G);//将邻接矩阵g转换成邻接表G
void ListToMat(ALGraph *G,MGraph &g);//将邻接表G转换成邻接矩阵g
void DispMat(MGraph g);//输出邻接矩阵g
void DispAdj(ALGraph *G);//输出邻接表G

#endif // GRAPH_H_INCLUDED

  2.源文件:graph.cpp,包含实现各种算法的函数的定义

#include <stdio.h>
#include <malloc.h>
#include "graph.h"

//功能:由一个反映图中顶点邻接关系的二维数组,构造出用邻接矩阵存储的图
//参数:Arr - 数组名,由于形式参数为二维数组时必须给出每行的元素个数,在此将参数Arr声明为一维数组名(指向int的指针)
//      n - 矩阵的阶数
//      g - 要构造出来的邻接矩阵数据结构
void ArrayToMat(int *Arr, int n, MGraph &g)
{
    int i,j,count=0;  //count用于统计边数,即矩阵中非0元素个数
    g.n=n;
    for (i=0; i<g.n; i++)
        for (j=0; j<g.n; j++)
        {
            g.edges[i][j]=Arr[i*n+j]; //将Arr看作n×n的二维数组,Arr[i*n+j]即是Arr[i][j],计算存储位置的功夫在此应用
            if(g.edges[i][j]!=0 && g.edges[i][j]!=INF)
                count++;
        }
    g.e=count;
}

void ArrayToList(int *Arr, int n, ALGraph *&G)
{
    int i,j,count=0;  //count用于统计边数,即矩阵中非0元素个数
    ArcNode *p;
    G=(ALGraph *)malloc(sizeof(ALGraph));
    G->n=n;
    for (i=0; i<n; i++)                 //给邻接表中所有头节点的指针域置初值
        G->adjlist[i].firstarc=NULL;
    for (i=0; i<n; i++)                 //检查邻接矩阵中每个元素
        for (j=n-1; j>=0; j--)
            if (Arr[i*n+j]!=0)      //存在一条边,将Arr看作n×n的二维数组,Arr[i*n+j]即是Arr[i][j]
            {
                p=(ArcNode *)malloc(sizeof(ArcNode));   //创建一个节点*p
                p->adjvex=j;
                p->info=Arr[i*n+j];
                p->nextarc=G->adjlist[i].firstarc;      //采用头插法插入*p
                G->adjlist[i].firstarc=p;
            }

    G->e=count;
}

void MatToList(MGraph g, ALGraph *&G)
//将邻接矩阵g转换成邻接表G
{
    int i,j;
    ArcNode *p;
    G=(ALGraph *)malloc(sizeof(ALGraph));
    for (i=0; i<g.n; i++)                   //给邻接表中所有头节点的指针域置初值
        G->adjlist[i].firstarc=NULL;
    for (i=0; i<g.n; i++)                   //检查邻接矩阵中每个元素
        for (j=g.n-1; j>=0; j--)
            if (g.edges[i][j]!=0)       //存在一条边
            {
                p=(ArcNode *)malloc(sizeof(ArcNode));   //创建一个节点*p
                p->adjvex=j;
                p->info=g.edges[i][j];
                p->nextarc=G->adjlist[i].firstarc;      //采用头插法插入*p
                G->adjlist[i].firstarc=p;
            }
    G->n=g.n;
    G->e=g.e;
}

void ListToMat(ALGraph *G,MGraph &g)
//将邻接表G转换成邻接矩阵g
{
    int i,j;
    ArcNode *p;
    g.n=G->n;   //根据一楼同学“举报”改的。g.n未赋值,下面的初始化不起作用
    g.e=G->e;
    for (i=0; i<g.n; i++)   //先初始化邻接矩阵
        for (j=0; j<g.n; j++)
            g.edges[i][j]=0;
    for (i=0; i<G->n; i++)  //根据邻接表,为邻接矩阵赋值
    {
        p=G->adjlist[i].firstarc;
        while (p!=NULL)
        {
            g.edges[i][p->adjvex]=p->info;
            p=p->nextarc;
        }
    }
}

void DispMat(MGraph g)
//输出邻接矩阵g
{
    int i,j;
    for (i=0; i<g.n; i++)
    {
        for (j=0; j<g.n; j++)
            if (g.edges[i][j]==INF)
                printf("%3s","∞");
            else
                printf("%3d",g.edges[i][j]);
        printf("\n");
    }
}

void DispAdj(ALGraph *G)
//输出邻接表G
{
    int i;
    ArcNode *p;
    for (i=0; i<G->n; i++)
    {
        p=G->adjlist[i].firstarc;
        printf("%3d: ",i);
        while (p!=NULL)
        {
            printf("-->%d/%d ",p->adjvex,p->info);
            p=p->nextarc;
        }
        printf("\n");
    }
}

3.在同一项目(project)中建立一个源文件(如main.cpp),编制main函数,完成相关的测试工作。 例:

#include <stdio.h>
#include <malloc.h>
#include "graph.h"

int main()
{
    MGraph g1,g2;
    ALGraph *G1,*G2;
    int A[6][6]=
    {
        {0,5,0,7,0,0},
        {0,0,4,0,0,0},
        {8,0,0,0,0,9},
        {0,0,5,0,0,6},
        {0,0,0,5,0,0},
        {3,0,0,0,1,0}
    };

    ArrayToMat(A[0], 6, g1);  //取二维数组的起始地址作实参,用A[0],因其实质为一维数组地址,与形参匹配
    printf(" 有向图g1的邻接矩阵:\n");
    DispMat(g1);

    ArrayToList(A[0], 6, G1);
    printf(" 有向图G1的邻接表:\n");
    DispAdj(G1);

    MatToList(g1,G2);
    printf(" 图g1的邻接矩阵转换成邻接表G2:\n");
    DispAdj(G2);

    ListToMat(G1,g2);
    printf(" 图G1的邻接表转换成邻接邻阵g2:\n");
    DispMat(g2);
    printf("\n");
    return 0;
}
目录
相关文章
|
4天前
|
存储 算法 C语言
数据结构基础详解(C语言): 二叉树的遍历_线索二叉树_树的存储结构_树与森林详解
本文从二叉树遍历入手,详细介绍了先序、中序和后序遍历方法,并探讨了如何构建二叉树及线索二叉树的概念。接着,文章讲解了树和森林的存储结构,特别是如何将树与森林转换为二叉树形式,以便利用二叉树的遍历方法。最后,讨论了树和森林的遍历算法,包括先根、后根和层次遍历。通过这些内容,读者可以全面了解二叉树及其相关概念。
|
4天前
|
存储 机器学习/深度学习 C语言
数据结构基础详解(C语言): 树与二叉树的基本类型与存储结构详解
本文介绍了树和二叉树的基本概念及性质。树是由节点组成的层次结构,其中节点的度为其分支数量,树的度为树中最大节点度数。二叉树是一种特殊的树,其节点最多有两个子节点,具有多种性质,如叶子节点数与度为2的节点数之间的关系。此外,还介绍了二叉树的不同形态,包括满二叉树、完全二叉树、二叉排序树和平衡二叉树,并探讨了二叉树的顺序存储和链式存储结构。
|
26天前
|
算法
【初阶数据结构】复杂度算法题篇
该方法基于如下的事实:当我们将数组的元素向右移动 k 次后,尾部 kmodn 个元素会移动至数组头部,其余元素向后移动 kmodn 个位置。
|
27天前
|
机器学习/深度学习 人工智能 算法
【人工智能】线性回归模型:数据结构、算法详解与人工智能应用,附代码实现
线性回归是一种预测性建模技术,它研究的是因变量(目标)和自变量(特征)之间的关系。这种关系可以表示为一个线性方程,其中因变量是自变量的线性组合。
38 2
|
26天前
|
算法
【初阶数据结构篇】二叉树算法题
二叉树是否对称,即左右子树是否对称.
|
26天前
|
存储 算法 测试技术
【初阶数据结构篇】实现顺序结构二叉树(堆的实现方法)
注意传过去的参数是插入的位置,即插入前的size,在调整完后再将size++
|
26天前
|
算法 索引
【初阶数据结构篇】单链表算法题进阶
深拷贝应该正好由 n 个全新节点组成,其中每个新节点的值都设为其对应的原节点的值。
|
26天前
|
存储 算法
【初阶数据结构篇】顺序表和链表算法题
此题可以先找到中间节点,然后把后半部分逆置,最近前后两部分一一比对,如果节点的值全部相同,则即为回文。
|
2天前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
|
4天前
|
存储 C语言
数据结构基础详解(C语言): 栈与队列的详解附完整代码
栈是一种仅允许在一端进行插入和删除操作的线性表,常用于解决括号匹配、函数调用等问题。栈分为顺序栈和链栈,顺序栈使用数组存储,链栈基于单链表实现。栈的主要操作包括初始化、销毁、入栈、出栈等。栈的应用广泛,如表达式求值、递归等场景。栈的顺序存储结构由数组和栈顶指针构成,链栈则基于单链表的头插法实现。