Skip to content

Commit 1f5c20e

Browse files
committed
Extract shortest path and minimum spanning tree from breadth-first search
1 parent efe956a commit 1f5c20e

File tree

46 files changed

+1547
-300
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1547
-300
lines changed

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

-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
//: # Breadth-First Search
2-
31
func breadthFirstSearch(graph: Graph, source: Node) -> [String] {
42
var queue = Queue<Node>()
53
queue.enqueue(source)
@@ -47,5 +45,3 @@ graph.addEdge(nodeF, neighbor: nodeG)
4745

4846
let nodesExplored = breadthFirstSearch(graph, source: nodeA)
4947
print(nodesExplored)
50-
51-
//: [Next: Shortest Path Example](@next)
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,2 @@
11
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2-
<playground version='6.0' target-platform='ios' display-mode='rendered'>
3-
<pages>
4-
<page name='Simple example'/>
5-
<page name='Shortest path example'/>
6-
<page name='Minimum spanning tree example'/>
7-
</pages>
8-
</playground>
2+
<playground version='6.0' target-platform='ios' display-mode='raw'/>

Breadth-First Search/README.markdown

+3-255
Original file line numberDiff line numberDiff line change
@@ -144,263 +144,11 @@ print(nodesExplored)
144144

145145
This will output: `["a", "b", "c", "d", "e", "f", "g", "h"]`
146146

147-
## Applications
147+
## What is BFS good for?
148148

149149
Breadth-first search can be used to solve many problems. A small selection:
150150

151-
* Computing the shortest path between a source node and each of the other nodes (only for unweighted graphs).
152-
* Calculating the minimum spanning tree on an unweighted graph.
153-
154-
## Shortest path example
155-
156-
Breadth-first search can be used to compute the [shortest path](../Shortest Path/) between a source node and each of the other nodes in the tree or graph, because it explores all of the immediate neighbor nodes first before moving to the next level neighbors.
157-
158-
Let's follow the animated example and calculate the shortest path to all the other nodes. Start with the source node ``a`` and add it to a queue with a distance of ``0``.
159-
160-
```swift
161-
queue.enqueue(a)
162-
a.distance = 0
163-
```
164-
165-
The queue is now ``[ a ]``. Dequeue ``a`` and enqueue its two neighbor nodes ``b`` and ``c`` with a distance of ``1``.
166-
167-
```swift
168-
queue.dequeue() // a
169-
queue.enqueue(b)
170-
b.distance = a.distance + 1 // result: 1
171-
queue.enqueue(c)
172-
c.distance = a.distance + 1 // result: 1
173-
```
174-
175-
The queue is now ``[ b, c ]``. Dequeue ``b`` and enqueue `b`'s neighbor nodes ``d`` and ``e`` with a distance of ``2``.
176-
177-
```swift
178-
queue.dequeue() // b
179-
queue.enqueue(d)
180-
d.distance = b.distance + 1 // result: 2
181-
queue.enqueue(e)
182-
e.distance = b.distance + 1 // result: 2
183-
```
184-
185-
Continue until the queue is empty to calculate the shortest path to all other nodes.
186-
187-
Here's the code:
188-
189-
```swift
190-
func breadthFirstSearchShortestPath(graph: Graph, source: Node) -> Graph {
191-
let shortestPathGraph = graph.duplicate()
192-
193-
var queue = Queue<Node>()
194-
let sourceInShortestPathsGraph = shortestPathGraph.findNodeWithLabel(source.label)
195-
queue.enqueue(sourceInShortestPathsGraph)
196-
sourceInShortestPathsGraph.distance = 0
197-
198-
while let current = queue.dequeue() {
199-
for edge in current.neighbors {
200-
let neighborNode = edge.neighbor
201-
if !neighborNode.hasDistance {
202-
queue.enqueue(neighborNode)
203-
neighborNode.distance = current.distance! + 1
204-
}
205-
}
206-
}
207-
208-
return shortestPathGraph
209-
}
210-
```
211-
212-
Put this code in a playground and test it like so:
213-
214-
```swift
215-
let shortestPathGraph = breadthFirstSearchShortestPath(graph, source: nodeA)
216-
print(shortestPathGraph.nodes)
217-
```
218-
219-
This will output:
220-
221-
Node(label: a, distance: 0), Node(label: b, distance: 1), Node(label: c, distance: 1),
222-
Node(label: d, distance: 2), Node(label: e, distance: 2), Node(label: f, distance: 2),
223-
Node(label: g, distance: 2), Node(label: h, distance: 3)
224-
225-
## Minimum spanning tree example
226-
227-
Breadth-first search can be used to calculate the [minimum spanning tree](../Minimum Spanning Tree/) on an unweighted graph. A minimum spanning tree describes a path that contains the smallest number of edges that are needed to visit every node in the graph.
228-
229-
Let's calculate the minimum spanning tree for the following graph:
230-
231-
![Minimum spanning tree](Images/Minimum_Spanning_Tree.png)
232-
233-
*Note: the minimum spanning tree is represented by the bold edges.*
234-
235-
Start with the source node ``a`` and add it to a queue and mark it as visited.
236-
237-
```swift
238-
queue.enqueue(a)
239-
a.visited = true
240-
```
241-
242-
The queue is now ``[ a ]``. Dequeue ``a`` and enqueue its immediate neighbor nodes ``b`` and ``h`` and mark them as visited.
243-
244-
```swift
245-
queue.dequeue() // a
246-
queue.enqueue(b)
247-
b.visited = true
248-
queue.enqueue(h)
249-
h.visited = true
250-
```
251-
252-
The queue is now ``[ b, h ]``. Dequeue ``b`` and enqueue the neighbor node ``c`` mark it as visited. Remove the edge between ``b`` to ``h`` because ``h`` has already been visited.
253-
254-
```swift
255-
queue.dequeue() // b
256-
queue.enqueue(c)
257-
c.visited = true
258-
b.removeEdgeTo(h)
259-
```
260-
261-
The queue is now ``[ h, c ]``. Dequeue ``h`` and enqueue the neighbor nodes ``g`` and ``i`` and mark them as visited.
262-
263-
```swift
264-
queue.dequeue() // h
265-
queue.enqueue(g)
266-
g.visited = true
267-
queue.enqueue(i)
268-
i.visited = true
269-
```
270-
271-
The queue is now ``[ c, g, i ]``. Dequeue ``c`` and enqueue the neighbor nodes ``d`` and ``f`` and mark them as visited. Remove the edge between ``c`` to ``i`` because ``i`` has already been visited.
272-
273-
```swift
274-
queue.dequeue() // c
275-
queue.enqueue(d)
276-
d.visited = true
277-
queue.enqueue(f)
278-
f.visited = true
279-
c.removeEdgeTo(i)
280-
```
281-
282-
The queue is now ``[ g, i, d, f ]``. Dequeue ``g`` and remove the edges between ``g`` to ``f`` and ``g`` to ``i`` because ``f`` and ``i`` have already been visited.
283-
284-
```swift
285-
queue.dequeue() // g
286-
g.removeEdgeTo(f)
287-
g.removeEdgeTo(i)
288-
```
289-
290-
The queue is now ``[ i, d, f ]``. Dequeue ``i``.
291-
292-
```swift
293-
queue.dequeue() // i
294-
```
295-
296-
The queue is now ``[ d, f ]``. Dequeue ``d`` and enqueue the neighbor node ``e`` mark it as visited. Remove the edge between ``d`` to ``f`` because ``f`` has already been visited.
297-
298-
```swift
299-
queue.dequeue() // d
300-
queue.enqueue(e)
301-
e.visited = true
302-
d.removeEdgeTo(f)
303-
```
304-
305-
The queue is now ``[ f, e ]``. Dequeue ``f``. Remove the edge between ``f`` to ``e`` because ``e`` has already been visited.
306-
307-
```swift
308-
queue.dequeue() // f
309-
f.removeEdgeTo(e)
310-
```
311-
312-
The queue is now ``[ e ]``. Dequeue ``e``.
313-
314-
```swift
315-
queue.dequeue() // e
316-
```
317-
318-
The queue is now empty, which means the minimum spanning tree has been computed.
319-
320-
Here's the code:
321-
322-
```swift
323-
func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph {
324-
let minimumSpanningTree = graph.duplicate()
325-
326-
var queue = Queue<Node>()
327-
let sourceInMinimumSpanningTree = minimumSpanningTree.findNodeWithLabel(source.label)
328-
queue.enqueue(sourceInMinimumSpanningTree)
329-
sourceInMinimumSpanningTree.visited = true
330-
331-
while let current = queue.dequeue() {
332-
for edge in current.neighbors {
333-
let neighborNode = edge.neighbor
334-
if !neighborNode.visited {
335-
neighborNode.visited = true
336-
queue.enqueue(neighborNode)
337-
} else {
338-
current.remove(edge)
339-
}
340-
}
341-
}
342-
343-
return minimumSpanningTree
344-
}
345-
```
346-
347-
This function returns a new `Graph` object that describes just the minimum spanning tree. In the figure, that would be the graph containing just the bold edges.
348-
349-
Put this code in a playground and test it like so:
350-
351-
```swift
352-
let graph = Graph()
353-
354-
let nodeA = graph.addNode("a")
355-
let nodeB = graph.addNode("b")
356-
let nodeC = graph.addNode("c")
357-
let nodeD = graph.addNode("d")
358-
let nodeE = graph.addNode("e")
359-
let nodeF = graph.addNode("f")
360-
let nodeG = graph.addNode("g")
361-
let nodeH = graph.addNode("h")
362-
let nodeI = graph.addNode("i")
363-
364-
graph.addEdge(nodeA, neighbor: nodeB)
365-
graph.addEdge(nodeA, neighbor: nodeH)
366-
graph.addEdge(nodeB, neighbor: nodeA)
367-
graph.addEdge(nodeB, neighbor: nodeC)
368-
graph.addEdge(nodeB, neighbor: nodeH)
369-
graph.addEdge(nodeC, neighbor: nodeB)
370-
graph.addEdge(nodeC, neighbor: nodeD)
371-
graph.addEdge(nodeC, neighbor: nodeF)
372-
graph.addEdge(nodeC, neighbor: nodeI)
373-
graph.addEdge(nodeD, neighbor: nodeC)
374-
graph.addEdge(nodeD, neighbor: nodeE)
375-
graph.addEdge(nodeD, neighbor: nodeF)
376-
graph.addEdge(nodeE, neighbor: nodeD)
377-
graph.addEdge(nodeE, neighbor: nodeF)
378-
graph.addEdge(nodeF, neighbor: nodeC)
379-
graph.addEdge(nodeF, neighbor: nodeD)
380-
graph.addEdge(nodeF, neighbor: nodeE)
381-
graph.addEdge(nodeF, neighbor: nodeG)
382-
graph.addEdge(nodeG, neighbor: nodeF)
383-
graph.addEdge(nodeG, neighbor: nodeH)
384-
graph.addEdge(nodeG, neighbor: nodeI)
385-
graph.addEdge(nodeH, neighbor: nodeA)
386-
graph.addEdge(nodeH, neighbor: nodeB)
387-
graph.addEdge(nodeH, neighbor: nodeG)
388-
graph.addEdge(nodeH, neighbor: nodeI)
389-
graph.addEdge(nodeI, neighbor: nodeC)
390-
graph.addEdge(nodeI, neighbor: nodeG)
391-
graph.addEdge(nodeI, neighbor: nodeH)
392-
393-
let minimumSpanningTree = breadthFirstSearchMinimumSpanningTree(graph, source: nodeA)
394-
395-
print(minimumSpanningTree) // [node: a edges: ["b", "h"]]
396-
// [node: b edges: ["c"]]
397-
// [node: c edges: ["d", "f"]]
398-
// [node: d edges: ["e"]]
399-
// [node: h edges: ["g", "i"]]
400-
```
401-
402-
## See also
403-
404-
[Graph](../Graph/), [Tree](../Tree/), [Queue](../Queue/), [Shortest Path](../Shortest Path/), [Minimum Spanning Tree](../Minimum Spanning Tree/).
151+
* Computing the [shortest path](../Shortest Path/) between a source node and each of the other nodes (only for unweighted graphs).
152+
* Calculating the [minimum spanning tree](../Minimum Spanning Tree/) on an unweighted graph.
405153

406154
*Written by [Chris Pilcher](https://github.com/chris-pilcher) and Matthijs Hollemans*
File renamed without changes.
File renamed without changes.

Breadth-First Search/Tests/Tests.xcodeproj/project.pbxproj

+2-18
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,7 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
83AACB421C844CED00DDAFC7 /* BreadthFirstSearchMinimumSpanningTreeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83AACB411C844CED00DDAFC7 /* BreadthFirstSearchMinimumSpanningTreeTests.swift */; };
11-
83AACB441C8456D200DDAFC7 /* BreadthFirstSearchShortestPathTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83AACB431C8456D200DDAFC7 /* BreadthFirstSearchShortestPathTests.swift */; };
1210
83F9C9681C84437C00B3A87F /* BreadthFirstSearch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C9651C84437C00B3A87F /* BreadthFirstSearch.swift */; };
13-
83F9C9691C84437C00B3A87F /* BreadthFirstSearchMinimumSpanningTree.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C9661C84437C00B3A87F /* BreadthFirstSearchMinimumSpanningTree.swift */; };
14-
83F9C96A1C84437C00B3A87F /* BreadthFirstSearchShortestPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C9671C84437C00B3A87F /* BreadthFirstSearchShortestPath.swift */; };
1511
83F9C96C1C8443E800B3A87F /* BreadthFirstSearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C96B1C8443E800B3A87F /* BreadthFirstSearchTests.swift */; };
1612
83F9C9721C84449D00B3A87F /* Graph.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C96E1C84449D00B3A87F /* Graph.swift */; };
1713
83F9C9741C84449D00B3A87F /* Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 83F9C9701C84449D00B3A87F /* Queue.swift */; };
@@ -20,14 +16,10 @@
2016
/* Begin PBXFileReference section */
2117
7B2BBC801C779D720067B71D /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
2218
7B2BBC941C779E7B0067B71D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; };
23-
83AACB411C844CED00DDAFC7 /* BreadthFirstSearchMinimumSpanningTreeTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BreadthFirstSearchMinimumSpanningTreeTests.swift; sourceTree = SOURCE_ROOT; };
24-
83AACB431C8456D200DDAFC7 /* BreadthFirstSearchShortestPathTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BreadthFirstSearchShortestPathTests.swift; sourceTree = SOURCE_ROOT; };
2519
83F9C9651C84437C00B3A87F /* BreadthFirstSearch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BreadthFirstSearch.swift; path = ../BreadthFirstSearch.swift; sourceTree = SOURCE_ROOT; };
26-
83F9C9661C84437C00B3A87F /* BreadthFirstSearchMinimumSpanningTree.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BreadthFirstSearchMinimumSpanningTree.swift; path = ../BreadthFirstSearchMinimumSpanningTree.swift; sourceTree = SOURCE_ROOT; };
27-
83F9C9671C84437C00B3A87F /* BreadthFirstSearchShortestPath.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = BreadthFirstSearchShortestPath.swift; path = ../BreadthFirstSearchShortestPath.swift; sourceTree = SOURCE_ROOT; };
2820
83F9C96B1C8443E800B3A87F /* BreadthFirstSearchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BreadthFirstSearchTests.swift; sourceTree = SOURCE_ROOT; };
29-
83F9C96E1C84449D00B3A87F /* Graph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Graph.swift; path = ../Graph.swift; sourceTree = SOURCE_ROOT; };
30-
83F9C9701C84449D00B3A87F /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Queue.swift; path = ../Queue.swift; sourceTree = SOURCE_ROOT; };
21+
83F9C96E1C84449D00B3A87F /* Graph.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Graph.swift; sourceTree = SOURCE_ROOT; };
22+
83F9C9701C84449D00B3A87F /* Queue.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queue.swift; sourceTree = SOURCE_ROOT; };
3123
/* End PBXFileReference section */
3224

