精解C语言最短路径算法:有向与无向图最短路径Dijkstra算法和节点输出

1、手动输入数据检验算法

#include <stdio.h>
#include <stdlib.h>
#define max1 10000000; //原词条这里的值太大,导致溢出,后面比较大小时会出错
int a[1000][1000];
int d[1000]; //d表示源节点到该节点的最小距离
int p[1000]; //p标记访问过的节点
int i, j, k;
int m; //m代表边数
int n; //n代表点数
int main()
{
    scanf("%d%d", &n, &m);
    int min1;
    int x, y, z;
    for (i = 1; i <= m; i++)
    {
        scanf("%d%d%d", &x, &y, &z);
        a[x][y] = z;
        a[y][x] = z;
    }
    for (i = 1; i <= n; i++)
        d[i] = max1;
    d[1] = 0;
    for (i = 1; i <= n; i++)
    {
        min1 = max1;
        //下面这个for循环的功能类似冒泡排序,目的是找到未访问节点中d[j]值最小的那个节点,
        //作为下一个访问节点,用k标记
        for (j = 1; j <= n; j++)
            if (!p[j] && d[j] < min1)
            {
                min1 = d[j];
                k = j;
            }
        //p[k]=d[k]; // 这是原来的代码,用下一 条代码替代。初始时,执行到这里k=1,而d[1]=0
        //从而p[1]等于0,这样的话,上面的循环在之后的每次执行之后,k还是等于1。
        p[k] = 1; //置1表示第k个节点已经访问过了
        for (j = 1; j <= n; j++)
            if (a[k][j] != 0 && !p[j] && d[j] > d[k] + a[k][j])
                d[j] = d[k] + a[k][j];
    }
    //最终输出从源节点到其他每个节点的最小距离
    for (i = 1; i < n; i++)
        printf("%d->", d[i]);
    printf("%d\n", d[n]);
    return 0;
}

参考文献

2、有向图单源最短路径

#include <stdio.h>
int main()
{
    int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
    int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
    //读入n和m,n表示顶点个数,m表示边的条数
    scanf("%d %d",&n,&m);
                                                                   
    //初始化
    for(i=1;i<=n;i++)
        for(j=1;j<=n;j++)
            if(i==j) e[i][j]=0;
              else e[i][j]=inf;
                                                                             
    //读入边
    for(i=1;i<=m;i++)
    {
        scanf("%d %d %d",&t1,&t2,&t3);
        e[t1][t2]=t3;
    }
    //初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
    for(i=1;i<=n;i++)
        dis[i]=e[1][i];
    //book数组初始化
    for(i=1;i<=n;i++)
        book[i]=0;
    book[1]=1;
                                                                   
    //Dijkstra算法核心语句
    for(i=1;i<=n-1;i++)
    {
        //找到离1号顶点最近的顶点
        min=inf;
        for(j=1;j<=n;j++)
        {
            if(book[j]==0 && dis[j]<min)
            {
                min=dis[j];
                u=j;
            }
        }
        book[u]=1;
        for(v=1;v<=n;v++)
        {
            if(e[u][v]<inf)
            {
                if(dis[v]>dis[u]+e[u][v])
                    dis[v]=dis[u]+e[u][v];
            }
        }
    }
                                                                   
    //输出最终的结果
    for(i=1;i<=n;i++)
        printf("%d ",dis[i]);
                                                                       
    getchar();
    getchar();
    return 0;
}

3、无向图单源最短路径

#define MAXSIZE 20
#define PLACENUM 12
#define INF 9999           // 此处定义999为无穷大
 
struct
{
    int vexnum,arcnum;  //节点数和边数
    int vexs[MAXSIZE];      // 节点名
    int arcs[MAXSIZE][MAXSIZE];   //俩个节点之间的值
} net;
/*补充的结构体net,2019.7.3*/
 
