6.1 实验目的
(1) 掌握图的基本概念。
(2) 掌握图的存储结构的设计与实现,基本运算的实现。
(3) 熟练掌握图的两种遍历算法、遍历生成树及遍历算法的应用。
6.2 实验任务
分别设计图(网)的邻接矩阵、邻接表存储结构,编写算法实现下列问题的求解。
1.打印出图(网)的两种遍历序。
实验测试数据基本要求:
第一组数据: udg8.grp
第二组数据: udg115.grp
第三组数据: dg6.grp
第四组数据: f14.grp
2.求给定图中的边(或弧)的数目。 实验测试数据基本要求:第一组数据: udg8.grp
第二组数据: udg115.grp
第三组数据: dg6.grp
第四组数据: f14.grp
3.对给定的图G及出发点v0,设计算法从V0出发深度优先遍历图G,并构造出相应的生成树或生成森林。
实验测试数据基本要求:
第一组数据: udg8.grp
第二组数据: dg6.grp
第三组数据: udn8.grp
第四组数据: dn10.grp
4.对给定的图G及出发点v0,设计算法从V0出发广度优先遍历图G,并构造出相应的生成树或生成森林。
第一组数据: udg8.grp
第二组数据: dg6.grp
第三组数据: un8.grp
第四组数据: dn10.grp
5.实现Prim算法,求解下列给定图G的最小生成树。 实验测试数据基本要求:第一组数据: udn6.grp
第二组数据: un8.grp
6.实现Kruskal算法,求解下列给定图G的最小生成树。 实验测试数据基本要求:第一组数据: udn6.grp
第二组数据: un8.grp
7.实现Dijkstra算法,求解下列给定图G指定顶点到其余顶点之间的最短路径。 实验测试数据基本要求:第一组数据: udn6.grp
第二组数据: un8.grp
第三组数据: dn8.grp
第四组数据: dn10.grp
8.实现Floyd算法,求解下列给定图G各顶点之间的最短路径。 实验测试数据基本要求:第一组数据: udn6.grp
第二组数据: un8.grp
第三组数据: dn8.grp
第四组数据: dn10.grp
9.设计算法求解下列给定图G的拓扑序列。 实验测试数据基本要求:
第一组数据: top6dg1.grp
第二组数据: top7dg1.grp
第三组数据: dn8.grp
第四组数据: dn10.grp
10.设计算法求解下列给定AOE网的关键路径。 实验测试数据基本要求:
第一组数据: dn8.grp(测试数据有环)
第二组数据: dn10.grp(测试数据有环)
#pragma warning(disable:4996);
#include"p6.h"
using namespace std;
void task1(){
Graph1 G1;
Graph2 G2;
char filename[20];
strcpy(filename,"udg8.grp");
CreateGraphFromFile(filename,G1);
dfs21(G1);
cout<<endl;
bfs21(G1);
cout<<endl;
DestroyGraph(G1);
CreateGrpFromFile(filename,G2);
dfs22(G2);
cout<<endl;
bfs22(G2);
//printGraph2(G2);
system("pause");
}
void task2(){
Graph1 G1;
Graph2 G2;
int i,j;
char filename[20];
strcpy(filename,"udg8.grp");
CreateGraphFromFile(filename,G1);
i=d21(G1);
cout<<i<<endl;
CreateGrpFromFile(filename,G2);
j=d22(G2);
cout<<j<<endl;
system("pause");
}
void task3(){
Graph2 G2;
csNode* T;
int i,j;
char filename[20];
strcpy(filename,"udg8.grp");
CreateGrpFromFile(filename,G2);
T=DFSForest(G2,T,1);
cout<<"树或森林的先序遍历序为:";
perOrderTraverse(T);
cout<<endl;
system("pause");
}
void task4(){
Graph2 G2;
csNode* T;
int i,j,m;
char filename[20];
strcpy(filename,"udg8.grp");
CreateGrpFromFile(filename,G2);
T=BFSForest(G2,T,1);
cout<<"树或森林的先序遍历序为:";
perOrderTraverse(T);
cout<<endl;
system("pause");
}
void task5(){
Graph1 G1;
Graph2 G2;
csNode *T,*T2;
int i,j;
char filename[20];
strcpy(filename,"udn6.grp");
CreateGraphFromFile(filename,G1);
Prim(G1,1);
cout<<endl;
CreateGrpFromFile(filename,G2);
Prim(G2,1);
system("pause");
}
void task6(){
Graph1 G1;
Graph2 G2;
csNode* T,*T2;
int i,j;
char filename[20];
strcpy(filename,"udn6.grp");
CreateGraphFromFile(filename,G1);
Kruskal(G1);
cout<<endl;
CreateGrpFromFile(filename,G2);
Kruskal(G2);
system("pause");
}
void task7(){
Graph1 G1;
Graph2 G2;
int str[MaxVerNum+1],str2[MaxVerNum+1];
int i,j;
char filename[20];
strcpy(filename,"udn6.grp");
CreateGraphFromFile(filename,G1);
Dijkstra(G1,str,str2,1);
cout<<endl;
CreateGrpFromFile(filename,G2);
Dijkstra(G2,str,str2,1);
system("pause");
}
void task8(){
Graph1 G1;
Graph2 G2;
int str[MaxVerNum][MaxVerNum];
cellType str2[MaxVerNum][MaxVerNum];
int i,j;
char filename[20];
strcpy(filename,"udn6.grp");
CreateGrpFromFile(filename,G2);
Floyd(G2,str2,str);
PrintFloyd(G2,str2,str);
system("pause");
}
void task9(){
Graph2 G2;
int str[MaxVerNum+1],str2[MaxVerNum+1],str3[MaxVerNum+1];
int i,j;
char filename[20];
strcpy(filename,"top6dg1.grp");
CreateGrpFromFile(filename,G2);
TopologicalSort(G2,str);
printTopoList(G2,str);
system("pause");
}
void task10(){
Graph2 G2;
int vet[MaxVerNum]={0},vlt[MaxVerNum]={0},topoList[MaxVerNum];
char filename[20];
strcpy(filename,"top7dg1.grp");
CreateGrpFromFile(filename,G2);
TopologicalSort(G2,topoList,vet);
antiTopologicalSort(G2,vlt,vet);
printCriticalPath(G2,topoList,vlt,vet);
system("pause");
}
int main(void){
while(true){
int i;
cout<<"********************************************************测试菜单功能****************************************************\n";
cout<<"<1>:打印出图(网)的两种遍历序\n";
cout<<"<2>:求给定图中的边(或弧)的数目\n";
cout<<"<3>:对给定的图G及出发点v0,设计算法从V0出发深度优先遍历图G,并构造出相应的生成树或生成森林\n";
cout<<"<4>:对给定的图G及出发点v0,设计算法从V0出发广度优先遍历图G,并构造出相应的生成树或生成森林\n";
cout<<"<5>:实现Prim算法,求解下列给定图G的最小生成树\n";
cout<<"<6>:实现Kruskal算法,求解下列给定图G的最小生成树\n";
cout<<"<7>:实现Dijkstra算法,求解下列给定图G指定顶点到其余顶点之间的最短路径\n";
cout<<"<8>:实现Floyd算法,求解下列给定图G各顶点之间的最短路径\n";
cout<<"<9>:设计算法求解下列给定图G的拓扑序列\n";
cout<<"<10>:设计算法求解下列给定AOE网的关键路径\n";
cout<<endl;
cout<<"输入选择功能序号:";
cin>>i;
switch(i){
case 1: task1();break;
case 2: task2();break;
case 3: task3();break;
case 4: task4();break;
case 5: task5();break;
case 6: task6();break;
case 7: task7();break;
case 8: task8();break;
case 9: task9();break;
case 10: task10();break;
}
system("CLS");
}
return 0;
}
#pragma warning(disable:4996);
#include"grpAdjLinkedList.h"
#include<iostream>
using namespace std;
void strLTrim(char* str); //删除字符串左边空格
//***************************2 文件创建图****************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图 *//
//* 入口参数 char fileName[],文件名 *//
//* 出口参数:Graph &G,即创建的图 *//
//* 返 回 值:bool,true创建成功;false创建失败 *//
//* 函 数 名:CreateGrpFromFile(char fileName[], Graph &G) *//
//* 备 注:本函数使用的数据文件以邻接矩阵为输入数据 *//
//*******************************************************************//
int CreateGrpFromFile1(char fileName[], Graph &G)
{
FILE* pFile; //定义顺序表的文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
char* ss;
int i=0,j=0;
int edgeNum=0; //边的数量
GraphKind graphType; //图类型枚举变量
pFile=fopen(fileName,"r");
if(!pFile)
{
printf("错误:文件%s打开失败。\n",fileName);
return false;
}
ss=fgets(str,1000,pFile);
strncpy(strTemp,str,2);
while((ss!=NULL) && (strstr(strTemp,"//")!=NULL) ) //跳过注释行
{
ss=fgets(str,1000,pFile);
strncpy(strTemp,str,2);
//cout<<strTemp<<endl;
}
//循环结束,str中应该已经是文件标识,判断文件格式
//cout<<str<<endl;
if(strstr(str,"Graph")==NULL)
{
printf("错误:打开的文件格式错误!\n");
fclose(pFile); //关闭文件
return false;
}
//读取图的类型
if(fgets(str,1000,pFile)==NULL)
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return false;
}
//设置图的类型
if(strstr(str,"UDG"))
graphType=UDG; //无向图
else if(strstr(str,"UDN"))
graphType=UDN; //无向网
else if(strstr(str,"DG"))
graphType=DG; //有向图
else if(strstr(str,"DN"))
graphType=DN; //有向网
else
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return false;
}
//读取顶点元素,到str
if(fgets(str,1000,pFile)==NULL)
{
printf("错误:读取图的顶点元素失败!\n");
fclose(pFile); //关闭文件
return false;
}
//顶点数据放入图的顶点数组
char* token=strtok(str," ");
int nNum=1;
while(token!=NULL)
{
G.VerList[nNum].data=*token;
G.VerList[nNum].firstEdge=NULL;
//p=NULL;
//eR=G.VerList[i].firstEdge;
token = strtok( NULL, " ");
nNum++;
}
//循环读取邻接矩阵数据
int nRow=1; //矩阵行下标
int nCol=1; //矩阵列下标
EdgeNode* eR; //边链表尾指针
EdgeNode* p;
while(fgets(str,1000,pFile)!=NULL)
{
eR=NULL;
p=NULL;
nCol=1; //列号设为0,一行重新开始
char* token=strtok(str," "); //以空格为分隔符,分割一行数据,写入邻接矩阵
while(token!=NULL)
{
if(atoi(token)>=1 && atoi(token)<INF) //考虑到网
{
p=new EdgeNode; //申请一个边链表结点
p->adjVer=nCol; //顶点的编号,从1开始
p->eInfo=atoi(token); //有权图保存权值,无权图为1
p->next=NULL;
if(G.VerList[nRow].firstEdge==NULL)
{
G.VerList[nRow].firstEdge=p;
eR=p;
}
else
{
eR->next=p;
eR=p; //新的尾指针
}
edgeNum++; //边数加1
}
token = strtok( NULL, " "); //读取下一个子串
nCol++;
}
nRow++; //一行数据处理完毕
}
G.VerNum=nNum; //图的顶点数
if(graphType==UDG || graphType==UDN)
G.ArcNum=edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.ArcNum=edgeNum;
G.gKind=graphType; //图的类型
fclose(pFile); //关闭文件
return true;
}
//***************************3 文件创建图****************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图 *//
//* 入口参数 char fileName[],文件名 *//
//* 出口参数:Graph &G,即创建的图 *//
//* 返 回 值:bool,true创建成功;false创建失败 *//
//* 函 数 名:CreateGraphUDFromFile(char fileName[], Graph &G) *//
//* 备注:本函数使用的数据文件格式以边(顶点对)为基本数据 *//
//*******************************************************************//
int CreateGraphFromFile(char fileName[], Graph &G)
{
FILE* pFile; //定义顺序表的文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
int i=0,j=0;
int edgeNum=0; //边的数量
eInfoType eWeight; //边的信息,常为边的权值
GraphKind graphType; //图类型枚举变量
pFile=fopen(fileName,"r");
if(!pFile)
{
printf("错误:文件%s打开失败。\n",fileName);
return false;
}
while(fgets(str,1000,pFile)!=NULL) //跳过空行和注释行
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //跳过注释行
continue;
else //非注释行、非空行,跳出循环
break;
}
//循环结束,str中应该已经是文件标识,判断文件格式
if(strstr(str,"Graph")==NULL)
{
printf("错误:打开的文件格式错误!\n");
fclose(pFile); //关闭文件
return false;
}
//读取图的类型,跳过空行及注释行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的类型标识
break;
}
//设置图的类型
if(strstr(str,"UDG"))
graphType=UDG; //无向图
else if(strstr(str,"UDN"))
graphType=UDN; //无向网
else if(strstr(str,"DG"))
graphType=DG; //有向图
else if(strstr(str,"DN"))
graphType=DN; //有向网
else
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return false;
}
//读取顶点元素,到str。跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的顶点元素行
break;
}
//顶点数据放入图的顶点数组
char* token=strtok(str," ");
int nNum=0;
while(token!=NULL)
{
G.VerList[nNum+1].data=*token;
G.VerList[nNum+1].firstEdge=NULL;
//p=NULL;
//eR=G.VerList[i].firstEdge;
token = strtok( NULL, " ");
nNum++;
}
//循环读取边(顶点对)数据
int nRow=1; //矩阵行下标
int nCol=1; //矩阵列下标
EdgeNode* eR; //边链表尾指针
EdgeNode* p;
elementType Nf,Ns; //边或弧的2个相邻顶点
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
//nCol=0; //列号设为0,一行重新开始
char* token=strtok(str," "); //以空格为分隔符,分割一行数据,写入邻接矩阵
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
Nf=*token; //获取边的第一个顶点
token = strtok( NULL, " "); //读取下一个子串,即第二个顶点
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
Ns=*token; //获取边的第二个顶点
//从第一个顶点获取行号
for(nRow=1;nRow<=nNum;nRow++)
{
if(G.VerList[nRow].data==Nf) //从顶点列表找到第一个顶点的编号
break;
}
//从第二个顶点获取列号
for(nCol=1;nCol<=nNum;nCol++)
{
if(G.VerList[nCol].data==Ns) //从顶点列表找到第二个顶点的编号
break;
}
//如果为网,读取权值
if(graphType==UDN || graphType==DN)
{
token = strtok( NULL, " "); //读取下一个子串,即边的附加信息,常为边的权重
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
eWeight=atoi(token); //取得边的附加信息
}
eR=G.VerList[nRow].firstEdge;
while(eR!=NULL && eR->next!=NULL)
{
eR=eR->next; //后移边链表指针,直至尾节点
}
p=new EdgeNode; //申请一个边链表结点
p->adjVer=nCol; //顶点的编号,从1开始
if(graphType==UDN || graphType==DN) //边的附加信息,对有权图保存权值,无权图为1
p->eInfo=eWeight;
else
p->eInfo=1;
p->next=NULL;
if(G.VerList[nRow].firstEdge==NULL)
{
G.VerList[nRow].firstEdge=p;
eR=p;
}
else
{
eR->next=p;
eR=p; //新的尾指针
}
edgeNum++; //边数加1
}
G.VerNum=nNum; //图的顶点数
if(graphType==UDG || graphType==UDN)
G.ArcNum=edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.ArcNum=edgeNum;
G.gKind=graphType; //图的类型
fclose(pFile); //关闭文件
return true;
}
//删除字符串、字符数组左边空格
void strLTrim(char* str)
{
int i,j;
int n=0;
n=strlen(str)+1;
for(i=0;i<n;i++)
{
if(str[i]!=' ') //找到左起第一个非空格位置
break;
}
//以第一个非空格字符为手字符移动字符串
for(j=0;j<n;j++)
{
str[j]=str[i];
i++;
}
}
//销毁图
void DestroyGraph(Graph &G)
{
EdgeNode *p,*u;
int vID;
for(vID=1; vID<=G.VerNum; vID++) //循环删除每个顶点的边链表
{
p=G.VerList[vID].firstEdge;
G.VerList[vID].firstEdge=NULL;
while(p) //循环删除当前顶点所有的关联边
{
u=p->next; //u指向下一个边结点
delete(p); //删除当前边结点
p=u;
}
}
p=NULL;
u=NULL;
G.VerNum=-1; //编辑图已经销毁
}
#pragma warning(disable:4996);
#include"grpAdjMatrix.h"
#include<iostream>
void strLTrim(char* str);
using namespace std;
//*************************从数据文件创建图**************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图 *//
//* 入口参数 char fileName[],文件名 *//
//* 出口参数: *//
//* 返 回 值:bool,true创建成功;false创建失败 *//
//* 函 数 名:CreateGrpFromFile(char fileName[]) *//
//*******************************************************************//
bool CreateGrpFromFile(char fileName[], Graph &G)
{
FILE* pFile; //定义顺序表的文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
cellType eWeight; //边的信息,常为边的权值
GraphKind GrpType; //图类型枚举变量
pFile=fopen(fileName,"r");
if(!pFile)
{
printf("错误:文件%s打开失败。\n",fileName);
return false;
}
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //跳过注释行
continue;
else //非注释行、非空行,跳出循环
break;
}
//循环结束,str中应该已经是文件标识,判断文件格式
if(strstr(str,"Graph")==NULL)
{
printf("错误:打开的文件格式错误!\n");
fclose(pFile); //关闭文件
return false;
}
//读取图的类型,跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的类型标识
break;
}
//设置图的类型
if(strstr(str,"UDG"))
GrpType=UDG; //无向图
else if(strstr(str,"UDN"))
GrpType=UDN; //无向网
else if(strstr(str,"DG"))
GrpType=DG; //有向图
else if(strstr(str,"DN"))
GrpType=DN; //有向网
else
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return false;
}
//读取顶点元素,到str。跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的顶点元素行
break;
}
//顶点数据放入图的顶点数组
char* token=strtok(str," ");
int nNum=1;
while(token!=NULL)
{
G.Data[nNum]=*token; // atoi(token); //顶点数据转换为整数,若为字符则不需转换
token = strtok( NULL, " ");
nNum++;
}
nNum--; //顶点数
//图的邻接矩阵初始化
int nRow=1; //矩阵行下标,从1开始
int nCol=1; //矩阵列下标,从1开始
if(GrpType==UDG || GrpType==DG)
{
for(nRow=1;nRow<=nNum;nRow++)
for(nCol=1;nCol<=nNum;nCol++)
G.AdjMatrix[nRow][nCol]=0;
}
else
{
for(nRow=1;nRow<=nNum;nRow++)
for(nCol=1;nCol<=nNum;nCol++)
G.AdjMatrix[nRow][nCol]=INF; //INF表示无穷大
}
//循环读取边的数据到邻接矩阵
int edgeNum=0; //边的数量
elementType Nf,Ns; //边或弧的2个相邻顶点
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
char* token=strtok(str," "); //以空格为分隔符,分割一行数据,写入邻接矩阵
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
Nf=*token; //获取边的第一个顶点
token = strtok( NULL, " "); //读取下一个子串,即第二个顶点
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
Ns=*token; //获取边的第二个顶点
//从第一个顶点获取行号
for(nRow=1;nRow<=nNum;nRow++)
{
if(G.Data[nRow]==Nf) //从顶点列表找到第一个顶点的编号
break;
}
//从第二个顶点获取列号
for(nCol=1;nCol<=nNum;nCol++)
{
if(G.Data[nCol]==Ns) //从顶点列表找到第二个顶点的编号
break;
}
//如果为网,读取权值
if(GrpType==UDN || GrpType==DN)
{
token = strtok( NULL, " "); //读取下一个子串,即边的附加信息,常为边的权重
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return false;
}
eWeight=atoi(token); //取得边的附加信息
}
if(GrpType==UDN || GrpType==DN) //如果为网,邻接矩阵中对应的边设置权值,否则置为1
G.AdjMatrix[nRow][nCol]=eWeight;
else
G.AdjMatrix[nRow][nCol]=1; //atoi(token); //字符串转为整数
edgeNum++; //边数加1
}
G.VerNum=nNum; //图的顶点数
if(GrpType==UDG || GrpType==UDN)
G.ArcNum=edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.ArcNum=edgeNum;
G.gKind=GrpType; //图的类型
fclose(pFile); //关闭文件
return true;
}
//删除字符串、字符数组左边空格
void strLTrim(char* str)
{
int i,j;
int n=0;
n=strlen(str)+1;
for(i=0;i<n;i++)
{
if(str[i]!=' ') //找到左起第一个非空格位置
break;
}
//以第一个非空格字符为手字符移动字符串
for(j=0;j<n;j++)
{
str[j]=str[i];
i++;
}
}
//Dijkstra算法--基于邻接表
//*************** Dijkstra算法--基于邻接表 *****************//
//* 函数功能:给定顶点,求解此点与网中其它顶点的最短路径 *//
//* 入口参数:Graph G,待访问的网(图) *//
//* int vID,指定顶点的编号 *//
//* 出口参数:int path[],返回最短路径信息 *//
//* int dist[],返回最短距离值 *//
//* 返 回 值:无 *//
//***********************************************************//
void Dijkstra(Graph1 &G, int path[], int dist[], int vID)
{
int solved[MaxVerNum]; //标记顶点是否已经求出最短路径(已在集合S中)。1-已求出;0-未求出。
int i,j;
int v; //顶点编号
eInfoType minDist; //保存最短距离值
EdgeNode *p; //指向边链表结点
//初始化集合S,距离数组dist[],路径数组path[]
for(i=1;i<=G.VerNum;i++)
{
solved[i]=0; //所有顶点均为处理
dist[i]=INF; //所有顶点初始距离置为无穷大(INF)
path[i]=-1; //所有顶点的前驱置为-1,即无前驱
}
//处理顶点vID
solved[vID]=1; //标记vID已经处理
dist[vID]=0;
path[vID]=-1;
//从邻接表初始化dist[]和path[]
p=G.VerList[vID].firstEdge; //顶点vID的边链表指针
while(p)
{
v=p->adjVer; //取得vID邻接顶点编号
dist[v]=p->eInfo; //取得vID与v之间边的权值,赋给dist[v]
path[v]=vID; //顶点v的前驱为vID
p=p->next;
}
//依次找出余下n-1个顶点加入集合S中
for(i=1;i<G.VerNum;i++)
{
minDist=INF;
//寻找集合V-S中距离vID最近的顶点
for(j=1;j<=G.VerNum;j++)
{
if(solved[j]==0 && dist[j]<minDist)
{
v=j; //j为V-S中候选的距离vID最近的顶点
minDist=dist[j];
}
}
if(minDist==INF) //S与V-S没有相邻的顶点,算法退出
return;
cout<<"选择顶点:"<<G.VerList[v].data<<"--距离:"<<minDist<<endl; //输出本次选择的顶点距离
solved[v]=1; //标记顶点v以找到最短距离,加入集合S中
//对选中的顶点v,更新集合V-S中所有与v邻接的顶点距离vID的距离
p=G.VerList[v].firstEdge; //取得v的边链表指针
while(p)
{
j=p->adjVer; //取得v的邻接顶点编号
if(solved[j]==0 && minDist+p->eInfo<dist[j])
{
dist[j]=minDist+p->eInfo; //更新顶点j的最小距离
path[j]=v; //j的前驱改为顶点v
}
p=p->next;
}
}
}
//打印Dijkstra算法结果
void PrintDijkstra(Graph1 &G, int path[], int dist[], int vID)
{
int sPath[MaxVerNum]; //按顺序保存从vID到目标顶点最短路径上各个顶点的编号
int vPre; //保存前驱顶点编号
int i,j;
int top=-1; //标记vID当目标顶点经过的顶点个数
for(i=1;i<=G.VerNum;i++)
{
cout<<G.VerList[vID].data<<" to "<<G.VerList[i].data;
if(dist[i]==INF)
cout<<" 无可达路径。"<<endl;
else
{
cout<<" 最短距离:"<<dist[i]<<endl;
cout<<" 路径:";
}
top++;
sPath[top]=i; //sPath[0]保存当前目标顶点编号i
vPre=path[i]; //取得顶点i的直接前驱到vPre
while(vPre!=-1) //从第i个顶点,通过前驱顶点往前搜索到根结点,给出最短路径途经的顶点序列
{
top++;
sPath[top]=vPre; //当前顶点vPre存入最短路径途径顶点序列数组
vPre=path[vPre]; //取得当前顶点的前驱顶点编号到vPre
}
//依次打印从vID到i顶点的最短路径顶点序列,如果最短路径存在
if(dist[i]!=INF)
{
for(j=top;j>=0;j--) //sPath[top]为指定的起始顶点vID,sPath[0]为顶点i
{
cout<<G.VerList[sPath[j]].data<<" ";
}
}
top=-1; //初始化top,以取得下一个顶点
cout<<endl;
}
}
//Dijkstra算法--基于邻接矩阵
//************** Dijkstra算法--基于邻接矩阵 ****************//
//* 函数功能:给定顶点,求解此点与网中其它顶点的最短路径 *//
//* 入口参数:Graph G,待访问的网(图) *//
//* int vID,指定顶点的编号 *//
//* 出口参数:int path[],返回最短路径信息 *//
//* int dist[],返回最短距离值 *//
//* 返 回 值:无 *//
//***********************************************************//
void Dijkstra(Graph2 &G, int path[], int dist[], int vID)
{
int solved[MaxVerNum]; //标记顶点是否已经求出最短路径。1--已求解,0--未求解
int i,j;
int v;
cellType minDist; //最短距离,cellType为自定义的邻接矩阵中元素的数据类型
//初始化集合S和距离向量
for(i=1;i<=G.VerNum;i++)
{
solved[i]=0; //所有顶点均待求
dist[i]=G.AdjMatrix[vID][i];
if(dist[i]!=INF)
path[i]=vID; //第i顶点的前驱为vID
else
path[i]=-1; //当前顶点i无前驱
}
solved[vID]=1; //标记顶点vID已求解
dist[vID]=0; //vID到自身的距离为0
path[vID]=-1; //vID为起始顶点,无前驱
//依次找出其它n-1个顶点加入已求解集合S
for(i=1; i<G.VerNum; i++)
{
minDist=INF;
for(j=1;j<=G.VerNum;j++) //在未解顶点中寻找距vID距离最近的顶点,编号保存到v。
{
if(solved[j]==0 && dist[j]<minDist) //j目前尚在V-S中,为未解顶点
{
v=j;
minDist=dist[j];
}
}
if(minDist==INF)
return;
cout<<"选择顶点:"<<G.Data[v]<<"--距离:"<<minDist<<endl; //输出本次选择的顶点距离
solved[v]=1; //顶点v已找到最短距离,加入已解集合S中
//对选中的顶点v,修改未解顶点集V-S中,v的邻接顶点(直接后继)到顶点vID的距离
for(j=1; j<=G.VerNum; j++)
{
if(solved[j]==0 && (minDist+G.AdjMatrix[v][j])<dist[j])
{
dist[j]=minDist+G.AdjMatrix[v][j]; //更新顶点j到顶点vID的最短距离。
path[j]=v; //更新顶点j的直接前驱为顶点v
}
}
}
/* cout<<"*******************"<<endl;
for(i=1; i<=G.VerNum;i++)
{
cout<<i<<"--"<<path[i]<<endl;
}
cout<<"*******************"<<endl;
*/
}
void PrintDijkstra(Graph2 &G, int path[], int dist[], int vID )
{
int sPath[MaxVerNum]; //定义一个类似栈操作的数组
int vPre; //前驱结点编号
int top=-1; //栈顶
int i;
int j;
for(i=1; i<=G.VerNum; i++)
{
cout<<G.Data[vID]<<" to "<<G.Data[i];
if(dist[i]==INF)
cout<<" 无可达路径。"<<endl;
else
{
cout<<" 最短距离:"<<dist[i]<<endl;
cout<<" 路径:";
}
//printf("\nDistanced: %7d, path:", dist[i]);
top++;
sPath[top]=i; //sPath[0]为当前顶点i
vPre=path[i]; //i顶点的前驱顶点
while(vPre!=-1) //从第i个顶点,通过前驱顶点往前搜索到根结点,给出最短路径
{
top++;
sPath[top]=vPre;
vPre=path[vPre];
}
/* while(top>0)
{
top--;
cout<<G.Data[sPath[top]]<<" ";
//printf("%2d", sPath[top]);
}
*/
if(dist[i]!=INF)
{
for(j=top;j>=0;j--) //sPath[top]为指定的起始顶点
{
cout<<G.Data[sPath[j]]<<" ";
}
}
top=-1;
cout<<endl;
}
}
//Floyd算法
//typedef cellType dist[MaxVerNum][MaxVerNum];
//typedef int path[MaxVerNum][MaxVerNum];
void Floyd(Graph2 &G, cellType dist[MaxVerNum][MaxVerNum], int path[MaxVerNum][MaxVerNum])
{
int i,j,k;
//初始化距离矩阵和路径矩阵
for(i=1;i<=G.VerNum;i++)
{
for(j=1;j<=G.VerNum;j++)
{
dist[i][j]=G.AdjMatrix[i][j]; //距离矩阵初始化为邻接矩阵
//初始化路径矩阵,路径矩阵元素path[i][j]中保存编号j顶点的前驱的顶点编号
if( i!=j && G.AdjMatrix[i][j]<INF) //如果i,j之间存在边,则j的前驱为i。否则前驱置为-1
path[i][j]=i;
else
path[i][j]=-1;
}
}
//从k=1开始,迭代到k=G.verNum。依次选择一个顶点k,作为顶点i、j之间的中转顶点,优化顶点i、j之间的距离
//下面是Floyd算法的核心--三重for循环
for(k=1; k<=G.VerNum; k++)
{
for(i=1; i<=G.VerNum; i++)
{
for(j=1; j<=G.VerNum;j++)
{
if(i!=j && dist[i][k]+dist[k][j]<dist[i][j]) //k作为中转跳点,i、j之间距离变小,接收k作为中转点,更新i、j之间的距离
{
dist[i][j]=dist[i][k]+dist[k][j]; //更新距离
path[i][j]=path[k][j]; //修改前驱顶点
}
}
}
//打印k顶点作为中转跳点后优化的距离矩阵
cout<<"第"<<k<<"轮优化后的距离矩阵:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
cout<<"\t";
for(j=1;j<=G.VerNum;j++)
{
if((G.gKind==UDN || G.gKind==DN) && dist[i][j]==INF)
cout<<"INF"<<"\t"; //网,无穷大时,打印“INF”表示
else
cout<<dist[i][j]<<"\t";
}
cout<<endl;
}
//打印k顶点作为中转跳点后优化的路径矩阵
cout<<"第"<<k<<"轮优化后的路径矩阵:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
cout<<"\t";
for(j=1;j<=G.VerNum;j++)
{
cout<<path[i][j]<<"\t";
}
cout<<endl;
}
}
}
//打印Floyd算法给出的最短路径
void PrintFloyd(Graph2 &G, cellType dist[MaxVerNum][MaxVerNum], int path[MaxVerNum][MaxVerNum])
{
int sPath[MaxVerNum]; //定义一个类似栈操作的数组
int pra; //前驱结点编号
int top=-1; //栈顶
int i;
int j;
int m;
for(i=1; i<=G.VerNum; i++)
{
for(j=1; j<=G.VerNum; j++)
{
cout<<G.Data[i]<<" to "<<G.Data[j];
if(dist[i][j]==INF)
cout<<" 无可达路径。"<<endl;
else
{
cout<<" 最短距离:"<<dist[i][j]<<endl;
cout<<" 路径:";
top++;
sPath[top]=j; //sPath[0]为当前顶点i
pra=path[i][j]; //i顶点的前驱顶点
while(pra!=i)
{
top++;
sPath[top]=pra;
pra=path[i][pra];
}
top++;
sPath[top]=i; //加进起始顶点i
if(dist[i][j]!=INF)
{
for(m=top;m>=0;m--) //sPath[top]为指定的起始顶点
{
cout<<G.Data[sPath[m]]<<" ";
}
}
top=-1;
cout<<endl;
}
}
}
}
#pragma warning(disable:4996);
//************************************************************//
//* 图的邻接链表表示的头文件,文件名:GraphAdjLinkList.h *//
//* *//
//************************************************************//
#include<iostream>
#define INF 65535 //定义无穷大
#define MaxVerNum 100 //定义最大顶点个数
using namespace std;
typedef char elementType; //定义图中顶点的数据类型
typedef int eInfoType; //边链表中关于边的信息的数据类型,比如,带权图中可以表示边的权值
typedef int cellType; //定义邻接矩阵中元素的数据类型
//对无权图,1-相邻(有边),0-不相邻(无边)
//对有权图,为边的权值,特别是无穷大。
typedef enum{UDG, UDN, DG, DN} GraphKind; //枚举图的类型--无向图,无向网,有向图,有向网
typedef struct eNode //边链表结点结构
{
int adjVer; //邻接顶点地址,此处为顶点在顶点表中序号,从1开始
eInfoType eInfo; //边链表中表示边的相关信息,比如表的权值
struct eNode* next; //指向边链表中的下一个结点
}EdgeNode; //边链表结点类型
typedef struct vNode //顶点表中元素结构
{
elementType data; //存放图中顶点的数据
EdgeNode* firstEdge; //指向此顶点关联的第一条边的指针,即边链表的头指针
}VerNode;
typedef struct GraphAdjLinkList
{
VerNode VerList[MaxVerNum+1]; //存放顶点的顺序表,数组0单元不用
int VerNum; //顶点数
int ArcNum; //弧(边)数
GraphKind gKind; //图的类型:0-无向图;1-无向网;2-有向图;3-有向网
}Graph; //图的类型名
bool visited[MaxVerNum+1]; //全局数组,标记顶点是否已经被访问。0--未访问;1--已访问。数组0单元不用
//******************* 访问图中顶点的函数*********************//
//* 函数功能:打印图中顶点元素,并标记为已经访问 *//
//* 入口参数 Graph G,待访问的图;int verID 目标顶点编号 *//
//* 出口参数:无 *//
//* 返 回 值:空 *//
//* 函 数 名:visit(Graph &G, int verID) *//
//***********************************************************//
void visit(Graph &G, int verID)
{ //顶点编号从1开始,数组0单元不用
cout<<G.VerList[verID].data<<"\t";
visited[verID]=true;
}
//******************* 图中查找目标顶点 *********************//
//* 函数功能:给定顶点元素,在图中查找此顶点元素的编号 *//
//* 入口参数 Graph G,待访问的图;elementType v 目标顶点 *//
//* 出口参数:无 *//
//* 返 回 值:int。如果目标顶点存在,返回顶点编号, *//
//* 顶点编号从1开始;否则返回-1 *//
//* 函 数 名:visit(Graph &G, int verID) *//
//***********************************************************//
int LocateVertex(Graph &G, elementType v)
{
for(int i=1;i<=G.VerNum;i++)
{
if( G.VerList[i].data==v )
return i;
}
return -1;
}
//搜索顶点v的第一个邻接顶点
int firstAdj(Graph &G, int v)
{
EdgeNode *p;
p=G.VerList[v].firstEdge;
if(p)
return p->adjVer;
else
return 0;
}
//搜索顶点v位于邻接点w之后的下一个邻接点
int nextAdj(Graph &G, int v, int w)
{
EdgeNode *p;
p=G.VerList[v].firstEdge; //取顶点v的边链表头指针
while(p->next)
{
if(p->adjVer==w)
return p->next->adjVer; //返回w之后下一个邻接点编号
p=p->next;
}
return 0; //未找到下一个邻接点,返回0
}
//******************** 打印图的相关信息 *********************//
//* 函数功能:打印图的相关信息 *//
//* 入口参数 Graph G,待打印的图 *//
//* 出口参数:无 *//
//* 返 回 值:空 *//
//* 函 数 名:printGraph(Graph &G) *//
//***********************************************************//
void printGraph(Graph &G)
{
int i=0,j=0;
//打印图的类型
switch(G.gKind)
{
case UDG:
cout<<"图类型:无向图"<<endl;
break;
case UDN:
cout<<"图类型:无向网"<<endl;
break;
case DG:
cout<<"图类型:有向图"<<endl;
break;
case DN:
cout<<"图类型:有向网"<<endl;
break;
default:
cout<<"图类型错误。"<<endl;
break;
}
//打印图的顶点数
cout<<"顶点数:"<<G.VerNum<<endl;
//打印图的边数
cout<<"边 数:"<<G.ArcNum<<endl;
//打印顶点及其编号
cout<<"编号\t顶点\t边链表"<<endl;
EdgeNode* p;
for(i=1;i<=G.VerNum;i++)
{
cout<<i<<"\t"<<G.VerList[i].data<<"\t";
p=G.VerList[i].firstEdge;
while(p!=NULL)
{
cout<<p->adjVer<<"\t";
p=p->next;
}
cout<<endl;
}
cout<<endl;
//打印邻接矩阵
cout<<"邻接矩阵:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
cout<<"\t";
p=G.VerList[i].firstEdge;
j=1;
while(p!=NULL || j<=G.VerNum)
{
if((j<=G.VerNum) && (p!=NULL) && j==p->adjVer) //有边
{
cout<<p->eInfo<<"\t";
j++;
p=p->next;
}
else //无边
{
if(G.gKind==UDN || G.gKind==DN)
cout<<"INF"<<"\t"; //网,无边时打印权值为无穷大,用“INF”表示
else
cout<<"0"<<"\t"; //图,无边时用0表示
j++;
}
}
cout<<endl;
}
}
#pragma warning(disable:4996);
//************************************************************//
//* 图的邻接矩阵存储的头文件,文件名:grpAdjMatrix.h *//
//* *//
//************************************************************//
#include "stdio.h"
#include "stdlib.h"
#include <iostream>
#include "string.h"
using namespace std;
#define INF 65535 //定义无穷大
#define MaxVerNum 100 //定义最大顶点个数
//typedef int elementType; //定义图中顶点的数据类型
typedef char elementType; //定义图中顶点的数据类型
typedef int cellType; //定义邻接矩阵中元素的数据类型
//对无权图,1-相邻(有边),0-不相邻(无边)
//对有权图,为边的权值,特别是无穷大。
//枚举图的类型--无向图(UDG),无向网(UDN),有向图(DG),有向网(DN)
typedef enum{UDG, UDN, DG, DN} GraphKind;
bool visited[MaxVerNum+1]; //全局数组,标记顶点是否已经访问,visited[0]单元不用
//****************************************************//
//* 定义邻接矩阵表示的图结构。5个分量组成: *//
//* data[]数组保存图中顶点数据元素 *//
//* AdjMatrix[][]邻接矩阵 *//
//* VerNum顶点个数 *//
//* ArcNum边(弧)条数 *//
//* gKind枚举图的类型 *//
//* 考虑到名称的统一性,图类型名称定义为Graph *//
//****************************************************//
typedef struct GraphAdjMatrix
{
elementType Data[MaxVerNum+1]; //顶点数组,存放顶点元素的值,Data[0]单元不用
cellType AdjMatrix[MaxVerNum+1][MaxVerNum+1]; //邻接矩阵,数组下标为0单元不用,从AdjMatrix[1][1]单元开始
int VerNum; //顶点数
int ArcNum; //弧(边)数
GraphKind gKind; //图的类型:0-无向图;1-无向网;2-有向图;3-有向网
} Graph; //图的类型名
//******************* 访问图中顶点的函数*********************//
//* 函数功能:打印图中顶点元素,并标记为已经访问 *//
//* 入口参数 Graph G,待访问的图;int verID 目标顶点编号 *//
//* 出口参数:无 *//
//* 返 回 值:空 *//
//* 函 数 名:visit(Graph &G, int verID) *//
//***********************************************************//
void visit(Graph &G, int verID)
{ //顶点编号从1开始,数组0单元不用
cout<<G.Data[verID]<<"\t";
visited[verID]=true;
}
//******************* 图中查找目标顶点 *********************//
//* 函数功能:给定顶点元素,在图中查找此顶点元素 *//
//* 入口参数 Graph G,待访问的图;elementType v 目标顶点 *//
//* 出口参数:无 *//
//* 返 回 值:int。如果目标顶点存在,返回顶点编号, *//
//* 顶点编号从1开始;否则返回-1 *//
//* 函 数 名:visit(Graph &G, int verID) *//
//***********************************************************//
int LocateVertex(Graph &G, elementType v)
{
for(int i=1;i<=G.VerNum;i++)
{
if( G.Data[i]==v )
return i;
}
return -1;
}
//求顶点v的第一个邻接点
int firstAdj(Graph &G,int v)
{
int w;
for(w=1;w<=G.VerNum;w++)
{
if((G.AdjMatrix[v][w]>=1) &&
(G.AdjMatrix[v][w])<INF)
return w; //返回第一个邻接点编号
}
return 0; //未找到邻接点,返回0
}
//求顶点v的位于邻接点w后的下一个邻接点
int nextAdj(Graph &G,int v,int w)
{
int k;
for(k=w+1;k<=G.VerNum;k++)
{
if((G.AdjMatrix[v][k]>=1) &&
(G.AdjMatrix[v][k])<INF)
return k; //返回v的位于w之后的下一个邻接点k
}
return 0; //不存在下一个邻接点,返回0
}
//******************** 打印图的相关信息 *********************//
//* 函数功能:打印图的相关信息 *//
//* 入口参数:Graph G,待打印的图 *//
//* 出口参数:无 *//
//* 返 回 值:空 *//
//* 函 数 名:GraphPrint(Graph &G) *//
//***********************************************************//
void printGraph(Graph &G)
{
int i=0,j=0;
//打印图的类型
switch(G.gKind)
{
case UDG:
cout<<"图类型:无向图"<<endl;
break;
case UDN:
cout<<"图类型:无向网"<<endl;
break;
case DG:
cout<<"图类型:有向图"<<endl;
break;
case DN:
cout<<"图类型:有向网"<<endl;
break;
default:
cout<<"图类型错误。"<<endl;
break;
}
//打印图的顶点数
cout<<"顶点数:"<<G.VerNum<<endl;
//打印图的边数
cout<<"边 数:"<<G.ArcNum<<endl;
//打印顶点及其编号
cout<<"编 号:";
for(i=1;i<=G.VerNum;i++)
{
cout<<i<<"\t";
}
cout<<endl;
cout<<"顶 点:";
for(i=1;i<=G.VerNum;i++)
{
cout<<G.Data[i]<<"\t";
}
cout<<endl;
//打印邻接矩阵
cout<<"图的邻接矩阵:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
cout<<"\t";
for(j=1;j<=G.VerNum;j++)
{
if((G.gKind==UDN || G.gKind==DN) && G.AdjMatrix[i][j]==INF)
cout<<"INF"<<"\t"; //网,无穷大时,打印“INF”表示
else
cout<<G.AdjMatrix[i][j]<<"\t";
}
cout<<endl;
}
}
//Kruskal 算法--基于邻接链表
typedef struct edgetype1
{
int vBegin; //边的起始顶点编号,从1开始
int vEnd; //边的另一顶点编号,从1开始
eInfoType eWeight; //边的权值
}EdgeType1;
//从图的邻接链表读取所有边的信息,存储到一维数组edges[]中
void GetEdges(Graph1 &G, EdgeType1 edges[])
{
int i;
int k=0;
EdgeNode *p;
for(i=1;i<=G.VerNum;i++)
{
p=G.VerList[i].firstEdge;
while(p)
{
edges[k].vBegin=LocateVertex1(G, G.VerList[i].data); //由顶点数据获取顶点编号
edges[k].vEnd=p->adjVer;
edges[k].eWeight=p->eInfo;
p=p->next;
k++;
}
}
//for(i=0;i<k;i++)
//{
// cout<<edges[i].vBegin<<"<-->"<<edges[i].vEnd<<"\t"<<edges[i].eWeight<<endl;
//}
}
//获取当前可用最小边--替代方法:对edges[]数组进行递增排序后,就不需要此函数
EdgeType1 GetMinEdge(Graph1 &G, EdgeType1 edges[], int edgeUsed[], int &n) //n为返回的最小边在edges[]数组中的下标
{
EdgeType1 minEdge;
eInfoType wMin=INF; //保存最小权值
int i,M;
if(G.gKind==UDN || G.gKind==UDG)
M=G.ArcNum*2; //无向图和无向网,由于对称性,edges[]有效数据个数是边数的2倍
else
M=G.ArcNum;
for(i=0;i<M;i++)
{
if(edgeUsed[i]==0 && edges[i].eWeight<wMin)
{
wMin=edges[i].eWeight;
minEdge.eWeight=edges[i].eWeight;
minEdge.vBegin=edges[i].vBegin;
minEdge.vEnd=edges[i].vEnd;
n=i;
}
}
return minEdge; //返回取得的最小边
}
//Kruskal算法
void Kruskal(Graph1 &G)
{
int conVerID[MaxVerNum]; //存放连通分量(子树)编号
EdgeType1 edges[MaxVerNum*MaxVerNum]; //存放所有的边信息
EdgeType1 treeEdges[MaxVerNum-1]; //存放生成树的所有边信息,共n-1条边
int edgeUsed[MaxVerNum*MaxVerNum]; //与edges[]数组对应,标记一条边是否已经使用过。1--已用过,0--未用过
//也可以用排序算法先对图的所有边进行排序来完成这个工作。
EdgeType1 minEdge; //保存最小边
int i,j;
int n; //返回的最小边的序号
int conID; //获取连通分量编号
int M; //循环次数
if(G.gKind==UDG ||G.gKind==UDN)
M=G.ArcNum*2; //因为无向图、无向网邻接矩阵对称,有效数据是边数的2倍,所以乘2
else
M=G.ArcNum; //有向图或有向网,M=边数
//获取图所有边的信息,存入数组edges[]
GetEdges( G, edges );
for(i=0; i<M; i++)
edgeUsed[i]=0; //标记edges[]中所有边都可用。
//初始化连通分量编号。开始每个顶点作为一个连通分量,每个一个编号,从1开始,与顶点编号相同
for(i=1;i<=M;i++)
{
conVerID[i]=i; //顶点编号与数组下标差1
}
for(i=1; i<G.VerNum; i++) //取出n-1条边,构成生成树
{
minEdge=GetMinEdge(G,edges,edgeUsed,n); //取得本轮循环的最小边
while(conVerID[minEdge.vBegin]==conVerID[minEdge.vEnd]) //当前最小边2个顶点已经属于同一个连通分量,不可用,继续取下一条最小边、
{
edgeUsed[n]=1; //标记边edges[n](从0开始)不可用
minEdge=GetMinEdge(G,edges,edgeUsed,n); //继续取下一条最小边
}
//取得有效最小边,加入最小生成树中
treeEdges[i]=minEdge;
conID=conVerID[minEdge.vBegin]; //取得此最小边开始顶点的连通编号
for(j=1;j<=G.VerNum;j++)
{
if(conVerID[j]==conID)
conVerID[j]=conVerID[minEdge.vEnd];
}
edgeUsed[i]=1; //当前最小边标记为已使用边
}
//输出结果
eInfoType wAll=0; //总权值
cout<<endl; //输出结果
cout<<"Kruskal最小生成树-->>"<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<G.VerNum;i++) //n-1条边
{
cout<<"("<<G.VerList[treeEdges[i].vBegin].data<<","<<G.VerList[treeEdges[i].vEnd].data<<")";
cout<<" 或 ("<<treeEdges[i].vBegin<<","<<treeEdges[i].vEnd<<")。权值:"<<treeEdges[i].eWeight<<endl;
wAll+=treeEdges[i].eWeight;
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
//----并查集处理部分--------------------------------------
//----并查集处理结束---------------------------------------
//--使用并查集的Kruskal算法
void disjointKruskal(Graph1 &G)
{
EdgeType1 edges[MaxVerNum*MaxVerNum]; //存放图的所有边信息
EdgeType1 treeEdges[MaxVerNum]; //存放生成树中的边信息,n-1条
int edgeUsed[MaxVerNum*MaxVerNum]; //辅助数组,标记图中的边是否已经尝试过。1--已用过,0--未用过
//也可以用排序算法先对图的所有边进行排序来完成这个工作
int anc[MaxVerNum]; //---只在并查集处理时使用
unionIni(anc,MaxVerNum);
EdgeType1 minEdge; //保存最小边
int i,j;
int n; //返回的最小边的序号
//获取图所有边的信息,存入数组edges[]
GetEdges( G, edges );
//初始化可用边数组--可用排序算法取代
int M; //循环次数
if(G.gKind==UDG ||G.gKind==UDN)
M=G.ArcNum*2; //因为无向图、无向网邻接矩阵对称,有效数据是边数的2倍,所以乘2
else
M=G.ArcNum;
for(i=0; i<M; i++)
edgeUsed[i]=0;
for(i=1; i<G.VerNum; i++) //取出n-1条边,构成生成树
{
minEdge=GetMinEdge(G, edges, edgeUsed, n );
if(find(anc,minEdge.vBegin)==find(anc,minEdge.vEnd)) //并查集查询当前边的2个顶点是否在同一个连通分量上
{
edgeUsed[n]=1; //2个顶点属于同一个连通分量,必构成回路,此最小边不可用
minEdge=GetMinEdge( G, edges, edgeUsed, n ); //继续取下一条最小边,直到取得可用边为止
}
//到此取得了1条可用最小边,加入生成树中
treeEdges[i]=minEdge;
//并查集的合并处理,边的2个顶点合并到一个集合(同一个连通分量)
unionSet(anc,minEdge.vBegin,minEdge.vEnd);
edgeUsed[n]=1;
}
//输出结果
eInfoType wAll=0; //总权值
cout<<endl; //输出结果
cout<<"Kruskal(并查集)最小生成树-->>"<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<G.VerNum;i++) //n-1条边
{
cout<<"("<<G.VerList[treeEdges[i].vBegin].data<<","<<G.VerList[treeEdges[i].vEnd].data<<")";
cout<<" 或 ("<<treeEdges[i].vBegin<<","<<treeEdges[i].vEnd<<")。权值:"<<treeEdges[i].eWeight<<endl;
wAll+=treeEdges[i].eWeight;
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
//Kruskal算法--基于邻接矩阵
//所有数组0单元未用,使顶点编号与数组小标相同
//定义边的结构类型
typedef struct edgeType2
{
int vBegin; //边的起始顶点编号,从1开始
int vEnd; //边的终止顶点编号,从1开始
cellType eWeight; //边的权值
}EdgeType2;
//从图的邻接矩阵读取所有边的信息,存储到一个数组中
void GetEdges(Graph2 &G, EdgeType2 edges[])
{
int i,j;
int k=0; //作为边的编号
for(i=1;i<=G.VerNum;i++) //行循环
{
for(j=1;j<=G.VerNum;j++) //列循环
{
if((G.AdjMatrix[i][j]>=1) && //数组下标与顶点编号相同
(G.AdjMatrix[i][j]<INF))
{
edges[k].vBegin=i; //边的第一顶点
edges[k].vEnd=j; //边的第二顶点
edges[k].eWeight=G.AdjMatrix[i][j]; //边的权值
k++;
}
}
}
}
//获取当前可用最小边
EdgeType2 GetMinEdge(Graph2 &G, EdgeType2 edges[], int edgeUsed[], int &n) //n为返回的最小边在edges[]数组中的下标
{
EdgeType2 minEdge;
cellType wMin=INF; //保存最小权值
int i;
int M; //控制循环次数
if(G.gKind==UDG || G.gKind==UDN)
M=G.ArcNum*2; //无向网,无向图,应为对称性,邻接居中有效数据是边数的2倍
else
M=G.ArcNum; //有向图,有向网中,M即为图的边数
for(i=0; i<M ;i++) //因为无向图邻接矩阵对称中,有效数据是边数的2倍,所以乘2
{
if(edgeUsed[i]==0 && edges[i].eWeight<wMin)
{
wMin=edges[i].eWeight;
minEdge.eWeight=edges[i].eWeight;;
minEdge.vBegin=edges[i].vBegin;
minEdge.vEnd=edges[i].vEnd;
n=i;
}
}
return minEdge; //返回取得的最小边
}
//Kruskal算法
void Kruskal(Graph2 &G)
{
int conVerID[MaxVerNum]; //顶点的连通分量编号数组
EdgeType2 edges[MaxVerNum*MaxVerNum]; //存放图的所有边信息
EdgeType2 treeEdges[MaxVerNum]; //存放生成树中的边信息,n-1条
int edgeUsed[MaxVerNum*MaxVerNum]; //辅助数组,标记图中的边是否已经尝试过。1--已用过,0--未用过
//也可以用排序算法先对图的所有边进行排序来完成这个工作
EdgeType2 minEdge; //保存最小边
int i,j;
int k=0;
int conID; //边的终止顶点的连通分量的编号
int n; //返回的最小边的序号
//获取图所有边的信息,存入数组edges[]
GetEdges( G, edges );
//初始化可用边数组--可用排序算法取代
int M; //循环次数
if(G.gKind==UDG ||G.gKind==UDN)
M=G.ArcNum*2; //因为无向图、无向网邻接矩阵对称,有效数据是边数的2倍,所以乘2
else
M=G.ArcNum;
for(i=0; i<M; i++)
edgeUsed[i]=0;
//初始化连通分量编号。开始每个顶点作为一个连通分量,每个一个编号,从1开始,与顶点编号相同
for(i=1;i<=G.VerNum;i++)
{
conVerID[i]=i; //顶点编号与数组下标差1
}
for(i=1; i<G.VerNum; i++) //取出n-1条边,构成生成树
{
minEdge=GetMinEdge(G, edges, edgeUsed, n );
while(conVerID[minEdge.vBegin]==conVerID[minEdge.vEnd])
{
edgeUsed[n]=1; //2个顶点属于同一个连通分量,必构成回路,此最小边不可用
minEdge=GetMinEdge( G, edges, edgeUsed, n ); //继续取下一条最小边,直到取得可用边为止
}
//到此取得了1条可用最小边,加入生成树中
treeEdges[i]=minEdge;
conID=conVerID[minEdge.vEnd]; //取得最小边的终点编号
//conID=conVerID[minEdge.vBegin]; //取得最小边的起点编号
for(j=1;j<=G.VerNum;j++) //合并连通编号到最小编号
{
if(conVerID[j]==conID) //所有连通分量编号为conID的顶点,连通分量编号都改为最小边起始顶点的连通号
{
conVerID[j]=conVerID[minEdge.vBegin];
}
}
edgeUsed[n]=1;
}
//输出结果
cellType wAll=0; //总权值
cout<<endl; //输出结果
cout<<"Kruskal最小生成树-->>"<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<G.VerNum;i++) //n-1条边
{
cout<<"("<<G.Data[treeEdges[i].vBegin]<<","<<G.Data[treeEdges[i].vEnd]<<")";
cout<<" 或 ("<<treeEdges[i].vBegin<<","<<treeEdges[i].vEnd<<")。权值:"<<treeEdges[i].eWeight<<endl;
wAll+=treeEdges[i].eWeight;
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
//----并查集处理部分--------------------------------------
//1.初始化并查集
//--int anc[]存储每个集合的代表值,此处anc[0]单元未用;n为元素个数
//--anc[]可设为全局数组,在递归中更节约空间
void unionIni(int anc[], int n)
{
for(int i=1;i<=n; i++)
anc[i]=i;
}
//2.查找函数
int find(int anc[], int x)
{
if(x!=anc[x])
anc[x]=find(anc,anc[x]);
return anc[x];
}
//3.合并函数
void unionSet(int anc[], int x, int y)
{
int fx,fy;
fx=find(anc,x);
fy=find(anc,y);
if(fx!=fy)
anc[x]=fy;
}
//----并查集处理结束---------------------------------------
//--使用并查集的Kruskal算法
void disjointKruskal(Graph2 &G)
{
EdgeType2 edges[MaxVerNum*MaxVerNum]; //存放图的所有边信息
EdgeType2 treeEdges[MaxVerNum]; //存放生成树中的边信息,n-1条
int edgeUsed[MaxVerNum*MaxVerNum]; //辅助数组,标记图中的边是否已经尝试过。1--已用过,0--未用过
//也可以用排序算法先对图的所有边进行排序来完成这个工作
int anc[MaxVerNum]; //---只在并查集处理时使用
unionIni(anc,MaxVerNum);
EdgeType2 minEdge; //保存最小边
int i,j;
int n; //返回的最小边的序号
//获取图所有边的信息,存入数组edges[]
GetEdges( G, edges );
//初始化可用边数组--可用排序算法取代
int M; //循环次数
if(G.gKind==UDG ||G.gKind==UDN)
M=G.ArcNum*2; //因为无向图、无向网邻接矩阵对称,有效数据是边数的2倍,所以乘2
else
M=G.ArcNum;
for(i=0; i<M; i++)
edgeUsed[i]=0;
for(i=1; i<G.VerNum; i++) //取出n-1条边,构成生成树
{
minEdge=GetMinEdge(G, edges, edgeUsed, n );
if(find(anc,minEdge.vBegin)==find(anc,minEdge.vEnd)) //并查集查询当前边的2个顶点是否在同一个连通分量上
{
edgeUsed[n]=1; //2个顶点属于同一个连通分量,必构成回路,此最小边不可用
minEdge=GetMinEdge( G, edges, edgeUsed, n ); //继续取下一条最小边,直到取得可用边为止
}
//到此取得了1条可用最小边,加入生成树中
treeEdges[i]=minEdge;
//并查集的合并处理,边的2个顶点合并到一个集合(同一个连通分量)
unionSet(anc,minEdge.vBegin,minEdge.vEnd);
edgeUsed[n]=1;
}
//输出结果
cellType wAll=0; //总权值
cout<<endl; //输出结果
cout<<"Kruskal(并查集)最小生成树-->>"<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<G.VerNum;i++) //n-1条边
{
cout<<"("<<G.Data[treeEdges[i].vBegin]<<","<<G.Data[treeEdges[i].vEnd]<<")";
cout<<" 或 ("<<treeEdges[i].vBegin<<","<<treeEdges[i].vEnd<<")。权值:"<<treeEdges[i].eWeight<<endl;
wAll+=treeEdges[i].eWeight;
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
#pragma warning(disable:4996);
#ifndef p6_H_INCLUDED
#define p6_H_INCLUDED
#include<iostream>
#include<cstdlib>
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<cstring>
#include <queue>
#include <stack>
using namespace std;
#define MAXLEN 100
#define INF 65535 //定义无穷大
#define MaxVerNum 100 //定义最大顶点个数
typedef char elementType; //定义图中顶点的数据类型
typedef int cellType; //定义邻接矩阵中元素的数据类型
//对无权图,1-相邻(有边),0-不相邻(无边)
//对有权图,为边的权值,特别是无穷大。
//枚举图的类型--无向图(UDG),无向网(UDN),有向图(DG),有向网(DN)
typedef enum{UDG, UDN, DG, DN} GraphKind;
bool visited1[MaxVerNum+1]; //全局数组,标记顶点是否已经访问,visited[0]单元不用
bool visited2[MaxVerNum+1];
typedef struct sStack{
elementType data[MAXLEN];
int top;
}seqStack;
typedef struct GraphAdjMatrix{
elementType Data[MaxVerNum+1]; //顶点数组,存放顶点元素的值,Data[0]单元不用
cellType AdjMatrix[MaxVerNum+1][MaxVerNum+1]; //邻接矩阵,数组下标为0单元不用,从AdjMatrix[1][1]单元开始
int VerNum; //顶点数
int ArcNum; //弧(边)数
GraphKind gKind; //图的类型:0-无向图;1-无向网;2-有向图;3-有向网
} Graph2; //图的类型名
void initialStack(seqStack &S){//初始化
S.top=-1;
}
void pushStack(seqStack &S,int x){//入栈
if(S.top==MAXLEN-1){
cout<<"栈满"<<endl;
return;
}
else{
S.top++;
S.data[S.top]=x;
return;
}
}
void popStack(seqStack &S,int &x){//出栈
if(S.top==-1){
cout<<"栈空"<<endl;
return;
}
else{
x=S.data[S.top];
S.top--;
return;
}
}
void visit2(Graph2 &G, int verID){
cout<<G.Data[verID]<<"\t";
visited2[verID]=true;
}
int LocateVertex2(Graph2 &G, elementType v){
for(int i=1;i<=G.VerNum;i++)
{
if( G.Data[i]==v )
return i;
}
return -1;
}
int firstAdj2(Graph2 &G,int v){
int w;
for(w=0;w<G.VerNum;w++)
{
if((G.AdjMatrix[v][w]>=1)&&(G.AdjMatrix[v][w]<INF))
return w; //返回第一个邻接点编号
}
return 0; //未找到邻接点,返回0
}
int nextAdj2(Graph2 &G,int v,int w){
int k;
for(k=w+1;k<=G.VerNum;k++)
{
if((G.AdjMatrix[v][k])>=1&&(G.AdjMatrix[v][k])<INF)
return k; //返回v的位于w之后的下一个邻接点k
}
return 0; //不存在下一个邻接点,返回0
}
void printGraph2(Graph2 &G){
int i=0,j=0;
//打印图的类型
switch(G.gKind)
{
case UDG:
cout<<"图类型:无向图"<<endl;
break;
case UDN:
cout<<"图类型:无向网"<<endl;
break;
case DG:
cout<<"图类型:有向图"<<endl;
break;
case DN:
cout<<"图类型:有向网"<<endl;
break;
default:
cout<<"图类型错误。"<<endl;
break;
}
//打印图的顶点数
cout<<"顶点数:"<<G.VerNum<<endl;
//打印图的边数
cout<<"边 数:"<<G.ArcNum<<endl;
//打印顶点及其编号
cout<<"编 号:";
for(i=1;i<=G.VerNum;i++)
{
cout<<i<<"\t";
}
cout<<endl;
cout<<"顶 点:";
for(i=1;i<=G.VerNum;i++)
{
cout<<G.Data[i]<<"\t";
}
cout<<endl;
//打印邻接矩阵
cout<<"图的邻接矩阵:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
cout<<"\t";
for(j=1;j<=G.VerNum;j++)
{
if((G.gKind==UDN || G.gKind==DN) && G.AdjMatrix[i][j]==INF)
cout<<"INF"<<"\t"; //网,无穷大时,打印“INF”表示
else
cout<<G.AdjMatrix[i][j]<<"\t";
}
cout<<endl;
}
}
typedef char elementType;
typedef int eInfoType;
typedef int cellType;
typedef struct eNode {
int adjVer; //邻接顶点地址,此处为顶点在顶点表中序号,从1开始
eInfoType eInfo; //边链表中表示边的相关信息,比如表的权值
struct eNode* next; //指向边链表中的下一个结点
}EdgeNode; //边链表结点类型
typedef struct vNode {
elementType data; //存放图中顶点的数据
EdgeNode* firstEdge; //指向此顶点关联的第一条边的指针,即边链表的头指针
}VerNode;
typedef struct GraphAdjLinkList{
VerNode VerList[MaxVerNum+1]; //存放顶点的顺序表,数组0单元不用
int VerNum; //顶点数
int ArcNum; //弧(边)数
GraphKind gKind; //图的类型:0-无向图;1-无向网;2-有向图;3-有向网
}Graph1; //图的类型名
typedef struct sQueue{
int data[MAXLEN];
int front1;
int rear;
}seqQueue;
typedef struct csNode{
elementType data;
struct csNode *firstChild, *nextSibling;
}csNode,*csTree;
void initialQueue(seqQueue &Q){
Q.front1=0;
Q.rear=0;
}
void queueFront(seqQueue &Q,int &x){//取队头
if(Q.front1==Q.rear)
cout<<"no2"<<endl;
else
x=Q.data[(Q.front1+1)%MAXLEN];
}
void enQueue(seqQueue &Q,int x){//入队
if((((Q.rear)+1)%MAXLEN)==Q.front1)
cout<<"full"<<endl;
else{
Q.rear=((Q.rear)+1)%MAXLEN;
Q.data[Q.rear]=x;
}
}
void outQueue(seqQueue &Q){//出队
if(Q.rear==Q.front1)
cout<<"no1"<<endl;
else{
Q.front1=(Q.front1+1)%MAXLEN;
}
}
void visit1(Graph1 &G, int verID){
cout<<G.VerList[verID].data<<"\t";
visited1[verID]=true;
}
int LocateVertex1(Graph1 &G, elementType v){
for(int i=1;i<=G.VerNum;i++)
{
if( G.VerList[i].data==v )
return i;
}
return -1;
}
int firstAdj1(Graph1 &G, int v){
EdgeNode *p;
p=G.VerList[v].firstEdge;
if(p)
return p->adjVer;
else
return 0;
}
int nextAdj1(Graph1 &G, int v, int w){
EdgeNode *p;
p=G.VerList[v].firstEdge; //取顶点v的边链表头指针
while(p->next)
{
if(p->adjVer==w)
return p->next->adjVer; //返回w之后下一个邻接点编号
p=p->next;
}
return 0; //未找到下一个邻接点,返回0
}
void printGraph1(Graph1 &G){
int i=0,j=0;
//打印图的类型
switch(G.gKind)
{
case UDG:
cout<<"图类型:无向图"<<endl;
break;
case UDN:
cout<<"图类型:无向网"<<endl;
break;
case DG:
cout<<"图类型:有向图"<<endl;
break;
case DN:
cout<<"图类型:有向网"<<endl;
break;
default:
cout<<"图类型错误。"<<endl;
break;
}
//打印图的顶点数
cout<<"顶点数:"<<G.VerNum<<endl;
//打印图的边数
cout<<"边 数:"<<G.ArcNum<<endl;
//打印顶点及其编号
cout<<"编号\t顶点\t边链表"<<endl;
EdgeNode* p;
for(i=1;i<=G.VerNum;i++)
{
cout<<i<<"\t"<<G.VerList[i].data<<"\t";
p=G.VerList[i].firstEdge;
while(p!=NULL)
{
cout<<p->adjVer<<"\t";
p=p->next;
}
cout<<endl;
}
cout<<endl;
}
void strLTrim(char* str);
void CreateGrpFromFile(char fileName[], Graph2 &G){
FILE* pFile; //定义顺序表的文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
cellType eWeight; //边的信息,常为边的权值
GraphKind GrpType; //图类型枚举变量
pFile=fopen(fileName,"r");
if(!pFile)
{
printf("错误:文件%s打开失败。\n",fileName);
return;
}
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //跳过注释行
continue;
else //非注释行、非空行,跳出循环
break;
}
//循环结束,str中应该已经是文件标识,判断文件格式
if(strstr(str,"Graph")==NULL)
{
printf("错误:打开的文件格式错误!\n");
fclose(pFile); //关闭文件
return;
}
//读取图的类型,跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的类型标识
break;
}
//设置图的类型
if(strstr(str,"UDG"))
GrpType=UDG; //无向图
else if(strstr(str,"UDN"))
GrpType=UDN; //无向网
else if(strstr(str,"DG"))
GrpType=DG; //有向图
else if(strstr(str,"DN"))
GrpType=DN; //有向网
else
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return;
}
//读取顶点元素,到str。跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的顶点元素行
break;
}
//顶点数据放入图的顶点数组
char* token=strtok(str," ");
int nNum=1;
while(token!=NULL)
{
G.Data[nNum]=*token; // atoi(token); //顶点数据转换为整数,若为字符则不需转换
token = strtok( NULL, " ");
nNum++;
}
nNum--; //顶点数
//图的邻接矩阵初始化
int nRow=1; //矩阵行下标,从1开始
int nCol=1; //矩阵列下标,从1开始
if(GrpType==UDG || GrpType==DG)
{
for(nRow=1;nRow<=nNum;nRow++)
for(nCol=1;nCol<=nNum;nCol++)
G.AdjMatrix[nRow][nCol]=0;
}
else
{
for(nRow=1;nRow<=nNum;nRow++)
for(nCol=1;nCol<=nNum;nCol++)
G.AdjMatrix[nRow][nCol]=INF; //INF表示无穷大
}
//循环读取边的数据到邻接矩阵
int edgeNum=0; //边的数量
elementType Nf,Ns; //边或弧的2个相邻顶点
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
char* token=strtok(str," "); //以空格为分隔符,分割一行数据,写入邻接矩阵
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
Nf=*token; //获取边的第一个顶点
token = strtok( NULL, " "); //读取下一个子串,即第二个顶点
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
Ns=*token; //获取边的第二个顶点
//从第一个顶点获取行号
for(nRow=1;nRow<=nNum;nRow++)
{
if(G.Data[nRow]==Nf) //从顶点列表找到第一个顶点的编号
break;
}
//从第二个顶点获取列号
for(nCol=1;nCol<=nNum;nCol++)
{
if(G.Data[nCol]==Ns) //从顶点列表找到第二个顶点的编号
break;
}
//如果为网,读取权值
if(GrpType==UDN || GrpType==DN)
{
token = strtok( NULL, " "); //读取下一个子串,即边的附加信息,常为边的权重
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
eWeight=atoi(token); //取得边的附加信息
}
if(GrpType==UDN || GrpType==DN) //如果为网,邻接矩阵中对应的边设置权值,否则置为1
G.AdjMatrix[nRow][nCol]=eWeight;
else
G.AdjMatrix[nRow][nCol]=1; //atoi(token); //字符串转为整数
edgeNum++; //边数加1
}
G.VerNum=nNum; //图的顶点数
if(GrpType==UDG || GrpType==UDN)
G.ArcNum=edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.ArcNum=edgeNum;
G.gKind=GrpType; //图的类型
fclose(pFile); //关闭文件
return;
}
void DestroyGraph(Graph1 &G){
EdgeNode *p,*u;
int vID;
for(vID=1; vID<=G.VerNum; vID++) //循环删除每个顶点的边链表
{
p=G.VerList[vID].firstEdge;
G.VerList[vID].firstEdge=NULL;
while(p) //循环删除当前顶点所有的关联边
{
u=p->next; //u指向下一个边结点
delete(p); //删除当前边结点
p=u;
}
}
p=NULL;
u=NULL;
G.VerNum=-1; //编辑图已经销毁
}
void strLTrim(char* str){
int i,j;
int n=0;
n=strlen(str)+1;
for(i=0;i<n;i++)
{
if(str[i]!=' ') //找到左起第一个非空格位置
break;
}
//以第一个非空格字符为手字符移动字符串
for(j=0;j<n;j++)
{
str[j]=str[i];
i++;
}
}
void strLTrim(char* str);
void CreateGraphFromFile(char fileName[], Graph1 &G){
FILE* pFile; //定义顺序表的文件指针
char str[1000]; //存放读出一行文本的字符串
char strTemp[10]; //判断是否注释行
int i=0,j=0;
int edgeNum=0; //边的数量
eInfoType eWeight; //边的信息,常为边的权值
GraphKind graphType; //图类型枚举变量
pFile=fopen(fileName,"r");
if(!pFile)
{
printf("错误:文件%s打开失败。\n",fileName);
return;
}
while(fgets(str,1000,pFile)!=NULL) //跳过空行和注释行
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //跳过注释行
continue;
else //非注释行、非空行,跳出循环
break;
}
//循环结束,str中应该已经是文件标识,判断文件格式
if(strstr(str,"Graph")==NULL)
{
printf("错误:打开的文件格式错误!\n");
fclose(pFile); //关闭文件
return;
}
//读取图的类型,跳过空行及注释行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的类型标识
break;
}
//设置图的类型
if(strstr(str,"UDG"))
graphType=UDG; //无向图
else if(strstr(str,"UDN"))
graphType=UDN; //无向网
else if(strstr(str,"DG"))
graphType=DG; //有向图
else if(strstr(str,"DN"))
graphType=DN; //有向网
else
{
printf("错误:读取图的类型标记失败!\n");
fclose(pFile); //关闭文件
return;
}
//读取顶点元素,到str。跳过空行
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
else //非空行,也非注释行,即图的顶点元素行
break;
}
//顶点数据放入图的顶点数组
char* token=strtok(str," ");
int nNum=0;
while(token!=NULL)
{
G.VerList[nNum+1].data=*token;
G.VerList[nNum+1].firstEdge=NULL;
//p=NULL;
//eR=G.VerList[i].firstEdge;
token = strtok( NULL, " ");
nNum++;
}
//循环读取边(顶点对)数据
int nRow=1; //矩阵行下标
int nCol=1; //矩阵列下标
EdgeNode* eR; //边链表尾指针
EdgeNode* p;
elementType Nf,Ns; //边或弧的2个相邻顶点
while(fgets(str,1000,pFile)!=NULL)
{
//删除字符串左边空格
strLTrim(str);
if (str[0]=='\n') //空行,继续读取下一行
continue;
strncpy(strTemp,str,2);
if(strstr(strTemp,"//")!=NULL) //注释行,跳过,继续读取下一行
continue;
//nCol=0; //列号设为0,一行重新开始
char* token=strtok(str," "); //以空格为分隔符,分割一行数据,写入邻接矩阵
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
Nf=*token; //获取边的第一个顶点
token = strtok( NULL, " "); //读取下一个子串,即第二个顶点
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
Ns=*token; //获取边的第二个顶点
//从第一个顶点获取行号
for(nRow=1;nRow<=nNum;nRow++)
{
if(G.VerList[nRow].data==Nf) //从顶点列表找到第一个顶点的编号
break;
}
//从第二个顶点获取列号
for(nCol=1;nCol<=nNum;nCol++)
{
if(G.VerList[nCol].data==Ns) //从顶点列表找到第二个顶点的编号
break;
}
//如果为网,读取权值
if(graphType==UDN || graphType==DN)
{
token = strtok( NULL, " "); //读取下一个子串,即边的附加信息,常为边的权重
if(token==NULL) //分割为空串,失败退出
{
printf("错误:读取图的边数据失败!\n");
fclose(pFile); //关闭文件
return;
}
eWeight=atoi(token); //取得边的附加信息
}
eR=G.VerList[nRow].firstEdge;
while(eR!=NULL && eR->next!=NULL)
{
eR=eR->next; //后移边链表指针,直至尾节点
}
p=new EdgeNode; //申请一个边链表结点
p->adjVer=nCol; //顶点的编号,从1开始
if(graphType==UDN || graphType==DN) //边的附加信息,对有权图保存权值,无权图为1
p->eInfo=eWeight;
else
p->eInfo=1;
p->next=NULL;
if(G.VerList[nRow].firstEdge==NULL)
{
G.VerList[nRow].firstEdge=p;
eR=p;
}
else
{
eR->next=p;
eR=p; //新的尾指针
}
edgeNum++; //边数加1
}
G.VerNum=nNum; //图的顶点数
if(graphType==UDG || graphType==UDN)
G.ArcNum=edgeNum / 2; //无向图或网的边数等于统计的数字除2
else
G.ArcNum=edgeNum;
G.gKind=graphType; //图的类型
fclose(pFile); //关闭文件
return;
}
#include"PrimAdjLinkList.h"
#include"PrimAdjMatrix.h"
#include"KruskalAdjMatrix.h"
#include"KruskalAdjLinkList.h"
#include"DijkstraAdjMatrix.h"
#include"DijkstraAdjLinkList.h"
//#include"DijkstraReverseAdjLinkList.h"
#include"Floyd.h"
#include"TopoSortAdjLinkList.h"
#include"TopoSortAdjMatrix.h"
void dfs11(Graph1 G,int v){
int w;
visit1(G,v);
w=firstAdj1(G,v);
while(w!=0){
if(visited1[w]==false)
dfs11(G,w);
w=nextAdj1(G,v,w);
}
}
void dfs12(Graph2 G,int v){
int w=0;
visit2(G,v);
w=firstAdj2(G,v);
while(w!=0){
if(visited2[w]==false)
dfs12(G,w);
w=nextAdj2(G,v,w);
}
}
void dfs21(Graph1 G){
for(int i=1;i<=G.VerNum;i++)
visited1[i]=false;
for(int i=1;i<=G.VerNum;i++)
if(visited1[i]==false)
dfs11(G,i);
}
void dfs22(Graph2 G){
for(int i=1;i<=G.VerNum;i++)
visited2[i]=false;
for(int i=1;i<=G.VerNum;i++)
if(visited2[i]==false)
dfs12(G,i);
}
void bfs11(Graph1 G,int v){
int w;
seqQueue Q;
initialQueue(Q);
visit1(G,v);
enQueue(Q,v);
while(Q.front1!=Q.rear){
queueFront(Q,v);
outQueue(Q);
w=firstAdj1(G,v);
while(w!=0){
if(!visited1[w])
{
visit1(G,w);
enQueue(Q,w);
}
w=nextAdj1(G,v,w);
}
}
}
void bfs12(Graph2 G,int v){
int w;
seqQueue Q;
initialQueue(Q);
visit2(G,v);
enQueue(Q,v);
while(Q.front1!=Q.rear){
queueFront(Q,v);
outQueue(Q);
w=firstAdj2(G,v);
while(w!=0){
if(!visited2[w])
{
visit2(G,w);
enQueue(Q,w);
}
w=nextAdj2(G,v,w);
}
}
}
void bfs21(Graph1 G){
int i;
for(i=1;i<=G.VerNum;i++)
visited1[i]=false;
for(i=1;i<=G.VerNum;i++)
if(!visited1[i])
bfs11(G,i);
}
void bfs22(Graph2 G){
int i;
for(i=1;i<=G.VerNum;i++)
visited2[i]=false;
for(i=1;i<=G.VerNum;i++)
if(!visited2[i])
bfs12(G,i);
}
int E=0,F=0;
void d11(Graph1 G,int v){
int w;
visit1(G,v);
w=firstAdj1(G,v);
while(w!=0){
E++;
if(visited1[w]==false)
d11(G,w);
w=nextAdj1(G,v,w);
}
}
void d12(Graph2 G,int v){
int w=0;
visit2(G,v);
w=firstAdj2(G,v);
while(w!=0){
F++;
if(visited2[w]==false)
d12(G,w);
w=nextAdj2(G,v,w);
}
}
int d21(Graph1 G){
for(int i=1;i<=G.VerNum;i++)
visited1[i]=false;
for(int i=1;i<=G.VerNum;i++)
if(visited1[i]==false)
d11(G,i);
return E/2;
}
int d22(Graph2 G){
for(int i=1;i<=G.VerNum;i++)
visited2[i]=false;
for(int i=1;i<=G.VerNum;i++)
if(visited2[i]==false)
d12(G,i);
return F/2;
}
void perOrderTraverse(csNode *T){
if(T)
{
cout<<T->data<<" "; //访问根节点
perOrderTraverse(T->firstChild); //递归调用先序遍历左子树
perOrderTraverse(T->nextSibling); //递归调用先序遍历右子树
}
}
void levelOrderTraverse(csNode *T){
queue<csNode *> q;
csNode * u,*n,*p;
if(T==NULL)
{
return;
}
n=T;
while(n)
{
p=n;
q.push(p);
while(!q.empty())
{
p=q.front();
cout<<p->data<<" ";
u=p->firstChild;
while(u)
{
p=u;
q.push(p);
u=u->nextSibling;
}
q.pop();
}
n=n->nextSibling;
}
}
void DFSTree(Graph2 &G,int v,csTree&T){
int j,w,first=1; //first标记是否为根节点,初始化为1
csNode *p,*q;
q=T;
visited2[v]=1;
w=firstAdj2(G,v);
while(w!=0) //还存在临结点
{
if(!visited2[w] && G.AdjMatrix[v][w]>=1 && G.AdjMatrix[v][w]<INF)
{
p=new csNode;
p->firstChild=NULL;
p->nextSibling=NULL;
p->data=G.Data[w];
if(first) //(生成第一个孩子)
{
T->firstChild=p;
first=0;
}
else{ //(生成节点的兄弟节点)
q->nextSibling=p;
}
q=p;
DFSTree(G,w,q); //递归处理每个节点
}
w=nextAdj2(G,v,w);
}
}
//深度优先遍历生成森林
csTree DFSForest(Graph2 &G,csTree &T,int v){
int i;
csNode *p;
csTree q=NULL;
T=NULL;
for(i=1;i<=G.VerNum;i++) //初始化visited数组
{
visited2[i]=0;
}
for(v;v>=1;v--) //编号1到v
{
if(!visited2[v] )
{
p=new csNode;
p->firstChild=NULL;
p->nextSibling=NULL;
p->data=G.Data[v];
if(!T) //为根节点
{
T=p;
q=T;
}
else{ //生成数根节点的兄弟节点,也就是其他树的根节点
q->nextSibling=p;
}
q=p;
DFSTree(G,v,p);
}
}
for(v+=1;v<=G.VerNum;v++) //编号v到G.vernum
{
if(!visited2[v] )
{
p=new csNode;
p->firstChild=NULL;
p->nextSibling=NULL;
p->data=G.Data[v];
if(!T) //为根节点
{
T=p;
q=T;
}
else{ //生成数根节点的兄弟节点,也就是其他树的根节点
q->nextSibling=p;
}
q=p;
DFSTree(G,v,p);
}
}
return T;
}
//广度优先遍历生成树
void BFSTree(Graph2 &G,csTree&T,int v){
int i,w;
bool first=true; //标志变量,判断是否生成孩子节点
csNode *t,*q,*p;
queue<int> Q;
if(!T) //生成树的根节点
{
T=new csNode;
T->data=G.Data[v];
visited2[v]=true;
T->firstChild=T->nextSibling=NULL;
}
p=T;
Q.push(v);
//对其他层节点操作
while(!Q.empty())
{
v=Q.front(); //获取队头编号
Q.pop(); //并出队
w=firstAdj2(G,v);
first=true; //循环i层时需要把first置为true,以便生成T->firstChild
while(w!=0)
{
if(!visited2[w]&& G.AdjMatrix[v][w]>=1 && G.AdjMatrix[v][w]<INF)
{
Q.push(w); //第i层节点入队
visited2[w]=true; //设置其访问标志
q=new csNode;
q->data=G.Data[w];
q->firstChild=q->nextSibling=NULL;
if(first) //生成孩子节点
{
p->firstChild=q;
first=false;
}
else{ //生成兄弟节点
p->nextSibling=q;
}
p=q;
}
w=nextAdj2(G,v,w);
}
}
}
//广度优先遍历生成森林
csTree BFSForest(Graph2 &G,csTree&T,int v){
int i;
csNode *p=NULL;
csTree q=NULL;
T=NULL;
for(i=1;i<=G.VerNum;i++) //初始化visited数组
{
visited2[i]=0;
}
for(v;v>=1;v--) //编号1到v
{
if(!visited2[v] )
{
visited2[v]=true; //标记已访问
p=new csNode;
p->firstChild=NULL;
p->nextSibling=NULL;
p->data=G.Data[v];
if(!T) //为根节点
{
T=p;
q=T;
}
else{ //生成数根节点的兄弟节点,也就是其他树的根节点
q->nextSibling=p;
}
q=p;
BFSTree(G,p,v);
}
}
for(v+=1;v<=G.VerNum;v++) //编号v到G.vernum
{
if(!visited2[v] )
{
visited2[v]=true; //标记已访问
p=new csNode;
p->firstChild=NULL;
p->nextSibling=NULL;
p->data=G.Data[v];
if(!T) //为根节点
{
T=p;
q=T;
}
else{ //生成数根节点的兄弟节点,也就是其他树的根节点
q->nextSibling=p;
}
q=p;
BFSTree(G,p,v);
}
}
return T;
}
void getInDegree(Graph2 &G,int inds[]){
int i,j;
int k=0; //记录入度数
for(i=1;i<=G.VerNum;i++)
{
for(j=1;j<=G.VerNum;j++)
{
if(G.AdjMatrix[j][i]>0 && G.AdjMatrix[j][i]<INF)
{
k++;
}
}
inds[i]=k;
k=0; //再次初始化为0
}
}
int TopologicalSort(Graph2 &G,int topoList[],int vet[]){
//topoList[]数组用于存放拓扑序列
int inds[MaxVerNum]; //定义顶点入度数组
stack<int> S; //定义一个顺序栈,保存入度为0的节点
int i;
int v; //顶点编号
int vCount=0; //记录顶点入度为0的顶点数
for(i=1;i<=G.VerNum;i++) //初始化vet[]数组
{
vet[i]=0;
}
for(i=1;i<=G.VerNum;i++) //入度数组初始化,inds[0]不用
{
inds[i]=0;
}
for(i=1;i<G.VerNum;i++) //拓扑排序数组初始化
{
topoList[i-1]=-1; //编号初始化为-1
}
getInDegree(G,inds); //获取所有顶点的入度并保存在inds[]中
for(i=1;i<=G.VerNum;i++) //入度为0节点入栈
{
if(inds[i]==0)
{
S.push(i);
}
}
while(!S.empty())
{
v=S.top(); //或取栈顶元素
S.pop(); //出栈
topoList[vCount]=v; //当前入度为0的顶点v,加入拓扑序列
vCount++;
for(i=1;i<=G.VerNum;i++)
{
if(G.AdjMatrix[v][i]>=1 && G.AdjMatrix[v][i]<INF)
{
//与v邻接的顶点i入度减1
if(!(--inds[i]))//顶点i的入度已经为0,入栈
{
S.push(i);
}
if(vet[v]+G.AdjMatrix[v][i] > vet[i])
{
vet[i]=vet[v]+G.AdjMatrix[v][i]; //求其v的邻接顶点最早发生时间
}
}
}
}
if(vCount==G.VerNum)
return 1; //返回无回路标记
else
return 0;
}
//输出拓扑序列
void printTopoList(Graph2 &G,int topoList[],int vet[]){
int i;
if(TopologicalSort(G,topoList,vet))
{
cout<<"拓扑序列为:";
for(i=0;i<G.VerNum;i++)
{
cout<<G.Data[topoList[i]]<<" ";
}
for(i=0;i<G.VerNum;i++)
{
cout<<endl;
cout<<"编号"<<G.Data[topoList[i]]<<"最早发生时间:"<<vet[topoList[i]];
}
}
else{
cout<<"有有向环,不能生成拓扑序列!"<<endl;
}
}
//获取所有顶点的出度并保存数组outds[]中
void getOutDegree(Graph2 &G,int outds[]){
int i,j;
int k=0; //记录出度数
for(i=1;i<=G.VerNum;i++)
{
for(j=1;j<=G.VerNum;j++)
{
if(G.AdjMatrix[i][j]>0 && G.AdjMatrix[i][j]<INF)
{
k++;
}
}
outds[i]=k;
k=0; //再次初始化为0
}
}
//逆拓扑排序
int antiTopologicalSort(Graph2 &G,int vlt[],int vet[]){
int outds[MaxVerNum]; //定义顶点出度数组
stack<int> S; //保存出度为0的顶点
int i;
int v; //顶点编号
int vCount=0; //记录顶点入度为0的顶点数
int max=0;
for(i=1;i<=G.VerNum;i++)
{
if(max<vet[i])
{
max=vet[i]; //获取最大时间(汇点时间)
}
}
for(i=1;i<=G.VerNum;i++)
{
vlt[i]=max; //全部赋值(汇点时间)
}
for(i=1;i<=G.VerNum;i++) //出度数组初始化。outds[0]不用
{
outds[i]=0;
}
// for(i=1;i<G.VerNum;i++) //拓扑排序数组初始化
// {
// antiTopoList[i-1]=-1; //编号初始化为-1
// }
getOutDegree(G,outds); //获取所有顶点的出度并保存数组outds[]中
for(i=1;i<=G.VerNum;i++) //出度为0节点入栈
{
if(outds[i]==0)
{
S.push(i);
}
}
while(!S.empty())
{
v=S.top(); //获取栈顶元素
S.pop(); //出栈
// antiTopoList[vCount]=v; //当前出度为0的顶点v,加入拓扑序列
vCount++;
for(i=1;i<=G.VerNum;i++)
{
if(G.AdjMatrix[i][v]>=1 && G.AdjMatrix[i][v]<INF)
{
//邻接v的顶点的出度减1
if(!(--outds[i]))
{
S.push(i); //出度为0的顶点入栈
}
if(vlt[v]-G.AdjMatrix[i][v] < vlt[i])
{
vlt[i]=vlt[v]-G.AdjMatrix[i][v]; //求其v上一个顶点的最迟发生时间
}
}
}
}
if(vCount==G.VerNum)
return 1; //返回无回路标记
else
return 0;
}
//输出逆拓扑排序
void printAntiTopoList(Graph2 &G,int topoList[],int vlt[],int vet[]){
int i;
if(antiTopologicalSort(G,vlt,vet))
{
cout<<"拓扑序列为:";
for(i=0;i<G.VerNum;i++)
{
cout<<G.Data[topoList[i]]<<" ";
}
for(i=0;i<G.VerNum;i++)
{
cout<<endl;
cout<<"编号"<<G.Data[topoList[i]]<<"最晚发生时间:"<<vlt[topoList[i]];
}
}
else{
cout<<"有有向环,不能生成逆拓扑序列!"<<endl;
}
}
//输出一条关键路径
void printCriticalPath(Graph2 &G,int topoList[],int vlt[],int vet[]){
int i=0,j=0,k=0,v=0;
int vPre=0;
if(!TopologicalSort(G,topoList,vet))
{
cout<<"无拓扑序列!"<<endl;
return ;
}
cout<<"关键路径为:";
i=G.VerNum;
k=topoList[v];
while(k!=vPre) //如上一次访问顶点编号与此次相同则退出
{
if(vet[k]==vlt[k])
{
if(k!=vPre)
{
cout<<G.Data[k]<<" "; //不同则输出
}
vPre=k;
for(j=1;j<=G.VerNum;j++)
{
if(G.AdjMatrix[k][j]>=1 && G.AdjMatrix[k][j] < INF)
{
if(vet[j]==vlt[j])
{
break;
}
}
}
}
k=j; //或取k的邻接点
}
}
#endif
int inTree1[MaxVerNum+1]={0}; //标记顶点已经在Prim生成树中,或已经访问过。1或true--已访问,0或false--未访问
//inTree[0]单元未用
//或者为标记已经在集合U中的顶点
//保存候选边的信息
typedef struct minEdgeType1
{
int v; //V-U中当前选中的顶点编号,从1开始。即刚从V-U中选出放到U中的顶点
cellType eWeight; //U中某个顶点到V-U中当前顶点v的最小距离
} MinEdgeType1;
////????????同时返回此边的权值,用引用或指针返回
//eWeight为当前边的权值
//检查图G中编号为vBegin和vEnd之间是否有边
int HasEdge(Graph1 &G, int vBegin, int vEnd, eInfoType &eWeight)
{
EdgeNode *p; //边链表结点指针
int f=0; //是否有边的标记
eWeight=INF; //边的权值初始化为无穷大
p=G.VerList[vBegin].firstEdge;
while(p)
{
if( p->adjVer==vEnd )
{
f=1; //vBegin与vEnd之间有边,退出循环,返回1
eWeight=p->eInfo;
break;
}
p=p->next;
}
return f;
}
//初始化候选边数组,和已经选择边数组
void InitMinEdges(Graph1 &G, MinEdgeType1 minEdges[],int vID)
{
int i;
eInfoType eWeight;
for(i=1;i<=G.VerNum;i++)
{
//初始化候选边数组
if(HasEdge(G, vID, i, eWeight))
{
minEdges[i].v=vID;
minEdges[i].eWeight=eWeight;
}
else
minEdges[i].eWeight=INF;
}
}
//从候选边集合中选出最小边,返回在V-U中的关联顶点编号
int GetMinEdge(Graph1 &G, MinEdgeType1 minEdges[])
{
eInfoType eMin=INF; //保存最小的权值
int i,j;
for(i=1;i<=G.VerNum;i++)
{
if(inTree1[i]==0 && minEdges[i].eWeight<eMin)
{
j=i; //如果当前编号为i的顶点在集合V-U中,且权值比eMin小,暂选为最小边
eMin=minEdges[i].eWeight;
}
}
return j; //j即为V-U中,最小边关联顶点的编号
}
//新选出一条最小边后,顶点加入U中,更新其邻接顶点的最小权值
void ChangeMinEdgesWeight(Graph1 &G, MinEdgeType1 minEdges[], int vID)
{
//对新选出的编号为vID的顶点(新加入集合U中),调整候选边集合
int i,j;
eInfoType eWeight;
for(i=1;i<=G.VerNum;i++)
{
if(inTree1[i]==0) //编号i顶点在V-U中,不在U中
{
//检查vID与i之间是否相邻(有边)
//检查U中顶点vID与i之间的边权值是否更小,若更小则更新(vID,i)权值
if(HasEdge(G,vID,i, eWeight) && eWeight<minEdges[i].eWeight)
{
minEdges[i].v=vID;
minEdges[i].eWeight=eWeight;
}
}
}
}
void Prim(Graph1 &G, int vID)
{
MinEdgeType1 minEdges[MaxVerNum]; //minEdges[i]的下标加1,即i+1为选定边的起始点
//minEdges[i].v为选定边的终点
int i;
int curID; //当前选择顶点编号
eInfoType wAll=0; //权值总和
InitMinEdges(G, minEdges, vID); //初始化候选边数组
inTree1[vID]=true; //标记vID已在生成树上,即集合U中
for(i=1;i<G.VerNum;i++) //选择n-1条边,形成生成树
{
curID=GetMinEdge(G, minEdges); //选择V-U中最小边关联的顶点
inTree1[curID]=true; //标记curID已选进U中
ChangeMinEdgesWeight(G, minEdges, curID); //修改权值
}
cout<<endl; //输出结果
cout<<"Prim生成树起始顶点:"<<G.VerList[vID].data<<",编号:"<<vID<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
if(i!=vID)
{
cout<<"("<<G.VerList[minEdges[i].v].data<<","<<G.VerList[i].data<<")";
cout<<" 或 ("<<minEdges[i].v<<","<<i<<")。权值:"<<minEdges[i].eWeight<<endl;
wAll+=minEdges[i].eWeight;
}
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
//Prim算法基于邻接矩阵的实现
int inTree2[MaxVerNum+1]={0}; //标记顶点已经在Prim生成树中,或已经访问过。1或true--已访问,0或false--未访问
//inTree[0]单元未用
//或者为标记已经在集合U中的顶点
//保存候选边的信息
typedef struct minEdgeType2
{
int v; //V-U中当前选中的顶点编号,从1开始。即刚从V-U中选出放到U中的顶点
cellType eWeight; //U中某个顶点到V-U中当前顶点v的最小距离
} MinEdgeType2;
//保存已经选择的边的信息
typedef struct selectedEdge
{
int vBegin; //选中边的起始顶点编号,从1开始
int vEnd; //选中边的结束顶点编号,从1开始
cellType eWeight;
} SelectedEdgeType;
//检查图G中编号为vBegin和vEnd之间是否有边
int HasEdge(Graph2 &G, int vBegin, int vEnd)
{
if(G.gKind==UDN || G.gKind==DN) //网情况
{
if(G.AdjMatrix[vBegin][vEnd]!=INF) //权值非无穷大即有边
return 1; //有边,返回1
else
return 0; //无边,返回0
}
else //图情况
{
if(G.AdjMatrix[vBegin][vEnd]==1) //顶点编号与数组下标差1
return 1; //有边,返回1
else
return 0; //无边,返回0
}
}
//初始化候选边数组,和已经选择边数组
void InitMinEdges(Graph2 &G, MinEdgeType2 minEdges[],int vID)
{
int i;
for(i=1;i<=G.VerNum;i++)
{
//初始化候选边数组
if(HasEdge(G, vID, i))
{
minEdges[i].v=vID;
minEdges[i].eWeight=G.AdjMatrix[vID][i]; //权值初始化为无穷大
}
else
minEdges[i].eWeight=INF;
}
}
//从候选边集合中选出最小边,返回在V-U中的关联顶点编号
int GetMinEdge(Graph2 &G, MinEdgeType2 minEdges[])
{
cellType eMin=INF; //保存最小的权值
int i,j;
for(i=1;i<=G.VerNum;i++)
{
if(inTree2[i]==0 && minEdges[i].eWeight<eMin)
{
j=i; //如果当前编号为i的顶点在集合V-U中,且权值比eMin小,暂选为最小边
eMin=minEdges[i].eWeight;
}
}
return j; //j即为V-U中,最小边关联顶点的编号
}
void ChangeMinEdgesWeight(Graph2 &G, MinEdgeType2 minEdges[], int vID)
{
//对新选出的编号为vID的顶点(新加入集合U中),调整候选边集合
int i,j;
for(i=1;i<=G.VerNum;i++)
{
if(inTree2[i]==0) //编号i顶点在V-U中,不在U中
{
//检查vID与i之间是否相邻(有边)
//检查U中顶点vID与i之间的边权值是否更小,若更小则更新(vID,i)权值
if(HasEdge(G,vID,i) && G.AdjMatrix[vID][i]<minEdges[i].eWeight)
{
minEdges[i].v=vID;
minEdges[i].eWeight=G.AdjMatrix[vID][i];
}
}
}
}
void Prim(Graph2 &G, int vID)
{
MinEdgeType2 minEdges[MaxVerNum]; //minEdges[i]的下标加1,即i+1为选定边的起始点
//minEdges[i].v为选定边的终点
int i;
int curID; //当前选择顶点编号
cellType wAll=0; //权值总和
InitMinEdges(G, minEdges, vID); //初始化候选边数组
inTree2[vID]=true; //标记vID已在生成树上,即集合U中
for(i=1;i<G.VerNum;i++) //选择n-1条边,形成生成树
{
curID=GetMinEdge(G, minEdges); //选择V-U中最小边关联的顶点
inTree2[curID]=true; //标记curID已选进U中
ChangeMinEdgesWeight(G, minEdges, curID); //修改权值
}
cout<<endl; //输出结果
cout<<"Prim生成树起始顶点:"<<G.Data[vID]<<",编号:"<<vID<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
//cout<<i<<"--"<<minEdges[i].v<<", "<<minEdges[i].eWeight<<endl;
if(i!=vID)
{
cout<<"("<<G.Data[minEdges[i].v]<<","<<G.Data[i]<<")";
cout<<" 或 ("<<minEdges[i].v<<","<<i<<")。权值:"<<minEdges[i].eWeight<<endl;
//cout<<i<<"--"<<minEdges[i].v<<", "<<minEdges[i].eWeight<<endl;
//cout<<minEdges[i].eWeight<<"\t";
wAll+=minEdges[i].eWeight;
}
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
void PrimPrint(Graph2 &G, MinEdgeType2 minEdges[], int vID)
{
int i;
cellType wAll=0;
cout<<"Prim生成树起始顶点:"<<vID<<endl;
cout<<"选择的边和权值:"<<endl;
for(i=1;i<=G.VerNum;i++)
{
if(i!=vID)
{
cout<<i<<"--"<<minEdges[i].v<<", "<<minEdges[i].eWeight<<endl;
wAll+=minEdges[i].eWeight;
}
}
cout<<"生成树总权值:"<<wAll<<endl;
cout<<endl;
}
//拓扑排序算法--基于邻接表
//初始化获取每个顶点的入度,存入入度数组inds[]中
void GetInDegrees(Graph1 &G, int inds[])
{
EdgeNode *p; //边链表指针
int i;
int k;
for(i=1;i<=G.VerNum;i++)
{
p=G.VerList[i].firstEdge;
while(p)
{
k=p->adjVer;
inds[k]++; //编号为k的顶点入度加1
p=p->next;
}
}
for(i=1;i<=G.VerNum;i++) //输出每个顶点的初始入度
{
cout<<i<<"--"<<inds[i]<<endl;
}
}
//拓扑排序算法--不使用栈和队列,使用一个标记数组solved[]
int TopologicalSort(Graph1 &G, int topoList[])
{
int inds[MaxVerNum]; //存放顶点入度
int solved[MaxVerNum]; //标记入度为0的顶点是否已经处理。0--未处理;1--已处理。
int i;
int v=-1; //顶点编号
int vCount=0; //记录入度为0的定点数
EdgeNode *p; //指向边链表结点的指针
//初始化
for(i=1;i<=G.VerNum;i++)
{
inds[i]=0; //所有顶点初始入度置为0
topoList[i]=-1; //拓扑序列初始化为-1
solved[i]=0; //所有顶点均未处理
}
//从邻接表获取各顶点初始入度
GetInDegrees(G,inds);
//取得第一个入度为0的顶点(如果存在),保存到v
for(i=1;i<=G.VerNum;i++)
{
if(inds[i]==0 && solved[i]==0)
{
v=i;
break; //取得一个入度为0顶点,即退出循环
}
}
while(v!=-1)
{
topoList[vCount+1]=v;
solved[v]=1;
vCount++;
//以顶点v相邻的顶点入度减1
p=G.VerList[v].firstEdge;
while(p)
{
v=p->adjVer;
inds[v]--; //邻接点入度减1
p=p->next;
}
v=-1;
//取得下一个未处理的、入度为0的顶点到v
for(i=1;i<=G.VerNum;i++)
{
if(inds[i]==0 && solved[i]==0)
{
v=i;
break; //取得一个未处理且入度为0的顶点,即退出本循环
}
}
}
if(vCount==G.VerNum)
return 1; //拓扑排序成功
else
return 0; //存在回路,拓扑排序失败
}
//拓扑排序算法--基于邻接矩阵
//初始化获取每个顶点的入度,存入入度数组inds[]中
void GetInDegrees(Graph2 &G, int inds[])
{
int i,j;
for(j=1;j<=G.VerNum;j++) //邻接矩阵列循环
{
for(i=1;i<=G.VerNum;i++) //邻接矩阵行循环
{
if(G.AdjMatrix[i][j]>=1 && G.AdjMatrix[i][j]<INF)
inds[j]++; //编号j的入度加1.
}
}
//for(i=1;i<=G.VerNum;i++) //输出每个顶点的初始入度
//{
// cout<<i<<"--"<<inds[i]<<endl;
//}
}
//拓扑排序算法--不使用栈和队列,使用一个标记数组solved[]
int TopologicalSort(Graph2 &G, int topoList[])
{
int inds[MaxVerNum]; //保存图中个顶点的入度
int solved[MaxVerNum]; //标记图中入度为0的顶点是否已经处理过。0--未处理;1--已处理。
int i;
int v=-1; //当前处理的入度为0的顶点编号
int vCount=0; //记录入度为0的顶点个数。
//初始化
for(i=1;i<=G.VerNum;i++)
{
inds[i]=0; //所有顶点入度置为0
solved[i]=0; //所有顶点标记为未处理
topoList[i]=-1; //拓扑序列元素全部初始化为-1
}
//从邻接矩阵获取图中各个顶点的初始入度,由inds[]数组返回
GetInDegrees(G,inds);
//取得第一个入度为0的顶点编号(如果存在),保存到v
for(i=1;i<=G.VerNum;i++)
{
if(inds[i]==0 && !solved[i])
{
v=i;
break;
}
}
while(v!=-1)
{
topoList[vCount+1]=v; //顶点v加入拓扑序列
vCount++; //入度为0顶点数加1
solved[v]=1; //标记顶点v已经处理
//与顶点v相邻的顶点入度减1
for(i=1;i<=G.VerNum;i++)
{
if(G.AdjMatrix[v][i]>=1 && G.AdjMatrix[v][i]<INF && inds[i]>0)
inds[i]--; //顶点i入度减1
}
//获取下一个入度为0,且尚未处理的顶点
v=-1;
for(i=1;i<=G.VerNum;i++)
{
if(inds[i]==0 && !solved[i])
{
v=i;
break; //找到符合条件的顶点,退出循环--入度为0,且未处理
}
}
}
if(vCount==G.VerNum)
return 1; //无回路,拓扑排序成功
else
return 0; //有回路,拓扑排序失败
}
void printTopoList(Graph2 &G,int topoList[])
{
int i;
if(TopologicalSort(G,topoList))
{
cout<<"拓扑序列为:";
for(i=1;i<G.VerNum;i++)
{
cout<<G.Data[topoList[i]]<<" ";
}
}
else{
cout<<"有有向环,不能生成拓扑序列!"<<endl;
}
}
1916





