Dijkstra 和 Floyd 装甲进化

关于“你们的”十一

关于你们的十一,反正我是睁一只眼闭一只眼,我没看见好吧。看着朋友圈里的小伙伴各种秀美食,秀美景,秀自己的狗有多蠢,特别是国内的同学朋友,不管上不上班,反正这个假他们肯定是放的。而我却在图书馆的角落默默地写着算法作业。一个为期2周的小 project,看题目也知道了,就俩简单的算法,网上也有很多相关的教程,那我为什么还要写这篇文章呢?

首先,我写的是“装甲”进化版的 Dijkstra & Floyd。用的是 Swift(虽然教授不厌其烦的说 C 是最好的学算法的语言),我本科 CS 用的 C++,实在是不喜欢 C++。我先介绍一下教授的要求吧。

要求太长了,想看一看的点这里,我简化一下:

  • 2 个算法,Dijkstra 和 Floyd 最短路径算法
  • 3 种数据结构,2D Array, 1D Array, Linked List
  • 2 种 Test cases, 一种 Complete Graph, 一种 Sparse Graph
  • 12 张图,对应上面的 2*3*2=12 个程序的时间复杂度

我上课听到这个要求后的第一反应是我要做一个 iOS App。教授是要我们 plot 出 12 张图,我如果不用 iOS 作图,难道还要用 R 像我们上 Machine Learning 那样做图吗?用大数据的软件做这种图,有点蠢的。

Paste_Image.png

BTW, 文章同步更新在我的博客,欢迎访问。

2 个算法,3 个数据结构

关于这两个算法的思路,与 2D array 和 Linked list 版本的教程,我强烈推荐 Geeksforgeeks,这是 2D array 版本的,这是 Linked List 版本的;Floyd 的呢,网上我只找到了 2D array 版本的。那 1D 和 Linked list 怎么办呢?如果你也是喜欢 Swift, 想用学一些 Swift 的数据结构和算法,我强烈推荐这两个网站:Swift Algorithm ClubSwiftStructures

我在这里就讲一讲上面提供的教程里没有的几个程序吧,1D 的 Dijkstra,1DLinked list 的 Floyd。

1D Array

1D 的原因是因为有时候我们的数据会非常大,如果只存取/操作一半的数据,会节省很多的时间和空间。所以这里 1D 的主要思路就是如何将 2D 的数据 map 到 1D 上面。

比如这么一个 2D array:

我们取它的上半部分来操作。在 2D 中 0 行 4 列的数是 6,那么在 1D 中 6 是第几个?是第 3 个(所有 index 都从 0 开始),就有 (0, 4) -> 3,再来一组,第 1 行 2 列的 3,在 1D 中是第 4 个,(1, 2) -> 4。如此计算出 (i, j) -> x,这个 x 是多少?如果数组共有 n 行 n 列,那么 x = (2n - 1- i)*i/2 + j - i - 1。这是求解 1D array 的 Dijkstra 和 Floyd 的关键。我们这次用的是无向图,所以对应的还有 (2*n-1-j)*j/2+i-j-1,判断条件就是 i < j 还是 i > j

我用 Dijkstra 的代码来说明:

func dijkstra1D(_ graph: [Int], _ src: Int)
{
    let V = Int(sqrt(Double(2 * graph.count))) + 1
    var dist = Array(repeating: Int.max, count: V)
    var sptSet = Array(repeating: false, count: V)
    dist[src] = 0
    for _ in 0..<V-1
    {
        let u = minDistance(dist, sptSet, V)
        sptSet[u] = true
        for v in 0..<V {
            let index = (2*V - 1 - u)*u/2+v-u-1
            let temp = (2*V - 1 - v)*v/2+u-v-1
            if sptSet[v] == false && dist[u] != Int.max && dist[u] + graph[index] < dist[v] {
                if u > v {
                    if graph[temp] > 0 && dist[u] + graph[temp] < dist[v] {
                        dist[v] = dist[u] + graph[temp]
                    }
                } else if u < v {
                    if graph[index] > 0 {
                        dist[v] = dist[u] + graph[index]
                    }
                }
            }
        }
    }
//    print4(dist, V)
}

