图的存储结构分析

图的基本概念与性质

图是研究数据元素之间的多对多的关系。在这种结构中,任意两个元素之间可能存在关系。即结点之间的关系可以是任意的,图中任意元素之间都可能相关。

图的定义和术语

一个图(G)定义为一个偶对(V,E) ,记为G=(V,E) 。其中: V是顶点(Vertex)的非空有限集合,记为V(G);E是无序集V&V的一个子集,记为E(G) ,其元素是图的弧(Arc)。


弧(Arc) :表示两个顶点v和w之间存在一个关系,用顶点偶对<v,w>表示。通常根据图的顶点偶对将图分为有向图和无向图。


有向图(Digraph) : 若图G的关系集合E(G)中,顶点偶对<v,w>的v和w之间是有序的,称图G是有向图。

在有向图中,若 <v,w>存在 ,表示从顶点v到顶点w有一条弧。 其中:v称为弧尾(tail)或始点(initial node),w称为弧头(head)或终点(terminal node) 。

无向图(Undigraph): 若图G的关系集合E(G)中,顶点偶对<v,w>的v和w之间是无序的,称图G是无向图。

在无向图中,若<v,w>存在 ,有<w,v>存在 ,即E(G)是对称,则用无序对(v,w) 表示v和w之间的一条边(Edge),因此(v,w) 和(w,v)代表的是同一条边。

完全无向图:对于无向图,若图中顶点数为n ,用e表示边的数目,则e <=[0,n(n-1)/2] 。具有n(n-1)/2条边的无向图称为完全无向图。
完全有向图:对于有向图,若图中顶点数为n ,用e表示弧的数目,则e <= [0,n(n-1)] 。具有n(n-1)条边的有向图称为完全有向图.

有很少边或弧的图(e<n㏒n)的图称为稀疏图,反之称为稠密图。

权(Weight):与图的边和弧相关的数。权可以表示从一个顶点到另一个顶点的距离或耗费。

顶点的邻接(Adjacent):对于无向图G=(V,E),若边(v,w)存在,则称顶点v和w 互为邻接点,即v和w相邻接。边(v,w)依附(incident)与顶点v和w 。

对于有向图G=(V ,E),若有向弧<v,w>存在,则称顶点v “邻接到”顶点w,顶点w “邻接自”顶点v ,弧<v,w> 与顶点v和w “相关联” 。

顶点的度、入度、出度:对于无向图G=(V,E), vi属于V,图G中依附于vi的边的数目称为顶点vi的度(degree),记为TD(vi)。

显然,在无向图中,所有顶点度的和是图中边的2倍。 即 ∑TD(vi)=2e i=1, 2, …, n ,e为图的边数。

对有向图G=(V,E),若vi 属于V ,图G中以vi作为起点的有向边(弧)的数目称为顶点vi的出度(Outdegree),记为OD(vi) ;以vi作为终点的有向边(弧)的数目称为顶点vi的入度(Indegree),记为ID(vi) 。顶点vi的出度与入度之和称为vi的度,记为TD(vi) 。即TD(vi)=OD(vi)+ID(vi) 。

路径(Path)、路径长度、回路(Cycle) :对无向图G=(V,E),若从顶点vi经过若干条边能到达vj,称顶点vi和vj是连通的,又称顶点vi到vj有路径。

对有向图G=(V,E),从顶点vi到vj有有向路径,指的是从顶点vi经过若干条有向边(弧)能到达vj。或路径是图G中连接两顶点之间所经过的顶点序列。

路径上边或有向边(弧)的数目称为该路径的长度

在一条路径中,若没有重复相同的顶点,该路径称为简单路径;第一个顶点和最后一个顶点相同的路径称为回路(环);在一个回路中,若除第一个与最后一个顶点外,其余顶点不重复出现的回路称为简单回路(简单环)。

连通图、图的连通分量:对无向图G=(V,E),若vi ,vj 属于V,vi和vj都是连通的,则称图G是连通图,否则称为非连通图。若G是非连通图,则极大的连通子图称为G的连通分量。

对有向图G=(V,E),若vi ,vj 属于V,都有以vi为起点, vj 为终点以及以vj为起点,vi为终点的有向路径,称图G是强连通图,否则称为非强连通图。若G是非强连通图,则极大的强连通子图称为G的强连通分量。

“极大”的含义:指的是对子图再增加图G中的其它顶点,子图就不再连通。

生成树、生成森林:一个连通图(无向图)的生成树是一个极小连通子图,它含有图中全部n个顶点和只有足以构成一棵树的n-1条边,称为图的生成树,如图所示。

