IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> Dijkstra和Floyd -> 正文阅读

[数据结构与算法]Dijkstra和Floyd

问题简介

最短路径问题:
从图中的某个顶点出发到达另外一个顶点的所经过的边的权重和最小的一条路径,称为最短路径。
对于无权图,可以假设每条边的权重是1。
这里分别介绍一下Dijkstra算法和Floyd算法。

Dijkstra算法

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,采用广度优先遍历,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

示例

我们需要求得上图中 V 1 V_1 V1?到各个顶点的最短距离( V = { V 1 , V 2 , V 3 , V 4 , V 5 , V 6 } V=\{V_1, V_2, V_3, V_4, V_5, V_6\} V={V1?,V2?,V3?,V4?,V5?,V6?})。首先我们初始化两个集合, T T T S S S T T T为已经求得距离的顶点的集合, S S S为剩余顶点。初始的时候, T = { V 1 } T=\{V_1\} T={V1?} S = { V ? T } S=\{V-T\} S={V?T}
再初始化一个 d i s t dist dist数组,代表目前已经求得的最短距离:

然后。我们要找到与集合 V 1 V_1 V1?中元素距离最近的顶点,便是 V 3 V_3 V3?,距离为10。那么此时的“10”便是确定值了(因为目前离 V 1 V_1 V1?顶点最近的是 V 3 V_3 V3?顶点,并且这个图所有的边都是正数,那么肯定不可能通过第三个顶点中转,使得 V 1 V_1 V1?顶点到 V 3 V_3 V3?顶点的路程进一步缩短了。假如 V 1 V_1 V1?通过 V x V_x Vx?再到 V 3 V_3 V3?距离更短,这与 V 1 V_1 V1?到各顶点距离最短的是 V 3 V_3 V3?矛盾,所以并不存在 V x V_x Vx?),我们将 V 3 V_3 V3?加入到集合 T T T中。
此时的 T T T被更新过了,那么代表 d i s t dist dist数组中的元素也要进行更新,可以发现顶点 V 3 V_3 V3?有一条弧为 < V 3 , V 4 > <V_3, V_4> <V3?,V4?>,且 V 1 ? > V 3 ? > V 4 V_1->V_3->V_4 V1??>V3??>V4?的距离为60,比原来的 V 1 ? > V 4 V_1->V_4 V1??>V4?的距离( ∞ \infty 代表无法到达)更小,所以将 d i s t dist dist数组进行更新:

蓝色的表明已经是确定值,不会再更改,红色表明该距离刚才才更新过。此时,我们重复上述操作,在未标蓝的距离中( ∞ , 60 , 30 , 100 \infty, 60, 30, 100 ,60,30,100)找到最小的距离,为 V 5 V_5 V5?,距离是“30”。那么,将 V 5 V_5 V5?加入 T T T中,通过 V 5 V_5 V5?的边再次更新 d i s t dist dist

然后继续循环:

这便是顶点 V 1 V_1 V1?到各个顶点的最短距离,其中,到 V 2 V_2 V2?的距离为 ∞ \infty 代表着顶点 V 1 V_1 V1?无法到达 V 2 V_2 V2?

代码

#include <iostream>
#include <cstring>
using namespace std;
#define INF 0x3f3f3f3f
const int maxn = 1e3 + 7;
int dist[maxn];
int edge[maxn][maxn];
bool visited[maxn];
int v, n, m;

void init(){
    memset(dist, INF, sizeof dist); // 源点到任意点的距离初始化为无穷大;
    memset(edge, INF, sizeof edge);
    memset(visited, false, sizeof visited);
}

