数据结构之图(c语言版)

图是比树更复杂的结构,树是一对多的关系,图是多对多的关系。

一、基本概念

1、定义:图(graph)是由一些点(vertex)和这些点之间的连线(edge)所组成的;其中,点通常被成为"顶点(vertex)",而点与点之间的连线则被成为"边或弧"(edege)。通常记为,G=(V,E)。

2、根据边是否有方向,将图可以划分为:无向图有向图

3、度,在无向图中,某个顶点的度是邻接到该顶点的边(或弧)的数目。

在有向图中,度还有"入度"和"出度"之分。
某个顶点的入度,是指以该顶点为终点的边的数目。而顶点的出度,则是指以该顶点为起点的边的数目。
顶点的度=入度+出度。

4、弧头和弧尾

有向图中:用<A,B>,<B,C>,<B,F>,A->B,A是弧尾,B是 弧头。

无向图中:用(A,B),(A,C),(B,C),弧头和弧尾没有区别。

5、权

弧如果有值的话,称为权。

二、图的存储结构

图的存储结构有许多种,有邻接矩阵,邻接表,十字链表等。

邻接矩阵

用矩阵表示,用线性表存储数据,直观简单,但是浪费空间。

在这里插入图片描述

邻接表

用数组和链表表示结构,节省空间,可伸缩。
数组中存储了链表,链表的头节点代表定点,存储着数据及下一个顶点的引用,后面的节点存储着下标和下一个顶点的引用。

在这里插入图片描述

在这里插入图片描述

图来自《大话数据结构》

三、图的建立(邻接表)

由上可知,邻接表由数组和链表构成。首先一个结构体数组存储着数据和指向下一个顶点的指针,数组下标代表着顶点的序号。

所有数据都放在顶部方便修改,用结构体数组存储着边和顶点。

#include<stdio.h>
#include<stdlib.h>


#define MAXVEX 10 //最大顶点数

static int VexNum=5;//当前顶点数
static int edgeNum=6;//当前边数

typedef struct edgeNode{//边表节点
    int index;//下标
   // EdgeType weight;//权值
    struct edgeNode *next;//指向下一个边表
}EdgeNode;

typedef struct vexNode{//顶点表节点
    int data;       //顶点数据A
    EdgeNode *firstEdge;//指向第一个边表
}VexNode,VexList[MAXVEX];

typedef struct  graphList{//邻接表
   VexList vexlist;
}GraphList;



//建立邻接表
GraphList* createGraph(){
    GraphList *list=(GraphList *)malloc(sizeof(GraphList));

    printf("建立顶点表:\n");
    printf("输入顶点数据:\n");
    for(int i=0;i<VexNum;i++){//顶点数据
        
        //输入数据//数据放入节点
        scanf("%d",&(list->vexlist[i].data));
        list->vexlist[i].firstEdge=NULL;//边表先不指向
    }

    printf("建立边表:\n");
    printf("输入弧头i和弧尾j:\n");
    for(int k=0;k<edgeNum;k++){
        EdgeNode *e=(EdgeNode *)malloc(sizeof(EdgeNode));
        int i,j;
        
        scanf("%d%d",&i,&j);
        e->index=j;
        e->next=list->vexlist[i].firstEdge;
        list->vexlist[i].firstEdge=e;

        //无向图需要指回去
        e=(EdgeNode *)malloc(sizeof(EdgeNode));
        e->index=i;
        e->next=list->vexlist[j].firstEdge;
        list->vexlist[j].firstEdge=e;
    }

    printf("打印图:\n");
    for(int i=0;i<VexNum;i++){
        EdgeNode *p=list->vexlist[i].firstEdge;
        while(p){
            printf("(%d %d)",list->vexlist[i].data,list->vexlist[p->index].data);
            p=p->next;
        }
        printf("\n");
    }

    printf("结束\n");
    return list;
}



可以在主函数中测试一下:

int main(){
    GraphList *g=createGraph();
  
}

实现这个简单的表:

在这里插入图片描述

建立顶点表:
输入顶点数据:
4 3 2 1 0
建立边表:
输入弧头i和弧尾j:
0 4
1 2
2 0
2 3
3 4
1 0
打印图:
(4 3)(4 2)(4 0)
(3 4)(3 2)
(2 1)(2 4)(2 3)
(1 0)(1 2)
(0 1)(0 4)
结束

四、图的遍历

图的遍历有深度优先和广度优先。

深度优先遍历是从图中某个顶点出发,访问此顶点,然后从它未被访问到的邻接点出发深度优先遍历图,直到图中所有和它有路径相通的顶点都被访问到.,类似树的先序遍历。

广度优先遍历从某个顶点出发,访问其所有相邻元素,再从某个相邻元素开始广度优先遍历,类似树的层级遍历。

深度优先遍历

从一个元素开始,一直访问其相邻元素,访问后的元素被标记,下次不再访问。

int visit[MAXVEX];//标记是否被遍历,0未遍历,1已经遍历

//深度优先
void DFS(GraphList *list,int i){
    EdgeNode *p;
    visit[i]=1;//标记遍历
    printf("%d ",list->vexlist[i].data);
    p=list->vexlist[i].firstEdge;//指向边表
    while(p){
        if(visit[p->index]==0){
            DFS(list,p->index);
        }
        p=p->next;//指向下一边
    }
}

//深度优先遍历
void DFSVisit(GraphList *list){
    int i;
    for(i=0;i<VexNum;i++){
        visit[i]=0;//全部标记未遍历
    }
    for(i=0;i<VexNum;i++){
        if (visit[i]==0)
        {
            DFS(list,i);
        }    
    }

}

主方法测试:

int main(){
    GraphList *g=createGraph();
    printf("开始深度优先遍历:\n");
    DFSVisit(g);
}

按照上图所示,结果如下:

开始深度优先遍历:
4 3 2 1 0

广度优先遍历

广度优先遍历从一个元素开始访问其全部相邻元素,再按顺序对每个元素进行广度优先遍历,我们需要一个队列来存储等待遍历的元素。

在这里插入图片描述

广度优先遍历下,输入顶点 01234以及各条弧。

得到遍历结果: 01243

posted @ 2024-04-11 11:37  cgl_dong  阅读(89)  评论(0编辑  收藏  举报