图实验(基于邻接矩阵)---合工大数据结构实验6+7

图实验(基于邻接矩阵)—合工大数据结构实验6+7

createGrpAdjMatrix.h文件

//************************************************************//
//*    图的邻接矩阵存储的头文件,文件名:grpAdjMatrix.h      *//
//*                                                          *//
//************************************************************//

//#include "stdio.h"
//#include "stdlib.h"
//#include "iostream.h"
//#include "string.h"
#include<iostream>
using namespace std;
#define INF 65535          //定义无穷大
#define MaxVerNum  100     //定义最大顶点个数
//typedef int elementType;  //定义图中顶点的数据类型
typedef char elementType;  //定义图中顶点的数据类型
typedef int cellType;      //定义邻接矩阵中元素的数据类型
                           //对无权图,1-相邻(有边),0-不相邻(无边)
                           //对有权图,为边的权值,特别是无穷大。
                           //枚举图的类型--无向图(UDG),无向网(UDN),有向图(DG),有向网(DN)
// #ifndef _COMMON
// #define _COMMON
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;
	}

}
//#include"grpAdjMatrix.h"
#include"string.h"
// using namespace std;
void strLTrim(char* str);


//*************************从数据文件创建图**************************//
//* 函数功能:从文本文件创建邻接矩阵表示的图                        *//
//* 入口参数  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++;
	}
}

graph.h文件:算法核心

// #include"createGrpAdjLinkedList.h"
// // #include"grpAdjLinkedList.h"
// #include"grpAdjMatrix.h"

#include"createGrpAdjMatrix.h"//邻接矩阵
//#include"createGrpAdjLinkedList.h"邻接表
#include<queue>
#include<algorithm>
#include<stack>
#define INTF 2147483646//定义int最大值
void shownemu()
{
    cout<<"-------------------------------------------菜单---------------------------------------\n";
    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<<"                             -------------实验七-------------\n";
    cout<<"7实现Dijkstra算法,求解给定图G指定顶点到其余顶点之间的最短路径\n";
    cout<<"8实现Floyd   算法,求解给定图G各顶点之间的最短路径\n";
    cout<<"9求解给定图G的拓扑序列\n";
    cout<<"10求解给定AOE网的关键路径\n";
    cout<<"   说明:此文件以邻接矩阵为存储结构\n";
    cout<<"--------------------------------------------------------------------------------------\n";
}
void dfs_generate_tree(Graph g, bool visited[], int u, int parent[])//第三题函数
{
    visited[u] = true;
    
    for(int i = 1; i <= g.VerNum; i++)
    {
        if(!visited[i] && g.AdjMatrix[u][i] != INF && g.AdjMatrix[u][i] != 0)
        {
            parent[i] = u; // 记录当前节点的父节点
            dfs_generate_tree(g, visited, i, parent);
        }
    }
}
void dfs_show(Graph g,bool visitedd[],int u)//1&3   无生成树或生成森林 只有dfs遍历序列
{
    if(u>g.VerNum) return;
    visitedd[u]=true;
    cout<<g.Data[u]<<" ";
    for(int i=1;i<=g.VerNum;i++)
    {
        if(!visitedd[i]&&g.AdjMatrix[u][i]!=INF&&g.AdjMatrix[u][i]!=0)
        {
            dfs_show(g,visitedd,i);
        }
    }
}
void dfsTraverse_1(Graph g,bool visitedd[],int u)//主要作用是实现非连通图的遍历,若只有连通图的dfs遍历 上一个函数完全可以实现 第一题的函数 无生成树
{
    int i=0;
    int conNum=0;
    for(i=1;i<=g.VerNum;i++)
    {
        visitedd[i]=false;
    }
    dfs_show(g,visitedd,u);
    for(i=1;i<=g.VerNum;i++)//遍历其他连通分量
    {
        if(!visitedd[i])
        {
            dfs_show(g,visitedd,i);
        }
    }
}
void dfsTraverse_3(Graph g, bool visited[], int u)//构造dfs序列对应生成树
{
    int i = 0;
    int conNum = 0;
    
    int *parent = new int[g.VerNum + 1]; // 用于存储生成树的父节点信息
    
    for(i = 1; i <= g.VerNum; i++)
    {
        visited[i] = false;
        parent[i] = -1; // 初始化父节点为-1
    }
    
    dfs_generate_tree(g, visited, u, parent);
    
    // 打印生成树
    cout<<"\ndfs生成树:\n";
    for(i = 1; i <= g.VerNum; i++)
    {
        if(parent[i] != -1)
        {
            cout << "(" << g.Data[parent[i]] << ", " << g.Data[i] << ")" << endl;
        }
        // else cout<<g.Data[i]<<endl;
    }
    // 遍历其他连通分量
     for(i = 1; i <= g.VerNum; i++)
    {
        if(!visited[i])
        {
            dfs_show(g, visited, i);
        }
    }
    delete[] parent; // 释放内存
}





