6. 深度优先和广度优先
之前介绍了最简单的搜索法:二分搜索。虽然它的算法复杂度非常低只有 O(logn),但使用起来也有局限:只有在输入是排序的情况下才能使用。这次讲解两个更复杂的搜索算法:
- 深度优先搜索(Depth-First-Search,以下简称DFS)
- 广度优先搜索(Breadth-First-Search,以下简称BFS)
基本概念
DFS 和 BFS 的具体定义这里不做赘述。笔者谈谈自己对此的形象理解:假如你在家中发现钥匙不见了,为了找到钥匙,你有两种选择:
- 从当前角落开始,顺着一个方向不停的找。假如这个方向全部搜索完毕依然没有找到钥匙,就回到起始角落,从另一个方向寻找,直到找到钥匙或所有方向都搜索完毕为止。这种方法就是 DFS。
- 从当前角落开始,每次把最近所有方向的角落全部搜索一遍,直到找到钥匙或所有方向都搜索完毕为止。这种方法就是 BFS。
我们假设共有 10 个角落,起始角落为 1,它的周围有 4 个方向,如下图:
DFS 的搜索步骤为:
- 1
- 2 -> 3 -> 4
- 5
- 6 ->7 -> 8
- 9 -> 10
即每次把一个方向彻底搜索完全后,才返回搜索下一个方向。
BFS 的搜索步骤为:
- 1
- 2 -> 5 -> 6 -> 9
- 3 -> 4
- 7
- 10
- 8
即每次访问上一步周围所有方向上的角落。
细心的朋友会记得,我之前在讲二叉树的时候,讲到了前序遍历和层级遍历,而这两者本质上就是 DFS 和 BFS。
DFS 的 Swift 实现:
func dfs(_ root: Node?) {
guard let root = root else {
return
}
visit(root)
root.visited = true
for node in root.neighbors {
if !node.visited {
dfs(node)
}
}
}
BFS 的 Swift 实现:
func bfs(_ root: Node?) {
var queue = [Node]()
if let root = root {
queue.append(root)
}
while !queue.isEmpty {
let current = queue.removeFirst()
visit(current)
current.visited = true
for node in current.neighbors {
if !node.visited {
queue.append(node)
}
}
}
}
永远记住:DFS 的实现用递归,BFS 的实现用循环(配合队列)。
iOS 实战演练
硅谷面试 iOS 工程师,有这样一个环节,给你 1 ~ 1.5 小时,从头开始实现一个小 App。我们来看这样一个题目:
实现一个找单词 App : 给定一个初始的字母矩阵,你可以从任一字母开始,上下左右,任意方向、任意长度,选出其中所有单词。
很多人拿到这道题目就懵了,完全不是我们熟悉的 UITableView 或者 UICollectionView 啊,这要咋整。我们来一步步分析。
第一步:实现字母矩阵
首先,我们肯定有个字符二阶矩阵作为输入,姑且记做:matrix: [[Character]]
。现在要把它展现在手机上,那么可行的方法,就是创建一个 UILabel 二维矩阵,记做 labels: [[UILabel]]
,矩阵中每一个 UILabel 对应的内容就是相应的字母。同时,我们可以维护 2 个全局变量,xOffset 和 yOffset。然后在for循环中创建相应的 UILabel 同时将其添加进 lables 中便于以后使用,代码如下:
var