Chapter 36: Graphs
前言
圖是由點跟邊所構成的。本章介紹兩種不同方式來組成graph。
大綱
Weighted graphs
Directed graphs
Undirected graphs
Common operations
Defining a vertex
Defining an edge
Adjacency list
Implementation
Creating a vertex
Creating a directed edge
Creating an undirected edge
Retrieving the outgoing edges from a vertex
Retrieving the weight of an edge
Visualizing the adjacency list
Building a network
Adjacency matrix
Implementation
Creating a Vertex
Creating edges
Retrieving the outgoing edges from a vertex
Retrieving the weight of an edge
Visualize an adjacency matrix
Building a network
Graph analysis
Weighted graphs
邊具有權重
Directed graphs: 邊具有方向性
Undirected graphs: 無向邊可以看成雙向的意思
Common operations
常見的操作
加入點
加入方向邊
加入無向邊
取得於某個點相連的所有邊
取得某兩點之間邊的權重
public enum EdgeType {
case directed
case undirected
}
public protocol Graph {
associatedtype Element
// 建立一個頂點並加入圖中
func createVertex(data: Element) -> Vertex<Element>
// 建立一個有向邊並加入圖中
func addDirectedEdge(from source: Vertex<Element>,
to destination: Vertex<Element>,
weight: Double?)
// 建立一個無向邊並加入圖中
func addUndirectedEdge(between source: Vertex<Element>,
and destination: Vertex<Element>,
weight: Double?)
// 利用EdgeType來決定邊並加入圖中
func add(_ edge: EdgeType, from source: Vertex<Element>,
to destination: Vertex<Element>,
weight: Double?)
// 取得從某個頂點出發的所有邊集合
func edges(from source: Vertex<Element>) -> [Edge<Element>]
// 取得任兩個頂點之間的所有邊的權重和
func weight(from source: Vertex<Element>,
to destination: Vertex<Element>) -> Double?
}
Defining a vertex
A vertex has a unique index within its graph and holds a piece of data
The Hashable protocol inherits from Equatable, so you must also satisfy this protocol's requirement. The compiler can synthesize conformance to both protocols, which is why the extensions above are empty.
// A vertex has a unique index within its graph and holds a piece of data.
public struct Vertex<T> {
public let index: Int
public let data: T
}
// The Hashable protocol inherits from Equatable, so you must also satisfy this protocol's requirement. The compiler can synthesize conformance to both protocols, which is why the extensions above are empty.
extension Vertex: Hashable where T: Hashable {}
extension Vertex: Equatable where T: Equatable {}
extension Vertex: CustomStringConvertible {
public var description: String {
return "\(index): \(data)"
}
}
Defining an edge
邊可能具有權重,並且連接兩個點
public struct Edge<T> {
public let source: Vertex<T>
public let destination: Vertex<T>
public let weight: Double?
}
Adjacency list


Init
public class AdjacencyList<T: Hashable>: Graph {
// 利用Dict來存相關訊息,因為要將利用T來當作Dict的key, 所以T必須是Hashable
private var adjacencies: [Vertex<T>: [Edge<T>]] = [:]
public init() {}
}
Creating a vertex
public func createVertex(data: T) -> Vertex<T> {
let vertex = Vertex(index: adjacencies.count, data: data)
adjacencies[vertex] = []
return vertex
}
Creating a directed edge
public func addDirectedEdge(from source: Vertex<T>,
to destination: Vertex<T>,
weight: Double?) {
let edge = Edge(source: source, destination: destination, weight: weight)
adjacencies[source]?.append(edge)
}
Creating an undirected edge
extension Graph {
// 無向邊就是雙向邊
public func addUndirectedEdge(between source: Vertex<Element>,
and destination: Vertex<Element>,
weight: Double?) {
addDirectedEdge(from: source, to: destination, weight: weight)
addDirectedEdge(from: destination, to: source, weight: weight)
}
public func add(_ edge: EdgeType, from source: Vertex<Element>,
to destination: Vertex<Element>,
weight: Double?) {
switch edge {
case .directed:
addDirectedEdge(from: source, to: destination, weight: weight)
case .undirected:
addUndirectedEdge(between: source, and: destination, weight: weight)
}
}
}
Retrieving the outgoing edges from a vertex
public func edges(from source: Vertex<T>) -> [Edge<T>] {
return adjacencies[source] ?? []
}
Retrieving the weight of an edge
public func weight(from source: Vertex<T>, to destination: Vertex<T>) -> Double? {
return edges(from: source)
.first { $0.destination == destination }?
.weight
}
Adjacency matrix


Init
public class AdjacencyMatrix<T>: Graph {
private var vertices: [Vertex<T>] = []
// weight是個martix
private var weights: [[Double?]] = []
public init() {}
}
Creating a Vertex
public func createVertex(data: T) -> Vertex<T> {
let vertex = Vertex(index: vertices.count, data: data)
vertices.append(vertex)
// 在matrix上把新增的點對應的列跟行都設成nil
for i in 0..<weights.count {
weights[i].append(nil)
}
let row = [Double?](repeating: nil, count: vertices.count)
weights.append(row)
return vertex
}


Creating edges
public func addDirectedEdge(from source: Vertex<T>, to destination: Vertex<T>, weight: Double?) {
// 在martix對應位置上加上weight
weights[source.index][destination.index] = weight
}
Retrieving the outgoing edges from a vertex
public func edges(from source: Vertex<T>) -> [Edge<T>] {
var edges: [Edge<T>] = []
for column in 0..<weights.count {
if let weight = weights[source.index][column] {
edges.append(Edge(source: source, destination: vertices[column], weight: weight))
}
}
return edges
}
Retrieving the weight of an edge
public func weight(from source: Vertex<T>, to destination: Vertex<T>) -> Double? {
return weights[source.index][destination.index]
}
Graph analysis

If there are few edges in your graph, it is considered a sparse graph, and an adjacency list would be a good fit.
If your graph has lots of edges, it’s considered a dense graph, and an adjacency matrix would be a better fit as you’d be able to access your weights and edges far more quickly.
Last updated
Was this helpful?