void Dijkstra(int x,int y)      // x为源点,y为终点
{
    int i,j,k;
    int min;
    int u;   //下一个放入集合p的点
    int dis[net.vexnum];   //  最短路径
    int mark[net.vexnum];   //   被mark的便是已经遍历,未被mark的便是未遍历
    /*首先进行最短路径初始化*/
    for(i=0; i<net.vexnum; i++)
    {
        mark[i] = 0;
        dis[i] = net.arcs[x][i];
    }
 
 
    mark[x]=1;          // 标记源点
    
    
    for(k=0; k<net.vexnum; k++)          // for 大循环
    {
        min = INF;   //  min初始化最大值,便于后来数据替换(每一个点的出度入度判断)
        
        /*寻找遍历到点联通路径(与之相连线的点)中权值最小的一条; 标记遍历点;*/
        for(i=0; i<net.vexnum; i++)
        {
            if(mark[i]==0&&min>dis[i])      //判断未遍历点 且 被赋值的最短路径(dis[i]<INF),未被赋值的点     //                                                     应当min==dis[i]=INF
            {
               min = dis[i];             //在已知赋值最短路径中,寻找权值最小的点并将他作为下一个遍历 
               u=i;                            //点u点
            }
        }
 
 
        mark[u]=1;      //标记u点,下面u修正将会以最短路径进行辐射
 
        /*修正最短路径*/
        for(i=0;i<net.vexnum;i++)
        {
            if(!mark[i]&&dis[i]>dis[u]+net.arcs[u][i])                 // !mark[i]判断不去走回头路,         //                                                                                dis[i]>dis[u]+net.arcs[u][i]有俩个用途:①若u链接的是x源点没有赋值最短路径的点,那么这里可以赋值②若是赋值过的点,那么可以判断是上一个dis[i](此时是被赋值过的)是不是真正的最短路径,即修正。
 
            {
                dis[i] = dis[u] + net.arcs[u][i];      //若A->C比A->B->C更长那么A->B->C则是到C的最短路径,下图将解释。
         
                   }
             }
    }
     printf("最短路径值为:             %d",dis[y]);
}

4、输出最短路径节点

#include <stdio.h>

#define M 65535 //无穷大
#define N 5     //顶点数

