图的存储方式

2,075 阅读5分钟
  • 图的概念

    图(Graph) 是由顶点的有穷⾮空集合 和 顶点之间边的集合组成. 通常表示为: G(V,E). 其中,G 表示⼀个图, V是图G中的顶点集合,E 是图G中边的集合.

    我们将组成栈、队列、树、链表的元素叫做结点,注意组成图的元素呢,叫顶点,图中的顶点之间的关系是多对多的关系如图:

  • 各种图的定义

    1. 无向图&无向边 下图所示就是个无向图,通俗点说就是图的边没有方向两个顶点之间可相互存在指向关系
    2. 有向图&有向边 其实就是边存在方向的图
    3. 无向完全图 若无向图中有n个顶点,则最多有n(n-1)/2条弧,我们又将具有n(n-1)/2条弧的无向图称作无向完全图
    4. 有向完全图 若有向图中有n个顶点,则最多有n(n-1)/2条弧,我们又将具有n(n-1)/2条弧的有向图称有无向完全图
    5. 连通图 连通图基于连通的概念。在一个无向图 G中,若从顶点i到顶点j有路径相连(当然从j到i也一定有路径),则称i和j是连通的
    6. 非连通图 有至少一个点无法连接上的叫非连通图
  • 图的储存-邻接矩阵实现思路

    1. 创建一个数组用来存储图中设计的所有顶点
    2. 创建一个二维数组用来存储顶点之间的关系
  • 邻接矩阵存储相关代码

    #include <stdio.h>
    #include "stdlib.h"
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    #define MAXVEX 100 /* 最大顶点数,应由用户定义 */
    #define INFINITYC 65535
    
    typedef int Status;    /* Status是函数的类型,其值是函数结果状态代码,如OK等 */
    typedef char VertexType; /* 顶点类型应由用户定义  */
    typedef int EdgeType; /* 边上的权值类型应由用户定义 */
    
    typedef struct {
        VertexType * vers;  //用来存储顶点信息的数据
        EdgeType **edges;   //用来储存b顶点之间的关系的二维数组
        int numNodes,numEdges;  //顶点的数量和边的数量
    } Graph;
    
    Status initGraph(int numNodes,int numEdges,Graph *G){
        G->numEdges = numEdges;
        G->numNodes = numNodes;
        G->vers = (VertexType *)malloc(sizeof(VertexType) * numNodes);
        if(G->vers == NULL) return ERROR;
        //初始化一个长为边的数量宽也是边的数量的二维数组
        G->edges = (EdgeType **)malloc(sizeof(EdgeType *) * numEdges);
        if(G->edges == NULL) return ERROR;
        for(int i = 0; i < numEdges; i ++){
            G->edges[i] = (EdgeType *)malloc(sizeof(EdgeType) * numEdges);
            if(G->edges[i] == NULL) return ERROR;
        }
        //循环遍历
        //这里有个小细节就是 没有关系的时候我们设置为无穷大,有关系的时候对应位置可以储存权值
        //初始化的时候默认每个顶点都没有对应关系
        for(int i = 0; i < G->numNodes; i ++)
            for(int n = 0; n < G->numNodes; n ++)
                G->edges[i][n] = INFINITYC;
        return OK;
    }
    
    void CreateGraph(Graph *G){
    
        int i,j,k,w,numNodes,numEdges;
        printf("输入顶点数和边数:\n");
        //1. 输入顶点数/边数
        scanf("%d,%d",&numNodes,&numEdges);
        getchar();
        //初始化
        initGraph(numNodes, numEdges, G);
        printf("顶点数:%d,边数:%d\n",G->numNodes,G->numEdges);
        //输入顶点的信息
        for(i = 0; i <= G->numNodes; i ++){
            scanf("%c",&G->vers[i]);
        }
        //4.输入边表信息
        for(k = 0; k < G->numEdges;k++){
            printf("输入边(vi,vj)上的下标i,下标j,权w\n");
            scanf("%d,%d,%d",&i,&j,&w);
            
            G->edges[i][j] = w;
            //如果无向图,矩阵对称;
            G->edges[j][i] = G->edges[i][j];
            
        }
        
        
        /*5.打印邻接矩阵*/
        for (int i = 0; i < G->numNodes; i++) {
            printf("\n");
            for (int j = 0; j < G->numNodes; j++) {
                printf("%d ",G->edges[i][j]);
            }
        }
        printf("\n");
        
        
    }
    
  • 图的存储-邻接表实现思路

    1. 创建一个顶点数组用于存储顶点信息(数组中的结点一个是data用于存储顶点内的数据还一个是指针用于指向关系链表中的额第一个结点)
    2. 创建一个关系链表用于存储所有和当前顶点有关系的顶点信息(链表中的结点信息大概有index当前顶点的索引,data当前顶点的内容,next用于指向下一个和对应顶点数组中的顶点有关系的结点)

    用一个比喻来说,其实图的心里存储的就是一个链表数组,每个链表都含有头结点,头结点存储的是顶点信息,头结点以后的结点存储的是和头结点有关系的顶点

  • 邻接表存储的相关代码

    #include <stdio.h>
    #include "stdlib.h"
    
    #define OK 1
    #define ERROR 0
    #define TRUE 1
    #define FALSE 0
    #define M 100
    
    typedef int Status;
    typedef char Element;
    
    //顶点的关系链表中的结点
    typedef struct Node {
        int index;  //顶点对应顶点表中的下标
        Element data; //顶点的内容
        struct Node *next; //指针指向下一个j顶点
    }Node;
    
    //顶点表中的结点
    typedef struct vNode{
        struct Node * firstNode;  //如果不存在有关系的顶点是指向null
        Element data;
    }vNode,Adjlist[M];
    
    //总图的一些信息
    typedef struct Graph{
        Adjlist adjlist;       //顶点表
        int arc_num;           //边的个数
        int node_num;          //节点个数
        int is_directed;      //是不是有向图
    }Graph, *GraphLink;
    
    void creatGraph(GraphLink *g){
        int i,j,k;
        Node *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].firstNode = NULL;
        }
        
        printf("输入边信息:\n");
        for (k = 0; k < (*g)->arc_num; k++){
            getchar();
            scanf("%d %d", &i, &j);
            p = (Node *)malloc(sizeof(Node));
            p->index = j;
            p->data = (*g)->adjlist[j].data;
            p->next = (*g)->adjlist[i].firstNode;
            (*g)->adjlist[i].firstNode = p;
            
            if((*g)->is_directed == 1){
                p = (Node *)malloc(sizeof(Node));
                p->index = i;
                p->data = (*g)->adjlist[i].data;
                p->next = (*g)->adjlist[j].firstNode;
                (*g)->adjlist[j].firstNode = p;
            }
        }
    }
    
    void putGraph(GraphLink g){
        int i;
        printf("邻接表中存储信息:\n");
        //遍历一遍顶点坐标,每个再进去走一次
        for (i = 0; i < g->node_num; i++) {
            Node * p = g->adjlist[i].firstNode;
            while (p) {
                printf("%c->%c ", g->adjlist[i].data, g->adjlist[p->index].data);
                p = p->next;
            }
            printf("\n");
        }
    }