数据结构与算法学习 (12)图的存储

图状结构是一种比树形结构更复杂的非线性结构。在树形结构中,结点间具有分支层次关系,每一层上的结点只能和上一层的至多一个结点相关,但可能和下一层的多个结点相关。而在图状结构中,任意两个结点之间都可能相关,即结点之间的邻接关系可以是任意的。因此,图是 比树更一般、更复杂的非线性结构,常被用于描述各种复杂的数据对象,在自然科学、社会科学和人文科学等许多领域有着非常广泛的应用。

图(Graph)是由非空的顶点集合和一个描述顶点之间的关系——边(或者弧)的集合组成的,其形式化定义为:G=(V,E)、V={v1|v1包含data object}、E={(v1,vj)|(vi,vj 包含V^P(vj,vj)。其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合,集合E中P(vi,vj)表示顶点vi和顶点vj之间有一条直接连线,即偶对(v1,vj)表示一条边。如:G2=(V2,E2)、V2={v1,v2,v3,v4}、E2={,,,}。

1、无向图:图中顶点与顶点连接没有方向;无向边:图中边没有方向

2.有向图:图中顶点与顶点之间有方向;有向边:图中边具有方向


3.完全图:顶点与顶点之间完全连接


4.网:当图的边加上权重后就是网。



5.顶点的度、入度、出度:顶点的度(Degree)是指依附于某顶点v的边数,通常记为TD(v)。顶点v的入度是指以顶点v为终点的弧的数目,记为ID(V);出度是指以顶点v为始点的弧的数目,记为OD(V)。有TD(V)=ID(v)+OD(v)。

6.边的权、网:与边有关的数据信息称为权(Weight)。在实际应用中,权值可以有某种含义。例如,在一个反映城市交通线路的图中,边上的权值可以表示该条线路的长度或等级;对于一个电子线路图,边上的权值可以表示两个端点之间的电阻、电流或电压值;对于反映工程进度的图而言,边上的权值可以表示从前一个工程到后一个工程所需要的时间或其他代价等。边上带权的图称为网或网络(network)。
7.路径、路径长度:顶点vp到顶点vq之间的路径(path)是指顶点序列vp、vi1、vi2、···、vim、vq。其中,(vp,vi1)、(vi1,vi2)、···、(vim,vq)分别为图中的边。路径上边的数目称为路径长度。

8.简单路径、回路、简单回路:序列中顶点不重复出现的路径称为简单路径。路径中第一个顶点与最后一个顶点相同的 路径称为回路或环(Cycle)。除第一个顶点与最后一个顶点之外,其他顶点不重复出现的回路称为简单回路,或者简单环。

9.子图:对于图G=(V,E),G'=(V',E'),若存在 V'是V的子集, E'是E的子集,则称图 G'是G的的一个子图。

10.连通、连通图、连通分量:在无向图中,如果从一个顶点vi到另一个顶点vj(i=!j)存在路径,则称顶点vi和vj是连通的。如果图中任意两个顶点都是连通的,则称该图是连通图。无向图的极大连通子图称为连通分量,极大连通子图是指在保证连通与子图的条件下,包含原图中所有的顶点与边

存储结构
邻接矩阵:就是用一维数组存储图中的顶点信息,用矩阵表示图中各顶点的信息,用矩阵表示图中各顶点的信息,用矩阵表示图中各顶点之间的邻接关系。假设图G=(V,E)有n个确定的顶点,即V ={v0,v1,···,vn-1},则表示G中各顶点相邻关系的矩阵为一个n×n的矩阵,矩阵的元素为:

A[i][j]={1,若(vi,vj)或是E(G)中的边 ;2,若(vi,vj)或不是E(G)中的边。


若G是网,则邻接矩阵可定义为:

A[i][j]={wij,若(vi,vj)或是E(G)中的边 ;0或&,若(vi,vj)或不是E(G)中的边。

其中,wij表示边(Vi,vj)或上的权值;表示一个计算机允许的、大于所有边上权值的数。



代码实现:

#define MAXVEX 100 /* 最大顶点数,应由用户定义 */
#define INFINITYC 0

typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由用户定义  */
typedef int EdgeType; /* 边上的权值类型应由用户定义 */

typedef struct{
    
    VertexType vexs[MAXVEX];/*定点表*/
    EdgeType arc[MAXVEX][MAXVEX];/*邻接矩阵,可看出边表*/
    int  numNodes,numEdges;/*图中的顶点数和边数*/
    
}MYGraph;

void CreateMGraph(MGraph *G){
    
    int i,j,k,w;
    printf("输入顶点数和边数:\n");
    //1. 输入顶点数/边数
    scanf("%d,%d",&G->numNodes,&G->numEdges);
    printf("顶点数:%d,边数:%d\n",G->numNodes,G->numEdges);
    
    //2.输入顶点信息/顶点表
    for(i = 0; i<= G->numNodes;i++)
        scanf("%c",&G->vexs[I]);
    
    //3.初始化邻接矩阵
    for(i = 0; i < G->numNodes;i++)
         for(j = 0; j < G->numNodes;j++)
             G->arc[i][j] = INFINITYC;
    
    //4.输入边表信息
    for(k = 0; k < G->numEdges;k++){
        printf("输入边(vi,vj)上的下标i,下标j,权w\n");
        scanf("%d,%d,%d",&i,&j,&w);
        
        G->arc[i][j] = w;
        //如果无向图,矩阵对称;
        G->arc[j][i] = G->arc[i][j];
        
    }
    /*5.打印邻接矩阵*/
    for (int i = 0; i < G->numNodes; i++) {
        printf("\n");
        for (int j = 0; j < G->numNodes; j++) {
            printf("%d ",G->arc[i][j]);
        }
    }
    printf("\n");
}

邻接表:是图的一种顺序存储于链式存储结合的存储方法。邻接表表示法类似于树的孩子链表表示法。就是对于图G中的每个顶点vi,将所有邻接于vi的顶点vj链成一个单链表,这个单链表就称为顶点vi的邻接表,再将所有顶点的邻接表表头放到数组中,就构成了图邻接表。

171e3f6f95b83455.png
171e3f8354be4022.png
171e3ff40a2be34f.png

邻接表的实现思路:


171e4002cda265a1.png

代码实现:

#include 
#include "stdlib.h"
#include "math.h"
#include "time.h"

#define M 100
#define true 1
#define false 0

typedef char Element;
typedef int BOOL;
//邻接表的节点
typedef struct Node{
    int adj_vex_index;  //弧头的下标,也就是被指向的下标
    Element data;       //权重值
    struct Node * next; //边指针
}EdgeNode;

//顶点节点表
typedef struct vNode{
    Element data;          //顶点的权值
    EdgeNode * firstedge;  //顶点下一个是谁?
}VertexNode, Adjlist[M];

//总图的一些信息
typedef struct Graph{
    Adjlist adjlist;       //顶点表
    int arc_num;           //边的个数
    int node_num;          //节点个数
    BOOL is_directed;      //是不是有向图
}Graph, *GraphLink;
#pragma mark - 邻接表的存储
void SaveGraph(GraphLink *Grap){
    int i,j,k;
    EdgeNode *p;
    printf("输入顶点数目,边数和有向?:\n");
    scanf("%d %d %d", &(*Grap)->node_num, &(*Grap)->arc_num, &(*Grap)->is_directed);
    for (i = 0; i<(*Grap)->node_num; i++) {
        getchar();
        scanf("%c",&(*Grap)->adjlist[i].data);
        (*Grap)->adjlist[i].firstedge = NULL;
    }
    printf("输入边信息\n");
    for (k = 0; k<(*Grap)->arc_num; k++) {
        getchar();
        scanf("%d %d",&i,&j);
        //创建结点
        p = (EdgeNode *)malloc(sizeof(EdgeNode));
        //设置弧头下标
        p->adj_vex_index = j;
        //使用头插法
        p->next = (*Grap)->adjlist[i].firstedge;
        (*Grap)->adjlist[i].firstedge = p;
        if (!(*Grap)->is_directed) {//无向图时
            //t无向图是对称的,所以代码给上面一致,唯一的区别是坐标变为j,j变为i
            //创建结点
            p = (EdgeNode *)malloc(sizeof(EdgeNode));
            //设置弧头下标
            p->adj_vex_index = i;
            //使用头插法
            p->next = (*Grap)->adjlist[j].firstedge;
            (*Grap)->adjlist[j].firstedge = p;
        }
    }
}
#pragma mark - 输出邻接表数据
void putGraph(GraphLink g){
    int i;
    printf("邻接表中存储信息:\n");
    //遍历一遍顶点坐标,每个再进去走一次
    for (i = 0; i < g->node_num; i++) {
        EdgeNode * p = g->adjlist[i].firstedge;
        while (p) {
            printf("%c->%c ", g->adjlist[i].data, g->adjlist[p->adj_vex_index].data);
            p = p->next;
        }
        printf("\n");
    }
}

int main(int argc, const char * argv[]) {
    // insert code here...
    printf("Hello, World!\n");
    GraphLink Grap = (GraphLink)malloc(sizeof(Graph));
    SaveGraph(&Grap);
    putGraph(Grap);
    return 0;
}



你可能感兴趣的:(数据结构与算法学习 (12)图的存储)