第八天------最小生成树 附源码

10 篇文章 0 订阅
9 篇文章 0 订阅

一、目的

掌握图的常见应用算法的思想及其程序实现。

 

二、内容

1、内容

a)   (1)键盘输入数据,分别建立一个有向图的邻接表和一个无向图的邻接矩阵。

b)   (2)输出该邻接表和邻接矩阵。

c)   (3)以有向图的邻接表为基础输出它的拓扑排序序列。

d)   (4)以无向图的邻接矩阵为基础实现最小生成树的PRIM算法。

e)   (5)以有向图的邻接矩阵为基础实现Dijkstra算法输出单源点到其它顶点的最短路径。

f)   (6)在主函数中设计一个简单的菜单,分别调试上述算法。

g)   (7)*综合训练:校园导航

h)   1)问题描述:

i)  在给出校园各主要建筑的名称信息及有路线连通的建筑之间的距离(或行进时间)的基础上,利用校园导航系统计算出给定的起点到终点之间距离最近(或行进时间最短)的行进路线。

j)        2)设计要求:文件读入或键盘方式读入校园主要建筑信息及建筑间的距离(或行进时间)信息。创建完地图后,以文件形式保存,以免重复创建。计算出给定的起点到终点之间距离最近(或行进时间最短)的行进路线,并输出该路线(包括经过哪些建筑)及其总距离(或总行进时间)。

 

2、主要数据类型与变量

1.类型定义

邻接表存储见07

邻接矩阵存储示例

        #define MAX_VERTEX_NUM 20  //顶点最大个数

        typedef enum {DG, DN, UDG, UDN} GraphKind;

typedef structArcCell{

           VRType  adj;

           int       weight; //边的权值

        }ArcCell; AdjMatrix[MAX_VERTEX_NUM] [MAX_VERTEX_NUM]; 

        typedef struct{

  VertexTypevexs[MAX_VERTEX_NUM];

  AdjMatrix arcs;

          int        vexnum, arcnum; //顶点的实际数,边的实际数

          GraphKind kind;

      }MGraph;   

 

 

 

 

三、系统测试

1、测试方案

通过图形化界面,选择进入那个功能,然后开始一系列的测试,比如第一个的拓扑排序,通过输入顶点与边,然后实现拓扑排序。

2、测试结果

              1.拓补排序

                     2.最小生成树

 

3.最短路径

 

附:程序源代码

 

DS.h

/*该头文件在每一章的程序中都要用到*/

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <math.h>

#include <limits.h>

#include "MGraph.h"

 

#define TRUE 1

#define FALSE 0

#define OK 1

#define ERROR 0

#define INFINITE 0xFFFFFFFF

 

typedef int Status;

 

SqStack.h

#define STACK_INIT_SIZE 100

#define STACKINCREMENT 10

typedef int SElemType;

typedef int Status;

 

typedef struct {

    SElemType  * base;   //栈底指针

    SElemType  *top;     //栈顶指针

    Status    stacksize;      //栈可使用的最大容量

} SqStack;

 

Status InitStack (SqStack &S);          //初始化空栈

Status GetTop(SqStack S, SElemType &e); //获得栈顶元素

Status Push(SqStack &S, SElemType e);  //入栈操作

Status Pop(SqStack &S, SElemType &e);   //出栈操作

Status StackEmpty(SqStack S);          //判断栈是否为空

 

MGraph.h

#ifndef MGRAPH_H_INCLUDED

#define MGRAPH_H_INCLUDED

 

#define MAX 3000

#define  N  10  /*最多顶点数*/

typedef  char  VertexType; /*顶点类型*/

typedef  struct{

     VertexType vexs[N]; /*存顶点的数组*/

     int  arcs[N][N];/*存边信息的矩阵*/

     int vexnum, arcnum;  /*顶点数,边数 */

}MGraph;

 

#endif // MGRAPH_H_INCLUDED

 

ALGraph.h

#include "SqStack.h"

 

 

#ifndef ALGRAPH_H_INCLUDED

#define ALGRAPH_H_INCLUDED

 

#define  N  10  /*最多顶点数*/

typedef char VertexType; /*顶点类型*/

typedef int Status;

//定义边结点的结构

typedef struct ArcNode