void bfs_generate_tree(Graph g, bool visited[], int u, int parent[])
{
    visited[u] = true;
    
    queue<int> q;
    q.push(u);
    
    while(!q.empty())
    {
        int temp = q.front();
        q.pop();
        
        for(int w = firstAdj(g, temp); w != 0; w = nextAdj(g, temp, w))
        {
            if(!visited[w])
            {
                parent[w] = temp; // 记录当前节点的父节点
                visited[w] = true;
                q.push(w);
            }
        }
    }
}
void bfs_show(Graph g,bool visitedd[],int u)//1&&4
{
    if(u>g.VerNum) return ;
    visitedd[u]=true;
    cout<<g.Data[u]<<" ";
    queue<int> q;
    q.push(u);//将首个元素入队
    while(!q.empty())
    {
        int temp=q.front();
        q.pop();
        for(int w=firstAdj(g,u);w!=0;w=nextAdj(g,temp,w))
        {
            if(!visitedd[w])
            {
                cout<<g.Data[w]<<" ";
                visitedd[w]=true;
                q.push(w);
                
            }
        
    }
}
}

void bfsTraverse_1(Graph g,bool visitedd[],int u)//第一题无生成树版主要作用是实现非连通图的遍历,若只有连通图的dfs遍历 上一个函数完全可以实现
{
    int i=0;
    int conNum=0;
    for(i=1;i<=g.VerNum;i++)
    {
        visitedd[i]=false;
    }
    bfs_show(g,visitedd,u);
    for(i=1;i<=g.VerNum;i++)
    {
        if(!visitedd[i])
        {
            bfs_show(g,visitedd,i);
        }
    }
}
void bfsTraverse_4(Graph g, bool visited[], int u)
{
    int i = 0;
    int conNum = 0;
    
    int *parent = new int[g.VerNum + 1]; // 用于存储生成树的父节点信息
    
    for(i = 1; i <= g.VerNum; i++)
    {
        visited[i] = false;
        parent[i] = -1; // 初始化父节点为-1
    }
    
    bfs_generate_tree(g, visited, u, parent);
    
    // 打印生成树
    cout << "\nBFS 生成树: " << endl;
    for(i = 1; i <= g.VerNum; i++)
    {
        if(parent[i] != -1)
        {
            cout << "(" << g.Data[parent[i]] << ", " << g.Data[i] << ")" << endl;
        }
    }
    
    // 遍历其他连通分量
    for(i = 1; i <= g.VerNum; i++)
    {
        if(!visited[i])
        {
            bfs_show(g, visited, i);
        }
    }
    
    delete[] parent; // 释放内存
}


