Skip to content

Commit 31db302

Browse files
committed
Merge pull request #68 from aliHafizji/topological_sort
Add Topological Sort
2 parents 78af3a3 + 5e228b4 commit 31db302

File tree

13 files changed

+681
-0
lines changed

13 files changed

+681
-0
lines changed
183 KB
Loading
111 KB
Loading

Topological Sort/README.markdown

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Topological Sort
2+
3+
Topological sort is an algorithm aimed at ordering a directed graph such that for each directed edge *uv* from vertex *u* to vertex *v*, *u* comes before *v*.
4+
5+
## Where is this used?
6+
7+
Let's consider that you want to learn all the algorithms present in the swift-algorithm-club, this might seem daunting at first but we can use topological sort to get things organized.
8+
9+
For e.g. To learn the depth first search algorithm you need to know how a graph is represented. Similarly, to understand how to calculate the length of a binary tree you need to know details of how a tree is traversed. If we were to represent these in the form of a graph it would be as follows:
10+
11+
![Example](Images/algorithm_example.png)
12+
13+
If we consider each algorithm to be a node in the graph you'll see dependancies among them i.e. to learn something you might have to know something else first. This is exactly what topological sort is used for, it will sort things out such that you know what to learn first.
14+
15+
## How does it work?
16+
17+
### Step 1: Find all nodes that have in-degree of 0
18+
19+
The in-degree of a node is the number of incoming edges to that node. All nodes that have no incoming edges have an in-degree of 0. These nodes are the starting points for the sort.
20+
21+
If you think about it in the context of the previous example these nodes represent algorithms that don't need anything else to be learnt, hence the sort starts with them.
22+
23+
### Step 2: Depth first search for traversal
24+
25+
Depth first search is an algorithm that is used to traverse a graph. This algorithm traverses all the child nodes recursively and uses backtracking.
26+
27+
To know more about this algorithm please take a look at the explanation [here](https://github.com/hollance/swift-algorithm-club/tree/master/Depth-First%20Search).
28+
29+
### Step 3: Remember all visited nodes
30+
31+
The last step of the sort is to maintain a list of all the nodes that have been visited. This is to avoid visiting the same node twice.
32+
33+
## Example
34+
35+
Consider the following graph:
36+
37+
![Graph Example](Images/graph_example.png)
38+
39+
*Step 1* Nodes with 0 in-degree: **5, 7, 3**
40+
*Step 2* Depth first search for each without remembering nodes that have already been visited:
41+
42+
```
43+
Node 3: 3, 8, 9, 10
44+
Node 7: 7, 11, 2, 8, 9
45+
Node 5: 5, 11, 2, 9, 10
46+
```
47+
48+
*Step 3* Remember nodes already visited in each DFS.
49+
50+
```
51+
Node 3: 3, 8, 9, 10
52+
Node 7: 7, 11, 2
53+
Node 5: 5
54+
```
55+
56+
The final sorted order is a concatenation of the above three traversals: **3, 8, 9, 10, 7, 11, 2, 5**
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//: Playground - noun: a place where people can play
2+
3+
import UIKit
4+
5+
let graph = Graph()
6+
7+
let node5 = graph.addNode("5")
8+
let node7 = graph.addNode("7")
9+
let node3 = graph.addNode("3")
10+
let node11 = graph.addNode("11")
11+
let node8 = graph.addNode("8")
12+
let node2 = graph.addNode("2")
13+
let node9 = graph.addNode("9")
14+
let node10 = graph.addNode("10")
15+
16+
graph.addEdge(fromNode: node5, toNode: node11)
17+
graph.addEdge(fromNode: node7, toNode: node11)
18+
graph.addEdge(fromNode: node7, toNode: node8)
19+
graph.addEdge(fromNode: node3, toNode: node8)
20+
graph.addEdge(fromNode: node3, toNode: node10)
21+
graph.addEdge(fromNode: node11, toNode: node2)
22+
graph.addEdge(fromNode: node11, toNode: node9)
23+
graph.addEdge(fromNode: node11, toNode: node10)
24+
graph.addEdge(fromNode: node8, toNode: node9)
25+
26+
27+
graph.topologicalSort()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
public class Graph: CustomStringConvertible {
2+
3+
public typealias Node = String
4+
5+
private var adjacencyLists: [Node : [Node]]
6+
7+
public init() {
8+
adjacencyLists = [Node : [Node]]()
9+
}
10+
11+
public func addNode(value: Node) -> Node {
12+
adjacencyLists[value] = []
13+
return value
14+
}
15+
16+
public func addEdge(fromNode from: Node, toNode to: Node) -> Bool {
17+
adjacencyLists[from]?.append(to)
18+
return adjacencyLists[from] != nil ? true : false
19+
}
20+
21+
public var description: String {
22+
return adjacencyLists.description
23+
}
24+
25+
private func adjacencyList(forNode node: Node) -> [Node]? {
26+
for (key, adjacencyList) in adjacencyLists {
27+
if key == node {
28+
return adjacencyList
29+
}
30+
}
31+
return nil
32+
}
33+
}
34+
35+
extension Graph {
36+
37+
typealias InDegree = Int
38+
private func calculateInDegreeOfNodes() -> [Node : InDegree] {
39+
40+
var inDegrees = [Node : InDegree]()
41+
42+
for (node, _) in adjacencyLists {
43+
inDegrees[node] = 0
44+
}
45+
46+
for (_, adjacencyList) in adjacencyLists {
47+
for nodeInList in adjacencyList {
48+
inDegrees[nodeInList] = (inDegrees[nodeInList] ?? 0) + 1
49+
}
50+
}
51+
return inDegrees
52+
}
53+
54+
private func depthFirstSearch(source: Node, inout visited: [Node : Bool]) -> [Node] {
55+
var result = [source]
56+
visited[source] = true
57+
58+
if let adjacencyList = adjacencyList(forNode: source) {
59+
for nodeInAdjacencyList in adjacencyList {
60+
if !(visited[nodeInAdjacencyList] ?? false) {
61+
result += (depthFirstSearch(nodeInAdjacencyList, visited: &visited))
62+
}
63+
}
64+
}
65+
66+
return result
67+
}
68+
69+
public func topologicalSort() -> [Node] {
70+
71+
let startNodes = calculateInDegreeOfNodes().filter({(_, indegree) in
72+
return indegree == 0
73+
}).map({(node, indegree) in
74+
return node
75+
})
76+
77+
var visited = [Node : Bool]()
78+
79+
for (node, _) in adjacencyLists {
80+
visited[node] = false
81+
}
82+
83+
var result = [Node]()
84+
85+
for startNode in startNodes {
86+
result += depthFirstSearch(startNode, visited: &visited)
87+
}
88+
89+
return result
90+
}
91+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import Foundation
2+
3+
public struct Stack<T> {
4+
private var array = [T]()
5+
6+
public var isEmpty: Bool {
7+
return array.isEmpty
8+
}
9+
10+
public var count: Int {
11+
return array.count
12+
}
13+
14+
public mutating func push(element: T) {
15+
array.append(element)
16+
}
17+
18+
public mutating func pop() -> T? {
19+
if isEmpty {
20+
return nil
21+
} else {
22+
return array.removeLast()
23+
}
24+
}
25+
26+
public func peek() -> T? {
27+
return array.last
28+
}
29+
}
30+
31+
extension Stack: SequenceType {
32+
public func generate() -> AnyGenerator<T> {
33+
var curr = self
34+
return anyGenerator {
35+
_ -> T? in
36+
return curr.pop()
37+
}
38+
}
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='5.0' target-platform='ios'>
3+
<timeline fileName='timeline.xctimeline'/>
4+
</playground>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<Timeline
3+
version = "3.0">
4+
<TimelineItems>
5+
</TimelineItems>
6+
</Timeline>

0 commit comments

Comments
 (0)