关于无向图的生成树的几个结论

  1. 一棵有n个顶点的生成树有且仅有n-1条边;

  2. 如果一个图有n个顶点和小于n-1条边,则是非连通图;

  3. 如果多于n-1条边,则一定有环;

  4. 有n-1条边的图不一定是生成树。

有向图的生成森林是这样一个子图,由若干棵有向树组成,含有图中全部顶点。

有向树是只有一个顶点的入度为0 ,其余顶点的入度均为1的有向图。

:每个边(或弧)都附加一个权值的图,称为带权图。带权的连通图(包括弱连通的有向图)称为网或网络。

图的存储分析

邻接矩阵矩阵存储的数据结构设计
 #define MAXVEX 100 /* 最⼤大顶点数,应由⽤用户定义 */
 #define INFINITY 65535 /* ⽤用65535表示∞ */
typedef int Status; /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
typedef char VertexType; /* 顶点类型应由⽤用户定义 */
typedef int EdgeType; /* 边上的权值类型应由⽤用户定义 */
typedef struct
{
VertexType vexs[MAXVEX]; /* 顶点表 */
EdgeType arc[MAXVEX][MAXVEX];/* 邻接矩阵,可看作边表 */
int numNodes, numEdges; /* 图中当前的顶点数和边数 */
}MGraph;
邻接矩阵矩阵存储代码实现思路路
  1. 确定顶点数/边数
  2. 读取顶点信息
  3. 初始化邻接矩阵
  4. 读⼊入边信息
  5. 循环打印
代码实现
#include "stdio.h"
#include "stdlib.h"

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

#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0
#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; /* 图中当前的顶点数和边数  */
}MGraph;

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");
}

int main(void)
{
    printf("邻接矩阵实现图的存储\n");
    /*图的存储-邻接矩阵*/
    MGraph G;
    CreateMGraph(&G);
    return 0;
}
邻接表存储的数据结构设计
//邻接表的结点
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;
邻接表存储的存储代码实现思路路
  1. 确定顶点数/边数
  2. 读取顶点信息
  3. 创建⼀一个结点 插⼊入到对应的顶点数组中
    1. 创建结点p
    2. 将结点p的adjvex 赋值 j
    3. 将结点p 插⼊入到对应的顶点数组下标i下
    4. 将顶点数组[i] 的firstedge设置为p 如果是无向图,则循环1~4步骤.
代码实现
#include "stdio.h"
#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;

void creatGraph(GraphLink *g){
    int i,j,k;
    EdgeNode *p;
    
    //1. 顶点,边,是否有向
    printf("输入顶点数目,边数和有向?:\n");
    scanf("%d %d %d", &(*g)->node_num, &(*g)->arc_num, &(*g)->is_directed);
    
    //2.顶点表
     printf("输入顶点信息:\n");
    for (i = 0; i < (*g)->node_num; i++) {
        getchar();
        scanf("%c", &(*g)->adjlist[i].data);
        (*g)->adjlist[i].firstedge = NULL;
    }
    
    //3.
    printf("输入边信息:\n");
    for (k = 0; k < (*g)->arc_num; k++){
        getchar();
        scanf("%d %d", &i, &j);
        
        //①新建一个节点
        p = (EdgeNode *)malloc(sizeof(EdgeNode));
        //②弧头的下标
        p->adj_vex_index = j;
        //③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
        p->next = (*g)->adjlist[i].firstedge;
        //④将顶点数组[i].firstedge 设置为p
        (*g)->adjlist[i].firstedge = p;
        
        //j->i
        if(!(*g)->is_directed)
        {
            // j -----> i
            //①新建一个节点
            p = (EdgeNode *)malloc(sizeof(EdgeNode));
            //②弧头的下标i
            p->adj_vex_index = i;
            //③头插法插进去,插的时候要找到弧尾,那就是顶点数组的下标i
            p->next = (*g)->adjlist[j].firstedge;
            //④将顶点数组[i].firstedge 设置为p
            (*g)->adjlist[j].firstedge = p;
        }
    }
}

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("邻接表实现图的存储\n");
    /*
     邻接表实现图的存储
     输入顶点数目,边数和有向?:
     4 5 0
     输入顶点信息:
     0 1 2 3
     输入边信息:
     0 1 0 2 0 3 2 1 2 3
     邻接表中存储信息:
     0->3 0->2 0->1
     1->2 1->0
     2->3 2->1 2->0
     3->2 3->0
    */
    /*
     邻接表实现图的存储
     输入顶点数目,边数和有向?:
     4 5 1
     输入顶点信息:
     0 1 2 3
     输入边信息:
     1 0 1 2 2 1 2 0 0 3
     邻接表中存储信息:
     0->3
     1->2 1->0
     2->0 2->1
     */
    GraphLink g = (Graph *)malloc(sizeof(Graph));
    creatGraph(&g);
    putGraph(g);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值