{

    Status   adjvex;                      //边的邻接点的数据

    Status   weight;                      //对应边的权值

    struct ArcNode  *nextarc;            //指向下一邻接点的指针

}ArcNode;

//定义定点结构

typedef  struct  VNode

{

    VertexType   data;             //顶点数据

    ArcNode     *firstarc;        //指向该顶点第一条弧的指针*/

}VNode,AdjList[N];

typedef struct

{

    AdjList    vertices;

    Status      vexnum, arcnum;      //图的顶点数和弧数

}ALGraph;

 

#endif // ALGRAPH_H_INCLUDED

 

SqStack.cpp

/*关于栈的基本操作定义*/

#include "DS.h"

#include "SqStack.h"

 

/*初始化一个空栈*/

Status  InitStack (SqStack &S){

    S.base = (SElemType *)malloc(STACK_INIT_SIZE *sizeof(SElemType));

    if (! S.base)  exit (OVERFLOW);

    S.top = S.base;

    S.stacksize = STACK_INIT_SIZE;

    return OK;

}

 

/*判断栈是否为空*/

Status StackEmpty(SqStack S)

{

    if(S.top == S.base)

        return OK;

    else

        return ERROR;

}

 

/*取栈顶元素*/

Status GetTop(SqStack S, SElemType &e){

    if (S.top == S.base)  return ERROR;

    e= * (S.top-1);

    return OK;

}

 

/*入栈*/

Status Push(SqStack &S, SElemType e){

    if (S.top - S.base>=S.stacksize){

       S.base=(SElemType*)realloc(S.base,

       (S.stacksize+STACKINCREMENT)*sizeof(SElemType));

        if(!S.base) exit(OVERFLOW);

        S.top=S.base+S.stacksize;

       S.stacksize+=STACKINCREMENT;

    }

    *S.top++ = e;

     return OK;

}

 

/*出栈*/

Status Pop(SqStack &S, SElemType &e){

    if( S.top == S.base) returnERROR;

    e = *--S.top;

    return OK;

}

 

Main.cpp

#include "DS.h"

#include "ALGraph.h"

 

 

/*查找顶点v在邻接表存储法中的下标*/

int LocateVex1(ALGraph G, char v)

{

    int i;

    for(i = 0; i < G.vexnum;i++)

        if(G.vertices[i].data == v)

            return i;               /*找到顶点返回下标*/

    return -1;

}

 

/*在邻接表中插入顶点v的邻接点w*/

int InsertList1(ALGraph &G, int i, int j)

{

       ArcNode *p;

       if(i==-1||j==-1||i==j)

        return ERROR;

       p=(ArcNode*)malloc(sizeof(ArcNode));

       p->adjvex=j;       /*插入邻接点*/

       p->nextarc=NULL;

       if(!G.vertices[i].firstarc)

       G.vertices[i].firstarc=p;  /*头插法*/

       else

       {

       p->nextarc=G.vertices[i].firstarc->nextarc;

       G.vertices[i].firstarc->nextarc=p;

       }

    return 1;

}

/*请写出有向带权图的邻接表的生成函数*/

int CreateDN1(ALGraph &G)

{

    int i, j, k,w;

    char v1, v2;

    printf("请输入有向图G的顶点数和边数:");

    scanf("%d %d",&G.vexnum, &G.arcnum);

    printf("请输入%d个顶点名字(以空格区分):", G.vexnum);

    for(i = 0; i < G.vexnum; i++)

    {

        scanf(" %c",&G.vertices[i].data);  /*请注意输入格式*/

        G.vertices[i].firstarc =NULL; /*邻接表初始化为空*/

    }

    for(k = 1; k <= G.arcnum;k++)

    {

        printf("请输入第%d条边的两个顶点(以字符表示,字符间以空格区分)和权值:", k);

        scanf(" %c %c%d", &v1, &v2,&w);  /*请注意输入格式*/

        i = LocateVex1(G, v1);       /*确定v1和v2的下标*/

        j = LocateVex1(G, v2);

        InsertList1(G, i, j);   /*生成各自的邻接表(记录权值)*/

    }

    return OK;

}

 

 

/*查找顶点v在邻接矩阵存储法中的下标*/

int LocateVex(MGraph G, char v)

