# 用Swift实现Dijkstra算法

### 快速介绍

#### Dijkstra算法

1. 找到权重最小且没有访问过的顶点路径
2. 标记已访问过,并继续访问从来没有访问过的顶点
3. 重复上述步骤

### Swift 代码实现

#### Node

``````class Node {
var visited = false
var connections:[Connection] = []
}
``````

Node更像是一个属性(算法需要),表示我们是否已经访问过它,还有一个连接其他顶点的边的数组

#### Connection

``````class Connection {
public let to:Node
public let weight:Int
public init(to node:Node, weight:Int) {
assert(weight>=0, "weight has to be equal or greater than zero"
self.to = node
self.weight = weight
}
}
``````

#### Path

``````class Path {
public let cumulativeWeight:Int
public let node:Node
public let previousPath:Path?

init(to node:Node, via connection:Connection? = nil, previousPath path:Path? = nil) {
if let previousPath = path,
let viaConnection = connection {
self.cumulativeWeight = viaConnection.weight + previousPath.cumulativeWeight
} else {
self.cumulativeWeight = 0
}
}
}
``````

## 算法

``````func shortestPath(source: Node, destination: Node) -> Path? {
var frontier: [Path] = [] {
didSet { frontier.sort { return \$0.cumulativeWeight < \$1.cumulativeWeight } } // the frontier has to be always ordered
}

frontier.append(Path(to: source)) // the frontier is made by a path that starts nowhere and ends in the source

while !frontier.isEmpty {
let cheapestPathInFrontier = frontier.removeFirst() // getting the cheapest path available
guard !cheapestPathInFrontier.node.visited else { continue } // making sure we haven't visited the node already

if cheapestPathInFrontier.node === destination {
return cheapestPathInFrontier // found the cheapest path 😎
}

cheapestPathInFrontier.node.visited = true

for connection in cheapestPathInFrontier.node.connections where !connection.to.visited { // adding new paths to our frontier
frontier.append(Path(to: connection.to, via: connection, previousPath: cheapestPathInFrontier))
}
} // end while
return nil // we didn't find a path 😣
}
``````

#### 3. 重复步骤

while循环现在完成了! 所以我们真的只需要重复上述两个步骤

### Swift Playground

``````class Node {
var visited = false
var connections: [Connection] = []
}

class Connection {
public let to: Node
public let weight: Int

public init(to node: Node, weight: Int) {
assert(weight >= 0, "weight has to be equal or greater than zero")
self.to = node
self.weight = weight
}
}

class Path {
public let cumulativeWeight: Int
public let node: Node
public let previousPath: Path?

init(to node: Node, via connection: Connection? = nil, previousPath path: Path? = nil) {
if
let previousPath = path,
let viaConnection = connection {
self.cumulativeWeight = viaConnection.weight + previousPath.cumulativeWeight
} else {
self.cumulativeWeight = 0
}

self.node = node
self.previousPath = path
}
}

extension Path {
var array: [Node] {
var array: [Node] = [self.node]

var iterativePath = self
while let path = iterativePath.previousPath {
array.append(path.node)

iterativePath = path
}

return array
}
}

func shortestPath(source: Node, destination: Node) -> Path? {
var frontier: [Path] = [] {
didSet { frontier.sort { return \$0.cumulativeWeight < \$1.cumulativeWeight } } // the frontier has to be always ordered
}

frontier.append(Path(to: source)) // the frontier is made by a path that starts nowhere and ends in the source

while !frontier.isEmpty {
let cheapestPathInFrontier = frontier.removeFirst() // getting the cheapest path available
guard !cheapestPathInFrontier.node.visited else { continue } // making sure we haven't visited the node already

if cheapestPathInFrontier.node === destination {
return cheapestPathInFrontier // found the cheapest path 😎
}

cheapestPathInFrontier.node.visited = true

for connection in cheapestPathInFrontier.node.connections where !connection.to.visited { // adding new paths to our frontier
frontier.append(Path(to: connection.to, via: connection, previousPath: cheapestPathInFrontier))
}
} // end while
return nil // we didn't find a path 😣
}

// **** EXAMPLE BELOW ****
class MyNode: Node {
let name: String

init(name: String) {
self.name = name
super.init()
}
}

let nodeA = MyNode(name: "A")
let nodeB = MyNode(name: "B")
let nodeC = MyNode(name: "C")
let nodeD = MyNode(name: "D")
let nodeE = MyNode(name: "E")

nodeA.connections.append(Connection(to: nodeB, weight: 1))
nodeB.connections.append(Connection(to: nodeC, weight: 3))
nodeC.connections.append(Connection(to: nodeD, weight: 1))
nodeB.connections.append(Connection(to: nodeE, weight: 1))
nodeE.connections.append(Connection(to: nodeC, weight: 1))

let sourceNode = nodeA
let destinationNode = nodeD

var path = shortestPath(source: sourceNode, destination: destinationNode)

if let succession: [String] = path?.array.reversed().flatMap({ \$0 as? MyNode}).map({\$0.name}) {
print("🏁 Quickest path: \(succession)")
} else {
print("💥 No path between \(sourceNode.name) & \(destinationNode.name)")
}
``````