Skip to content

Commit ecd1ee3

Browse files
committed
Improve graph
1 parent f15eb77 commit ecd1ee3

32 files changed

+355
-133
lines changed

Graph/AdjacencyList.swift

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
private var uniqueIDCounter = 0
2+
3+
public struct Edge<T> {
4+
public let from: Vertex<T>
5+
public let to: Vertex<T>
6+
public let weight: Double
7+
}
8+
9+
public struct Vertex<T> {
10+
public var data: T
11+
public let uniqueID: Int
12+
13+
private(set) public var edges: [Edge<T>] = [] // the adjacency list
14+
15+
public init(data: T) {
16+
self.data = data
17+
uniqueID = uniqueIDCounter
18+
uniqueIDCounter += 1
19+
}
20+
21+
// Creates the directed edge: self -----> dest
22+
public mutating func connectTo(destinationVertex: Vertex<T>, withWeight weight: Double = 0) {
23+
edges.append(Edge(from: self, to: destinationVertex, weight: weight))
24+
}
25+
26+
// Creates an undirected edge by making two directed edges: self ----> other, and other ----> self
27+
public mutating func connectBetween(inout otherVertex: Vertex<T>, withWeight weight: Double = 0) {
28+
connectTo(otherVertex, withWeight: weight)
29+
otherVertex.connectTo(self, withWeight: weight)
30+
}
31+
32+
// Does this vertex have an edge -----> otherVertex?
33+
public func edgeTo(otherVertex: Vertex<T>) -> Edge<T>? {
34+
for e in edges where e.to.uniqueID == otherVertex.uniqueID {
35+
return e
36+
}
37+
return nil
38+
}
39+
}