{

    int i;

    for(i = 0; i < G.vexnum;i++)

        if(G.vexs[i] == v)

            return i;

    return -1;

}

 

/*请写出有向带权图的邻接矩阵的生成函数*/

Status CreateDN(MGraph &G)

{

    int i, j, k, value;

    char v1, v2;

    printf("请输入图G的顶点数和边数:");

    scanf("%d %d",&G.vexnum, &G.arcnum);

    printf("请输入%d个顶点名字(以空格区分):", G.vexnum);

    for(i = 0; i < G.vexnum;i++)

        scanf(" %c",&G.vexs[i]);  /*请注意输入格式*/

    for(i = 0; i < G.vexnum;i++)

        for(j = 0; j < G.vexnum;j++)

            G.arcs[i][j] = MAX;

    for(k = 1; k <= G.arcnum;k++)

    {

        printf("请输入第%d条边的两个顶点(以字符表示,字符间以空格区分)及权值:", k);

        scanf(" %c %c%d", &v1, &v2, &value); /*请注意输入格式*/

        i = LocateVex(G, v1);       /*确定v1和v2的下标*/

        j = LocateVex(G, v2);

        if(i >= 0 && j>= 0)        /*修改邻接矩阵的信息*/

            G.arcs[i][j] = value;

    }

    return OK;

}

 

 

/*在邻接表中插入顶点v的邻接点w*/

int InsertList(ALGraph &G, int i, int j)

{

       ArcNode *p;

       if(i==-1||j==-1||i==j)

        return ERROR;

       p=(ArcNode*)malloc(sizeof(ArcNode));

       p->adjvex=j;       /*插入邻接点*/

       p->nextarc=NULL;

       if(!G.vertices[i].firstarc)

       G.vertices[i].firstarc=p;  /*头插法*/

       else

       {

       p->nextarc=G.vertices[i].firstarc->nextarc;

       G.vertices[i].firstarc->nextarc=p;

       }

    return 1;

}

 

 

/*键盘输入数据,分别建立一个有向图的邻接表和一个无向图的邻接矩阵。

(2)输出该邻接表和邻接矩阵。

(3)以有向图的邻接表为基础输出它的拓扑排序序列。

(4)以无向图的邻接矩阵为基础实现最小生成树的PRIM算法。

(5)以有向图的邻接矩阵为基础实现Dijkstra算法输出单源点到其它顶点的最短路径。

(6)在主函数中设计一个简单的菜单,分别调试上述算法。*/

 

 

//生成入度

void FindIndegree(ALGraph G,int *arr)

{

    ArcNode *p;

    for(int i=0; i<G.vexnum;i++)

    {

        p=G.vertices[i].firstarc;

        while(p)

        {

            intindex=p->adjvex;//得到这个节点所指向的节点的下标

            arr[index]++;

            p=p->nextarc;

        }

    }

}

 

int TopoSort(ALGraph G)

{

    int indegree[30]= {0};

    SqStack S;

    FindIndegree(G,indegree);//生成各顶点的入度

    InitStack(S);

    for(int i=0; i<G.vexnum;++i)

    {

        if(!indegree[i])

            Push(S,i);

    }

    int count=0;

    while(!StackEmpty(S))

    {

        ArcNode *p;

        int i;

        Pop(S,i);

        printf("%c",G.vertices[i].data);

        count++;

        p=G.vertices[i].firstarc;

        while(p)

        {

            int k;

            k=p->adjvex;

            if(!(--indegree[k]))

            {

                Push(S,k);

            }

            p=p->nextarc;

        }

    }

    if(count<G.vexnum)//用来判断回路

        return -1;

    else

        return 1;

}

 

//ar VertexType  arcs 矩阵 ,vexnum定点数,arcnum 边数

struct node

{

    VertexType adjvex;

    int lowcost;

} closedge[30]; //用来储存已经加入到树中的节点

 

/*最小生成树思想:每次从已有的顶点的周围的边中选取权值最小的边

加入到图中,将另外的点加入到这个顶点集中,然后循环这个过程*/

 

int minimum(MGraph G,struct node * closedge)//返回最小代价边