vector<int> Dijkstra(Graph g,int v0,bool visitedd[])//7 //此函数返回距离数组
{
    int miin,pos;
   visitedd[v0]=true;
   int n=g.VerNum;
   vector<int> dist(100,INTF);
   /*初始化dist数组*/
    for(int i=1;i<=n;i++)
    {
        dist[i]=g.AdjMatrix[v0][i];
    }
   for(int i=1;i<=n;i++)
   {
        miin=INF;
        for(int j=1;j<=n;j++)
        {
            if(!visitedd[j]&&dist[j]<miin)
            {
                miin=dist[j];
                pos=j;
            }
        }
        /*此次循环之后找到了未在已选点中的点的集合中到起点的最短距离的那个点 下标为j*/
        /*然后更新dist数组*/
        visitedd[pos]=true;
        for(int j=1;j<=n;j++)
        {
            if(!visitedd[j]&&dist[j]>dist[pos]+g.AdjMatrix[pos][j])
            {
                dist[j]=dist[pos]+g.AdjMatrix[pos][j];
            }
        }
   }
   return dist;
}
int path[100][100];
vector<vector<int>> Floyd(Graph g)//8
{
    int n=g.VerNum;
    vector<vector<int>> dist(100,vector<int>(100,0));
    for(int i=1;i<=n;i++)//初始化dist和path
    {
        for(int j=1;j<=n;j++)
        {
            dist[i][j]=g.AdjMatrix[i][j];
            if(i!=j&&g.AdjMatrix[i][j]!=0)
            {
                path[i][j]=i;
            }
            else
            {
                path[i][j]=-1;
            }
        }
    }
    for(int m=1;m<=n;m++)
    {
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                if(dist[i][j]>dist[i][m]+dist[m][j]&&i!=j)
                {
                    dist[i][j]=dist[i][m]+dist[m][j];
                    path[i][j]=path[m][j];
                }
            }
        }
    }
    return dist;
}
bool topo(Graph g, vector<int>& topologicalOrder) {//9
    vector<int> ans;
    vector<int> no_use;
    queue<int> q;
    int n = g.VerNum;
    int in[100]={0};//存放结点入度个数
    for(int i=1;i<=g.VerNum;i++)
    {
        for(int j=1;j<=g.VerNum;j++)
            {
                if(g.AdjMatrix[j][i]!=0&&g.AdjMatrix[j][i]!=INF)
                    in[i]++;
            }
    }
    for (int i = 1; i <= n; i++) {
        if (in[i] == 0) {
            q.push(i);
        }
    }
    
    while (!q.empty()) {
        int current = q.front();
        ans.push_back(current);
        q.pop();

        for (int j = 1; j <= n; j++) {
            if (g.AdjMatrix[current][j] != 0 && g.AdjMatrix[current][j] != INF) {
                in[j]--;
                if (in[j] == 0) {
                    q.push(j);
                }
            }
        }
    }
    
    if (ans.size()!=n) {
        return false;
    }
    
    topologicalOrder=ans;
    return true;
}


/*以下是Kruskal算法*/

typedef struct {
    int start;
    int end;
    int weight;
} Edge;

bool compareEdges(const Edge& e1, const Edge& e2) {
    return e1.weight < e2.weight;
}
//并查集 寻找根节点
int findRoot(vector<int>& p, int x) {
    if (p[x] !=x)
        p[x] = findRoot(p, p[x]);
    return p[x];
}

void kruskal(const Graph& graph) {
    vector<Edge> edges;//用数组存储边
    for (int i=1;i<=graph.VerNum;i++)//初始化边数组 
    {
        for (int j=1;j<=graph.VerNum;j++) {
            if (graph.AdjMatrix[i][j]!= 0&&graph.AdjMatrix[i][j]!=INF) {
                Edge edge;
                edge.start=i;//起点
                edge.end=j;//终点
                edge.weight=graph.AdjMatrix[i][j];
                edges.push_back(edge);
            }
        }
    }

    sort(edges.begin(),edges.end(),compareEdges);//按边的距离大小排序

    vector<int> parent(graph.VerNum+1);
    for (int i=1;i<=graph.VerNum;i++) {
        parent[i]=i;//初始化
    }

    vector<Edge> mst;//结果数组
    int numEdges=0;

    for (const auto&edge:edges) {
        int rootStart=findRoot(parent,edge.start);
        int rootEnd=findRoot(parent,edge.end);

        if (rootStart!=rootEnd) {
            mst.push_back(edge);
            numEdges++;
            parent[rootStart] = rootEnd;

            if (numEdges==graph.VerNum-1)//当生成树的顶点数等于图的总结点数-1时跳出循环
                break;
        }
    }

    cout << "用Kauskal算法最小生成树为:" << endl;
    for (const auto&edge:mst) {//输出结果
        cout <<graph.Data[edge.start] << " - " << graph.Data[edge.end] << " : " << edge.weight << endl;
    }
}
/*以下是prim算法*/
void Prim(Graph graph) {
    int lowCost[MaxVerNum + 1];    // 存储顶点到最小生成树的最小权值
    int closest[MaxVerNum + 1];    // 存储最小生成树中与当前顶点最近的顶点

    // 初始化
    for (int i = 1; i <= graph.VerNum; i++) {
        lowCost[i] = graph.AdjMatrix[1][i];    // 初始时将顶点1与其他顶点的权值存入lowCost数组
        closest[i] = 1;    // 初始时将顶点1作为与其他顶点最近的顶点
    }

    for (int i = 2; i <= graph.VerNum; i++) {
        int minCost = INF;
        int minVertex = -1;

        // 在当前顶点集合中找到距离最小生成树最近的顶点
        for (int j = 2; j <= graph.VerNum; j++) {
            if (lowCost[j] != 0 && lowCost[j] < minCost) {
                minCost = lowCost[j];
                minVertex = j;
            }
        }

        if (minVertex == -1) {
            // 所有顶点都已经加入最小生成树,退出循环
            break;
        }

        // 输出最小生成树的边
        cout <<graph.Data[ closest[minVertex] ]<< " - " << graph.Data[minVertex ]<<":"<<graph.AdjMatrix[closest[minVertex]][minVertex]<<endl;

        // 将最近的顶点加入最小生成树
        lowCost[minVertex] = 0;

        // 更新与最近顶点相连的顶点到最小生成树的最小权值
        for (int j = 2; j <= graph.VerNum; j++) {
            if (graph.AdjMatrix[minVertex][j] < lowCost[j]) {
                lowCost[j] = graph.AdjMatrix[minVertex][j];
                closest[j] = minVertex;
            }
        }
    }
}



