图:图(Graph)是由定点的有穷非空集合和定点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中的顶点的集合,E是图G中边的集合。
图的两个重要的存储方式:
1、 邻接矩阵:
/**************图数据结构***********************/ #define VERTEX_MAX 26 //图的最大顶点数 #define MAXVALUE 32767 //最大值(可设为一个最大整数) typedef struct { char Vertex[VERTEX_MAX]; //保存顶点信息(序号或字母) int Edges[VERTEX_MAX][VERTEX_MAX]; //保存边的权 int isTrav[VERTEX_MAX]; //遍历标志 int VertexNum; //顶点数量 int EdgeNum;//边数量 int GraphType; //图的类型(0:无向图,1:有向图) }MatrixGraph; //定义邻接矩阵图结构
void CreateMatrixGraph(MatrixGraph *G);//创建邻接矩阵图 void OutMatrix(MatrixGraph *G); //输出邻接矩阵
/***********创建邻接矩阵存储方式的图****************/ void CreateMatrixGraph(MatrixGraph *G)//创建邻接矩阵图 { int i,j,k,weight; char start,end; //边的起始顶点 printf("输入各顶点信息\n"); for(i=0;i<G->VertexNum;i++) //输入顶点 { getchar(); printf("第%d个顶点:",i+1); scanf("%c",&(G->Vertex[i])); //保存到各顶点数组元素中 } printf("输入构成各边的两个顶点及权值(用逗号分隔):\n"); for(k=0;k<G->EdgeNum;k++) //输入边的信息 { getchar(); //暂停输入 printf("第%d条边:",k+1); scanf("%c,%c,%d",&start,&end,&weight); for(i=0;start!=G->Vertex[i];i++); //在已有顶点中查找始点 for(j=0;end!=G->Vertex[j];j++); //在已有顶点中查找结终点 G->Edges[i][j]=weight; //对应位置保存权值,表示有一条边 if(G->GraphType==0) //若是无向图 G->Edges[j][i]=weight;//在对角位置保存权值 } } /***********输出邻接矩阵存储方式的图****************/ void OutMatrix(MatrixGraph *G)//输出邻接矩阵 { int i,j; for(j=0;j<G->VertexNum;j++) printf("\t%c",G->Vertex[j]); //在第1行输出顶点信息 printf("\n"); for(i=0;i<G->VertexNum;i++) { printf("%c",G->Vertex[i]); for(j=0;j<G->VertexNum;j++) { if(G->Edges[i][j]==MAXVALUE) //若权值为最大值 printf("\t∞"); //输出无穷大符号 else printf("\t%d",G->Edges[i][j]); //输出边的权值 } printf("\n"); } }
/*******************测试代码*******************************/ int main() { MatrixGraph G; //定义保存邻接矩阵结构的图 int i,j; printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数 for(i=0;i<G.VertexNum;i++) //清空矩阵 for(j=0;j<G.VertexNum;j++) G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为最大值 CreateMatrixGraph(&G); //创建用邻接表保存的图 printf("邻接矩阵数据如下:\n"); OutMatrix(&G); getch(); return 0; } |
2、 邻接表存储方式:
/************************图邻接表存储结构****************/ #define VERTEX_MAX 20 //图的最大顶点数 typedef struct edgeNode { int Vertex; //顶点信息(序号或字母) int weight; //权值 struct edgeNode *next; //指向下一个顶点指针 (当前顶点和指向的下一顶点构成一条边) }EdgeNode; //邻接表边结构
typedef struct { EdgeNode* AdjList[VERTEX_MAX]; //指向每个顶点的指针 int VextexNum,EdgeNum; //图的顶点的数量和边的数量 int GraphType; //图的类型(0:无向图,1:有向图) }ListGraph; //图的结构
void CreateGraph(ListGraph *G); //生成图的邻接表 void OutList(ListGraph *G); //输出邻接表
/************************创建图邻接表存储结构****************/ void CreateGraph(ListGraph *G) //构造邻接表结构图 { int i,weight; int start,end; EdgeNode *s; for(i=1;i<=G->VextexNum;i++)//将图中各顶点指针清空 G->AdjList[i]=NULL; for(i=1;i<=G->EdgeNum;i++) //输入各边的两个顶点 { getchar(); printf("第%d条边:",i); scanf("%d,%d,%d",&start,&end,&weight); //输入边的起点和终点 s=(EdgeNode *)malloc(sizeof(EdgeNode)); //申请保存一个顶点的内存 s->next=G->AdjList[start]; //插入到邻接表中 s->Vertex=end; //保存终点编号 s->weight=weight; //保存权值 G->AdjList[start]=s; //邻接表对应顶点指向该点 if(G->GraphType==0) //若是无向图,再插入到终点的边链中 { s=(EdgeNode *)malloc(sizeof(EdgeNode)); //申请保存一个顶点的内存 s->next=G->AdjList[end]; s->Vertex=start; s->weight=weight; G->AdjList[end]=s; } } } /************************输出图邻接表存储结构****************/ void OutList(ListGraph *G) { int i; EdgeNode *s; for(i=1;i<=G->VextexNum;i++) { printf("顶点%d",i); s=G->AdjList[i]; while(s) { printf("->%d(%d)",s->Vertex,s->weight); s=s->next; } printf("\n"); } }
/*****************测试代码**************************/ int main() { ListGraph G; //定义保存邻接表结构的图 printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VextexNum,&G.EdgeNum); //输入图顶点数和边数 printf("输入构成各边的两个顶点及权值(用逗号分隔):\n"); CreateGraph(&G); //生成邻接表结构的图 printf("输出图的邻接表:\n"); OutList(&G); getch(); return 0; } |
深度优先遍历
深度优先遍历方法类似于树的先序遍历,具体过程如下:
(1)从isTrav数组中选择一个未被访的顶点Vi,将其标记为已访问。
(2)接着从Vi的一个未被访问过的邻接点出发进行深度优先遍历。
(3)重复步骤(2),直至图中所有和Vi有路径相通的顶点都被访问过。
(4)重复步骤(1)至步骤(3)的操作,直至所有顶点都被访问过。
广度优先遍历
广度优先遍历类似于树的按层次遍历,具体过程如下:
(1)从isTrav数组中选择一个未被访问的顶点Vi,将其标记为已访问。
(2)接着依次访问Vi的所有未被访问的邻接点,并标记为已被访问过。
(3)从这些邻接点出发进行广度优先遍历,直至图中所有和Vi有路径相通的顶点都被访问过。
(4)重复步骤(1)至步骤(3)的操作,直至所有顶点都被访问过。
/*****************本程序是以邻接矩阵存储方式的图的*********************/ #include <stdio.h> #include "3-6 AdjMatrixGraph.c" #define QUEUE_MAXSIZE 30 //队列的最大容量 typedef struct { int Data[QUEUE_MAXSIZE]; //数据域 int head; //队头指针 int tail; //队尾指针 }SeqQueue; //队列结构 //队列操作函数 void QueueInit(SeqQueue *q); //初始化一个队列 int QueueIsEmpty(SeqQueue q); //判断队列是否空 int QueueIn(SeqQueue *q,int n); //将一个元素入队列 int QueueOut(SeqQueue *q,int *ch); //将一个元素出队列
//图操作函数 void DFSTraverse(MatrixGraph *G); //深度优先遍历 void BFSTraverse(MatrixGraph *G); //广度优先遍历 void DFSM(MatrixGraph *G,int i); void BFSM(MatrixGraph *G,int i);
void QueueInit(SeqQueue *Q) //队列初始化 { Q->head=Q->tail=0; } int QueueIsEmpty(SeqQueue Q) //判断队列是否已空,若空返回1,否则返回0 { return Q.head==Q.tail; } int QueueIn(SeqQueue *Q,int ch) //入队列,成功返回1,失败返回0 { if((Q->tail+1) % QUEUE_MAXSIZE ==Q->head) //若队列已满 return 0; //返回错误; Q->Data[Q->tail]=ch; //将数据ch入队列 Q->tail=(Q->tail+1) % QUEUE_MAXSIZE; //调整队尾指针 return 1; //成功,返回1 } int QueueOut(SeqQueue *Q,int *ch) //出队列,成功返回1,并用ch返回该元素值,失败返回0 { if(Q->head==Q->tail) //若队列为空 return 0; //返回错误 *ch=Q->Data[Q->head]; //返回队首元素 Q->head=(Q->head+1) % QUEUE_MAXSIZE; //调整队首指针 return 1; //成功出队列,返回1 }
/*********************深度优先遍历**************************/ void DFSTraverse(MatrixGraph *G) //深度优先遍历 { int i; for(i=0;i<G->VertexNum;i++) //清除各顶点遍历标志 G->isTrav[i]=0; printf("深度优先遍历结点:"); for(i=0;i<G->VertexNum;i++) if(!G->isTrav[i]) //若该点未遍历 DFSM(G,i); //调用函数遍历 printf("\n");
} void DFSM(MatrixGraph *G,int i) //从第i个结点开始,深度遍历图 { int j; G->isTrav[i]=1; //标记该顶点已处理过 printf("->%c",G->Vertex[i]);//输出结点数据 // printf("%d->",i); //输出结点序号
//添加处理节点的操作 for(j=0;j<G->VertexNum;j++) if(G->Edges[i][j]!=MAXVALUE && !G->isTrav[i]) DFSM(G,j); //递归进行遍历 }
/*********************广度优先遍历**************************/ void BFSTraverse(MatrixGraph *G) //广度优先 { int i; for (i=0;i<G->VertexNum;i++) //清除各顶点遍历标志 G->isTrav[i]=0; printf("广度优先遍历结点:"); for (i=0;i<G->VertexNum;i++) if (!G->isTrav[i]) BFSM(G,i); printf("\n"); } void BFSM(MatrixGraph *G,int k) //广度优先遍历 { int i,j; SeqQueue Q; //创建循环队列 QueueInit(&Q); //初始化循环队列
G->isTrav[k]=1; //标记该顶点 printf("->%c",G->Vertex[k]); //输出第一个顶点
//添加处理节点的操作 QueueIn(&Q,k); //入队列 while (!QueueIsEmpty(Q)) //队列不为空 { QueueOut(&Q,&i); //出队列 for (j=0;j<G->VertexNum;j++) if(G->Edges[i][j]!=MAXVALUE && !G->isTrav[j]) { printf("->%c",G->Vertex[j]); G->isTrav[j]=1; //标记该顶点 //处理顶点 QueueIn(&Q,j); //出队列 } } }
/*********************测试代码**************************/ int main() { MatrixGraph G; //定义保存邻接表结构的图 int path[VERTEX_MAX]; int i,j,s,t; char select; do { printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数 for(i=0;i<G.VertexNum;i++) //清空矩阵 for(j=0;j<G.VertexNum;j++) G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0 CreateMatrixGraph(&G); //生成邻接表结构的图 printf("邻接矩阵数据如下:\n"); OutMatrix(&G); //输出邻接矩阵 DFSTraverse(&G); //深度优先搜索遍历图 BFSTraverse(&G); //广度优先搜索遍历图 printf("图遍历完毕,继续进行吗?(Y/N)"); scanf(" %c",&select); }while(select!='N' && select!='n'); getch(); return 0; } |
求最小生成树:
1、Prim算法
#define USED 0 //已使用,加入U集合 #define NOADJ -1 //非邻接顶点 void Prim(MatrixGraph G)//最小生成树 { int i,j,k,min,sum=0; int weight[VERTEX_MAX];//权值 char tmpvertex[VERTEX_MAX];//临时顶点信息
for(i=1;i<G.VertexNum;i++) //保存邻接矩阵中的一行数据 { weight[i]=G.Edges[0][i]; //权值 if(weight[i]==MAXVALUE) tmpvertex[i]=NOADJ; //非邻接顶点 else tmpvertex[i]=G.Vertex[0]; //邻接顶点 } tmpvertex[0]=USED; //将0号顶点并入U集 weight[0]=MAXVALUE; //设已使用顶点权值为最大值 for(i=1;i<G.VertexNum;i++) { min=weight[0]; //最小权值 k=i; for(j=1;j<G.VertexNum;j++) //查找权值最小的一个邻接边 if(weight[j]<min && tmpvertex[j]>0) //找到具有更小权值的未使用边 { min=weight[j]; //保存权值 k=j; //保存邻接点序号 } sum+=min;//累加权值 printf("(%c,%c),",tmpvertex[k],G.Vertex[k]); //输出生成树一条边 tmpvertex[k]=USED; //将编号为k的顶点并入U集 weight[k]=MAXVALUE; //已使用顶点的权值为最大值 for(j=0;j<G.VertexNum;j++) //重新选择最小边 if(G.Edges[k][j]<weight[j] && tmpvertex[j]!=0) { weight[j]=G.Edges[k][j]; //权值 tmpvertex[j]=G.Vertex[k]; //上一个顶点信息 } } printf("\n最小生成树的总权值为:%d\n",sum); }
/********************测试代码************************/ #include "3-6 AdjMatrixGraph.c" #include "3-12 Prim.c" int main() { MatrixGraph G; //定义保存邻接表结构的图 int path[VERTEX_MAX]; int i,j,s,t; char select; do { printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数
for(i=0;i<G.VertexNum;i++) //清空矩阵 for(j=0;j<G.VertexNum;j++) G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0 CreateMatrixGraph(&G); //生成邻接表结构的图 printf("邻接矩阵数据如下:\n"); OutMatrix(&G);
printf("最小生成树的边为:\n"); Prim(G);
printf("继续进行吗?(Y/N)"); scanf(" %c",&select); getchar(); }while(select!='N' && select!='n'); getch(); return 0; } |
2、Dijkstra算法
void Dijkstra(MatrixGraph G) { int weight[VERTEX_MAX];//某源点到各顶点的最短路径长度 int path[VERTEX_MAX];//某源点到终点经过的顶点集合的数组 int tmpvertex[VERTEX_MAX];//最短路径的终点集合 int i,j,k,v0,min; printf("\n请输入源点的编号:"); scanf("%d",&v0); v0--; //编号自减1(因数组是从0开始) for(i=0;i<G.VertexNum;i++) //初始辅助数组 { weight[i]=G.Edges[v0][i]; //保存最小权值 if(weight[i]<MAXVALUE && weight[i]>0) //有效权值 path[i]=v0; //保存边 tmpvertex[i]=0; //初始化顶点集合为空 } tmpvertex[v0]=1; //将顶点v0添加到集合U中 weight[v0]=0; //将源顶点的权值设为0 for(i=0;i<G.VertexNum;i++) { min=MAXVALUE; //将min中保存一个最大值 k=v0; //源顶点序号 for(j=0;j<G.VertexNum;j++) //在U集合中查找未用顶点的最小权值 if(tmpvertex[j]==0 && weight[j]<min) { min=weight[j]; k=j; } tmpvertex[k]=1; //将顶点k加入集合U for(j=0;j<G.VertexNum;j++) //以顶点k为中间点,重新计算权值 if(tmpvertex[j]==0 && weight[k]+G.Edges[k][j]<weight[j]) //有更小权值的路径 { weight[j]=weight[k]+G.Edges[k][j]; //更新权值 path[j]=k; } } printf("\n顶点%c到各顶点的最短路径为(终点 < 源点):\n",G.Vertex[v0]); for(i=0;i<G.VertexNum;i++)//输出结果 { if(tmpvertex[i]==1) { k=i; while(k!=v0) { j=k; printf("%c < ",G.Vertex[k]); k=path[k]; } printf("%c\n",G.Vertex[k]); }else printf("%c<-%c:无路径\n",G.Vertex[i],G.Vertex[v0]); } }
/********************测试代码**********************/ #include <stdio.h> #include "3-6 AdjMatrixGraph.c" #include "3-14 Dijkstra.c" int main() { MatrixGraph G; //定义保存邻接表结构的图 int path[VERTEX_MAX]; int i,j,s,t; char select; do { printf("输入生成图的类型(0:无向图,1:有向图):"); scanf("%d",&G.GraphType); //图的种类 printf("输入图的顶点数量和边数量:"); scanf("%d,%d",&G.VertexNum,&G.EdgeNum); //输入图顶点数和边数
for(i=0;i<G.VertexNum;i++) //清空矩阵 for(j=0;j<G.VertexNum;j++) G.Edges[i][j]=MAXVALUE; //设置矩阵中各元素的值为0 CreateMatrixGraph(&G); //生成邻接表结构的图 printf("邻接矩阵数据如下:\n"); OutMatrix(&G);
printf("最短路径:\n"); Dijkstra(G);
printf("继续进行吗?(Y/N)"); scanf(" %c",&select); getchar(); }while(select!='N' && select!='n'); getch(); return 0; } |
/*****************************************Java实现图******************************/
Java实现连接矩阵存储结构的图
package com.jtlyuan.graph; publicclass MGraph { publicstaticintVERTEX_MAX = 25; char[] Vertex = newchar[VERTEX_MAX]; // 保存顶点信息(序号或字母) int[][] Edges = newint[VERTEX_MAX][VERTEX_MAX]; // 保存边的权 boolean[] isTrav = newboolean[VERTEX_MAX]; // 遍历标志 intVertexNum; // 顶点数量 intEdgeNum;// 边数量 publicchar[] getVertex() { returnVertex; } publicvoid setVertex(char[] vertex) { Vertex = vertex; } publicint[][] getEdges() { returnEdges; } publicvoid setEdges(int[][] edges) { Edges = edges; } publicint getVertexNum() { returnVertexNum; } publicvoid setVertexNum(int vertexNum) { VertexNum = vertexNum; } publicint getEdgeNum() { returnEdgeNum; } publicvoid setEdgeNum(int edgeNum) { EdgeNum = edgeNum; } } |
深度优先遍历和广度优先遍历
package com.jtlyuan.graph;
import java.util.LinkedList; import java.util.Queue;
publicclass GraphTraDemo { /****深度优先遍历******/ publicvoid DFSTra(MGraph g){ for(int i=0;i<g.VertexNum;i++){ g.isTrav[i]=false; } for(int i =0 ;i<g.VertexNum;i++){ if(!g.isTrav[i]){ DFS(g,i); } } } privatevoid DFS(MGraph g, int i) { g.isTrav[i]=true; System.out.print(g.Vertex[i]+ " --> "); for(int j = 0; j<g.VertexNum;j++){ if(g.Edges[i][j]!=0&&!g.isTrav[j]){ DFS(g,j); } } } /******广度优先遍历*********/ publicvoid BFSTra(MGraph g){ for(int i=0;i<g.VertexNum;i++){ g.isTrav[i]=false; } for(int i =0 ;i<g.VertexNum;i++){ if(!g.isTrav[i]){ BFS(g,i); } } } privatevoid BFS(MGraph g, int i) { Queue<Integer> q = new LinkedList<Integer>(); q.add(i); g.isTrav[i]=true; System.out.print(g.Vertex[i]+"-->");
while(!q.isEmpty()){ q.poll(); for(int j = 0 ;j<g.VertexNum;j++){ if(g.Edges[i][j]!=0&&!g.isTrav[j]){ System.out.print(g.Vertex[j]+"-->"); g.isTrav[j]=true; q.add(j); } } } } publicstaticvoid main(String[] args) { MGraph g = new MGraph(); g.setVertexNum(5); g.setEdgeNum(6); g.Vertex[0]='A'; g.Vertex[1]='B'; g.Vertex[2]='C'; g.Vertex[3]='D'; g.Vertex[4]='E'; g.Edges[0][1]=3; g.Edges[0][3]=2; g.Edges[1][2]=9; g.Edges[2][4]=7; g.Edges[3][4]=6; g.Edges[4][1]=3; System.out.println("深度遍历"); new GraphTraDemo().DFSTra(g); System.out.println("\n广度遍历"); new GraphTraDemo().BFSTra(g); } } /*打印了 深度优先遍历 A --> B --> C --> E --> D --> 广度优先遍历 A-->B-->D-->C-->E--> */ |