中间的 if u > velse if u < v 里面的就是我上面说到的关键部分了。

Linked List

Dijkstra 的 Linked List 版本上面已经提供了教程了,应该没人比 geeksforgeeks 写的更好了。。我这里稍微讲一下 Floyd 版本的。因为 Floyd 的算法思路还是比较简单的,关键是要遍历很多次整个图,因为 2D 里面它就有 3 个循环。我是弄了个数组,把链表转成了 2D 的来解决了,好像这样不太好,因为这样的话链表的存储优势就完全没了,到最后还是转成了 2D 来做。但用我们教授的话来说就是,每个程序的实现都要按照要求来做,要求不同,实现也会有不同。。。这里我们教授的 input 输入没有很复杂,所以专成 2D 完全可以 😄 下面就是代码的主要部分。

var dist = Array(repeating: Array(repeatElement(Int(Int32.max), count: V)), count: V)
    var mark = Array(repeating: false, count: V)
    var u = 0, v = 0
    for i in 0..<V {
        var cur = graph.array[i].head
        u = i
        mark[u] = true
        while cur != nil {
            v = cur!.dest
            if mark[v] == false {
                dist[u][v] = cur!.weight
                dist[v][u] = cur!.weight
            }
            cur = cur?.next
        }
    }

2 种类型的图

Complete Graph 用教授的话定义就是:

a graph with a link between every pair of nodes

而 Sparse 呢:

a graph with exactly n-1 links

生成这两种图的时候,我们需要生成对应于 2D, 1D 和 linked list 版本的,所以会有 6 个 function。程序有点长,就不放上来了。讲几个注意点,因为是无向图,所以都要注意两边的三角区域都要赋值,比如这个 complete graph 的:

completeGraph[i][j] = rand
completeGraph[j][i] = rand

生成 sparse graph 的时候,我们画一下图就可以知道,从 0 开始,随机选出下一个点,假如是 4*4 的表格,下个点是 3,那么我们再选下个点的时候,需要从 1 和 2 中选,这里我们需要一个类似于 chosenNodes 的标记,如果随机到的是已经选过的,我们有两种方法,一种是在随即一遍,还有一种是取模,很明显取模会快很多,特别是剩下的数不多的时候。下面是生成 sparse graph 的函数:

func sparse(_ n: Int) -> [[Int]]
    {
        var sparseGraph = Array(repeating: Array(repeatElement(0, count: n)), count:n)
        var chosenNodes = Array(repeating: false, count: n)
        var i = 0
        while chosenNodes.contains(false) {
            chosenNodes[i] = true
            if chosenNodes.contains(false) {
                var pickJ = Int(arc4random_uniform(UInt32(n)))
                if chosenNodes[pickJ] == true {
                    while chosenNodes[pickJ] == true {
                        pickJ = (pickJ + 1)%(n)
                    }
                }
                let j = pickJ
                let rand = Int(arc4random_uniform(UInt32(n))) + 1
                sparseGraph[i][j] = rand
                sparseGraph[j][i] = rand
                i = j
            }
        }
        return sparseGraph
    }

基于链表的图的生成很简单,只要根据循环用几次 addEdge() 就可以了。

使用 Charts 作图

关于如何使用 Charts,这里有一篇很好的教程,因为他是在 swift 3 之前写的,所以语法有点小错误,有疑问的可以看看我的 project,当然最权威的还是官方的 github

如您在上面所看到的,我做的是 Complete graph 和 Sparse graph 两种分开的折线图,比较各算法在同样的图上的速度。我本来想过很多种分类作图的方式,感觉还是这样比较直观。gif 试了好几遍没放上来,只好放图了,下面放一张 sparse 的高清图:

Paste_Image.png

如果喜欢我的分享,请点赞💗和关注,也可以 tip 一波安抚一下国庆敲代码的心。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容