swift 文章收集人工智能技术杂谈iOS学习记录

Swift最短路径之Bellman-Ford和Bellman-F

2016-08-02  本文已影响219人  我系哆啦

在讲Bellman-Ford之前先介绍另一种存储图的方法:邻接表。

邻接表

先上数据,以下是一个包含4个顶点,5条边的图。n = 4(顶点编号为1~4), m = 5,接下来每一行3个数xyz,表示顶点x到顶点y的边的权值为z。现在用邻接表来存储一个读出这个图:
<pre>
4 5
1 4 9
2 4 6
1 2 5
4 3 8
1 3 7
</pre>

Paste_Image.png Paste_Image.png

<pre>
for i in 0...4 {
var k = first[i]
while k != -1 {
print("(u[k]) (v[k]) (w[k])")
k = next[k]
}
}
</pre>

<pre>
//4个顶点,5条边,如下【1,4,9】表示顶点1到顶点4,权重为9的一条边
let map = [[1,4,9],
[2,4,6],
[1,2,5],
[4,3,8],
[1,3,7]]
//u,v,w数组的大小要比图的边的数目大1
var u:[Int] = Array.init(repeatElement(0, count: map.count + 1))
var v:[Int] = Array.init(repeatElement(0, count: map.count + 1))
var w:[Int] = Array.init(repeatElement(0, count: map.count + 1))
//first和next数组的大小要比图的顶点的数目大1
var first:[Int] = Array.init(repeatElement(-1, count: 5))
var next:[Int] = Array.init(repeatElement(-1, count: 5))

//创建邻接表
func creatMap(map:[Array<Int>]) {

for i in map.indices {
    
    u[i] = map[i][0]
    v[i] = map[i][1]
    w[i] = map[i][2]
    
    next[i] = first[u[i]]
    first[u[i]] = i
}

}

//遍历邻接表

func listMap() {

for i in 0...4 {
    var k = first[i]
    while k != -1 {
       print("\(u[k]) \(v[k]) \(w[k])")
        k = next[k]
    }
}

}

creatMap(map: map)
listMap()
/*
1 3 7
1 2 5
1 4 9
2 4 6
4 3 8
*/
</pre>

Bellman-Ford算法

