预计阅读15min
深度优先搜索(Depth-First Search, DFS)是一种图遍历的方法。因发明「深度优先搜索算法」,约翰 · 霍普克洛夫特与罗伯特 · 塔扬在1986年共同获得计算机领域的最高奖:图灵奖。
来看看深度优先搜索干了一件什么事吧。
图简介 给定一幅由节点和边组成的图G(V,E),V是顶点的集合,E表示边的集合。顶点是对现实对象的一种抽象,而边表示的是两个对象之间一定的关系。
图在计算机中的存储方式有:矩阵存储(适用于稠密的图),邻接表存储(适合稀疏图)。例:无向图的矩阵
图又分有向图和无向图,无向图的矩阵表示是一个对称矩阵(M(i,j)=M(j,i)),有向图的矩阵则不一定对称。图还有无权图和有权图 之分。无权图的矩阵里的数值只表示是否连通,而有权图矩阵里的数值大小可表示价值、距离、代价等现实意义。
图的遍历当我们需要从图中获取信息时,我们就需要访问图的节点,用dfs的目的不唯一,可能是为了查找是否存在某个节点,可能是要数图中有多少个环,或者判断图能否一笔画等等。重点是避免重复访问也不能漏掉一些节点。图的遍历方式有深度优先遍历(DFS)和广度优先遍历(BFS)两种。本文只介绍深度优先遍历。所谓DFS,通俗的理解就是,当我访问完一个节点(v1)后,我从与当前节点相邻且未访问过的节点集合(S)中拿出一个来,对选出来的这个节点(v2)我们重复与前一个节点相同的过程,直到没有满足条件的节点可以被访问,于是我们退回到v1处,从S中再选出一个未被访问过的节点进行上述过程,直到整张图的节点都被访问过。以上的描述很明显具有递归结构。我们也知道,递归算法都可以用栈来实现。DFS算法递归实现的框架如下:
《小巷》
小巷
又弯又长
没有门
没有窗
我拿把旧钥匙
敲着厚厚的墙
----------------------顾城
深度优先搜索(Depth-First Search, DFS)是一种图遍历的方法。因发明「深度优先搜索算法」,约翰 · 霍普克洛夫特与罗伯特 · 塔扬在1986年共同获得计算机领域的最高奖:图灵奖。

图简介 给定一幅由节点和边组成的图G(V,E),V是顶点的集合,E表示边的集合。顶点是对现实对象的一种抽象,而边表示的是两个对象之间一定的关系。

v1 | v2 | v3 | v4 | |
v1 | 1 | 1 | 0 | 1 |
v2 | 1 | 1 | 0 | 1 |
v3 | 0 | 0 | 1 | 0 |
v4 | 1 | 1 | 0 | 1 |
图的遍历当我们需要从图中获取信息时,我们就需要访问图的节点,用dfs的目的不唯一,可能是为了查找是否存在某个节点,可能是要数图中有多少个环,或者判断图能否一笔画等等。重点是避免重复访问也不能漏掉一些节点。图的遍历方式有深度优先遍历(DFS)和广度优先遍历(BFS)两种。本文只介绍深度优先遍历。所谓DFS,通俗的理解就是,当我访问完一个节点(v1)后,我从与当前节点相邻且未访问过的节点集合(S)中拿出一个来,对选出来的这个节点(v2)我们重复与前一个节点相同的过程,直到没有满足条件的节点可以被访问,于是我们退回到v1处,从S中再选出一个未被访问过的节点进行上述过程,直到整张图的节点都被访问过。以上的描述很明显具有递归结构。我们也知道,递归算法都可以用栈来实现。DFS算法递归实现的框架如下:
1 procedure DFS(G, V) is
2 将顶点V标记为 “已遍历”
3 对于 V 的所有邻接点 W
4 如果 w 未被遍历过 则
5 深度优先遍历 (G,W)
我们需要一个数组来记录某个节点是否访问过。用栈来实现DFS算法栈的特点是先进后出(Fisrt in last out), 利用这 个特点,我们可以在访问当前节点之后,把它所有当前未被访问的邻点入栈,然后弹出栈顶元素,即最后一个入栈的节点。对弹出的节点标记为已访问,然后同样把他的所有当前未被访问的邻点入栈(后入栈的节点会比先入栈的节点先出来,以此达到了深度优先遍历的目的)。这样一直进行下去,直到栈空。procedure DFS-iterative(G, v) islet S be a stack //声明s为一个栈S.push(v) //根节点(起始节点)入栈while S is not empty do //栈不空时一直循环 v = S.pop() //弹出栈顶元素赋值给v label v as visited //标记v为已访问
//v的所有未被访问的邻点中入栈 for all edges from v to w in G.adjacentEdges(v) do
if w is unvisited S.push(w)
注意以上程序只能访问一个连通枝中的节点,若要访问一个不连通的图中的中的所有节点,只需要在该程序以外加一个大循环,从未被访问过的节点开始访问其他的连通枝即可。
例:矩阵中的最长递增路径
难度:困难给定一个整数矩阵,找出最长递增路径的长度。对于每个单元格,你可以往上,下,左,右四个方向移动。 你不能在对角线方向上移动或移动到边界外(即不允许环绕)。 示例 1: 输入: nums = [ [9,9,4], [6,6,8], [2,1,1] ] 输出: 4 解释: 最长递增路径为 [1, 2, 6, 9]。来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/longest-increasing-path-in-a-matrix 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。分析: 求最长路径肯定会想到DFS,但是本题若直接对每个节点进行一下DFS然后取最长的长度, 会超时。可以借助动态规划的思想--即记忆化数组。由于我们的题目要求是最长的递增路径,而从每个节点开始的最长的递增路径是一定的。我们可以在深度优先遍历的过程中记下已经搜索过的节点的最长递增路径。下一次在经过该节点时直接用就行了,不需要再搜索。计算关系式是: 在数组索引不越界的情况下: lenth(x,y)=1+max(lenth(x-1,y),lenth(x+1,y),lenth(x,y-1),lenth(x,y+1))我们设置一个seen数组,其形状和给的矩阵形状相同,用来记录每个节点是否访问过以及其最长的递增路径长度,若seen[x][y]==0,则(x,y)没访问过,否则seen[x][y]至少为1。 程序如下:
class Solution {
public:
//递归实现,在下一个程序被调用
int dfs(vector >& matrix,vector >&seen, int x, int y,int&m,int&n){
int maxv=1;
if(seen[x][y])return seen[x][y];
if((x-1)>=0&&matrix[x][y]
if((y-1)>=0&&matrix[x][y]
if((x+1)
if((y+1)
seen[x][y]=maxv;
return maxv;
}
int longestIncreasingPath(vector >& matrix) {
if(matrix.size()==0)return 0;
int m=matrix.size(), n=matrix[0].size(),maxv=1;
vector >seen(m,vector(n,0));
for(int i=0;i
{
for(int j=0;j
{ if(seen[i][j])maxv=max(maxv,seen[i][j]);//(i,j)位置已搜索过
else maxv=max(maxv,dfs(matrix,seen,i,j,m,n));//否则搜索(i,j)
}
}
return maxv;
}
};
今天分享《人工智能的未来》
回复futur获取链接