//Dijkstra算法函数,求给定顶点到其余各点的最短路径
//参数:邻接矩阵、出发点的下标、结果数组、路径前一点记录
void Dijkstra(int Cost[][N], int v0, int Distance[], int prev[])
{
    int s[N];
    int mindis, dis;
    int i, j, u;
    //初始化
    for (i = 0; i < N; i++)
    {
        Distance[i] = Cost[v0][i];
        s[i] = 0;
        if (Distance[i] == M)
            prev[i] = -1;
        else
            prev[i] = v0;
    }
    Distance[v0] = 0;
    s[v0] = 1; //标记v0
    //在当前还未找到最短路径的顶点中,
    //寻找具有最短距离的顶点
    for (i = 1; i < N; i++)
    { //每循环一次,求得一个最短路径
        mindis = M;
        u = v0;
        for (j = 0; j < N; j++) //求离出发点最近的顶点
            if (s[j] == 0 && Distance[j] < mindis)
            {
                mindis = Distance[j];
                u = j;
            } // if语句体结束,j循环结束
        s[u] = 1;
        for (j = 0; j < N; j++) //修改递增路径序列(集合)
            if (s[j] == 0 && Cost[u][j] < M)
            { //对还未求得最短路径的顶点
                //求出由最近的顶点 直达各顶点的距离
                dis = Distance[u] + Cost[u][j];
                // 如果新的路径更短,就替换掉原路径

                if (Distance[j] > dis)
                {
                    Distance[j] = dis;
                    prev[j] = u;
                }
            } // if 语句体结束,j循环结束
    }         // i循环结束
}
// 输出最短路径
// 参数:路径前一点记录、出发点的下标、到达点下标
void PrintPrev(int prev[], int v0, int vn)
{
    int tmp = vn;
    int i, j;
    //临时存路径
    int tmpprv[N];
    //初始化数组
    for (i = 0; i < N; i++)
        tmpprv[i] = 0;

    //记录到达点下标
    tmpprv[0] = vn + 1;
    //中间点用循环记录
    for (i = 0, j = 1; j < N; j++)
    {
        if (prev[tmp] != -1 && tmp != 0)
        {
            tmpprv[i] = prev[tmp] + 1;
            tmp = prev[tmp];
            i++;
        }
        else
            break;
    }

    //输出路径,数组逆向输出
    for (i = N - 1; i >= 0; i--)
    {
        if (tmpprv[i] != 0)
        { //排除0元素
            printf("V%d", tmpprv[i]);
            if (i) //不是最后一个输出符号
                printf("-->");
        }
    }
    printf("-->V%d", vn + 1);
}
//主函数
int main()
{
    //给出有向网的顶点数组
    char *Vertex[N] = {"V1", "V2", "V3", "V4", "V5"};
    //给出有向网的邻接矩阵
    int Cost[N][N] = {
        {0, 10, M, 30, 100},
        {M, 0, 50, M, M},
        {M, M, 0, M, 10},
        {M, M, 20, 0, 60},
        {M, M, M, M, 0},
    };
    int Distance[N]; //存放求得的最短路径长度
    int prev[N];     //存放求得的最短路径
    int i;
    //调用Dijkstra算法函数,求顶点V1到其余各点的最短路径
    //参数:邻接矩阵、顶点数、出发点的下标、 结果数组
    Dijkstra(Cost, 0, Distance, prev);
    for (i = 0; i < N; i++)
    {
        //输出最短路径长度
        printf("%s-->%s:%d\t", Vertex[0], Vertex[i], Distance[i]);
        //输出最短路径
        PrintPrev(prev, 0, i);
        printf("\n");
    }
    system("pause");

    return 0;
}

执行结果:

在这里插入图片描述

5、完整手动输出的函数结果

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define INFINITY 65535
#define BEGIN -1
#define MAXVEX 100
#define NOTEXIST -1
#define TRUE 1
#define FALSE 0 

int path[MAXVEX];
int dist[MAXVEX];
int known[MAXVEX];
typedef char VertexType;
typedef int WeightType;
typedef struct ENode
{
    int ivex; //顶点 索引
    WeightType weight;
    struct ENode *next_edge;
} ENode;

typedef struct VNode
{
    VertexType data; // 顶点 信息
    ENode *first_edge;
} VNode;

typedef struct Graph
{
    VNode vex[MAXVEX];
    int vex_num, edge_num;
} Graph;

char read_char()
{
    char ch;
    do
    {
        ch = getchar();
    } while (!isalpha(ch));
    return ch;
}

int get_pos(Graph g, char ch)
{
    int i;
    for (i = 0; i < g.vex_num; i++)
    {
        if (ch == g.vex[i].data)
            return i;
    }

    return -1;
}

void link_last(ENode *list, ENode *last)
{
    ENode *p;
    p = list;
    while (p->next_edge != NULL)
    {
        p = p->next_edge;
    }
    p->next_edge = last;
}
void create_graph(Graph *g)
{
    int i, w;
    printf("请输入顶点数和边数:\n");
    scanf("%d%d", &g->vex_num, &g->edge_num);
    printf("请输入顶点信息:\n");
    for (i = 0; i < g->vex_num; i++)
    {
        g->vex[i].first_edge = NULL;
        g->vex[i].data = read_char();
    }
    printf("请输入边 :\n");
    for (i = 0; i < g->edge_num; i++)
    {
        int p1, p2;
        char c1, c2;
        c1 = read_char();
        c2 = read_char();
        scanf("%d", &w);
        p1 = get_pos(*g, c1);
        p2 = get_pos(*g, c2);
        ENode *node1;
        node1 = (ENode *)malloc(sizeof(ENode));
        if (node1 == NULL)
        {
            printf("error");
            return;
        }
        node1->next_edge = NULL;
        node1->ivex = p2;
        node1->weight = w;
        if (g->vex[p1].first_edge == NULL)
            g->vex[p1].first_edge = node1;
        else
            link_last(g->vex[p1].first_edge, node1);
    }
}
int get_weight(Graph g, int start, int end)
{
    ENode *node;
    if (start == end)
    {
        return 0;
    }
    node = g.vex[start].first_edge;
    while (node != NULL)
    {
        if (end == node->ivex)
        {
            return node->weight;
        }
        node = node->next_edge;
    }
    return INFINITY;
}

