Skip to content

Commit 3b7ee9e

Browse files
committed
Depth-First Search
1 parent 1f94273 commit 3b7ee9e

File tree

7 files changed

+152
-32
lines changed

7 files changed

+152
-32
lines changed
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// last checked with Xcode 10.1
2+
3+
func depthFirstSearch(_ graph: Graph, source: Node) -> [String] {
4+
var nodesExplored = [source.label]
5+
source.visited = true
6+
7+
for edge in source.neighbors {
8+
if !edge.neighbor.visited {
9+
nodesExplored += depthFirstSearch(graph, source: edge.neighbor)
10+
}
11+
}
12+
return nodesExplored
13+
}
14+
15+
let graph = Graph()
16+
17+
let nodeA = graph.addNode("a")
18+
let nodeB = graph.addNode("b")
19+
let nodeC = graph.addNode("c")
20+
let nodeD = graph.addNode("d")
21+
let nodeE = graph.addNode("e")
22+
let nodeF = graph.addNode("f")
23+
let nodeG = graph.addNode("g")
24+
let nodeH = graph.addNode("h")
25+
26+
graph.addEdge(nodeA, neighbor: nodeB)
27+
graph.addEdge(nodeA, neighbor: nodeC)
28+
graph.addEdge(nodeB, neighbor: nodeD)
29+
graph.addEdge(nodeB, neighbor: nodeE)
30+
graph.addEdge(nodeC, neighbor: nodeF)
31+
graph.addEdge(nodeC, neighbor: nodeG)
32+
graph.addEdge(nodeE, neighbor: nodeH)
33+
graph.addEdge(nodeE, neighbor: nodeF)
34+
graph.addEdge(nodeF, neighbor: nodeG)
35+
36+
let nodesExplored = depthFirstSearch(graph, source: nodeA)
37+
print(nodesExplored)
38+
Lines changed: 11 additions & 0 deletions
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+
}
Lines changed: 56 additions & 0 deletions
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+
@discardableResult
9+
public func addNode(_ label: String) -> Node {
10+
let node = Node(label)
11+
nodes.append(node)
12+
return node
13+
}
14+
15+
public func addEdge(_ source: Node, neighbor: Node) {
16+
let edge = Edge(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+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
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.remove(at: neighbors.index { $0 === edge }!)
27+
}
28+
}
29+
30+
public func == (_ lhs: Node, rhs: Node) -> Bool {
31+
return lhs.label == rhs.label && lhs.neighbors == rhs.neighbors
32+
}
33+
Lines changed: 4 additions & 0 deletions
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>

Depth-First Search/README.markdown

Lines changed: 9 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,31 @@
1-
# Depth-First Search
2-
# 深度优先搜索(DFS)
1+
# 深度优先搜索(DFS,Depth-First Search)
32

