您的位置:首页 > 理论基础 > 数据结构算法

数据结构 18 图 最短路径-迪杰斯特拉最短算法

2014-01-03 20:01 387 查看
Dijkstra算法 最短路径算法











#include <stdio.h>
#include <stdlib.h>

#define VNUM 5
#define MV 65536

// 比较直接和间接路径会不会变短 就是中转

int P[VNUM];    // 记录了前一个顶点的索引
int Dist[VNUM]; // 保存了顶点sv边的权值
int Mark[VNUM]; // 记录某个顶点是否被标记

int Matrix[VNUM][VNUM] =
{
{0, 10, MV, 30, 100},
{MV, 0, 50, MV, MV },
{MV, MV, 0, MV, 10 },
{MV, MV, 20, 0, 60 },
{MV, MV, MV, MV, 0 }
};

void Dijkstra(int sv) // O(n*n)
{
int i = 0;
int j = 0;

if((0 <= sv) && (sv < VNUM))
{
for(i = 0; i < VNUM; i++)       // 初始化
{
Dist[i] = Matrix[sv][i];    // 保存顶点的所有权值
P[i] = sv;                  // 保存顶点为sv
Mark[i] = 0;                // 设置所有顶点的状态为0 没有被标记
}

Mark[sv] = 1;

for(i = 0; i < VNUM; i++)       // 遍历每个顶点
{
int min = MV;
int index = -1;

for(j = 0; j < VNUM; j++)   // 1.遍历这个顶点的所有边 找到某个未被标记的顶点边权值的最小值 并且标记这条边另外一个顶点作为新顶点
{
if( !Mark[j] && (Dist[j]) < min )
{
min = Dist[j];
index = j;          // 这里的index表示原来顶点权值最小边对应的另外一个顶点的index 即新顶点
}
}

if( index > -1 )
{
Mark[index] = 1;        // 设置新顶点状态为1 标记
}
for(j = 0; j < VNUM; j++)   // 2.更新边的数组
{
// 这里可以见算法精髓的那副三角图的关系 比较sv直接到j的大小和通过index顶点到j的大小 将较小值保存到Dist数组对应j位置
if( !Mark[j] && (min + Matrix[index][j] < Dist[j]))
{
Dist[j] = min + Matrix[index][j];
P[j] = index;       // P记录了前一个顶点 设置P的j位置保存index
}
}
}
for(i = 0; i < VNUM; i++)
{
int p = i;
printf("%d -> %d : %d\n", sv, p, Dist[p]);

do
{
printf("%d <- ", p);
p = P[p];
}while( p != sv );

printf("%d\n", p);
}
}
}
int main()
{
Dijkstra(0);

return 0;
}


自己实现

#include <stdio.h>
#include <stdlib.h>
#include "graph.h"

#define SIZE 5
#define NOW 65536

int dist[SIZE];     // 记录了从某个顶点到各个顶点的最短路径 即权值
int visited[SIZE];  // 记录某个顶点是否被访问过
int record[SIZE];   // 记录访问过的最短路径节点

int matrix[SIZE][SIZE] =
{
{0, 10, NOW, 30, 100},
{NOW, 0, 50, NOW, NOW},
{NOW, NOW, 0, NOW, 10},
{NOW, NOW, 20, 0, 60},
{NOW, NOW, NOW, NOW, 0}
};

void dijkstra(int vertex)
{
int i = 0;
int j = 0;

if(0 <= vertex && vertex < SIZE)
{
for(i = 0; i < SIZE; ++i)
{
dist[i] = matrix[vertex][i]; // 保存顶点vertex的所有权
visited[i] = 0;              // 没有顶点被访问
record[i] = vertex;
}
visited[vertex] = 1;

for(i = 0; i < SIZE; ++i)        // 循环遍历每个节点
{
int minWeight = NOW;
int otherVertex = -1;

// 1.知道vertex个的最小权 并且记录另外一个新连的节点
for(j = 0; j < SIZE; ++j) // 这里曾经把j误写成i 这个错误太难发现了 陷入死循环
{
if(0 == visited[j] && dist[j] < minWeight) // 得到vertex的最小权
{
minWeight = dist[j];
otherVertex = j;                       // 最小权对应的另外一个顶点
}
}
if(otherVertex > -1)
{
visited[otherVertex] = 1; // 标记新的顶点
}

// 2.访问另外一个节点otherVertex 利用三角关系找到vertex到otherVertex最小权 即到第3个顶点的最小距离
for(j =  0; j < SIZE; ++j)
{
// vertex到index顶点相连的第3个顶点的距离 这是一个三角关系
if( (0 == visited[j]) && ((minWeight + matrix[otherVertex][j]) < dist[j]) )
{
dist[j] = minWeight + matrix[otherVertex][j];
record[j] = otherVertex;
}
}
}

// 打印结果
for(i = 0; i < SIZE; ++i)
{
int p = i;
printf("%d -> %d : %d\n", vertex, p, dist[p]); // 打印从vertex到各个顶点的最短距离

do
{
printf("%d <- ", p);
p =record[p];
}while(p != vertex);

printf("%d\n", p);
}
}
}