/*以下是第二题的函数实现*/
int showallb(Graph g)//输出所有的边
{
    int n=g.VerNum;
    int num=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            if(g.AdjMatrix[i][j]!=0&&g.AdjMatrix[i][j]!=INF)
            {
                cout<<g.Data[i]<<"->"<<g.Data[j]<<endl;
                num++;
            }
        }
    }
    return num;
}
/*以下是关键路径求解*/
// 计算关键路径
void CriticalPath(const Graph& graph)
{
    vector<int> topologicalOrder;                    // 拓扑排序的结果
    vector<int> ve(graph.VerNum+1,0);             // 事件的最早发生时间
    vector<int> vl(graph.VerNum+1,INF);       // 事件的最晚发生时间

    if (!topo(graph,topologicalOrder))//通过查找拓扑排序算法 查找是否存在通路
    {
        cout<<"存在环,无法求解关键路径"<<endl;
        return;
    }

    // 计算事件的最早发生时间
    for(int i=0; i<topologicalOrder.size();i++)
    {
        int v=topologicalOrder[i];
        for(int w=1;w<=graph.VerNum;w++)
        {
            if (graph.AdjMatrix[v][w]!= 0&&graph.AdjMatrix[v][w]!=INF)
                ve[w]=max(ve[w],ve[v]+graph.AdjMatrix[v][w]);
        }
    }

    // 初始化结束顶点的最晚发生时间为其最早发生时间
    vl[graph.VerNum] = ve[graph.VerNum];//也就是最后一个顶点

    // 计算事件的最晚发生时间
    for (int i=topologicalOrder.size()-1;i>=0;i--)
    {
        int v=topologicalOrder[i];
        for (int w=1;w<=graph.VerNum;w++)
        {
            if (graph.AdjMatrix[v][w]!=0&&graph.AdjMatrix[v][w]!=INF)
                vl[v]=min(vl[v], vl[w]-graph.AdjMatrix[v][w]);
        }
    }

    // 计算关键路径
    cout<<"关键路径:"<<endl;
    for (int i=1;i<=graph.VerNum;i++)
    {
        for (int j=1;j<=graph.VerNum;j++)
        {
            if (graph.AdjMatrix[i][j]!=0&&graph.AdjMatrix[i][j]!=INF)
            {
                int e=ve[i];
                int l=vl[j]-graph.AdjMatrix[i][j];
                if (e==l)//最早等于最晚 即为关键路径上的点
                    cout<<graph.Data[i]<<" -> "<<graph.Data[j]<<endl;
            }
        }
    }
}

下面是主函数