Bellman-Ford算法可以解决带有负权边(边的权值为负数)的图。算法非常简单:
<pre>
for _ in 1..<(n-1) {
for i in 1...m {
if dist[v[i]] > (dist[u[i]] + w[i]) {
dist[v[i]] = (dist[u[i]] + w[i])
}
}
</pre>

<pre>
if dist[v[i]] > (dist[u[i]] + w[i]) {
dist[v[i]] = (dist[u[i]] + w[i])
}
</pre>

这两句代码的意思其实松弛源点到v[i]这条边。

Paste_Image.png

接下来用一个dist数组来存储1号顶点到所有顶点的距离,然后开始进行4轮对所有的边进行松弛

Paste_Image.png

这里有个问题,需要进行多少轮松弛呢,答案是最多n-1轮,n是顶点的个数,注意是最多!!如上图所示,进行第4轮松弛后,结果并未更改。因此,我们可以在这个地方进行优化。添加一个变量来标记在本轮松弛中是否发生了变化,如果没有发生变化,则可以提前跳出循环。

<pre>
let max:Int = 10000
//5个顶点,5条边,顶点从1开始算,边也从1开始算
let n = 5, m = 5
let map = [[0,0,0],
[2,3,2],
[1,2,-3],
[1,5,5],
[4,5,2],
[3,4,3]]
//u,v,w数组的大小要比图的边的数目大1
var u:[Int] = Array.init(repeatElement(0, count: 6))
var v:[Int] = Array.init(repeatElement(0, count: 6))
var w:[Int] = Array.init(repeatElement(0, count: 6))

func bellmanFord(map:[Array<Int>]) {

for i in 1...5 {
    
    u[i] = map[i][0]
    v[i] = map[i][1]
    w[i] = map[i][2]
}

var dist:[Int] = Array.init(repeatElement(max, count: 6))
dist[1] = 0

var check:Int //检测dist是否有更新

for _ in 1..<(n-1) {
    check = 0
    for i in 1...m {
        if dist[v[i]] > (dist[u[i]] + w[i]) {
            dist[v[i]] = (dist[u[i]] + w[i])
            
            check = 1
        }
    }
    
    if check == 0 { //如果数组dist没有更新,提前退出循环结束算法
        break
    }
}

print(dist)

}
bellmanFord(map: map) //0, -3, -1, 2, 4
</pre>

Bellman-Ford的队列优化算法

在上面介绍的Bellman-Ford算法中,在每实施一次松弛操作之后,就会有一些顶点已经求得其最短路径,此后,这些顶点的最短路径的值就会一直保持不变,不再受到后续松弛操作的影响,但是每次还要判断是否需要松弛,这里浪费了时间。因此,我们可以考虑每次仅针对最短路径值发生了变化的顶点的所有出边执行松弛操作。这就是Bellman-Ford的队列优化算法。
那么,如何知道当前哪些点的最短路程发生了变化呢?
用一个队列来维护这些点,每次选取队首顶点u,对顶点u的所有出边进行松弛操作。假如有一条u->v的边,如果松弛成功(dist[v] > dist[v] + e[u][v]),则将顶点v放入队尾,需要注意的是,同一个顶点不能同时在队列中出现多次,但是允许一个顶点在出队后再入队。在将顶点u的所有的出边松弛完毕后,就将顶点u出队。接下来不断从队列中取出新的队首顶点进行如上操作,直至队列为空。这就是Bellman-Ford的队列优化算法。下面举一个例子。

Paste_Image.png Paste_Image.png Paste_Image.png Paste_Image.png

完整代码如下:

<pre>
let max:Int = 10000
//5个顶点,7条边,顶点从1开始算,边也从1开始算
let map = [[0,0,0],
[1,2,2],
[1,5,10],
[2,3,3],
[2,5,7],
[3,4,4],
[4,5,5],
[5,3,6]]
//u,v,w数组的大小要比图的边的数目大1
var u:[Int] = Array.init(repeatElement(0, count: 8))
var v:[Int] = Array.init(repeatElement(0, count: 8))
var w:[Int] = Array.init(repeatElement(0, count: 8))
//first和next数组的大小要比图的顶点的数目大1
var first:[Int] = Array.init(repeatElement(-1, count: 9))
var next:[Int] = Array.init(repeatElement(-1, count: 9))

//创建邻接表
func creatMap(map:[Array<Int>]) {

for i in 1...7 {
    
    u[i] = map[i][0]
    v[i] = map[i][1]
    w[i] = map[i][2]
    
    next[i] = first[u[i]]
    first[u[i]] = i
}

}

func bellman_ford(map:[Array<Int>]) {

creatMap(map: map)

var dist:[Int] = Array.init(repeatElement(max, count: 6))
dist[1] = 0

var book:[Int] = Array.init(repeatElement(0, count: 8))
book[1] = 1

var queue:[Int] = Array.init(repeatElement(0, count: 100))
var head = 1,tail = 1

queue[tail] = 1
tail+=1

var k = 0
while head < tail {
    k = first[queue[head]]
    while k != -1 {
        if dist[v[k]] > (dist[u[k]] + w[k]) {
            dist[v[k]] = dist[u[k]] + w[k]
            
                if book[v[k]] == 0 {
                queue[tail] = v[k]
                    tail+=1
            }
        }
        k = next[k]
    }
    book[queue[head]] = 0
    head+=1
}

print(dist)

}

bellman_ford(map: map) //0, 2, 5, 9, 9
</pre>

上一篇 下一篇

猜你喜欢

热点阅读