数据结构第七周笔记(7)——图(中)(慕课浙大版本--XiaoYu)

173 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第19天,点击查看活动详情

小白专场:哈利 波特的考试-C语言实现

小白-HP.1 题意理解

image-20220802215252987

样例第一列表示动物的个数(也就是顶点的个数)      第二列表示边的个数
D是最短距离的矩阵,记录从顶点i到顶点j之间的最短距离
​
Harry究竟应该带哪只动物去的问题的时候,首先检查每一行里面最大的那个数字,最大的那个数字代表着第一个动物变成最大数字的那个动物是最麻烦的,无穷符号指当前的那个动物
​
要带什么动物去会使我们最难变的动物变成最好变的呢?答案是动物4号,它最难变的咒语是70

小白-HP.2 程序框架搭建

int main()
{
    读入图;
    分析图;
    return 0;
}

代码实际演示

int main()
{
    //有两种图,一种邻接表的,一种邻接矩阵的。这里选择了邻接矩阵的方法来表示图
    Graph G = BuildGraph();
    FindAnimal( G );
    return 0;
}
​
//FindMaxDist(i)是最大距离,对第i个动物去求他所有最短距离里面的最大值,要求的是所有这些最大值里面的最小值
//FindMin是最小距离

image-20220802224222941

小白-HP.3 选择动物

void FindAnimal(MGraph Graph)
{
    WeightType D[MaxVertexNum][MaxVertexNum],MaxDist,MinDist; 
    Vertex Animal, i;
    
    Floyd(Graph , D);
    
    //FindMin:从每个动物i的最短距离的最大值中,找到最小值MinDist,以及对应的动物编号Animal
    MinDist = INFINITY;
    for( i = 0;i<Graph->Mv;i++){
        MaxDist = FindMaxDist(D,i,Graph->Nv);//找到第i个动物它的最大的那个距离赋给MaxDist这个变量
    
    //还有一个特殊情况需要考虑:带一只动物去根本不可能,当图不连通的时候,图有不止一个连通集。该从哪里意识到图不连通了呢?
        if(MaxDist == INFINITY){//返回距离无穷大的话说明有i无法变出的动物
            printf("0\n");
            return;
        }
        
        
        if( MinDist > MaxDist ){//找到最长距离更小的动物
            MinDist = MaxDist; Animal = i + 1;//更新距离,记录编号。这里是i+1是因为动物编号从1开始,但是图的循环从0开始,所有需要加一
        }
    }      
    printf("%d %d\n",Animal,MinDist);
}

上述代码中涉及到封装起来的函数

WeightType FindMaxDist(WeightType D[][MaxVertexNum],Vertex i,int N)
{
    WeightType MaxDist;
    Vertex j;
    
    MaxDist = O;//初始化为一个很小的数,比如0
    for(j = 0;j < N;j++ )//找出i到其他动物j的最长距离
        if( i != j && D[i][j]>MaxDist )//邻接矩阵所有的两个顶点之间的距离全部初始化为正无穷,所以对角元的值永远是正无穷。所以必须要把对角元排除掉跳过去,不排除的话这个无穷大必然会符合这个判断条件从而让MaxDist永远的返回正无穷
            MaxDist=[i][j];
    return MaxDist;
}

小白-HP.4 模块的引用与裁剪

image-20220803001249910

#define MaxVertexNum 100//最大顶点数设置为100
#define INFINITY 65535//无穷大设为双字节无符号整数的最大值65535
typedef int Vertex//用顶点下标表示顶点,为整型
typedef int WeightType;//边的权值设为整型//边的定义
typedef struct ENode *PtrToENode;
struct ENode{
    Vertex V1,V2;//有向边<V1,V2>
    WeightType Weight;//权重
};
typedef PtrToENode Edge;
​
//图结点的定义
typedef struct GNode *PtrToENode;
struct GNode{
    int Nv;//顶点数
    int Ne;//边数
    WeightType G[MaxVertexNum][MaxVertexNum];//邻接矩阵
    DataType Data[MaxVertexNum];//存顶点的数据
    //注意:很多情况下,顶点无数据,此时Data[]可以不出现
};
typedef PtrToENode MGraph;//以邻接矩阵存储的图类型 