{

    int min=3000;

    int index=0;

    //需要遍历已经加入到顶点集的元素的那一行,来寻找权值最小的边

    for(int i=0; i<G.vexnum;i++)

    {

        if(closedge[i].lowcost==0)

        {

            for(int j=0;j<G.vexnum; j++)

            {

               if(closedge[j].lowcost==0)

                    continue;

                else if(G.arcs[i][j] < min)

                {

                   min=G.arcs[i][j];

                    index=j;

                }

            }

 

        }

    }

    for(int i=0; i<G.vexnum;i++)

    {

       if(closedge[i].lowcost<min && closedge[i].lowcost!=0)

        {

           min=closedge[i].lowcost;

            index=i;

        }

    }

    return index;

}

 

void CreateTree(MGraph G,VertexType u)

{

 

    //决定生成树从哪开始生成

    int k=LocateVex(G,u);

    //初始化辅助数组

    for(int j=0; j<G.vexnum;++j)

    {

        if(j!=k)

        {

            closedge[j].adjvex=u;

           closedge[j].lowcost=G.arcs[k][j];

        }

    }

    closedge[k].lowcost=0;

 

    for(int i=1; i<G.vexnum;++i)

    {

        k=minimum(G,closedge);//k现在是要加入到顶点集的下一个顶点在数组中对应的下标

        printf("%c --- %c",closedge[k].adjvex,G.vexs[k]);

        closedge[k].lowcost=0;//将第k个顶点加入到数组中

        for(int j=0; j<G.vexnum;++j)

        {

           if(G.arcs[k][j]<closedge[j].lowcost)

            {

               closedge[j].adjvex=G.vexs[k];

               closedge[j].lowcost=G.arcs[k][j];

            }

        }

    }

}

 

//最短路径问题

void Dijkstra(MGraph G,int v0,int *dist,int *path)

{

    int s[N];

    int minDis,u;

    for(int i=0; i<G.vexnum;i++)

    {

        s[i]=0;

        dist[i]=G.arcs[v0][i];

        if(dist[i]<INT_MAX)

            path[i]=v0;

        else

            path[i]=-1;

    }

    s[v0]=1;

    dist[v0]=0;

    for(int i=1; i<G.vexnum;i++)

    {

        minDis=INT_MAX;

        for(int j=1;j<=G.vexnum; j++)

        {

            if(dist[j]<minDis&& !s[j])

            {

                minDis=dist[j];

                u=j;

            }

            if(minDis<INT_MAX)

                s[u]=1;

        }

 

        for(int j=0; j<G.vexnum;j++)

        {

            if(!s[j]&&G.arcs[u][j]<INT_MAX)

               if(dist[j]>dist[u]+G.arcs[u][j])

                {

                   dist[j]=minDis+G.arcs[u][j];

                    path[j]=u;

                }

        }

    }

 

    for(int i=0; i<G.vexnum;i++)

    {

        printf("%d",dist[i]);

    }

}

 

void menu()

{

    printf("\t\t\t   单链表基本操作\n\n");

    printf("\t\t\t1.拓扑排序\n");

    printf("\t\t\t2.最小生成树\n");

    printf("\t\t\t3.最短路径\n");

    printf("\t\t\t0.退          出\n\n");

}

 

int main()

{

    int choice;

    menu();

    printf("请输入你的操作:");

   scanf("%d",&choice);

    while(1){

 

        if(choice==1)

        {

            ALGraph G;

            //拓扑排序

            CreateDN1(G);

            printf("拓扑排序为:\n");

            TopoSort(G);

            break;

        }

        else if(choice==2)

        {

            MGraph G;

            printf("请建立有向图:\n");

            CreateDN(G);

            char ch;

            printf("请确认最小生成树的定点元素:\n");

            scanf("%c",&ch);

            CreateTree(G,ch);

            break;

        }

        else if(choice==3)

        {

            //最短路径问题

            MGraph G;

            printf("请建立有向图:\n");

            CreateDN(G);

            int dist[G.vexnum];//用来储存每个节点到起始点的最短路径

            int  path[G.vexnum];

            //为顶点i的最优前驱节点

            printf("请问从哪个点开始遍历:\n");

            char ch;

            scanf("%c",&ch);

            int v0=LocateVex(G,ch);

           Dijkstra(G,v0,dist,path);

            break;

        }

        else

        {

            break;

        }

    }

 

    return 0;

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值