基于Folyd算法和迪杰斯特拉算法实现的最短路径问题

最短路径问题

问题引入:在有权值的图结构中(网)的各个顶点中,顶点之间的最短距离是多大?

应用:计算机的网络路由,机器人探路问题,游戏指引系统等等。

关于图的最短路径问题的一般可以分成以下两种:

  1. 求图中每两个顶点之间的最短距离。 – floyd算法(佛洛依德算法)
  2. 求图中由一个顶点出发,到另外各个顶点的最短距离。 – Dijkstra算法(迪杰斯特拉算法)

其实对图中的顶点使用迪杰斯特拉算法即可求得图中每两个顶点之间的最短距离。

Folyd算法 - 佛洛依德算法

  这是一个非常易于理解的算法(嗯…数学解释好像还是有点复杂),简单来看就是使用暴力搜索的方式。怎么样计算两个顶点之间的最短距离呢?先获得这两个顶点的距离,看有没有其他路径比之更短就是了,简单粗暴的方法就是使用循环进行遍历啦。

算法过程如下

  1. 创建一个二维数组D,用来保存顶点i,j之间的最短距离,初始化时,如果两个顶点之间有弧,则其最短距离等于其弧的权重,否则设置一个极大值。
  2. 判断是否有其他顶点k, 是的D[i] [k] + D[k] [j] < D[i] [j],如果有,则更新D[i] [j] = D[i] [k] + D[k] [j]。
  3. 重复2过程,直到遍历完所有的节点, 数组D中保存即为顶点之间的最短距离。

代码实现

#include 
#include 
#include 
using namespace std;

/**
 *	- g 表示由邻接矩阵结构表示的图 - 0坐标不使用, 最大值设置为1e18
 *  - n 表示顶点的数量
 */
void flody(int **g, int n) {
    int D[n + 1][n + 1];	//0索引不使用
    memset(D, 127 / 3, sizeof(D));	//设置为一个极大值
    
    for (int i = 1; i <= n; i++) {		//初始化D数组,将其值初始化为图中边的权重
        for (int j = 1; j <= n; j++) {
            if (g[i][j] != 1e18) {
                D[i][j] = g[i][j];
            }
        }
        D[i][i] = 0;	//顶点自身的权重为0
    }
    
    //三重循环遍历,下面的变量可以看成是顶点的位置
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= n; j++) {
            for (int k = 1; k <= n; k++) {
                if (D[i][j] < D[i][k] + D[k][j]) {	//存在一个中间顶点k是的i,j之间的距离更小, 则替换该值
                    D[i][j] = D[i][k] + D[k][j];
                }
            }
        }
    }
    //最后的D数组可以根据需要返回或者打印出来看一下效果
}

备注:上面其实可以不用另外创建一个D数组初始化操作的,可以发现D数组初始化完成后基本和图的邻接矩阵是一样的,因此可以直接对图进行操作,但是这样会改变邻接举证原本的结构,即图原本的信息如果没有再保存起来可能会丢失,可以根据实际情况来~

时间复杂度:除去一些初始化的操作,程序的主体很明显是那三重循环,所以其时间复杂度为O(n^3)的。

Dijkstra算法 - 迪杰斯特拉算法

  与Flody算法是计算每两个顶点之间的最短距离不同的是,迪杰斯特拉算法计算的是从一个源点出发,计算其到图中其他指定顶点的最短距离

算法过程如下:

  • 图结构G使用邻接矩阵保存,集合U用来保存某个顶点是否已经被确定其最短距离了,如果某个顶点被确定其最短距离,则添加进集合U中;数组D用来保存源点到其他顶点之间的距离(集合U和数组D都会不断更新)。
  1. 初始化U,将v0添加到U中,即U[v0] = true;
  2. 初始化D数组,将v0到其他顶点的距离初始化弧的权值,即D[vi] = G[v0] [vi];没有边直接关联的设置为一个极大值;
  3. 在数组D中找到一条还未被确定最短路径的弧,即D[k] = Min{D[vi]}(vi ∈ V - U);同时将vk加入到U中,U[vk] = true;
  4. 下一步,更新D中源点v0到其他顶点的路径长度,假设为vi,如果U[vi] = false, 同时D[k] + G[k] [i] < D[i],则设置D[i] = D[k] + G[k] [i];
  5. 重复上述过程,当图中所有的顶点都加入U中时,D数组的值为源点到其他顶点的最短距离。

注:上面的vi均用下标值表示顶点

代码实现

#include 
#include 
#include 
using namespace std;

int getMinEdge(int *D, int n, int *U) {
    int min = 1 << 30, index = 0;
    for (int i = 1; i <= n; i++) {
        if (U[i] == 0 && D[i] < min) {
            min = D[i];
            index = i;
        }
    }
    return index;
}


int main() {
    cin >> n >> m;
    int a[n + 1][n + 1], D[n + 1];
    memset(a, 127/3, sizeof(a));
    int x, y, z;
    for (int i = 1; i <= m; i++) {
        cin >> x >> y >> z;
        a[x][y] = z;
        if (x == 1) {	//假设顶点v1为源点, 初始化D数组
            D[y] = z;	
        } else {
            D[y] = 1 << 30;	//其他和v1无直接关联的边的设置为一个极大值
        }
    }
    int U[n + 1] = {0};		//用0来表示该顶点还没有被确定最短距离
    U[1] = 1;				//1 表示该顶点已经被确定最短距离了v1是源点的最短距离一开始就能确定下来
    
    for (int i = 1; i <= n; i++) {
        int k = getMin(D, n, U);
        U[k] = 1;
        for (int j = 1; j <= n; j++) {
            if (U[j] == 0 && a[k][j] + D[k] < D[j]) {	//更新源点到其他顶点的最短距离
                D[j] = a[k][j] + D[k];
            }
        } 
    }
    
    for (int i = 1; i <= n; i++) {
        printf("%d\t", D[i]);
    }
}

  时间复杂度:程序中除去那些初始化的操作,主体就是两个for循环了,因此其时间复杂度为O(n^2),如果对每一个顶点使用迪杰斯特拉算法,则其时间复杂度为O(n**3),和Flody算法一样。

测试用例

输入:
4 4
1 2 4
2 3 7
2 4 1
3 4 6
输出:0 4 11 5

你可能感兴趣的:(数据结构,数据结构,算法,图论)