void dijkstra(){
    for(int i = 0; i <= n; i++){
        dist[i] = edge[v][i]; // 找到和源点相连的顶点,然后将距离填入dist;
    }
    dist[v] = 0; // 源点自己到自己距离为0;
    visited[v] = true;
    for(int i = 2; i <= n; i++){
        int temp = INF;
        int u = v;
        for(int j = 1; j <= n; j++){ // 找到当前与源点相连,距离最小的顶点;
            if(!visited[j] && dist[j] < temp){
                u = j;
                temp = dist[j];
            }
        }
        visited[u] = true; // 标记这个点进入集合T;
        for(int j = 1; j <= n; j++){
            if (!visited[j] && edge[u][j] < INF){ // 在u的周围寻找边;
                int newdist = dist[u] + edge[u][j];
                if (newdist < dist[j]){ //要是能够使得dist数组变小,便更新dist;
                    dist[j] = newdist;
                }
            }
        }
    }
}

int main(){
    while(scanf("%d%d", &n, &m) && n && m){
        init();
        int x, y, t;
        for(int i = 1; i <= m; i ++){
            cin>>x>>y>>t;
            edge[x][y] = t;
        }
        v = 1; // v为源点;
        dijkstra();
        for(int k = 1; k <= n; k++){
            if (dist[k] == INF){
                cout<<"INF"<<' ';
            }else{
                cout<<dist[k]<<' ';
            }
        }
    }
}

Floyd算法

Floyd算法相对来说更容易理解一点。
首先初始化一个二维数组,保存目前顶点两两之间的距离,无法到达的顶点设为 ∞ \infty
这里先贴代码:

#include <iostream>
#include <cstring>
#define INF 0x3f3f3f3f

using namespace std;
const int maxn = 1e3 + 7;
int G[maxn][maxn]; //存图;
int path[maxn][maxn]; //记录路径;
int n, m;

void init(){
    memset(G, INF, sizeof G);
    memset(path, -1, sizeof path);
    for ( int i = 0; i < maxn; i ++){
        G[i][i] = 0; //自己到自己的距离为0;
    }
}

void floyd(){
    for (int k = 1; k <= n; k ++){ //中间值;
        for (int i = 1; i <= n; i++){ //横坐标;
            for (int j = 1; j<= n; j++){ //纵坐标;
                if (G[i][j] > G[i][k] + G[k][j]){
                    G[i][j] = G[i][k] + G[k][j]; //如果经过k顶点的距离更短,就更新;
                    path[i][j] = k;
                }
            }
        }
    }
}

void print_path(int u, int v){
    cout<<u<<"->";
    while (path[u][v] != -1){
        u = path[u][v];
        cout<<u<<"->";
    }
    cout<<v<<endl;
}

int main(){
    while (scanf("%d%d", &n, &m) && n && m){
        init();
        int x, y, t;
        for (int i = 0; i < m; i++){
            scanf("%d%d%d", &x, &y, &t);
            G[x][y] = t;
        }
        floyd();
        for (int q = 1; q <= n; q++){
            if (G[1][q] == INF){
                cout<<"INF"<<' '<<endl;
                cout<<"无法到达"<<endl;
            }else{
                cout<<G[1][q]<<' '<<endl;
                print_path(1, q);
            }
        }
    }
}

搞懂Floyd算法的关键就是代码中的k的循环,i和j就是两个顶点,k代表i到j允许经过顶点k,然后计算通过顶点k时的距离是否更短,是的话便更新。然后k再循环到下一个顶点,一直到k循环完毕,便意味着i到j,允许经过所有顶点时,最短的距离。

Dijkstra和Floyd的区别

总结:

  1. Dijkstra不能处理负权图,Flyod能处理负权图;
  2. Dijkstra处理单源最短路径,Flyod是处理多源最短路径;
  3. Dijkstra时间复杂度为 O ( n 2 ) O(n^2) O(n2),Floyd时间复杂度为 O ( n 3 ) O(n^3) O(n3)

所以:所以题目中如果是单源点正权图,就用Dijkstra;如果是任意两个点之间的最短路径或者是负权图,就用Floyd。

  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-04-06 16:19:17  更:2022-04-06 16:20:06 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 9:56:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码