void print_graph(Graph g)
{
    int i;
    ENode *p;
    for (i = 0; i < g.vex_num; i++)
    {
        printf("%c", g.vex[i].data);
        p = g.vex[i].first_edge;
        while (p != NULL)
        {
            printf("(%c, %c)->w =%d ", g.vex[i].data, g.vex[p->ivex].data, p->weight);
            p = p->next_edge;
        }
        printf("\n");
    }
}

void init_graph(Graph g, int start)
{
    int i;
    for (i = 0; i < g.vex_num; i++)
    {
        //	dist[i] = get_weight(g, start, i);//如果最开始 就获取起始点的临节点
        path[i] = BEGIN; //需要改变相应临节点的path 值
        known[i] = FALSE;
        dist[i] = INFINITY;
    }
    dist[start] = 0;
    //known[start] = TRUE;
}

int find_min(Graph g)
{
    int i;
    int index;
    int min;
    min = INFINITY; //每次初始为INFINITY
    for (i = 0; i < g.vex_num; i++)
    {
        if (dist[i] < min && known[i] == FALSE)
        {
            min = dist[i];
            index = i;
        }
    }
    if (min == INFINITY)
        return NOTEXIST;
    else
    {
        return index;
    }
}

void Dijkstra(Graph g)
{
    ENode *p;
    int v;
    while (1)
    {
        v = find_min(g);
        if (v == NOTEXIST)
            break;
        known[v] = TRUE;
        p = g.vex[v].first_edge;
        while (p != NULL)
        {
            if (known[p->ivex] == FALSE)
            {
                if (dist[v] + p->weight < dist[p->ivex])
                {
                    dist[p->ivex] = dist[v] + p->weight;
                    path[p->ivex] = v;
                }
            }
            p = p->next_edge;
        }
    }
}
void print_path2(Graph g, int end) //这里 直接传递最后位置的索引 类比树 的后续遍历
{
    if (path[end] != BEGIN)
    {
        print_path2(g, path[end]);
        printf("->");
    }
    printf("%c ", g.vex[end].data);
}

int main()
{
    Graph g;
    int start, end;
    char ch1, ch2;
    create_graph(&g);

    printf("请输入起始点与终点:\n");
    ch1 = read_char();
    ch2 = read_char();
    start = get_pos(g, ch1);
    end = get_pos(g, ch2);
    init_graph(g, start);

    Dijkstra(g);
    if (dist[end] == INFINITY)
        printf("\n该两点间无路径.");
    else
    {
        printf("最短路径为:\n\n");
        print_path2(g, end);
        printf("\n\n最小花费 : %d", dist[end]);
    }
    getchar();
    getchar();

    return 0;
}

附录算法