Graph/AdjacencyMatrix.swift

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
public struct Vertex<T> {
2+
public var data: T
3+
private let index: Int
4+
}
5+
6+
public struct Graph<T> {
7+
// If adjacencyMatrix[i][j] is not nil, then there is an edge from
8+
// vertex i to vertex j.
9+
private var adjacencyMatrix: [[Double?]] = []
10+
11+
// Adds a new vertex to the matrix.
12+
// Performance: possibly O(n^2) because of the resizing of the matrix.
13+
public mutating func createVertex(data: T) -> Vertex<T> {
14+
let vertex = Vertex(data: data, index: adjacencyMatrix.count)
15+
16+
// Expand each existing row to the right one column.
17+
for i in 0..<adjacencyMatrix.count {
18+
adjacencyMatrix[i].append(nil)
19+
}
20+
21+
// Add one new row at the bottom.
22+
let newRow = [Double?](count: adjacencyMatrix.count + 1, repeatedValue: nil)
23+
adjacencyMatrix.append(newRow)
24+
25+
return vertex
26+
}
27+
28+
// Creates a directed edge source -----> dest.
29+
public mutating func connect(sourceVertex: Vertex<T>, to destinationVertex: Vertex<T>, withWeight weight: Double = 0) {
30+
adjacencyMatrix[sourceVertex.index][destinationVertex.index] = weight
31+
}
32+
33+
// Creates an undirected edge by making 2 directed edges:
34+
// some ----> other, and other ----> some.
35+
public mutating func connect(someVertex: Vertex<T>, symmetricallyWithVertex withVertex: Vertex<T>, withWeight weight: Double = 0) {
36+
adjacencyMatrix[someVertex.index][withVertex.index] = weight
37+
adjacencyMatrix[withVertex.index][someVertex.index] = weight
38+
}
39+
40+
public func weightFrom(sourceVertex: Vertex<T>, toDestinationVertex: Vertex<T>) -> Double? {
41+
return adjacencyMatrix[sourceVertex.index][toDestinationVertex.index]
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -1,64 +1,65 @@
1-
/*
1+
//: # Graph using an adjacency list
2+
//:
3+
//: In an adjacency list implementation of a graph, each vertex stores an
4+
//: array of edges, indicating to which other vertices it has an edge.
5+
//: (Note the directionality: we only record "to" edges, not "from" edges.)
26

3-
A graph implementation, using an adjacency list.
7+
private var uniqueIDCounter = 0
48

5-
In an adjacency list implementation, each vertex stores an array of edges, indicating to which vertices it has an edge (note the directionality). The edge stores the source and destination vertices, as well as a weight.
6-
7-
*/
8-
9-
var uniqueIDCounter: Int = 0
10-
11-
public struct GraphEdge<T> {
12-
public let from: GraphVertex<T>
13-
public let to: GraphVertex<T>
9+
public struct Edge<T> {
10+
public let from: Vertex<T>
11+
public let to: Vertex<T>
1412
public let weight: Double
1513
}
1614

17-
18-
public struct GraphVertex<T> {
15+
public struct Vertex<T> {
1916
public var data: T
20-
private var edges: [GraphEdge<T>] = [] // This is an adjacency list, rather than matrix
17+
public let uniqueID: Int
2118

22-
private let uniqueID: Int
19+
private(set) public var edges: [Edge<T>] = [] // the adjacency list
2320

2421
public init(data: T) {
2522
self.data = data
2623
uniqueID = uniqueIDCounter
2724
uniqueIDCounter += 1
2825
}
29-
30-
// Creates a directed edge self -----> dest
31-
public mutating func connectTo(destinationVertex: GraphVertex<T>, withWeight weight: Double = 1.0) {
32-
edges.append(GraphEdge(from: self, to: destinationVertex, weight: weight))
26+
27+
// Creates the directed edge: self -----> dest
28+
public mutating func connectTo(destinationVertex: Vertex<T>, withWeight weight: Double = 0) {
29+
edges.append(Edge(from: self, to: destinationVertex, weight: weight))
3330
}
3431

35-
// Creates an undirected edge by making 2 directed edges: self ----> other, and other ----> self
36-
public mutating func connectBetween(inout otherVertex: GraphVertex<T>, withWeight weight: Double = 1.0) {
32+
// Creates an undirected edge by making two directed edges: self ----> other, and other ----> self
33+
public mutating func connectBetween(inout otherVertex: Vertex<T>, withWeight weight: Double = 0) {
3734
connectTo(otherVertex, withWeight: weight)
3835
otherVertex.connectTo(self, withWeight: weight)
3936
}
4037

41-
// Queries for an edge from self -----> otherVertex
42-
public func edgeTo(otherVertex: GraphVertex<T>) -> GraphEdge<T>? {
43-
for e in edges {
44-
if e.to.uniqueID == otherVertex.uniqueID {
45-
return e
46-
}
38+
// Does this vertex have an edge -----> otherVertex?
39+
public func edgeTo(otherVertex: Vertex<T>) -> Edge<T>? {
40+
for e in edges where e.to.uniqueID == otherVertex.uniqueID {
41+
return e
4742
}
48-
4943
return nil
5044
}
5145
}
5246

5347

5448

55-
// Create 4 separate vertices
56-
var v1 = GraphVertex(data: 1)
57-
var v2 = GraphVertex(data: 2)
58-
var v3 = GraphVertex(data: 3)
59-
var v4 = GraphVertex(data: 4)
49+
//: ### Demo
50+
51+
// Create the vertices
52+
var v1 = Vertex(data: 1)
53+
var v2 = Vertex(data: 2)
54+
var v3 = Vertex(data: 3)
55+
var v4 = Vertex(data: 4)
56+
var v5 = Vertex(data: 5)
6057

61-
// Setup a cycle like so:
58+
// Set up a cycle like so:
59+
// v5
60+
// ^
61+
// | (3.2)
62+
// |
6263
// v1 ---(1)---> v2 ---(1)---> v3 ---(4.5)---> v4
6364
// ^ |
6465
// | V
@@ -68,6 +69,8 @@ v1.connectTo(v2, withWeight: 1.0)
6869
v2.connectTo(v3, withWeight: 1.0)
6970
v3.connectTo(v4, withWeight: 4.5)
7071
v4.connectTo(v1, withWeight: 2.8)
72+
v2.connectTo(v5, withWeight: 3.2)
73+
7174

7275
// Returns the weight of the edge from v1 to v2 (1.0)
7376
v1.edgeTo(v2)?.weight
@@ -80,4 +83,5 @@ v3.edgeTo(v4)?.weight
8083

8184
// Returns the weight of the edge from v4 to v1 (2.8)
8285
v4.edgeTo(v1)?.weight
83-
//: [Next](@next)
86+
87+
//: [Next: Adjacency Matrix](@next)
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,87 @@
1-
//: [Previous](@previous)
2-
/*
1+
//: [Previous: Adjacency List](@previous)
32

4-
A graph implementation, using an adjacency matrix.
3+
//: # Graph using an adjacency matrix
4+
//:
5+
//: In an adjacency matrix implementation, each vertex is given an index
6+
//: from `0..<V`, where `V` is the total number of vertices. The matrix is
7+
//: a 2D array, with each entry indicating if the corresponding two vertices
8+
//: are connected, and the weight of that edge.
9+
//:
10+
//: For example, if `matrix[3][5] = 4.6`, then vertex #3 is connected to
11+
//: vertex #5, with a weight of 4.6. Note that vertex #5 is not necessarily
12+
//: connected back to vertex #3 (that would be recorded in `matrix[5][3]`).
513

6-
In an adjacency matrix implementation, each vertex is associated with an index from 0..<V (V being the number of vertices). Then a matrix (a 2D array) is stored for the entire graph, with each entry indicating if the corresponding vertices are connected, and the weight. For example, if matrix[3][5] = 4.6, then vertex #3 is connected to vertex #5, with a weight of 4.6. Note that vertex #5 is not necessarily connected to vertex #3.
7-
8-
*/
9-
10-
public struct GraphVertex<T> {
14+
public struct Vertex<T> {
1115
public var data: T
1216
private let index: Int
1317
}
1418

1519
public struct Graph<T> {
16-
17-
// nil entries are used to mark that two vertices are NOT connected.
18-
// If adjacencyMatrix[i][j] is not nil, then there is an edge from vertex i to vertex j.
20+
// If adjacencyMatrix[i][j] is not nil, then there is an edge from
21+
// vertex i to vertex j.
1922
private var adjacencyMatrix: [[Double?]] = []
2023

21-
// Possibly O(n^2) because of the resizing of the matrix
22-
public mutating func createVertex(data: T) -> GraphVertex<T> {
23-
let vertex = GraphVertex(data: data, index: adjacencyMatrix.count)
24+
// Adds a new vertex to the matrix.
25+
// Performance: possibly O(n^2) because of the resizing of the matrix.
26+
public mutating func createVertex(data: T) -> Vertex<T> {
27+
let vertex = Vertex(data: data, index: adjacencyMatrix.count)
2428

25-
// Expand each existing row to the right one column
29+
// Expand each existing row to the right one column.
2630
for i in 0..<adjacencyMatrix.count {
2731
adjacencyMatrix[i].append(nil)
2832
}
2933

30-
// Add one new row at the bottom
34+
// Add one new row at the bottom.
3135
let newRow = [Double?](count: adjacencyMatrix.count + 1, repeatedValue: nil)
3236
adjacencyMatrix.append(newRow)
33-
37+
3438
return vertex
3539
}
3640

37-
// Creates a directed edge source -----> dest. Represented by M[source][dest] = weight
38-
public mutating func connect(sourceVertex: GraphVertex<T>, toDestinationVertex: GraphVertex<T>, withWeight weight: Double = 0) {
39-
adjacencyMatrix[sourceVertex.index][toDestinationVertex.index] = weight
41+
// Creates a directed edge source -----> dest.
42+
public mutating func connect(sourceVertex: Vertex<T>, to destinationVertex: Vertex<T>, withWeight weight: Double = 0) {
43+
adjacencyMatrix[sourceVertex.index][destinationVertex.index] = weight
4044
}
4145

42-
// Creates an undirected edge by making 2 directed edges: some ----> other, and other ----> some
43-
public mutating func connect(someVertex: GraphVertex<T>, symmetricallyWithVertex withVertex: GraphVertex<T>, withWeight weight: Double = 0) {
46+
// Creates an undirected edge by making 2 directed edges:
47+
// some ----> other, and other ----> some.
48+
public mutating func connect(someVertex: Vertex<T>, symmetricallyWithVertex withVertex: Vertex<T>, withWeight weight: Double = 0) {
4449
adjacencyMatrix[someVertex.index][withVertex.index] = weight
4550
adjacencyMatrix[withVertex.index][someVertex.index] = weight
4651
}
47-
48-
public func weightFrom(sourceVertex: GraphVertex<T>, toDestinationVertex: GraphVertex<T>) -> Double? {
52+
53+
public func weightFrom(sourceVertex: Vertex<T>, toDestinationVertex: Vertex<T>) -> Double? {
4954
return adjacencyMatrix[sourceVertex.index][toDestinationVertex.index]
5055
}
51-
5256
}
5357

5458

5559

56-
// Create 4 separate vertices
60+
//: ### Demo
61+
62+
// Create the vertices
5763
var graph = Graph<Int>()
5864
let v1 = graph.createVertex(1)
5965
let v2 = graph.createVertex(2)
6066
let v3 = graph.createVertex(3)
6167
let v4 = graph.createVertex(4)
68+
let v5 = graph.createVertex(5)
6269

63-
// Setup a cycle like so:
70+
// Set up a cycle like so:
71+
// v5
72+
// ^
73+
// | (3.2)
74+
// |
6475
// v1 ---(1)---> v2 ---(1)---> v3 ---(4.5)---> v4
6576
// ^ |
6677
// | V
6778
// ---------<-----------<---------(2.8)----<----|
6879

69-
graph.connect(v1, toDestinationVertex: v2, withWeight: 1.0)
70-
graph.connect(v2, toDestinationVertex: v3, withWeight: 1.0)
71-
graph.connect(v3, toDestinationVertex: v4, withWeight: 4.5)
72-
graph.connect(v4, toDestinationVertex: v1, withWeight: 2.8)
80+
graph.connect(v1, to: v2, withWeight: 1.0)
81+
graph.connect(v2, to: v3, withWeight: 1.0)
82+
graph.connect(v3, to: v4, withWeight: 4.5)
83+
graph.connect(v4, to: v1, withWeight: 2.8)
84+
graph.connect(v2, to: v5, withWeight: 3.2)
7385

7486
// Returns the weight of the edge from v1 to v2 (1.0)
7587
graph.weightFrom(v1, toDestinationVertex: v2)
@@ -83,3 +95,5 @@ graph.weightFrom(v3, toDestinationVertex: v4)
8395
// Returns the weight of the edge from v4 to v1 (2.8)
8496
graph.weightFrom(v4, toDestinationVertex: v1)
8597

98+
print(graph.adjacencyMatrix)
99+

Graph/Images/AdjacencyList.graffle

2.52 KB
Binary file not shown.

Graph/Images/AdjacencyList.png

17.7 KB
Loading

Graph/Images/AdjacencyMatrix.graffle

3.15 KB
Binary file not shown.

Graph/Images/AdjacencyMatrix.png

20.7 KB
Loading

Graph/Images/ChordMap.graffle

2.19 KB
Binary file not shown.

Graph/Images/ChordMap.png

16.8 KB
Loading

Graph/Images/CoreData.graffle

2.04 KB
Binary file not shown.

Graph/Images/CoreData.png

5.5 KB
Loading

Graph/Images/DAG.graffle

1.97 KB
Binary file not shown.

Graph/Images/DAG.png

8.91 KB
Loading

Graph/Images/Demo1.graffle

2.12 KB
Binary file not shown.

Graph/Images/Demo1.png

9.1 KB
Loading

Graph/Images/Flights.graffle

2.51 KB
Binary file not shown.

Graph/Images/Flights.png

33.9 KB
Loading

Graph/Images/FlightsDirected.graffle

2.21 KB
Binary file not shown.

Graph/Images/FlightsDirected.png

31.3 KB
Loading

Graph/Images/Graph.graffle

2.11 KB
Binary file not shown.

Graph/Images/Graph.png

14.2 KB
Loading

Graph/Images/SocialNetwork.graffle

2.36 KB
Binary file not shown.

Graph/Images/SocialNetwork.png

31.2 KB
Loading

Graph/Images/StateMachine.graffle

2.46 KB
Binary file not shown.

Graph/Images/StateMachine.png

23.6 KB
Loading

Graph/Images/Tasks.graffle

2.31 KB
Binary file not shown.

Graph/Images/Tasks.png

14.6 KB
Loading

Graph/Images/TreeAndList.graffle

2.59 KB
Binary file not shown.

Graph/Images/TreeAndList.png

18.7 KB
Loading

0 commit comments

Comments
 (0)