在C语言中,常用的图的基本操作包括:
图的应用非常广泛,如社交网络中的好友关系、电子地图中的城市路线、网络拓扑结构等等。因此,熟练掌握图的基本操作对于开发具有一定的重要性。
本文 只是小书童在学习中的笔记
Filename:“graph_node.h”
- #pragma once
-
- /*===============================================-邻接矩阵存储方式=================================================*/
- #define MaxVertexNum 30 //设置最大顶点数
-
- typedef char VertexType; //设置结点元素类型
- typedef int EdgeType; //设置边元素数据类型
-
- typedef struct {
- VertexType vertexs[MaxVertexNum]; //顶点表
- EdgeType arcs[MaxVertexNum][MaxVertexNum]; //临接矩阵,即边表
- int vertexNum, edgeNum; //顶点数和边数
- }MGragh; //MGragh 是以邻接矩阵储存的图类型
-
-
-
- /*======================邻接表存储算法==========================*/
-
- #define MaxVertexNum 30 //设定最大顶点数
-
- typedef char InfoType;
-
- typedef struct node {//表节点
- int adjvertex;//邻接点域,一般存放顶点和对应的序号和在表头向量中的下标
- InfoType vertex; //与边弧相关的信息
- struct node* next; //指向下一个邻接点的指针域
- }EdgeNode;
-
- typedef struct vnode {//顶点结点
- VertexType vertex;
- EdgeNode* firstedge;
- }VertexNode;
-
- typedef struct {
- VertexNode adjlist[MaxVertexNum]; //邻接表
- int vertexNum, edgeNum; //顶点数和边数
- }ALGraph; //以邻接表方式存储的图类型
-
- /*===========================十字链表存储表示=============================*/
-
- typedef struct ArcNode {
- int tailvertex, headvertex; //该弧的尾和头顶点的位置
- struct ArcNode* hlink, * tlink;//分别为弧头相同和弧尾相同的链域
- InfoType info; //该弧的相关信息
- }ArcNode;
-
- typedef struct VertexNode_arc {
- VertexType vertex;
- ArcNode* firsin, * firstout; //分别指向该顶点的第一条入弧和出弧
- }Vertex_arc_Node;
-
- typedef struct {
- Vertex_arc_Node xlist[MaxVertexNum]; //表头向量
- int vertexNum, edgeNum; //顶点数和弧数
- }OLGraph;
- /*===========================表存储表示=============================*/
-
-
- /*========================AVO--拓扑排序算法====================*/
-
- //设计结构体
- #define MaxVertexNum 30
- typedef struct node_Avo //设计表结点
- {
- int adjvertex;//邻接点域,存放顶点对应的序号或在表头向量中的下标
- struct node_Avo* next;//指向下一个邻接点的指针
- }EdgeNode_Avo;
-
- typedef struct vnode_Avo//设计顶点表结点
- {
- int indegree;//存放顶点的入度
- int vertex;//顶点域
- EdgeNode_Avo* firstedge;//边表头指针
- }VertexNode_Avo;
- typedef struct
- {
- VertexNode_Avo adjlist[MaxVertexNum];//邻接表
- int vertexNum, edgeNum;//顶点数 边数
- }ALGraph_Avo;
-
- /*=====================AVE--关键路径算法=========================*/
- //设计结构体
- typedef struct node_Ave
- { //设计表节点
- int adjvertex;//邻接点域
- int info;//弧的权值
- struct node_Ave* next;//下个结点的指针域
- }EdgeNode_Ave;
-
- typedef struct
- { //设计表头结点
- int indegree;//存放顶点的入度
- int vertex;///顶点域
- EdgeNode_Ave* firstedge;//边表头指针
- }VertexNode_Ave;
-
- typedef struct
- {
- VertexNode_Ave adjlist[MaxVertexNum];//邻接表
- int vertexNum, edgeNum;//顶点数 边数
- }ALGraph_Ave;//设计Ave图的储存结构
-
-
Filename:“graph.h”
- #pragma once
- #include<stdio.h>
- #include"graph_node.h"
-
-
- /*================================图的基本操作===============================*/
- #include<stdlib.h>
-
- //创建图
-
- #define MAX_WEIGHT 10000
- //建立图的邻接矩阵表达
- void Creatgraph(MGragh *G)
- {
- int i, j, k, w;
- printf("Emter vertexNum and edgeNum:(vertexNum,edgeNum):");
- scanf("%d,%d", &(G->vertexNum), &(G->edgeNum)); //输入图的顶点数和边数
- for (i = 0; i < G->vertexNum; i++)
- {
- printf("Eenter vertex:");
- scanf("%c", &(G->vertexs[i]));
- }
- for (i = 0; i < G->vertexNum; i++)
- for (j = 0; j < G->vertexNum; j++)
- G->arcs[i][j] = MAX_WEIGHT; //初始化邻接矩阵
- for (k = 0; k < G->edgeNum; j++)
- {
- printf("Emter vertex_a to vertex_b and weight:(vertex_a,vertex_b,weight):");
- /*
- printf("Emter vertex_a to vertex_b :(vertex_a,vertex_b):");
- scanf("%d,%d,%d", &i, &j);
- G->arcs[i][j] = 1;
- */
- scanf("%d,%d,%d", &i, &j,&w);//输入的为结点位置
- G->arcs[i][j] = w; //若为带权图,输入权值w,赋值G->arcs[i][j]=w;
- G->arcs[j][i] = w; //为无向图
- }
-
- }
-
- //建立有向图的邻接表存储
- void CreatALGraph(ALGraph* G)
- {
- //建立有向图的邻接表存储
- int i, j, k;
- EdgeNode* p;
- printf("Enter vertexNum and edgeNum(a,b):");
- scanf("%d,%d", &(G->vertexNum), &(G->edgeNum)); //读入顶点数和边数
- getchar();
- for (i = 0; i < G->vertexNum; i++)
- {
- printf("Enter vertex'name:");
- scanf("%c", &(G->adjlist[i].vertex));
- getchar();
- G->adjlist[i].firstedge = NULL;
- }
- for (k = 0; k < G->edgeNum; k++)
- {
- printf("Enter vertex to vertex(a,b):");
- scanf("%d,%d", &i, &j);//输入链表中结点的位置
- p = (EdgeNode*)malloc(sizeof(EdgeNode));
- p->adjvertex = j;
- p->next = G->adjlist[i].firstedge;
- G->adjlist[i].firstedge = p; //头插法
- }
- }
-
- //建立十字链表的的有向图
- void CreateOLgraph(OLGraph *G)
- {
- //创建弧指针
- ArcNode* p;
- //采用十字链表表示,构建有向图
- printf("Enter vertexNum and edgeNum(a,b):");
- scanf("%d,%d", &(G->vertexNum), &(G->edgeNum)); //读入顶点数和弧数
- for (int i = 0; i < G->vertexNum; i++)
- {
- printf("Eenter vertex:");
- scanf("%c", &(G->xlist[i].vertex));//输入顶点值
- G->xlist[i].firsin = NULL; G->xlist[i].firstout = NULL;//初始化指针
- }
- //输入各弧,并构造十字链表
- for (int k = 0; k < G->edgeNum; k++)
- {
- int i, j;
- printf("Enter vertex to vertex(a,b):");
- scanf("%d,%d", &i, &j);//此时输入位置,亦可创建locaeVErtex来查询位置
- p = (ArcNode*)malloc(sizeof(ArcNode));
- p->tailvertex = i; p->headvertex = j;
- p->tlink = G->xlist[i].firsin; G->xlist[j].firstout = p;
- p->hlink = G->xlist[j].firsin; G->xlist[j].firsin = p;
- //对弧结点进行赋值并完成弧链头的插入
- }
- }
-
- #define false 0
- #define true 1
-
- int visited[MaxVertexNum];
- void DFS(ALGraph G, int v);
-
- void DFStraverse(ALGraph G)
- {//深度优先遍历以邻接表示的图
- printf("深度优先遍历以邻接表示的图:\n");
- int v;
- for (v = 0; v < G.vertexNum; v++)
- visited[v] = false; //标志向量初始化
- for (v = 0; v < G.vertexNum; v++)
- if (!visited[v])DFS(G, v);
- }
-
- void DFS(ALGraph G, int v)
- {//从第v个顶点出发深度优先遍历图G
- EdgeNode* p;
- int w;
- printf("%c\t",G.adjlist[v].vertex); visited[v] = true;//访问第v个顶点,并把访问标志置true
- for (p = G.adjlist[v].firstedge; p; p = p->next)
- {
- w = p->adjvertex;
- if (!visited[w]) DFS(G, w);//对尚未访问的邻接顶点w递归调用DFS
- }
- }
-
- //广度优先遍历--邻接表
- #include"Seqqueue.h"
-
- void BFS(ALGraph G, int v)
- {
- printf("广度优先遍历图:\n");
- int s;
- for (s = 0; s < G.vertexNum; s++)
- visited[s] = false; //标志向量初始化
- //从v出发按广度优先遍历图G;使用辅助队列和访问标志数组visited
- EdgeNode* p;
- int w;
- VertexNode u;
- PSeqQueue Q; //定义一个队列
- Q = Init_SeqQueue(); //初始化队列
- printf("%c\t", G.adjlist[v].vertex); //访问v
- visited[v] = true; //访问标志量true
- In_SeqQueue(Q, G.adjlist[v]);
- while (!Empty_SeqQueue(Q))
- {
- Out_SeqQueue(Q, &u);
- for (p = u.firstedge; p; p = p->next)
- {
- w = p->adjvertex;
- if (!visited[w])
- {
- printf("%c\t", G.adjlist[w].vertex);
- visited[w] = true;
- In_SeqQueue(Q, G.adjlist[w]);
- }
- }
-
- }
- }
- /*==================最小生成树的实现=======================*/
-
- //prim算法生成最小生成树
- #define INFINITY 30000//定义权值的最大值与建立邻接矩阵中的无穷等价代换
- #define MaxVertexNum 30 //最大的顶点数为30
-
- typedef struct
- {
- int adjvertex; //某顶点与已经构建好的生成树的顶点之间的权值最小的顶点
- int lowcost; //某顶点与已经构建好的树生成树的顶点的最小权值
- }CloseEdge[MaxVertexNum];
-
- void MiniSpanTree_PRIM(MGragh G, int u, CloseEdge closeedge)
- {
- //从第u个顶点出发构造图G的最小生成树,最小生成树的信息存放在closeedge
- int i, j, k, w;
- for(i=0;i<G.vertexNum;i++)
- if (i != u)
- {//初始化辅助数组
- closeedge[i].adjvertex = u;
- closeedge[i].lowcost = G.arcs[u][i];
- }
- closeedge[u].lowcost = 0;
- for (i = 0; i < G.vertexNum-1; i++)
- {
- w = INFINITY;
- for(j=0;j<G.vertexNum;j++) //在辅助数组中选择权最小的顶点
- if (closeedge[j].lowcost != 0 && closeedge[j].lowcost < w)
- {
- w = closeedge[j].lowcost;
- k = j;
- }//求出了生成树的下一个结点
- closeedge[k].lowcost = 0;//第k个结点接入生成树
-
- for(j=0;j<G.vertexNum;j++)//更新辅助数组
- if (G.arcs[k][j] < closeedge[j].lowcost)
- {
- closeedge[j].adjvertex = k;
- closeedge[j].lowcost = G.arcs[k][j];
- }
- //打印生成树
- for (i = 0; i < G.vertexNum; i++)
- {
- if (i != u)
- printf(" %d -> %d, %d", i, closeedge[i].adjvertex,G.arcs[i][closeedge[i].adjvertex]);
- }
- }
- }
-
- //===============krusal算法生成最小生成树
-
- #define MaxVertexNum 30 //最大的顶点数为30
- #define MaxEdge 100
- #define WeightType int
-
- typedef struct ENode
- {
- int vertex1, vertex2;
- WeightType weight;
- }ENode;
-
- typedef struct {
- int vertexNum, edgeNum; //顶点个数,边的个数
- VertexType vertexs[MaxVertexNum]; //顶点信息
- ENode edges[MaxVertexNum]; //边的信息
- }ELGraph;
-
- void sort(ENode edge[])
- {
- //将G.edges大小排序
- }
-
- void kruskal(ELGraph G, ENode TE[])
- {
- //使用krusal算法构造图的最小生成树,用TE[]存放
- int i, j, k;
- int f[MaxVertexNum];
- for (i = 0; i < G.vertexNum; i++) f[i] = i;//初始化为f数组,每个顶点的连通序号均为自己的坐标
- sort(G.edges);//将边集排序
- j = 0; k = 0;
- int s1, s2;
- while (k < G.edgeNum - 1)//选取n-1条边
- {
- s1 = f[G.edges[j].vertex1];
- s2 = f[G.edges[j].vertex2]; //选取边集中的两个结点
- if (s1 != s2)
- {
- TE[k].vertex1 = G.edges[j].vertex1;
- TE[k].vertex2 = G.edges[j].vertex2;
- TE[k].weight = G.edges[j].weight;
- k++;
- for (i = 0; i < G.vertexNum; i++)
- if (f[i] == s2)f[i] = s1; //修改连通的编号
- }
- j++;
- }
-
- }
-
- /*=================================最短路径==================================*/
- //从一个源点到其他结点的最短路径
- //迪杰斯特拉
- //初始状态设计集合S中只包含源点V0,之后不断的从顶点集合T中选取到V0路径最短的点u加入S
- //每次加入后都要更新T中各顶点的最小路径值<--原来的最短路径长度与顶点u的最短路径长度加上u到该顶点的路径长度,中的最小值
- //不断重复,知道T为空
-
- #define INFINITY 30000//定义权的最大值
- #define MaxVertexNum 30 //假设有向网顶点的数最大为30
- #define true 1
- #define false 0
-
- void ShorttestPath_DIJ(MGragh G, int v0, int p[], int D[])
- {
- //Dijkstra 算法求有向网G的v0结点到其余结点的最短路径
- //入口参数:有向网G 源点v0 P[v]表示v的前驱结点 D[v]表示v0到顶点v的最短带权路径长度
- //final[v]为true则表明已经从v0到v的最短路径
- int i, j, w, v;
- int min;
- int final[MaxVertexNum];
- //声明辅助
-
- for(v=0;v<=G.vertexNum-1;v++)//初始化辅助数组
- {
- final[v] = false; //初始化S集合判断数组
- D[v] = G.arcs[v0][v]; //初始化最短路径辅助数组
- p[v] = -1;//初始化为-1,表示无前驱
- if (D[v] < INFINITY)
- p[v] = v0;//若结点v0->v0成立则前驱改为v0结点
- }//初始化
- D[v0] = 0; final[v0] = true;//初始u时候,v0属于S集合
-
- /*开始主循环 ,每次求得v0到某个顶点v的最小路径,并加入到S集合*/
- for (i = 1; i < G.vertexNum; i++)
- {
- v = -1;
- min = INFINITY;
- for(w=0;w<=G.vertexNum-1;w++)//寻找距离v0最近的结点
- if ((!final[w]) && (D[w] < min))
- {
- v = w;
- min = D[w];
- }
- if (v == -1) break;//表明所有与v0有通路的结点均已经找到最短路径,退出循环
- final[w] = true;//将结点加入S集合
- //更新最短路径
- for(w=0;w<G.vertexNum;w++)
- if (!final[w] && (min + G.arcs[v][w] < D[w]))
- {
- D[w] = min + G.arcs[v][w];
- p[w] = w;//修改前驱
- }
- }
- }
-
- void Print_ShorttestPat(MGragh G, int v0, int p[], int D[])
- {
- //入口参数:有向网G 源点v0 P[v]表示v的前驱结点 D[v]表示v0到顶点v的最短带权路径长度
- //final[v]为true则表明已经从v0到v的最短路径
- int v, i, j;
- printf("Theshortest path from Vertex %d to the other Vertex:\n", v0);
- for (v = 0; v < G.vertexNum; v++)
- {
- if (p[v] == -1)continue;//表明顶点v0到v没有通路
- printf("%-4d", D[v]);
- printf("%d<-", v);
- i = v;
- while (p[i] != -1)
- {
- printf("%d<-", p[i]);
- i = p[i];
- }
- printf("\n");
- }
- }
- //每一对顶点之间的最短路径
- //第一种利用上面的算法使得v0遍历T集合从而求得最短路径
-
- void ShorttestPath_Floyd(MGragh G, int p[][MaxVertexNum], int D[][MaxVertexNum])
- {
- //入口参数:有向网G, 各对顶点v和w之间的最短路径p[v][w] 带权长度D[v][w]
- int v, w, u;
- for(v=0;v<G.vertexNum;v++)
- for (w = 0; w < G.vertexNum; w++)
- {
- D[v][w] = G.arcs[v][w];
- p[v][w] = v;
- if (D[v][w] == INFINITY)//没有直接路径
- p[v][w] = -1;
- }//初始化 D p
- for(u=0;u<G.vertexNum;u++)
- for (v = 0; v < G.vertexNum; v++)
- for (w = 0; w < G.vertexNum; w++)
- if (D[v][u] + D[u][w] < D[v][w])//从v到u到w的一条路径最短
- {
- D[v][w] = D[v][u] + D[u][w];
- p[v][w] = u;
- }
- }
-
- /*========================AVO--拓扑排序算法====================*/
- //定义创建函数
- //与上述的相似
-
- //求各个顶点的入度
- void FindInDegree(ALGraph_Avo* G)
- {
- //入口参数邻接表方式存储的图结构
- int i;
- EdgeNode_Avo* p;
- for (i = 0; i < G->vertexNum; i++)
- G->adjlist[i].indegree = 0;//初始化入度
- for (i = 0; i < G->vertexNum; i++)
- {
- for (p = G->adjlist[i].firstedge; p; p = p->next)
- {
- G->adjlist[p->adjvertex].indegree++;//遍历到结点->使其入度加1
- }
- }//
- }//FindInDegree
-
- void Top_Start(ALGraph_Avo G)
- {
- //对邻接链表存储的图,输出其拓扑序列
- //入口参数:带有入度元素的邻接链表格式储存的图结构
- int i, j, k, count = 0;
- int top;//栈顶指针初始化
- EdgeNode_Avo* p;
- FindInDegree(&G);//求各个顶点的入度
- for (i = 0; i < G.vertexNum; i++)//依次将入度为零的顶点压入链式栈
- {
- if (G.adjlist[i].indegree == 0)
- {
- G.adjlist[i].indegree = top;
- top = i;
- }
- }
- while (top != -1)//当栈非空的时候
- {
- j = top;
- top = G.adjlist[top].indegree;//从栈中退出一个顶点并输出
- printf("%3d", G.adjlist[j].vertex);
- count++;//排序到的顶点数
- for (p = G.adjlist[j].firstedge; p; p = p->next)
- {
- k = p->adjvertex;
- G.adjlist[k].indegree--; //当前输入的顶点入度减一
- if (G.adjlist[k].indegree == 0)
- {
- G.adjlist[k].indegree = top;
- top = k;
- }
- }
- }
- if (count < G.vertexNum) printf("The nework has a cycle\n");
- }
-
- /*===================AVE--求各个顶点事件的最早发生时间=======================*/
- //求各个顶点的入度
- void FindInDegree(ALGraph_Ave* G)
- {
- //入口参数邻接表方式存储的图结构
- int i;
- EdgeNode_Ave* p;
- for (i = 0; i < G->vertexNum; i++)
- G->adjlist[i].indegree = 0;//初始化入度
- for (i = 0; i < G->vertexNum; i++)
- {
- for (p = G->adjlist[i].firstedge; p; p = p->next)
- {
- G->adjlist[p->adjvertex].indegree++;//遍历到结点->使其入度加1
- }
- }//
- }//FindInDegree
- int TopOreder(ALGraph_Ave G, int tpord[], int ve[])
- {
- //有向图G,采用邻接表存储结构,求各个顶点事件的最早发生时间
- //若G无回路,用数组tpord[]保存G的一个拓扑序列,返回1,否则返回0
- //入口参数:Ave->邻接表示的图结构 tpord[]G的拓扑序列 ve[]保存各个顶点的最早发生时间<-初始为各个边的权值
- int i, j, k,top, count = 0;
- top = -1;//栈顶指针初始化
- EdgeNode_Ave* p;
- FindInDegree(&G);//求顶点入度
- for (i = 0; i < G.vertexNum; i++)//将入度为0的顶点入栈
- {
- if (G.adjlist[i].indegree == 0)
- {
- G.adjlist[i].indegree = top;
- top = i;
- }
- }
- while (top != -1)
- {
- j = top;
- top = G.adjlist[top].indegree;//从栈中退出一个元素并输出
- tpord[count++] = G.adjlist[j].vertex;
- for (p = G.adjlist[j].firstedge; p; p = p->next)
- {
- k = p->adjvertex;
- G.adjlist[k].indegree--;
- if (G.adjlist[k].indegree == 0)
- {
- G.adjlist[k].indegree = top;
- top = k;//修改栈顶元素
- }
- if (ve[j] + p->info > ve[k])
- ve[k] = ve[j] + p->info;//p->info为边的持续时间
- }
- }
- if (count < G.vertexNum) return 0;
- else return 1;
- }//TopOreder
-
- //输出G的各项关键活动
- int Criticalpath(ALGraph_Ave G)
- {
- //入口参数:关于有有向图设计的邻接表 成功返回1,否则返回0
- int i, j, k, e, l, ve[MaxVertexNum], v1[MaxVertexNum], order[MaxVertexNum];//辅助变量 order为Ave=图的一个拓扑序列
- EdgeNode_Ave* p;
- int count = G.vertexNum;
- if (TopOreder(G, order, ve) == 0) return 0;//该有向图有回路返回0
- for (i = 0; i < G.vertexNum; i++) v1[i] = ve[G.vertexNum - 1]; //初始化顶点事件的最迟发生时间
- for(i=count;i>0;i--)//按照拓扑序列求各个顶点的最迟发生时间
- {
- j = order[i - 1];
- for (p = G.adjlist[j].firstedge; p; p = p->next)
- {
- k = p->adjvertex;
- if (v1[k] - p->info < v1[j]) v1[j] = v1[k] - p->info;
- }
- }
- for(j=0;j<G.vertexNum;j++)//求 e,l 关键活动
- for (p = G.adjlist[j].firstedge; p; p = p->next)
- {
- k = p->adjvertex;
- e = ve[j];
- l = v1[k] - p->info;
- if (l == e)
- printf("%d->%d", j, k);//输出关键活动
- }
- return 1;
- }
-