Skip to content

Commit 309f6aa

Browse files
committed
Merge pull request kodecocodes#63 from paulot/master
Adding simple example for Depth-First Search
2 parents 676f081 + e98fe5c commit 309f6aa

File tree

14 files changed

+704
-0
lines changed

14 files changed

+704
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
func depthFirstSearch(graph: Graph, source: Node) -> [String] {
2+
var nodesExplored: [String] = [source.label]
3+
source.visited = true
4+
5+
// Iterate through all neighbors, and for each one visit all of its neighbors
6+
for edge in source.neighbors {
7+
let neighbor: Node = edge.neighbor
8+
9+
if (!neighbor.visited) {
10+
nodesExplored += depthFirstSearch(graph, source: neighbor)
11+
}
12+
}
13+
return nodesExplored
14+
}
15+
16+
17+
let graph = Graph()
18+
19+
let nodeA = graph.addNode("a")
20+
let nodeB = graph.addNode("b")
21+
let nodeC = graph.addNode("c")
22+
let nodeD = graph.addNode("d")
23+
let nodeE = graph.addNode("e")
24+
let nodeF = graph.addNode("f")
25+
let nodeG = graph.addNode("g")
26+
let nodeH = graph.addNode("h")
27+
28+
graph.addEdge(nodeA, neighbor: nodeB)
29+
graph.addEdge(nodeA, neighbor: nodeC)
30+
graph.addEdge(nodeB, neighbor: nodeD)
31+
graph.addEdge(nodeB, neighbor: nodeE)
32+
graph.addEdge(nodeC, neighbor: nodeF)
33+
graph.addEdge(nodeC, neighbor: nodeG)
34+
graph.addEdge(nodeE, neighbor: nodeH)
35+
36+
37+
let nodesExplored = depthFirstSearch(graph, source: nodeA)
38+
print(nodesExplored)
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>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public class Edge : Equatable {
2+
public var neighbor: Node
3+
4+
public init(neighbor: Node) {
5+
self.neighbor = neighbor
6+
}
7+
}
8+
9+
public func ==(lhs: Edge, rhs: Edge) -> Bool {
10+
return lhs.neighbor == rhs.neighbor
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
public class Graph : CustomStringConvertible, Equatable {
2+
public private(set) var nodes: [Node]
3+
4+
public init() {
5+
self.nodes = []
6+
}
7+
8+
public func addNode(label: String) -> Node {
9+
let node = Node(label: label)
10+
nodes.append(node)
11+
return node
12+
}
13+
14+
public func addEdge(source: Node, neighbor: Node) {
15+
let edge = Edge(neighbor: neighbor)
16+
edge.neighbor = neighbor
17+
source.neighbors.append(edge)
18+
}
19+
20+
public var description: String {
21+
var description = ""
22+
23+
for node in nodes {
24+
if !node.neighbors.isEmpty {
25+
description += "[node: \(node.label) edges: \(node.neighbors.map{ $0.neighbor.label})]"
26+
}
27+
}
28+
return description
29+
}
30+
31+
public func findNodeWithLabel(label: String) -> Node {
32+
return nodes.filter{ $0.label == label }.first!
33+
}
34+
35+
public func duplicate() -> Graph {
36+
let duplicated = Graph()
37+
38+
for node in nodes {
39+
duplicated.addNode(node.label)
40+
}
41+
42+
for node in nodes {
43+
for edge in node.neighbors {
44+
let source = duplicated.findNodeWithLabel(node.label)
45+
let neighbour = duplicated.findNodeWithLabel(edge.neighbor.label)
46+
duplicated.addEdge(source, neighbor: neighbour)
47+
}
48+
}
49+
50+
return duplicated
51+
}
52+
}
53+
54+
public func ==(lhs: Graph, rhs: Graph) -> Bool {
55+
return lhs.nodes == rhs.nodes
56+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
public class Node : CustomStringConvertible, Equatable {
2+
public var neighbors: [Edge]
3+
4+
public private(set) var label: String
5+
public var distance: Int?
6+
public var visited: Bool
7+
8+
public init(label: String) {
9+
self.label = label
10+
neighbors = []
11+
visited = false
12+
}
13+
14+
public var description: String {
15+
if let distance = distance {
16+
return "Node(label: \(label), distance: \(distance))"
17+
}
18+
return "Node(label: \(label), distance: infinity)"
19+
}
20+
21+
public var hasDistance: Bool {
22+
return distance != nil
23+
}
24+
25+
public func remove(edge: Edge) {
26+
neighbors.removeAtIndex(neighbors.indexOf{ $0 === edge }!)
27+
}
28+
}
29+
30+
public func ==(lhs: Node, rhs: Node) -> Bool {
31+
return lhs.label == rhs.label && lhs.neighbors == rhs.neighbors
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<playground version='6.0' target-platform='ios' display-mode='rendered'/>
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
func depthFirstSearch(graph: Graph, source: Node) -> [String] {
2+
var nodesExplored: [String] = [source.label]
3+
source.visited = true
4+
5+
// Iterate through all neighbors, and for each one visit all of its neighbors
6+
for edge in source.neighbors {
7+
let neighbor: Node = edge.neighbor
8+
9+
if (!neighbor.visited) {
10+
nodesExplored += depthFirstSearch(graph, source: neighbor)
11+
}
12+
}
13+
14+
return nodesExplored
15+
}

Depth-First Search/Edge.swift

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public class Edge : Equatable {
2+
public var neighbor: Node
3+
4+
public init(neighbor: Node) {
5+
self.neighbor = neighbor
6+
}
7+
}
8+
9+
public func ==(lhs: Edge, rhs: Edge) -> Bool {
10+
return lhs.neighbor == rhs.neighbor
11+
}

Depth-First Search/Graph.swift

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
public class Graph : CustomStringConvertible, Equatable {
2+
public private(set) var nodes: [Node]
3+
4+
public init() {
5+
self.nodes = []
6+
}
7+
8+
public func addNode(label: String) -> Node {
9+
let node = Node(label: label)
10+
nodes.append(node)
11+
return node
12+
}
13+
14+
public func addEdge(source: Node, neighbor: Node) {
15+
let edge = Edge(neighbor: neighbor)
16+
edge.neighbor = neighbor
17+
source.neighbors.append(edge)
18+
}
19+
20+
public var description: String {
21+
var description = ""
22+
23+
for node in nodes {
24+
if !node.neighbors.isEmpty {
25+
description += "[node: \(node.label) edges: \(node.neighbors.map{ $0.neighbor.label})]"
26+
}
27+
}
28+
return description
29+
}
30+
31+
public func findNodeWithLabel(label: String) -> Node {
32+
return nodes.filter{ $0.label == label }.first!
33+
}
34+
35+
public func duplicate() -> Graph {
36+
let duplicated = Graph()
37+
38+
for node in nodes {
39+
duplicated.addNode(node.label)
40+
}
41+
42+
for node in nodes {
43+
for edge in node.neighbors {
44+
let source = duplicated.findNodeWithLabel(node.label)
45+
let neighbour = duplicated.findNodeWithLabel(edge.neighbor.label)
46+
duplicated.addEdge(source, neighbor: neighbour)
47+
}
48+
}
49+
50+
return duplicated
51+
}
52+
}
53+
54+
public func ==(lhs: Graph, rhs: Graph) -> Bool {
55+
return lhs.nodes == rhs.nodes
56+
}

Depth-First Search/Node.swift

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
public class Node : CustomStringConvertible, Equatable {
2+
public var neighbors: [Edge]
3+
4+
public private(set) var label: String
5+
public var distance: Int?
6+
public var visited: Bool
7+
8+
public init(label: String) {
9+
self.label = label
10+
neighbors = []
11+
visited = false
12+
}
13+
14+
public var description: String {
15+
if let distance = distance {
16+
return "Node(label: \(label), distance: \(distance))"
17+
}
18+
return "Node(label: \(label), distance: infinity)"
19+
}
20+
21+
public var hasDistance: Bool {
22+
return distance != nil
23+
}
24+
25+
public func remove(edge: Edge) {
26+
neighbors.removeAtIndex(neighbors.indexOf{ $0 === edge }!)
27+
}
28+
}
29+
30+
public func ==(lhs: Node, rhs: Node) -> Bool {
31+
return lhs.label == rhs.label && lhs.neighbors == rhs.neighbors
32+
}

Depth-First Search/README.markdown

+61
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Depth-First Search
2+
3+
Depth-first search (DFS) is an algorithm for traversing or searching [tree](../Tree/) or [graph](../Graph/) data structures. It starts at the tree source and explores as far as possible along each branch before backtracking.
4+
5+
## The code
6+
7+
Simple implementation of breadth-first search using a queue:
8+
9+
```swift
10+
func depthFirstSearch(graph: Graph, source: Node) -> [String] {
11+
var nodesExplored: [String] = [source.label]
12+
source.visited = true
13+
14+
// Iterate through all neighbors, and for each one visit all of its neighbors
15+
for edge in source.neighbors {
16+
let neighbor: Node = edge.neighbor
17+
18+
if (!neighbor.visited) {
19+
nodesExplored += depthFirstSearch(graph, source: neighbor)
20+
}
21+
}
22+
return nodesExplored
23+
}
24+
```
25+
26+
Put this code in a playground and test it like so:
27+
28+
```swift
29+
let graph = Graph() // Representing the graph from the animated example
30+
31+
let nodeA = graph.addNode("a")
32+
let nodeB = graph.addNode("b")
33+
let nodeC = graph.addNode("c")
34+
let nodeD = graph.addNode("d")
35+
let nodeE = graph.addNode("e")
36+
let nodeF = graph.addNode("f")
37+
let nodeG = graph.addNode("g")
38+
let nodeH = graph.addNode("h")
39+
40+
graph.addEdge(nodeA, neighbor: nodeB)
41+
graph.addEdge(nodeA, neighbor: nodeC)
42+
graph.addEdge(nodeB, neighbor: nodeD)
43+
graph.addEdge(nodeB, neighbor: nodeE)
44+
graph.addEdge(nodeC, neighbor: nodeF)
45+
graph.addEdge(nodeC, neighbor: nodeG)
46+
graph.addEdge(nodeE, neighbor: nodeH)
47+
48+
let nodesExplored = depthFirstSearch(graph, source: nodeA)
49+
print(nodesExplored)
50+
```
51+
52+
This will output: `["a", "b", "d", "e", "h", "c", "f", "g"]`
53+
54+
## Applications
55+
56+
Depth-first search can be used to solve many problems, for example:
57+
58+
* Finding connected components of a sparse graph
59+
* Topological sorting of nodes in a graph
60+
* Finding bridges of a graph (see: [Bridges](https://en.wikipedia.org/wiki/Bridge_(graph_theory)#Bridge-finding_algorithm))
61+
* Among others

0 commit comments

Comments
 (0)