4-
> This topic has been tutorialized [here](https://www.raywenderlich.com/157949/swift-algorithm-club-depth-first-search)
53
> 这个主题已经有辅导[文章](https://www.raywenderlich.com/157949/swift-algorithm-club-depth-first-search)
64
7-
Depth-first search (DFS) is an algorithm for traversing or searching [tree](../Tree/) or [graph](../Graph/) data structures. It starts at a source node and explores as far as possible along each branch before backtracking.
8-
深度优先搜索(DFS)是用于遍历或搜索[](../Tree/)[](../Graph/)数据结构的算法。 它从源节点开始,并在回溯之前尽可能地沿着每个分支进行探索。
5+
深度优先搜索(DFS)是用于遍历或搜索[](../Tree/)[](../Graph/)数据结构的算法。它从源节点开始,并在回溯之前尽可能地沿着每个分支进行探索。
96

10-
Depth-first search can be used on both directed and undirected graphs.
117
深度优先搜索可以用于有向图和无向图。
128

13-
## Animated example
149
## 动画示例
1510

16-
Here's how depth-first search works on a graph:
1711
以下是深度优先搜索在图上的工作方式:
1812

1913
![Animated example](Images/AnimatedExample.gif)
2014

21-
Let's say we start the search from node `A`. In depth-first search we look at the starting node's first neighbor and visit that. In the example that is node `B`. Then we look at node `B`'s first neighbor and visit it. This is node `D`. Since `D` doesn't have any unvisited neighbors of its own, we backtrack to node `B` and go to its other neighbor `E`. And so on, until we've visited all the nodes in the graph.
22-
假设我们从节点`A`开始搜索。 在深度优先搜索中,我们查看起始节点的第一个邻居并访问它。 在节点`B`的示例中。 然后我们看一下节点`B`的第一个邻居并访问它。 这是节点`D`。 由于`D`没有自己的任何未访问的邻居,我们回溯到节点`B`并转到其他邻居`E`。 依此类推,直到我们访问了图表中的所有节点。
15+
假设我们从节点`A`开始搜索。 在深度优先搜索中,我们查看起始节点的第一个邻居并访问它,在这个示例中是节点`B`。然后我们查找节点`B`的第一个邻居并访问它,它是节点`D`。由于`D`没有自己的任何未访问的邻居节点,我们回溯到节点`B`并转到其另外的邻居节点`E`。依此类推,直到我们访问了图中的所有节点。
2316

24-
Each time we visit the first neighbor and keep going until there's nowhere left to go, and then we backtrack to a point where there are again nodes to visit. When we've backtracked all the way to node `A`, the search is complete.
25-
每当我们访问第一个邻居并继续前进,直到无处可去,然后我们回溯到再次访问节点的点。 当我们一直回溯到节点`A`时,搜索就完成了。
17+
每当我们访问第一个邻居节点并继续前进,直到无处可去,然后我们回溯到之前访问的节点。 当我们一直回溯到节点`A`时,搜索就完成了。
2618

27-
For the example, the nodes were visited in the order `A`, `B`, `D`, `E`, `H`, `F`, `G`, `C`.
28-
例如,按照`A``B``D``E``H``F``G``C`的顺序访问节点。
19+
对于上面的例子,是按照`A``B``D``E``H``F``G``C`的顺序访问节点的。
2920

30-
The depth-first search process can also be visualized as a tree:
3121
深度优先搜索过程也可以显示为树:
3222

3323
![Traversal tree](Images/TraversalTree.png)
3424

35-
The parent of a node is the one that "discovered" that node. The root of the tree is the node you started the depth-first search from. Whenever there's a branch, that's where we backtracked.
3625
节点的父节点是“发现”该节点的节点。 树的根是您开始深度优先搜索的节点。 每当有一个分支时,那就是我们回溯的地方。
3726

38-
## The code
3927
## 代码
4028

41-
Simple recursive implementation of depth-first search:
4229
深度优先搜索的简单递归实现:
4330

4431
```swift
@@ -55,10 +42,8 @@ func depthFirstSearch(_ graph: Graph, source: Node) -> [String] {
5542
}
5643
```
5744

58-
Where a [breadth-first search](../Breadth-First%20Search/) visits all immediate neighbors first, a depth-first search tries to go as deep down the tree or graph as it can.
59-
[广度优先搜索](../Breadth-First%20Search/)首先访问所有直接邻居的情况下,深度优先搜索尝试尽可能地深入树或图形。
45+
[广度优先搜索](../Breadth-First%20Search/)首先访问所有直接邻居,而深度优先搜索尝试尽可能地深入树或图。
6046

61-
Put this code in a playground and test it like so:
6247
在 playground 里测试:
6348

6449
```swift
@@ -87,25 +72,18 @@ let nodesExplored = depthFirstSearch(graph, source: nodeA)
8772
print(nodesExplored)
8873
```
8974

90-
This will output: `["a", "b", "d", "e", "h", "f", "g", "c"]`
9175
打印结果是: `["a", "b", "d", "e", "h", "f", "g", "c"]`
9276

93-
## What is DFS good for?
9477
## DFS有什么用?
9578

96-
Depth-first search can be used to solve many problems, for example:
9779
深度优先搜索可用于解决许多问题,例如:
9880

99-
* Finding connected components of a sparse graph
100-
* [Topological sorting](../Topological%20Sort/) of nodes in a graph
101-
* Finding bridges of a graph (see: [Bridges](https://en.wikipedia.org/wiki/Bridge_(graph_theory)#Bridge-finding_algorithm))
102-
* And lots of others!
10381

10482
* 查找稀疏图的连通分量
10583
* 图中节点的[拓扑排序](../Topological%20Sort/)
106-
* 查找图形的桥梁(参见:[Bridges](https://en.wikipedia.org/wiki/Bridge_(graph_theory)#Bridge-finding_algorithm)
84+
* 查找图的桥梁(参见:[Bridges](https://en.wikipedia.org/wiki/Bridge_(graph_theory)#Bridge-finding_algorithm)
10785
* 还有很多其它应用!
10886

109-
*Written for Swift Algorithm Club by Paulo Tanaka and Matthijs Hollemans*
11087
*作者:Paulo Tanaka,Matthijs Hollemans*
111-
*翻译:[Andy Ron](https://github.com/andyRon)*
88+
*翻译:[Andy Ron](https://github.com/andyRon)*
89+
*校对:[Andy Ron](https://github.com/andyRon)*

Schedule.markdown

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -182,7 +182,7 @@
182182
|-----------------------------------|------------|:-------------:|----------|:---------:|
183183
| Graph | andyRon | Y | andyRon | Y❗️ |
184184
| Breadth-First Search | andyRon | Y | andyRon | Y❗️ |
185-
| Depth-First Search | andyRon | Y | | |
185+
| Depth-First Search | andyRon | Y | andyRon | Y❗️ |
186186
| Shortest Path | andyRon | Y | | |
187187
| Single-Source Shortest Paths | andyRon | Y | | |
188188
| Minimum Spanning Tree | andyRon | Y | | |

0 commit comments

Comments
 (0)