Skip to content

Commit efe956a

Browse files
committed
Replace animation with my own
The original was apparently taken from Wikipedia and I don't know if we had the rights to it. Besides, it was ugly.
1 parent 784023d commit efe956a

File tree

15 files changed

+69
-60
lines changed

15 files changed

+69
-60
lines changed

Breadth-First Search/BreadthFirstSearch.playground/Pages/Minimum spanning tree example.xcplaygroundpage/Contents.swift

+3-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph
1010
queue.enqueue(sourceInMinimumSpanningTree)
1111
sourceInMinimumSpanningTree.visited = true
1212

13-
while !queue.isEmpty {
14-
let current = queue.dequeue()!
13+
while let current = queue.dequeue() {
1514
for edge in current.neighbors {
1615
let neighborNode = edge.neighbor
1716
if !neighborNode.visited {
@@ -26,10 +25,12 @@ func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph
2625
return minimumSpanningTree
2726
}
2827

28+
2929
/*:
3030
![Animated example of a breadth-first search](Minimum_Spanning_Tree.png)
3131
*/
3232

33+
3334
let graph = Graph()
3435

3536
let nodeA = graph.addNode("a")

Breadth-First Search/BreadthFirstSearch.playground/Pages/Shortest path example.xcplaygroundpage/Contents.swift

+3-5
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ func breadthFirstSearchShortestPath(graph: Graph, source: Node) -> Graph {
1010
queue.enqueue(sourceInShortestPathsGraph)
1111
sourceInShortestPathsGraph.distance = 0
1212

13-
while !queue.isEmpty {
14-
let current = queue.dequeue()!
13+
while let current = queue.dequeue() {
1514
for edge in current.neighbors {
1615
let neighborNode = edge.neighbor
1716
if !neighborNode.hasDistance {
@@ -23,9 +22,8 @@ func breadthFirstSearchShortestPath(graph: Graph, source: Node) -> Graph {
2322

2423
return shortestPathGraph
2524
}
26-
/*:
27-
![Animated example of a breadth-first search](Animated_BFS.gif)
28-
*/
25+
26+
2927

3028
let graph = Graph()
3129

Breadth-First Search/BreadthFirstSearch.playground/Pages/Simple example.xcplaygroundpage/Contents.swift

+5-7
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,8 @@ func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
77
var nodesExplored = [source.label]
88
source.visited = true
99

10-
while !queue.isEmpty {
11-
let current = queue.dequeue()!
12-
for edge in current.neighbors {
10+
while let node = queue.dequeue() {
11+
for edge in node.neighbors {
1312
let neighborNode = edge.neighbor
1413
if !neighborNode.visited {
1514
queue.enqueue(neighborNode)
@@ -22,9 +21,7 @@ func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
2221
return nodesExplored
2322
}
2423

25-
/*:
26-
![Animated example of a breadth-first search](Animated_BFS.gif)
27-
*/
24+
2825

2926
let graph = Graph()
3027

@@ -44,10 +41,11 @@ graph.addEdge(nodeB, neighbor: nodeE)
4441
graph.addEdge(nodeC, neighbor: nodeF)
4542
graph.addEdge(nodeC, neighbor: nodeG)
4643
graph.addEdge(nodeE, neighbor: nodeH)
44+
graph.addEdge(nodeE, neighbor: nodeF)
45+
graph.addEdge(nodeF, neighbor: nodeG)
4746

4847

4948
let nodesExplored = breadthFirstSearch(graph, source: nodeA)
5049
print(nodesExplored)
5150

5251
//: [Next: Shortest Path Example](@next)
53-
Binary file not shown.

Breadth-First Search/BreadthFirstSearch.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@ func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
55
var nodesExplored = [source.label]
66
source.visited = true
77

8-
while !queue.isEmpty {
9-
let current = queue.dequeue()!
8+
while let current = queue.dequeue() {
109
for edge in current.neighbors {
1110
let neighborNode = edge.neighbor
1211
if !neighborNode.visited {

Breadth-First Search/BreadthFirstSearchMinimumSpanningTree.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph
66
queue.enqueue(sourceInMinimumSpanningTree)
77
sourceInMinimumSpanningTree.visited = true
88

9-
while !queue.isEmpty {
10-
let current = queue.dequeue()!
9+
while let current = queue.dequeue() {
1110
for edge in current.neighbors {
1211
let neighborNode = edge.neighbor
1312
if !neighborNode.visited {

Breadth-First Search/BreadthFirstSearchShortestPath.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,7 @@ func breadthFirstSearchShortestPath(graph: Graph, source: Node) -> Graph {
66
queue.enqueue(sourceInShortestPathsGraph)
77
sourceInShortestPathsGraph.distance = 0
88

9-
while !queue.isEmpty {
10-
let current = queue.dequeue()!
9+
while let current = queue.dequeue() {
1110
for edge in current.neighbors {
1211
let neighborNode = edge.neighbor
1312
if !neighborNode.hasDistance {
9.49 KB
Loading
Binary file not shown.
Binary file not shown.
-32.5 KB
Binary file not shown.
Binary file not shown.
9.59 KB
Loading

Breadth-First Search/README.markdown

+53-38
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,86 @@ Breadth-first search (BFS) is an algorithm for traversing or searching [tree](..
44

55
## Animated example
66

7-
![Animated example of a breadth-first search](Images/Animated_BFS.gif)
7+
Here's how breadth-first search works on a graph:
88

9-
Let's follow the animated example by using a [queue](../Queue/).
9+
![Animated example of a breadth-first search](Images/AnimatedExample.gif)
1010

11-
Start with the source node ``a`` and add it to a queue.
11+
When we visit a node, we color it black. We also put its neighbor nodes into a [queue](../Queue/). In the animation the nodes that are enqueued but not visited yet are shown in gray.
12+
13+
Let's follow the animated example. We start with the source node `A` and add it to a queue. In the animation this is shown as node `A` becoming gray.
1214

1315
```swift
14-
queue.enqueue(a)
16+
queue.enqueue(A)
1517
```
1618

17-
The queue is now ``[ a ]``. Dequeue ``a`` and enqueue its two neighbor nodes ``b`` and ``c``.
19+
The queue is now `[ A ]`. The idea is that, as long as there are nodes in the queue, we visit the node that's at the front of the queue, and enqueue its immediate neighbor nodes if they have not been visited yet.
20+
21+
To start traversing the graph, we pull the first node off the queue, `A`, and color it black. Then we enqueue its two neighbor nodes `B` and `C`. This colors them gray.
1822

1923
```swift
20-
queue.dequeue() // a
21-
queue.enqueue(b)
22-
queue.enqueue(c)
24+
queue.dequeue() // A
25+
queue.enqueue(B)
26+
queue.enqueue(C)
2327
```
2428

25-
The queue is now ``[ b, c ]``. Dequeue ``b`` and enqueue `b`'s neighbor nodes ``d`` and ``e``.
29+
The queue is now `[ B, C ]`. We dequeue `B`, and enqueue `B`'s neighbor nodes `D` and `E`.
2630

2731
```swift
28-
queue.dequeue() // b
29-
queue.enqueue(d)
30-
queue.enqueue(e)
32+
queue.dequeue() // B
33+
queue.enqueue(D)
34+
queue.enqueue(E)
3135
```
3236

33-
The queue is now ``[ c, d, e ]``. Dequeue ``c`` and enqueue `c`'s neighbor nodes ``f`` and ``g``.
37+
The queue is now `[ C, D, E ]`. Dequeue `C`, and enqueue `C`'s neighbor nodes `F` and `G`.
3438

3539
```swift
36-
queue.dequeue() // c
37-
queue.enqueue(f)
38-
queue.enqueue(g)
40+
queue.dequeue() // C
41+
queue.enqueue(F)
42+
queue.enqueue(G)
3943
```
4044

41-
The queue is now ``[ d, e, f, g ]``. Dequeue ``d`` which has no neighbor nodes.
45+
The queue is now `[ D, E, F, G ]`. Dequeue `D`, which has no neighbor nodes.
4246

4347
```swift
44-
queue.dequeue() // d
48+
queue.dequeue() // D
4549
```
4650

47-
The queue is now ``[ e, f, g ]``. Dequeue ``e`` and enqueue its single neighbor node ``h``.
51+
The queue is now `[ E, F, G ]`. Dequeue `E` and enqueue its single neighbor node `H`. Note that `B` is also a neighbor for `E` but we've already visited `B`, so we're not adding it to the queue again.
4852

4953
```swift
50-
queue.dequeue() // e
51-
queue.enqueue(h)
54+
queue.dequeue() // E
55+
queue.enqueue(H)
5256
```
5357

54-
The queue is now ``[ f, g, h ]``. Dequeue ``f`` which has no neighbor nodes.
58+
The queue is now `[ F, G, H ]`. Dequeue `F`, which has no unvisited neighbor nodes.
5559

5660
```swift
57-
queue.dequeue() // f
61+
queue.dequeue() // F
5862
```
5963

60-
The queue is now ``[ g, h ]``. Dequeue ``g`` which has no neighbor nodes.
64+
The queue is now `[ G, H ]`. Dequeue `G`, which has no unvisited neighbor nodes.
6165

6266
```swift
63-
queue.dequeue() // g
67+
queue.dequeue() // G
6468
```
6569

66-
The queue is now ``[ h ]``. Dequeue ``h`` which has no neighbor nodes.
70+
The queue is now `[ H ]`. Dequeue `H`, which has no unvisited neighbor nodes.
6771

6872
```swift
69-
queue.dequeue() // h
73+
queue.dequeue() // H
7074
```
7175

72-
The queue is now empty, meaning that all nodes have been explored. The order in which the nodes were explored is `a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`.
76+
The queue is now empty, meaning that all nodes have been explored. The order in which the nodes were explored is `A`, `B`, `C`, `D`, `E`, `F`, `G`, `H`.
77+
78+
We can show this as a tree:
79+
80+
![The BFS tree](Images/TraversalTree.png)
81+
82+
The parent of a node is the one that "discovered" that node. The root of the tree is the node you started the breadth-first search from.
83+
84+
For an unweighted graph, this tree defines a shortest path from the starting node to every other node in the tree. So breadth-first search is one way to find the shortest path between two nodes in a graph.
85+
86+
Breadth-first search can be used on directed and undirected graphs.
7387

7488
## The code
7589

@@ -83,9 +97,8 @@ func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
8397
var nodesExplored = [source.label]
8498
source.visited = true
8599

86-
while !queue.isEmpty {
87-
let current = queue.dequeue()!
88-
for edge in current.neighbors {
100+
while let node = queue.dequeue() {
101+
for edge in node.neighbors {
89102
let neighborNode = edge.neighbor
90103
if !neighborNode.visited {
91104
queue.enqueue(neighborNode)
@@ -99,10 +112,12 @@ func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
99112
}
100113
```
101114

115+
While there are nodes in the queue, we visit the first one and then enqueue its immediate neighbors if they haven't been visited yet.
116+
102117
Put this code in a playground and test it like so:
103118

104119
```swift
105-
let graph = Graph() // Representing the graph from the animated example
120+
let graph = Graph()
106121

107122
let nodeA = graph.addNode("a")
108123
let nodeB = graph.addNode("b")
@@ -120,6 +135,8 @@ graph.addEdge(nodeB, neighbor: nodeE)
120135
graph.addEdge(nodeC, neighbor: nodeF)
121136
graph.addEdge(nodeC, neighbor: nodeG)
122137
graph.addEdge(nodeE, neighbor: nodeH)
138+
graph.addEdge(nodeE, neighbor: nodeF)
139+
graph.addEdge(nodeF, neighbor: nodeG)
123140

124141
let nodesExplored = breadthFirstSearch(graph, source: nodeA)
125142
print(nodesExplored)
@@ -178,8 +195,7 @@ func breadthFirstSearchShortestPath(graph: Graph, source: Node) -> Graph {
178195
queue.enqueue(sourceInShortestPathsGraph)
179196
sourceInShortestPathsGraph.distance = 0
180197

181-
while !queue.isEmpty {
182-
let current = queue.dequeue()!
198+
while let current = queue.dequeue() {
183199
for edge in current.neighbors {
184200
let neighborNode = edge.neighbor
185201
if !neighborNode.hasDistance {
@@ -312,8 +328,7 @@ func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph
312328
queue.enqueue(sourceInMinimumSpanningTree)
313329
sourceInMinimumSpanningTree.visited = true
314330

315-
while !queue.isEmpty {
316-
let current = queue.dequeue()!
331+
while let current = queue.dequeue() {
317332
for edge in current.neighbors {
318333
let neighborNode = edge.neighbor
319334
if !neighborNode.visited {
@@ -386,6 +401,6 @@ print(minimumSpanningTree) // [node: a edges: ["b", "h"]]
386401

387402
## See also
388403

389-
[Graph](../Graph/), [Tree](../Tree/), [Queues](../Queue/), [Shortest Path](../Shortest Path/), [Minimum Spanning Tree](../Minimum Spanning Tree/).
404+
[Graph](../Graph/), [Tree](../Tree/), [Queue](../Queue/), [Shortest Path](../Shortest Path/), [Minimum Spanning Tree](../Minimum Spanning Tree/).
390405

391-
*Written by [Chris Pilcher](https://github.com/chris-pilcher)*
406+
*Written by [Chris Pilcher](https://github.com/chris-pilcher) and Matthijs Hollemans*

Breadth-First Search/Tests/BreadthFirstSearchTests.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ class BreadthFirstSearchTests: XCTestCase {
44

55
func testExploringTree() {
66
let tree = Graph()
7+
78
let nodeA = tree.addNode("a")
89
let nodeB = tree.addNode("b")
910
let nodeC = tree.addNode("c")
@@ -12,6 +13,7 @@ class BreadthFirstSearchTests: XCTestCase {
1213
let nodeF = tree.addNode("f")
1314
let nodeG = tree.addNode("g")
1415
let nodeH = tree.addNode("h")
16+
1517
tree.addEdge(nodeA, neighbor: nodeB)
1618
tree.addEdge(nodeA, neighbor: nodeC)
1719
tree.addEdge(nodeB, neighbor: nodeD)
@@ -70,7 +72,6 @@ class BreadthFirstSearchTests: XCTestCase {
7072
let nodesExplored = breadthFirstSearch(graph, source: nodeA)
7173

7274
XCTAssertEqual(nodesExplored, ["a", "b", "h", "c", "g", "i", "d", "f", "e"])
73-
7475
}
7576

7677
func testExploringGraphWithASingleNode() {
@@ -82,4 +83,3 @@ class BreadthFirstSearchTests: XCTestCase {
8283
XCTAssertEqual(nodesExplored, ["a"])
8384
}
8485
}
85-

0 commit comments

Comments
 (0)