CreateGraph原始代码

MGraph CreateGraph(int VertexNum)
{/*初始化一个有VertexNum个顶点但没有边的图*/
    Vertex V,W;
    MGraph Graph;
    
    Graph=(MGraph)malloc(sizeof(struct GNode));/*建立图*/
    Graph->Nv VertexNum;
    Graph->Ne = 0;
/*初始化邻接矩阵*/
/*注意:这里默认顶点编号从0开始,到(Graph->Nv-1)*/
for (V=0; V<Graph->Nv; V++)
    for (W=0; W<Graph->Nv; W++)
        Graph->G[V][W] = INFINITY;
​
    return Graph;
}
​
void InsertEdge(MGraph Graph,Edge E)
{
/*插入边<V1,V2>*/
    Graph->G[E->V1][E->V2] = E->Weight;
/*若是无向图,还要插入边<V2,V1>   因为是无向图,所以我们需要把读进来的这个权重同时赋予矩阵里面两个元素*/
    Graph->G[E->V2][E->V1] = E->Weight;
MGraph BuildGraph()
{
    MGraph Graph;
    Edge E;
    Vertex V;
    int Nv,i;
    
    scanf("%d",&Nv);//读入顶点个数
    Graph = CreateGraph(Nv);//初始化有Nv个顶点但没有边的图
    
    scanf("%d",&(Graph->Ne));//读入边数
    if(Graph->Ne != 0 ){//如果有边
        E = (Edge)malloc(sizeof(struct ENode));//建立边结点
        //读入边,格式为"起点 终点 权重",插入邻接矩阵
        for(i=0;i<Graph->Ne;i++){
            scanf("%d %d %d",&E->V1,&E->V2&E->Weight);//读进来的时候,编号是从1开始的
            //注意:;如果权重不是整型,Weight的读入格式要改
            E->V1--; E->V2--;//读进来的编号都-1,那就变成起始编号从0开始的了,原本1变成0,2变成1,依次变化
            InsertEdge(Graph,E);//插入边的时候,图默认顶点的编号是从0开始
        }
    }
    //如果顶点有数据的话,读入数据。没有的话这个for循环可以删掉
    for(V=0;V<Graph->Nv;V++)
        scanf(" %c",$(Graph->Data[V]));
     
    return Graph;
}

问:图的顶点从0开始编号,而本题目中动物从1开始编号。读输入时该如何处理使得图的相关模块可以顺利应用?

答案:E->V1--; E->V2--;

标准的Floyd算法

bool Floyd(MGraph Graph ,WeightType D[][MaxVertexNum],Vertex path[][MaxVertexNum])//返回布尔值(bool)
{
    Vertex i,j,k;
    //初始化
    for(i = 0;i<Graph->Nv;i++)
        for(j = 0;j<Graph->Nv;j++){
            D[i][j] = Graph->G[i][j];
            path[i][j] = -1;
        }
    
    for(k = 0;k <Graph->Nv;k++)
        for(i=0;i<Graph->Nv;i++)
            for(j=0;j<Graph->Nv;j++)
                if(D[i][k] + D[k][j] < D[i][j]){
                    D[i][j] = D[i][k] + D[k][j];
                    if( i == j && D[i][j]<0)//若发现负值圈,从i出发,走了一圈又回到i,这个最短距离是一个负的值,也对应了一个负值圈
                        return false;//不能正确解决,返回错误标记
                    path[i][j] = k;
                }
    return true;//算法执行完毕,返回正确标记
}

在本题中进行的改动部分:

image-20220803005320198