3325
/* Begin PBXFrameworksBuildPhase section */
@@ -62,10 +54,6 @@
6254
children = (
6355
83F9C9651C84437C00B3A87F /* BreadthFirstSearch.swift */,
6456
83F9C96B1C8443E800B3A87F /* BreadthFirstSearchTests.swift */,
65-
83F9C9661C84437C00B3A87F /* BreadthFirstSearchMinimumSpanningTree.swift */,
66-
83AACB411C844CED00DDAFC7 /* BreadthFirstSearchMinimumSpanningTreeTests.swift */,
67-
83F9C9671C84437C00B3A87F /* BreadthFirstSearchShortestPath.swift */,
68-
83AACB431C8456D200DDAFC7 /* BreadthFirstSearchShortestPathTests.swift */,
6957
83F9C96E1C84449D00B3A87F /* Graph.swift */,
7058
83F9C9701C84449D00B3A87F /* Queue.swift */,
7159
7B2BBC941C779E7B0067B71D /* Info.plist */,
@@ -142,13 +130,9 @@
142130
isa = PBXSourcesBuildPhase;
143131
buildActionMask = 2147483647;
144132
files = (
145-
83AACB441C8456D200DDAFC7 /* BreadthFirstSearchShortestPathTests.swift in Sources */,
146-
83AACB421C844CED00DDAFC7 /* BreadthFirstSearchMinimumSpanningTreeTests.swift in Sources */,
147133
83F9C9721C84449D00B3A87F /* Graph.swift in Sources */,
148-
83F9C9691C84437C00B3A87F /* BreadthFirstSearchMinimumSpanningTree.swift in Sources */,
149134
83F9C9681C84437C00B3A87F /* BreadthFirstSearch.swift in Sources */,
150135
83F9C9741C84449D00B3A87F /* Queue.swift in Sources */,
151-
83F9C96A1C84437C00B3A87F /* BreadthFirstSearchShortestPath.swift in Sources */,
152136
83F9C96C1C8443E800B3A87F /* BreadthFirstSearchTests.swift in Sources */,
153137
);
154138
runOnlyForDeploymentPostprocessing = 0;
19.9 KB
Loading
80 KB
Binary file not shown.
2.1 KB
Binary file not shown.

Minimum Spanning Tree/Images/Tree.png

11.5 KB
Loading

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

+1-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
//: [Previous](@previous)
2-
3-
//: # Minimum Spanning Tree Example
4-
51
func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph {
62
let minimumSpanningTree = graph.duplicate()
73

@@ -27,7 +23,7 @@ func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph
2723

2824

2925
/*:
30-
![Animated example of a breadth-first search](Minimum_Spanning_Tree.png)
26+
![Graph](Minimum_Spanning_Tree.png)
3127
*/
3228

3329

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+
}

0 commit comments

Comments
 (0)