// #include<iostream>
// #include"createGrpAdjLinkedList.h"
// #include"grpAdjLinkedList.h"
// #include<iostream>
#include"graph.h"
// #include<stack>
int main()
{
    shownemu();
    int choice;
    cout<<"请输入你的选择";
    cin>>choice;
    while(choice)
    {
        /*基于邻接矩阵*/
        char filename[100];
        Graph g;
        cout<<"请输入文件名称:";
        cin>>filename;
        bool jls=CreateGrpFromFile(filename,g);
        if(jls) printGraph(g);
        switch(choice)
        {
            case 0:
            {
                break;
            }
            case 1:
            {
                bool visitedd[100];
                for(int i=0;i<=g.VerNum;i++) visitedd[i]=false;
                cout<<"dfs结果如下:\n";
                dfsTraverse_1(g,visitedd,1);
                for(int i=0;i<=g.VerNum;i++) visitedd[i]=false;
                cout<<"\nbfs结果如下:\n";
                bfsTraverse_1(g,visitedd,1);
                system("pause");
                break;
            }
            case 2:
            {
                int n=showallb(g);
                cout<<"图的边(弧)的数目为"<<n<<endl;
                system("pause");
                break;               
            }
            case 3:
            {
                bool visitedd[100];
                int n;
                cout<<"请输入起点位置下标x";cin>>n;
                for(int i=0;i<=g.VerNum;i++) visitedd[i]=false;
                cout<<"从"<<n<<"出发的dfs图生成树及其遍历序列结果分别如下:\n";
                dfsTraverse_1(g,visitedd,n);//输出dfs遍历序列
                dfsTraverse_3(g,visitedd,n);//输出对应生成树  问题: 非连通图不可正确输出
                system("pause");
                break;
            }
            case 4:
            {
                bool visitedd[100];
                int n;
                cout<<"请输入起点位置下标x";cin>>n;
                for(int i=0;i<=g.VerNum;i++) visitedd[i]=false;
                cout<<"从"<<n<<"出发的bfs图生成树及其遍历序列结果分别如下:\n";
                bfsTraverse_1(g,visitedd,n);
                bfsTraverse_4(g,visitedd,n);
                
                system("pause");
                break;
            }
            case 5:
            {
                bool visitedd[100];
                for(int i=1;i<=g.VerNum;i++) visitedd[i]=false;
                for(int i=1;i<=g.VerNum;i++)
                {
                    for(int j=1;j<=g.VerNum;j++)
                    {
                        if(g.AdjMatrix[i][j]==0)
                        {
                            g.AdjMatrix[i][j]=INTF;//非通路初始为无穷大
                        }
                    }
                }
                cout<<"使用prim算法求解最小生成树为:\n";
                Prim(g);
                system("pause");
                break;
            }
            case 6:
            {
                kruskal(g);
                system("pause");
                break;
            }
            case 7:
            {
                cout<<"Dijkstra算法求最短路径\n";
                int begin;
                cout<<"请输入起点位置下标:";cin>>begin;
                bool visitedd[100]={false};
                auto dist=Dijkstra(g,begin,visitedd);
                cout<<"从"<<begin<<"到各点最短距离如下(INF表示无通路或其自身)\n";
                for(int i=1;i<=g.VerNum;i++)
                {
                    if(dist[i]==INF)
                        cout<<begin<<"->"<<i<<" INF\n";
                    else
                        cout<<begin<<"->"<<i<<" "<<dist[i]<<"\n";
                }
                system("pause");
                break;
            }
            case 8:
            {
                cout<<"Floyd求解最短路径\n";
                auto dist=Floyd(g);
                int n=g.VerNum;
                cout<<"各顶点之间最短路径如下:\n";
                for(int i=1;i<n;i++)
                    for(int j=i+1;j<=n;j++)
                    {
                        if(dist[i][j]!=65535)
                            cout<<i<<"->"<<j<<" "<<dist[i][j]<<endl;
                    }
                
                system("pause");
                break;
            }
            case 9:
            {
                vector<int> topologicalOrder;
                auto ans=topo(g,topologicalOrder);
                if(topologicalOrder.size()==0)
                {
                    cout<<"存在环,无拓朴序列\n";
                }
                else{
                    cout<<"拓扑序列为\n";
                for(auto i:topologicalOrder)
                {
                    cout<<g.Data[i]<<" ";
                }
                }
                system("pause");
                break;
            }
            case 10:
            {
                CriticalPath(g);
                system("pause");
                break;
            }

        }
            system("cls");
            shownemu();
            cout<<"请输入您的选择:";
            cin>>choice;
    }
}

运行如下:
在这里插入图片描述
示例文件:
dag11.grp

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值