int main()
{
dijkstra(0);

return 0;
}


Floyd 算法











自己的实现

#include <stdio.h>
#include <stdlib.h>
#include "graph.h"

#define SIZE 5
#define NOW 65536

int record[SIZE][SIZE];     // 记录两个顶点间经过的中间节点 初始化为第二个节点
int visited[SIZE][SIZE];    // 记录两个顶点间的权

int matrix[SIZE][SIZE] =
{
{0, 10, NOW, 30, 100},
{NOW, 0, 50, NOW, NOW},
{NOW, NOW, 0, NOW, 10},
{NOW, NOW, 20, 0, 60},
{NOW, NOW, NOW, NOW, 0}
};

void floyd()
{
int i = 0;
int j = 0;
int k = 0;

// 1.初始化 visited保存了 所有路径的权 record保存了最小权
for(i = 0; i < SIZE; ++i)
{
for(j = 0; j < SIZE; ++j)
{
visited[i][j] = matrix[i][j];
record[i][j] = j;
}
}

// 2.中转
for(i = 0; i < SIZE; ++i)
{
for(j = 0; j < SIZE; ++j)
{
for(k = 0; k < SIZE; ++k)
{
if(visited[j][i] + visited[i][k] < visited[j][k]) // j-i-k < j-k 核心就是中转的思维
{
visited[j][k] = visited[j][i] + visited[i][k];
record[j][k] = record[j][i]; // 将i保持到其中 用于打印
}
}
}
}

// 3.打印
for(i = 0; i < SIZE; ++i)
{
for(j = 0; j < SIZE; ++j)
{
int p = -1;

printf("%d -> %d : %d \n", i, j, visited[i][j]);
printf("%d", i);

p = i;

do
{
p = record[p][j]; // 得到p-j的中间节点 继续打印出全部经过的节点

printf("-> %d", p);
}while(p != j); // 因为初始值为j

printf("\n");
}
}
}

int main()
{
floyd();

return 0;
}


唐老师的实现

#include <stdio.h>
#include <stdlib.h>

#define VNUM 5
#define MV 65536

// 比较直接和间接路径会不会变短 就是中转

int P[VNUM][VNUM];   // 记录了顶点的索引
int A[VNUM][VNUM];

int Matrix[VNUM][VNUM] =
{
{0, 10, MV, 30, 100},
{MV, 0, 50, MV, MV },
{MV, MV, 0, MV, 10 },
{MV, MV, 20, 0, 60 },
{MV, MV, MV, MV, 0 }
};

void Floyd() // O(n*n*n)
{
int i = 0;
int j = 0;
int k = 0;

for(i = 0; i < VNUM; i++)        // 初始化
{
for(j = 0; j < VNUM; j++)
{
A[i][j] = Matrix[i][j];
P[i][j] = j;             // 从i 到 j的最短路径 第二个顶点的值
}
}

for(i = 0; i < VNUM; i++)        // 思想就是中转 每一个顶点都拿来中转
{
for(j = 0; j < VNUM; j++)
{
for(k = 0; k < VNUM; k++)
{
if( (A[j][i] + A[i][k]) < A[j][k] ) // 中转后小于原来的值 j->i + i->k  < j->k
{
A[j][k] = A[j][i] + A[i][k];    // 更新
P[j][k] = P[j][i];
}
}
}
}

for(i = 0; i < VNUM; i++) // 打印
{
for(j = 0; j < VNUM; j++)
{
int p = -1;

printf("%d -> %d : %d\n", i, j, A[i][j]);

printf("%d", i);

p = i;

do
{
p = P[p][j];

printf(" -> %d", p);
} while (p != j);

printf("\n");
}
}
}

int main()
{
Floyd();

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: