计科数据结构第六次实验

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;
    }
}
 

1.实验目的 (1)掌握树和森林的孩子兄弟链表(二叉链表)表示方法。 (2)掌握树和二叉树的结构及算法之间的对应关系。 (3)掌握树的两种遍历算法及其应用。 2.实验任务 设计、实现算法求解下列问题: (1)按先序、后序、层次遍历森林。 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (2)求森林的高度。 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (3)求森林结点总数。 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (4)求森林叶子结点数。 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (5)求森林的度。 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (6)先序输出结点值及其层次号。 例对7-1所示森林,输出为:(A,1) (B,2) (E,3) (K,4) (F,3) (G,3) (C,2) (H,3) (I,3) (D,2) (J,3) (L,1) (M,2) (N,2) (O,1) (P,2) 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre (7)输出广义表表示的树。 例对7-1所示森林,输出为:A( B(E(K),F,G),C(H,I),D(J)), L(M,N), O(P) ) 实验测试数据基本要求: 第一组数据: tree11.tre 第二组数据: f20.tre 3.实验说明 (以下给出的森林创建方法仅供参考,实验者可自行设计其它创建方法) (1)树(森林)的创建 本实验提供的创建代码,创建二叉链表表示的树(森林)分为2个步骤,第一步:读取文本文件,创建双亲表示的树(森林);第二部:从双亲表示转换为二叉链表表示的树(森林)。 (2)树(森林)数据文件格式说明 数据文件主要包含三个部分:树(森林)标识;结点列表;父子结点对(边)。 ①标识行 Tree or Forest,以区别其它数据文件,这一行是非必须的。 ②结点列表 给出树(森林)中的所有结点,结点次序无关,只要列出所有结点即可。如7-1所示的森林,结点列表可为: //下面为树(森林)的结点列表 A B C D E F G H I J K L M N O P。 ③父子结点对(边)信息 父子对信息严格按照父结点、子结点表示一对父子结点,父子对也次序无关,只要列出森林中所有父子对即可,例7-1所示森林,所有父子对为: //以下为父子结点对(边)信息 A B A C A D B E B F B G C H C I D J E K L M L N O P (3)创建树(森林)包含文件说明 createTree.h,包括树(森林)的双亲存储、二叉链表存储的定义;从文件创建双亲表示的树(森林);从双亲表示的森林创建二叉链表表示的森林;其它辅助算法。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值