avatar

算法系列之深度优先搜索

深度优先搜索

    深度优先搜索(缩写DFS)有点类似广度优先搜索,也是对一个连通图进行遍历的算法。它的思想是从一个顶点V0开始,沿着一条路一直走到底,如果发现不能到达目标解,那就返回到上一个节点,然后从另一条路开始走到底,这种尽量往深处走的概念即是深度优先的概念。

    深度优先搜索需要一个,将经过的节点push进栈中,等返回时,再pop出来

1、基本流程

1)DFS遍历过程

    遍历整个图

1

2、算法比较与应用 BFS VS DFS

  • BFS是用来搜索最短径路的解是比较合适的,比如求最少步数的解,最少交换次数的解,因为BFS搜索过程中遇到的解一定是离根最近的,所以遇到一个解,一定就是最优解,此时搜索算法可以终止。而DFS搜索到的解不一定是离根最近的,只有全局搜索完毕,才能从所有解中找出离根的最近的解。
  • 空间代价上,DFS是有优势的,DFS不需要保存搜索过程中的状态,而BFS在搜索过程中需要保存搜索过的状态,而且一般情况需要一个队列来记录。
  • DFS适合搜索全部的解,因为要搜索全部的解,那么BFS搜索过程中,遇到离根最近的解,并没有什么用,也必须遍历完整棵搜索树,DFS搜索也会搜索全部,但是相比DFS不用记录过多信息,所以搜素全部解的问题,DFS显然更加合适。

3、经典题目

1)根节点出发的最大路径和问题

问题

给定一个非空二叉树,返回其从根节点出发的最大路径和。

举例

1
2
3
4
5
6
7
8
9
输入: [-10,9,20,None,None,15,7]

-10
/ \
9 20
/ \
15 7

输出: 25

思路

我们对路径进行遍历,找出其从各个节点出发的最大路径和

树的构建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None

def initTree(tree,i):
if tree[i] is None:
return None
if i < len(tree):
root = TreeNode(tree[i])
if i*2+1 < len(tree):
root.left = initTree(tree,i*2+1)
if i*2+2 < len(tree):
root.right = initTree(tree,i*2+2)
return root

tree = [-10,9,20,None,None,15,7]
root = initTree(tree,0)

递归遍历

1
2
3
4
5
6
def DFS(root:TreeNode)->int:
if root is None:
return 0
left = max(DFS(root.left),0) #得到左子树的最大路径
right = max(DFS(root.right),0) #得到右子树的最大路径和
return root.val+ max(left,right) #返回当前节点的最大路径和

栈实现DFS

使用栈无法得到路径和,因此在这部分,我们计算树的总和,同时使用了List的数据结构作为树

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
def DFS(tree:List[int])->int:
s = tree[0] #初始化树的sum
Q = [] #模拟栈的实现
label = [False]*len(tree) #用来记录是否遍历到
Q.append(0)
label[0] = True
while(len(Q) != 0):
index = Q[-1] #得到栈的顶点
left = 2*index+1
right = 2*index+2
if(left < len(tree) and not label[left] and tree[right]): #将左子树压栈
label[left] = True
Q.append(left)
s += tree[left]
continue #一定要有continue,这样才能保证一条路走下去
if(right < len(tree) and not label[right] and tree[right]): #右子树压栈
label[right] = True
Q.append(right)
s += tree[right]
continue
Q.pop(-1) #当该节点的左子树和右子树都遍历完成后,弹出栈
return s

2)整个树的最大路径问题

leetcode—题124二叉树中的最大路径和

问题

给定一个非空二叉树,返回其最大路径和

示例

1
2
3
4
5
6
7
8
9
输入: [-10,9,20,null,null,15,7]

-10
/ \
9 20
/ \
15 7

输出: 42 (15--20--7这个路径和最大)

思路

我们根据第一题可以知道,我们可以用DFS计算某一条路径的和,那么我们可以使用DFS得到包括根节点的路径和node.val + max(left,right) ,同时,我们也可以计算以某一结点(非根节点)为开头的路径和,比如这里的15--20--7,我们完全可以通过node.val + left + right得到,因此,我们在递归使用DFS时,得到包括根节点的路径和,再从内部逻辑得到最大路径

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def DFS(root:TreeNode)->int:
def max_gain(node):
nonlocal max_sum #记录最大路径和
if node is None:
return 0
#得到左子树的最大路径和,max保证了负数,我们可以舍去
left = max(max_gain(node.left),0)
#得到右子树的最大路径和
right = max(max_gain(node.right),0)
#得到以子树的某节点未开始节点的最大路径和
node_gain = node.val + left + right
max_sum = max(max_sum,node_gain)
return node.val + max(left,right)

max_sum = -math.inf
max_gain(root)
return max_sum

3)矩阵中的路径问题

leetcode—剑指offer面试题12 矩阵中的路径

问题

   请设计一个函数,用来判断在一个矩阵中是否存在一条包含某字符串所有字符的路径。路径可以从矩阵中的任意一格开始,每一步可以在矩阵中向左、右、上、下移动一格。如果一条路径经过了矩阵的某一格,那么该路径不能再次进入该格子。

示例

   例如,在下面的3×4的矩阵中包含一条字符串“bfce”的路径(路径中的字母用加粗标出)。

[[“a”,”b“,”c”,”e”],
[“s”,”f“,”c“,”s”],
[“a”,”d”,”e“,”e”]]

   但矩阵中不包含字符串“abfb”的路径,因为字符串的第一个字符b占据了矩阵中的第一行第二个格子之后,路径不能再次进入这个格子。

思路

   遍历矩阵中的每一个结点,将其作为DFS的开始节点,进行DFS的搜索过程,要注意的是,DFS经过的字符在之后的搜索中不能再进入,因此在DFS经过该字符时,我们要对该字符进行替换处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def exist(board: List[List[str]], word: str) -> bool:
def dfs(i,j,k):
#如果该节点越界,或者不满足相等条件,返回False
if i<0 or i>=len(board) or j < 0 or j>=len(board[0]) or board[i][j] != word[k]:
return False
#如果到达了word的尾部,返回True
if k == len(word)-1:
return True
#对搜索过的节点进行替换处理,来保证之后不会再经过该节点
tmp,board[i][j] = board[i][j] , "/"
#对该节点的四个方向进行遍历
res = (dfs(i,j-1,k+1) or dfs(i,j+1,k+1) or dfs(i-1,j,k+1) or dfs(i+1,j,k+1))
#这条路径遍历完成后,将该节点恢复
board[i][j] = tmp
return res

for i in range(len(board)):
for j in range(len(board[0])):
if dfs(i,j,0):
return True
return False

参考链接

文章作者: 白丁
文章链接: http://baidinghub.github.io/2020/04/03/%E7%AE%97%E6%B3%95%E7%B3%BB%E5%88%97%E4%B9%8B%E6%B7%B1%E5%BA%A6%E4%BC%98%E5%85%88%E6%90%9C%E7%B4%A2/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 BaiDing's blog
打赏
  • 微信
    微信
  • 支付寶
    支付寶

评论