/*************************************************************************************************************
**							弗洛伊德(Floyd)算法
**	核心:是一种尝试的想法,从v到w只有两种方案:
			1:直接从v到w,即DFloyd[v][w]
			2:经过“另外的一个点”u,从v到w,即DFloyd[v][u] + DFloyd[u][w]
		  比较两种方案,取更小的
			1:比较	   :DFloyd[v][u] + DFloyd[u][w] < DFloyd[v][w]
			2:取更小的:DFloyd[v][w] = DFloyd[v][u] + DFloyd[u][w]
**	这里的Path矩阵的构造很巧,关于Path矩阵代码仅仅只有几行而已!!!下面分析Path矩阵
	1:矩阵Path的初值则为各个边的终点顶点-----相当于直接从v到w(上面两种方案的第一种)
	2:当且仅当通过“另外一个点”的时候,有更短路径,即更新路径Path[v][w],把“另外一个点”放进Path[v][w]里
** 路径输出
	1:不防把起点(G.vexs[i]),和Path[i][j]先输出(Path[i][j]是路径的第一个点)
	2:循环输出直到(Path[temp][j] == j)为止!!!
**	自己可能还没有讲清楚,不用担心我在画个图解释下
	请参考:https://mp.csdn.net/mdeditor/80579845
****************************************************************************************************************/
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>


#define MAXV 100
// #define INF 9999
typedef struct
{
    double edges[MAXV][MAXV]; 
    int n, e;
} MGraph;
void ppath(int path[][MAXV], int i, int j)
{
    int k;
    k = path[i][j];
    if (k == -1)
        return;
    ppath(path, i, k);
    printf("%d,", k);
    ppath(path, k, j);
}
void DisPath(double A[][MAXV], int path[][MAXV], int n)
{
    double INF = 9999;
    int i, j;
    for (i = 0; i < n; i++)
        for (j = 0; j < n; j++)
            if (A[i][j] != INF && i != j)
            {
                printf("  从%d到%d路径为:", i, j);
                printf("%d,", i);
                ppath(path, i, j);
                printf("%d", j);
                printf("\t路径长度为:%lf\n", A[i][j]);
            }
}
void Floyd(MGraph g) //弗洛伊德算法从每对顶点之间的最短路径
{
    double A[MAXV][MAXV];
    int path[MAXV][MAXV];
    int i, j, k, n = g.n;
    for (i = 0; i < n; i++) //给A数组置初值
        for (j = 0; j < n; j++)
        {
            A[i][j] = g.edges[i][j];
            path[i][j] = -1;
        }
    for (k = 0; k < n; k++) //计算Ak
    {
        for (i = 0; i < n; i++)
            for (j = 0; j < n; j++)
                if (A[i][j] > (A[i][k] + A[k][j]))
                {
                    A[i][j] = A[i][k] + A[k][j];
                    path[i][j] = k;
                }
    }
    printf("\n输出最短路径:\n");
    DisPath(A, path, n); //输出最短路径
}
void DispMat(MGraph g)
//输出邻接矩阵g
{
    int i, j;
    double INF = 9999;
    for (i = 0; i < g.n; i++)
    {
        for (j = 0; j < g.n; j++)
            if (g.edges[i][j] == INF)
                printf("%3s", "∞");
            else
                printf("%lf", g.edges[i][j]);
        printf("\n");
    }
}
int main()
{
    int i, j;
    MGraph g;
    double INF = 9999;
    // double B[MAXV][6] = {
    //     {0, 5.0, INF, 7, INF, INF},
    //     {INF, 0, 4, INF, INF, INF},
    //     {8, INF, 0, INF, INF, 9},
    //     {INF, INF, 5, 0, INF, 6},
    //     {INF, INF, INF, 5, 0, INF},
    //     {3, INF, INF, INF, 1, 0}};

    double B[MAXV][5] = {
        {0, 50.623800, INF, INF, INF},
        {50.623800, 0, 35.246200, INF, INF},
        {INF, 35.246200, 0, 38.558400, INF},
        {INF, INF, 38.558400,0, INF},
        {INF, INF, INF, INF, 0},
    };

    g.n = 5;
    g.e = 6;
    for (i = 0; i < g.n; i++)
        for (j = 0; j < g.n; j++)
            g.edges[i][j] = B[i][j];
    DispMat(g);
    Floyd(g);
    printf("\n");
    system("pause");
    return 0;
}

执行结果:
在这里插入图片描述

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

源代码杀手

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值