# include <stdio.h>
# include <stdlib.h>
# include <string.h>
# define VERTEXMAXNUM 10 //最大数值
# define WEIGHTMAXNUM 10 //权值最大数值
# define GraphKind_NUM 5 //图的类型数量
# define GraphKindName_NUM 3 //图的类型名称长度
typedef char VertexType; //顶点类型
typedef enum{DG,UDG,DN,UDN} GraphKind;//图的类型
typedef char AllGraphKind[GraphKind_NUM];//将图的类型以字符串形式输出
typedef char AllGraphKindName[GraphKindName_NUM];//将图的类型名称以字符串形式输入
typedef int Weight;//权值
//弧/边
typedef struct ArcNode *Arc;
struct ArcNode
{
int index;//记录邻接点在顶点数组中的下标
Arc nextarc;//指向下一条弧/边的指针
Weight weight;//图中边的权值
};
//顶点
typedef struct VertexNode
{
VertexType data;//顶点用A~Z表示
Arc firstarc;//顶点的第一条边(头插法)
int cnt;//作为边/弧的第一个顶点出现的次数
}Vertex;
//图_邻接表
typedef struct GNode *Graph;
struct GNode
{
int vexnum;//顶点数量
int arcnum;//弧/边的数量
Vertex vexs[VERTEXMAXNUM];//顶点数组
int len_thisvexs;//当前顶点数组的长度
GraphKind kind;//图的类型
};
/*输入图的类型*/
bool Input_GraphKind(Graph graph)
{
bool flag = true;
AllGraphKindName inputGraphKind;//存放输入的图的类型
printf("请输入图的类型:");
scanf("%s",inputGraphKind);
if(!strcmp(inputGraphKind,"DG"))
graph->kind = DG;
else if(!strcmp(inputGraphKind,"UDG"))
graph->kind = UDG;
else if(!strcmp(inputGraphKind,"DN"))
graph->kind = DN;
else if(!strcmp(inputGraphKind,"UDN"))
graph->kind = UDN;
else
{
flag = false;
printf("无效数值,请重新输入:\n");
}
return flag;
}
/*输入顶点数量*/
bool Input_GraphVexNum(Graph graph)
{
bool flag = true;
int inputVexNum;
printf("请输入顶点数量:");
fflush(stdin); //清除缓存
int ret = scanf("%d",&inputVexNum);
if(ret && getchar() == '\n')//防止输入无效字符
{
if(inputVexNum > 0 && inputVexNum <= VERTEXMAXNUM)
graph->vexnum = inputVexNum;
else if(inputVexNum <= 0)
{
flag = false;
printf("顶点数量<=0,请重新输入:\n");
}
else
{
flag = false;
printf("顶点数量>最大数值,请重新输入:\n");
}
}
else
{
flag = false;
printf("无效数值,请重新输入:\n");
}
return flag;
}
/*输入弧/边的数量*/
bool Input_GraphArcNum(Graph graph)
{
bool flag = true;
int inputArcNum;
if(graph->kind == DG || graph->kind == DN)
printf("请输入弧数量:");
else
printf("请输入边数量:");
fflush(stdin); //清除缓存
int ret = scanf("%d",&inputArcNum);
if(ret && getchar() == '\n')//防止输入无效字符
{
if(graph->kind == DG || graph->kind == DN)
{
if(inputArcNum >= 0 && inputArcNum <= graph->vexnum*(graph->vexnum - 1))
graph->arcnum = inputArcNum;
else if(inputArcNum < 0)
{
flag = false;
printf("输入的弧数量<0,请重新输入:\n");
}
else
{
flag = false;
printf("输入的弧数量>最大数值,请重新输入:\n");
}
}
else
{
if(inputArcNum >= 0 && inputArcNum <= graph->vexnum*(graph->vexnum - 1)/2)
graph->arcnum = inputArcNum;
else if(inputArcNum < 0)
{
flag = false;
printf("输入的边数量<0,请重新输入:\n");
}
else
{
flag = false;
printf("输入的边数量>最大数值,请重新输入:\n");
}
}
}
else
{
flag = false;
printf("无效数值,请重新输入:\n");
}
return flag;
}
/*检查输入的顶点是否重复*/
bool Check_InputVexIsRecurring(Graph graph,VertexType inputVex)
{
bool flag = false;
for(int i=0;i<graph->len_thisvexs;i++)
{
if(inputVex == graph->vexs[i].data)
{
flag = true;
printf("输入的顶点已存在,请重新输入:\n");
break;
}
}
return flag;
}
/*输入顶点*/
bool Input_GraphVex(Graph graph)
{
graph->len_thisvexs = 0;
bool flag = true;
VertexType inputVex;
for(int i=0;i<graph->vexnum;i++)
{
while(true)
{
printf("请输入第%d个顶点:",(i+1));
fflush(stdin); //清除缓存
scanf("%c",&inputVex);
if(!Check_InputVexIsRecurring(graph,inputVex))
break;
}
graph->vexs[i].data = inputVex;
graph->vexs[i].firstarc = NULL;//每个顶点指向第一条边的指针
graph->vexs[i].cnt = 0;
graph->len_thisvexs++;
}
return flag;
}
/*初始化图*/
void Init_Graph(Graph graph)
{
/*输入图的类型*/
while(true)
{
if(Input_GraphKind(graph))
break;
}
/*输入顶点数量*/
while(true)
{
if(Input_GraphVexNum(graph))
break;
}
/*输入弧/边的数量*/
while(true)
{
if(Input_GraphArcNum(graph))
break;
}
/*输入顶点*/
while(true)
{
if(Input_GraphVex(graph))
break;
}
}
/*输出图*/
void Show_Graph(Graph graph)
{
printf("\n");//与输入的数据隔开
/*输出图的类型*/
AllGraphKind thisGraphKind[GraphKind_NUM]= {"DG","UDG","DN","UDN"};//
printf("该图的类型是:%s\n",thisGraphKind[graph->kind]);
/*输出图的顶点数量*/
printf("该图有%d个顶点\n",graph->vexnum);
/*输出图的弧/边数量*/
if(graph->kind == DN || graph->kind == DG)
printf("该图有%d个弧\n",graph->arcnum);
else
printf("该图有%d个边\n",graph->arcnum);
/*输出图的顶点*/
for(int i=0;i<graph->vexnum;i++)
printf("第%d个顶点:%c\n",i+1,graph->vexs[i].data);
/*输出图的当前顶点数量*/
printf("该图当前有%d个顶点\n",graph->len_thisvexs);
/*输出图的权值*/
if(graph->kind == DN || graph->kind == UDN)
{
for(int i=0;i<graph->arcnum;i++)
{
Arc arcTemp = graph->vexs[i].firstarc;
while(arcTemp)
{
if(graph->kind == DN)
printf("%c -> %c的权值:%d\n",
graph->vexs[i].data,graph->vexs[arcTemp->index].data,arcTemp->weight);
else
printf("%c - %c的权值:%d\n",
graph->vexs[i].data,graph->vexs[arcTemp->index].data,arcTemp->weight);
arcTemp = arcTemp->nextarc;
}
}
}
/*展示图的邻接表结构*/
printf("该图的邻接表结构是:\n");
for(int i=0;i<graph->vexnum;i++)
{
printf("%c ",graph->vexs[i].data);//展示图中每个顶点
Arc arcTemp = graph->vexs[i].firstarc;//创建一个中间容器存放边/弧
while(arcTemp)
{
printf("-> ");
printf("%c ",graph->vexs[arcTemp->index].data);
arcTemp = arcTemp->nextarc;
}
printf("\n");//每个顶点隔开
}
}
/*查询顶点在图中的位置(即顶点数组的下标)*/
int Index_Graph(VertexType vex,Graph graph)
{
int index = -1;
for(int i=0;i<graph->vexnum;i++)
{
if(vex == graph->vexs[i].data)
index = i;
}
return index;
}
/*创建弧/边时,检查输入的两个顶点是否在图中(即顶点数组中)*/
bool Check_InputVexBelongsToGraph(Graph graph,int index)
{
bool flag = true;
if(index == -1)
{
flag = false;
printf("输入的顶点不在图中,请重新输入:\n");
}
return flag;
}
/*检查输入的边/弧的第一个顶点是否重复超出规定次数*/
bool Check_InputFirstVexOfArcIsRecurring(Graph graph,int V1_Index)
{
bool flag = false;
graph->vexs[V1_Index].cnt++;
if(graph->vexs[V1_Index].cnt >= graph->vexnum)
{
flag = true;
printf("输入的顶点已重复,请重新输入:\n");
}
return flag;
}
/*检查输入的边/弧是否重复*/
bool Check_InputArcIsRecurring(Graph graph,int V1_Index,int V2_Index)
{
bool flag = false;
Arc arcTemp = graph->vexs[V1_Index].firstarc;//中间容器存放每个顶点的每一条边
while(arcTemp)
{
if(arcTemp->index == V2_Index)
{
flag = true;
if(graph->kind == DG || graph->kind == DN)
printf("输入的弧已存在,请重新输入:\n");
else
printf("输入的边已存在,请重新输入:\n");
break;
}
arcTemp = arcTemp->nextarc;
}
return flag;
}
/*构造有向图*/
void Create_DG(Graph graph)
{
VertexType V1,V2;//一条弧/边需要两个顶点 V1:弧头 V2:弧尾
int V1_Index,V2_Index;//两个顶点的位置
for(int i=0;i<graph->arcnum;i++)
{
/*输入两个顶点*/
while(true)
{
printf("请输入第%d条弧的第1个顶点V1:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V1);
/*查询V1的位置*/
V1_Index = Index_Graph(V1,graph);
if(Check_InputVexBelongsToGraph(graph,V1_Index))
{
if(!Check_InputFirstVexOfArcIsRecurring(graph,V1_Index))
break;
}
}
while(true)
{
printf("请输入第%d条弧的第2个顶点V2:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V2);
/*查询V2的位置*/
V2_Index = Index_Graph(V2,graph);
if(Check_InputVexBelongsToGraph(graph,V2_Index))
{
if(!Check_InputArcIsRecurring(graph,V1_Index,V2_Index))
break;
}
}
/*创建V1 -> V2的弧结点*/
Arc V1_arc_V2 = (Arc)malloc(sizeof(ArcNode));
V1_arc_V2->index = V2_Index;
V1_arc_V2->nextarc = graph->vexs[V1_Index].firstarc;
V1_arc_V2->weight = -1;
graph->vexs[V1_Index].firstarc = V1_arc_V2;
}
}
/*构造无向图*/
void Create_UDG(Graph graph)
{
VertexType V1,V2;//一条弧/边需要两个顶点 V1:弧头 V2:弧尾
int V1_Index,V2_Index;//两个顶点的位置
for(int i=0;i<graph->arcnum;i++)
{
/*输入两个顶点*/
while(true)
{
printf("请输入第%d条边的第1个顶点V1:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V1);
/*查询V1的位置*/
V1_Index = Index_Graph(V1,graph);
if(Check_InputVexBelongsToGraph(graph,V1_Index))
{
if(!Check_InputFirstVexOfArcIsRecurring(graph,V1_Index))
break;
}
}
while(true)
{
printf("请输入第%d条边的第2个顶点V2:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V2);
/*查询V2的位置*/
V2_Index = Index_Graph(V2,graph);
if(Check_InputVexBelongsToGraph(graph,V1_Index))
{
if(!Check_InputArcIsRecurring(graph,V1_Index,V2_Index))
break;
}
}
/*创建V1~V2的边*/
Arc V1_arc_V2 = (Arc)malloc(sizeof(ArcNode));
V1_arc_V2->index = V2_Index;
V1_arc_V2->nextarc = graph->vexs[V1_Index].firstarc;
V1_arc_V2->weight = -1;
graph->vexs[V1_Index].firstarc = V1_arc_V2;
/*创建V2~V1的边*/
Arc V2_arc_V1 = (Arc)malloc(sizeof(ArcNode));
V2_arc_V1->index = V1_Index;
V2_arc_V1->nextarc = graph->vexs[V2_Index].firstarc;
V2_arc_V1->weight = -1;
graph->vexs[V2_Index].firstarc = V2_arc_V1;
}
}
/*输入权值*/
bool Input_Weight(Arc arc,Weight weight)
{
bool flag = true;
if(weight >= 0 && weight <= WEIGHTMAXNUM)
arc->weight = weight;
else if(weight < 0)
{
flag = false;
printf("输入的权值<0,请重新输入:\n");
}
else
{
flag = false;
printf("输入的权值>最大数值,请重新输入:\n");
}
return flag;
}
/*构造有向网*/
void Create_DN(Graph graph)
{
VertexType V1,V2;//一条弧/边需要两个顶点 V1:弧头 V2:弧尾
int V1_Index,V2_Index;//两个顶点的位置
Weight weight;//每一条边的权值
for(int i=0;i<graph->arcnum;i++)
{
/*输入两个顶点*/
while(true)
{
printf("请输入第%d条弧的第1个顶点V1:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V1);
/*查询V1的位置*/
V1_Index = Index_Graph(V1,graph);
if(Check_InputVexBelongsToGraph(graph,V1_Index))
{
if(!Check_InputFirstVexOfArcIsRecurring(graph,V1_Index))
break;
}
}
while(true)
{
printf("请输入第%d条弧的第2个顶点V2:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V2);
/*查询V2的位置*/
V2_Index = Index_Graph(V2,graph);
if(Check_InputVexBelongsToGraph(graph,V2_Index))
{
if(!Check_InputArcIsRecurring(graph,V1_Index,V2_Index))
break;
}
}
/*创建V1 -> V2的弧结点*/
Arc V1_arc_V2 = (Arc)malloc(sizeof(ArcNode));
while(true)//输入权值
{
printf("请输入第%d条弧的权值:",i+1);
fflush(stdin);//去除回车干扰
int ret = scanf("%d",&weight);
if(ret && getchar()=='\n')
{
if(Input_Weight(V1_arc_V2,weight))
break;
}
else
printf("无效数值,请重新输入:\n");
}
V1_arc_V2->index = V2_Index;
V1_arc_V2->nextarc = graph->vexs[V1_Index].firstarc;
graph->vexs[V1_Index].firstarc = V1_arc_V2;
}
}
/*构造无向网*/
void Create_UDN(Graph graph)
{
VertexType V1,V2;//一条弧/边需要两个顶点 V1:弧头 V2:弧尾
int V1_Index,V2_Index;//两个顶点的位置
Weight weight;
for(int i=0;i<graph->arcnum;i++)
{
/*输入两个顶点*/
while(true)
{
printf("请输入第%d条边的第1个顶点V1:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V1);
/*查询V1的位置*/
V1_Index = Index_Graph(V1,graph);
if(Check_InputVexBelongsToGraph(graph,V1_Index))
{
if(!Check_InputFirstVexOfArcIsRecurring(graph,V1_Index))
break;
}
}
while(true)
{
printf("请输入第%d条边的第2个顶点V2:",i+1);
fflush(stdin); //清除缓存
scanf("%c",&V2);
/*查询V2的位置*/
V2_Index = Index_Graph(V2,graph);
if(Check_InputVexBelongsToGraph(graph,V2_Index))
{
if(!Check_InputArcIsRecurring(graph,V1_Index,V2_Index))
break;
}
}
/*创建V1~V2的边*/
Arc V1_arc_V2 = (Arc)malloc(sizeof(ArcNode));
while(true)//输入权值
{
printf("请输入第%d条边的权值:",i+1);
fflush(stdin); //清除缓存
int ret = scanf("%d",&weight);
if(ret && getchar()=='\n')
{
if(Input_Weight(V1_arc_V2,weight))
break;
}
else
printf("无效数值,请重新输入:\n");
}
V1_arc_V2->index = V2_Index;
V1_arc_V2->nextarc = graph->vexs[V1_Index].firstarc;
graph->vexs[V1_Index].firstarc = V1_arc_V2;
/*创建V2~V1的边*/
Arc V2_arc_V1 = (Arc)malloc(sizeof(ArcNode));
V2_arc_V1->index = V1_Index;
V2_arc_V1->nextarc = graph->vexs[V2_Index].firstarc;
V2_arc_V1->weight = V1_arc_V2->weight;
graph->vexs[V2_Index].firstarc = V2_arc_V1;
}
}
/*构造图*/
void Create_Graph(Graph graph)
{
/*根据图的类型构造图*/
switch(graph->kind)
{
case DG:Create_DG(graph);
break;
case UDG:Create_UDG(graph);
break;
case DN:Create_DN(graph);
break;
case UDN:Create_UDN(graph);
break;
}
}
/*创建图/网的逆邻接数组(创建一个新数组)*/
Vertex* Create_InverseVexs(Graph graph)
{
Vertex *vexs = (Vertex*)malloc(graph->vexnum*(sizeof(Vertex)));
for(int i=0;i<graph->vexnum;i++)
{
vexs[i].data = graph->vexs[i].data;
vexs[i].cnt = 0;
vexs[i].firstarc = NULL;
}
return vexs;
}
/*创建图/网的逆邻接表*/
Vertex* Create_InverseGraph(Graph graph,Vertex *vexs)
{
for(int i=0;i<graph->vexnum;i++)
{
Arc arcTemp = graph->vexs[i].firstarc;
while(arcTemp)
{
Arc newArc = (Arc)malloc(sizeof(ArcNode));
newArc->index = i;
newArc->nextarc = vexs[arcTemp->index].firstarc;
vexs[arcTemp->index].firstarc = newArc;
arcTemp = arcTemp->nextarc;
}
}
return vexs;
}
/*展示图/网的逆邻接表结构*/
void Show_InverseTable(Vertex *vexs,int len)
{
printf("该图的逆邻接表结构是:\n");
for(int i=0;i<len;i++)
{
printf("%c",vexs[i].data);
Arc arcTemp = vexs[i].firstarc;
while(arcTemp)
{
printf("->");
printf("%c",vexs[arcTemp->index].data);
arcTemp = arcTemp->nextarc;
}
printf("\n");
}
}
int main ()
{
Graph graph = (Graph)malloc(sizeof(GNode));
Init_Graph(graph);
Create_Graph(graph);
Show_Graph(graph);
if(graph->kind == DG || graph->kind == DN)
{
int len = graph->vexnum;
Vertex* vexs = Create_InverseVexs(graph);
vexs = Create_InverseGraph(graph,vexs);
Show_InverseTable(vexs,len);
free(vexs);
}
return 0;
}