图的存储结构
邻接矩阵法
- 使用一个一维数组存储图的顶点信息,用一个二维数组存储图中边的信息
- 结点数为 n n n的图 G = ( V , E ) G=(V, E) G=(V,E)的邻接矩阵 A A A是 n ∗ n n*n n∗n的
- 将 G G G的顶点编号为 v 1 , v 2 , … , v n v_1,v_2,…,v_n v1,v2,…,vn。若 ( v i , v j ) ∈ E (v_i,v_j)\in E (vi,vj)∈E,则 A [ i ] [ j ] = 1 A[i][j]=1 A[i][j]=1,否则 A [ i ] [ j ] = 0 A[i][j]=0 A[i][j]=0
- 对于带权图,若 ( v i , v j ) ∈ E (v_i,v_j)\in E (vi,vj)∈E,则 A [ i ] [ j ] = w i j A[i][j]=w_{ij} A[i][j]=wij,否则 A [ i ] [ j ] = 0 或 ∞ A[i][j]=0或\infty A[i][j]=0或∞
- 有向图和无向图都可用邻接矩阵法,且无向图的邻接矩阵为对称矩阵(可使用压缩存储)
存储结构定义如下:
#define MaxVertexNum 100 // 顶点数目的最大值
typedef char VertexType;
typedef int EdgeType;
typedef struct{
VertexType Vex[MaxVertexNum]; // 顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum]; // 邻接矩阵
int vexnum, arcnum; // 顶点数及弧数
}MGraph;
邻接表法
- 当一个图为稀疏图时,使用邻接矩阵会浪费大量存储空间,使用邻接表法可以减少这种不必要的浪费
- 对图 G G G中每个顶点 v i v_i vi建立一个单链表(边表),第 i i i个单链表中的结点表示依附于顶点 v i v_i vi的边
- 边表的头指针和顶点的数据信息采用顺序存储
存储结构定义如下:
#define MaxVertexNum 100
typedef struct ArcNode{ // 边表结点
int adjvex; // 弧指向的顶点位置
struct ArcNode *next; // 指向下一条弧
InfoType info; // 边权值
}ArcNode;
typedef struct VNode{ // 顶点表结点
VertexType data; // 顶点信息
ArcNode *first; // 指向第一条依附于该顶点的弧的指针
}VNode, AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; // 邻接表
int vexnum, arcnum; // 顶点数和弧数
}ALGraph;
十字链表法
- 十字链表是有向图的一种链式存储结构
- 在十字链表中,对应于有向图中的每条弧有一个结点,对应于每个顶点也有一个结点
存储结构定义如下:
#define MaxVertexNum 100
typedef struct ArcNode{
int tailvex, headvex; // 尾域和头域,指向弧尾和弧头
struct ArcNode *hlink, *tlink; // hlink指向弧头相同的下一条弧,tlink指向弧尾相同的下一条弧
InfoType info; // 弧的权值
}ArcNode;
typdef struct VNode{
VertexType data; // 顶点相关数据信息
ArcNode *firstin, *firstout; // firstin和firstout分别指向以该顶点为弧头或弧尾的第一个弧结点
}VNode, AdjList[MaxVertexNum];
typedef struct{
AdjList vertices; // 十字链表
int vexnum, arcnum; // 顶点数和弧数
}OLGraph;
图的遍历
广度优先遍历(BFS)
- 基本思想:
- 首先访问起始顶点 v v v,然后从 v v v出发,依次访问 v v v的各个未被访问的邻接顶点 w 1 , w 2 , … , w i w_1,w_2,…,w_i w1,w2,…,wi,然后依次访问 w 1 , w 2 , … , w i w_1,w_2,…,w_i w1,w2,…,wi的所有未被访问过的邻接顶点,依次类推,直至图中所有顶点都被访问为止。
- 若此时图中还有顶点未被访问,则选择其中一个未被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问为止。
深度优先遍历(DFS)
- 基本思想:
- 首先访问起始顶点 v v v,然后从 v v v出发,访问与 v v v邻接的未被访问的任一顶点 w 1 w_1 w1,再访问 w 1 w_1 w1的未被访问过的任一邻接顶点 w 2 w_2 w2,依次类推,当不能继续向下访问时,依次退回到最近被访问的顶点,若它有其他邻接顶点未被访问,则从该结点继续上述过程,直至图中所有顶点都被访问为止。
- 若此时图中还有顶点未被访问,则选择其中一个未被访问的顶点作为起始点,重复上述过程,直至图中所有顶点都被访问为止。
基于邻接表的图的遍历代码实现
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#define MAX_VERTEX_NUM 20
typedef char VertexType;
typedef int VRType;
typedef int InfoType;
typedef int QElemType;
typedef struct ArcNode
{
int adjvex;
struct ArcNode *nextarc;
InfoType *info;
}ArcNode;
typedef struct VNode
{
VertexType data;
ArcNode *firstarc;
}VNode,AdjList[MAX_VERTEX_NUM];
typedef struct
{
AdjList vertices;
int vexnum,arcnum;
}ALGraph;
typedef struct QNode
{
QElemType data;
struct QNode *qnext;
}QNode,*PQNode;
typedef struct Queue
{
PQNode front;
PQNode rear;
}Queue,*PQueue;
PQueue initQueue()
{
PQueue pqueue = (PQueue)malloc(sizeof(Queue));
PQNode pqnode = (PQNode)malloc(sizeof(QNode));
if(pqnode==NULL)
{
printf("队列头空间申请失败!\n");
exit(-1);
}
pqueue->front = pqueue->rear = pqnode;
pqnode->qnext = NULL;
return pqueue;
}
void enQueue(PQueue pqueue,QElemType data)
{
PQNode pqnode = (PQNode)malloc(sizeof(QNode));
if(pqnode==NULL)
{
printf("队列结点申请失败!\n");
exit(-1);
}
pqnode->data = data;
pqnode->qnext = NULL;
pqueue->rear->qnext = pqnode;
pqueue->rear = pqnode;
}
bool isEmpty(PQueue pqueue)
{
if(pqueue->front == pqueue->rear)
return true;
return false;
}
QElemType deQueue(PQueue pqueue)
{
if(isEmpty(pqueue))
{
printf("队列为空\n");
exit(-1);
}
PQNode pqnode = pqueue->front->qnext;
pqueue->front->qnext = pqnode->qnext;
if(pqnode == pqueue->rear)
pqueue->rear = pqueue->front;
QElemType data = pqnode->data;
free(pqnode);
return data;
}
int locateVex(ALGraph alg,char v)
{
int i;
for(i=0;i<alg.vexnum;i++)
{
if(alg.vertices[i].data == v)
return i;
}
return -1;
}
void createALGraph(ALGraph *alg)
{
int i,j,v,k;
printf("输入图的顶点数和边数\n");
scanf("%d %d",&(*alg).vexnum,&(*alg).arcnum);
printf("输入顶点名称\n");
//fflush(stdin);
getchar();
for(i = 0;i<(*alg).vexnum;i++)
{
printf("输入第%d个顶点名称:",i);
scanf("%c",&(*alg).vertices[i].data);
(*alg).vertices[i].firstarc = NULL;
getchar();
}
printf("输入图的边信息\n");
char v1,v2;
ArcNode *s,*p;
for(k = 0;k<(*alg).arcnum;k++)
{
printf("输入第%d条边的两个节点v1 v2:",k);
scanf("%c %c",&v1,&v2);
i = locateVex((*alg),v1);
j = locateVex((*alg),v2);
p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = j;
p->nextarc = NULL;
if((*alg).vertices[i].firstarc==NULL)
{
(*alg).vertices[i].firstarc = p;
}
else
{
s = (*alg).vertices[i].firstarc;
while(s->nextarc!=NULL)
s = s->nextarc;
s->nextarc = p;
}
p = (ArcNode*)malloc(sizeof(ArcNode));
p->adjvex = i;
p->nextarc = NULL;
if((*alg).vertices[j].firstarc==NULL)
{
(*alg).vertices[j].firstarc = p;
}
else
{
s = (*alg).vertices[j].firstarc;
while(s->nextarc!=NULL)
s = s->nextarc;
s->nextarc = p;
}
getchar();
}
}
bool visited[MAX_VERTEX_NUM];
void DFS(ALGraph alg,int v)
{
ArcNode *p;
visited[v] = true;
printf("%c ",alg.vertices[v].data);
for(p = alg.vertices[v].firstarc;p!=NULL;p = p->nextarc)
{
if(!visited[p->adjvex])
DFS(alg,p->adjvex);
}
}
void DFSTraverse(ALGraph alg)
{
printf("深度优先遍历序列:");
int v;
for(v=0;v<alg.vexnum;v++)
visited[v] = false;
for(v=0;v<alg.vexnum;v++)
{
if(!visited[v])
DFS(alg,v);
}
}
void BFSTraverse(ALGraph alg)
{
printf("广度优先遍历序列:");
PQueue pqueue = initQueue();
ArcNode *p;
int i;
QElemType v;
for(i=0;i<alg.vexnum;i++)
visited[i] = false;
for(i=0;i<alg.vexnum;i++)
{
if(!visited[i])
{
visited[i] = true;
printf("%c ",alg.vertices[i].data);
enQueue(pqueue,i);
while(!isEmpty(pqueue))
{
v = deQueue(pqueue);
for(p = alg.vertices[v].firstarc;p!=NULL;p = p->nextarc)
{
if(!visited[p->adjvex])
{
printf("%c ",alg.vertices[p->adjvex].data);
visited[p->adjvex] = true;
enQueue(pqueue,p->adjvex);
}
}
}
}
}
}
int main(int argc, char *argv[]) {
ALGraph alg;
createALGraph(&alg);
DFSTraverse(alg);
printf("\n");
BFSTraverse(alg);
printf("\n");
return 0;
}