在开始今天的话题之前,我们先了解一个概念,什么是图的遍历?

图的遍历就是从图中某一点出发访遍图中其余剩余定点,且每个顶点仅被访问一次,这个过程叫做图的遍历。

图的遍历主要被分为深度优先遍历广度优先遍历

此外,我们还要了解两个概念,邻接链表邻接矩阵

邻接链表的存储方法跟树的孩子链表示法相类似,是一种顺序分配和链式分配相结合的存储结构。如这个表头结点所对应的顶点存在相邻顶点,则把相邻顶点依次存放于表头结点所指向的单向链表中。

邻接矩阵的逻辑结构分为两部分:V和E集合。因此,用一个一维数组存放图中所有顶点数据;用一个二维数组存放顶点间关系(边或弧)的数据,这个二维数组称为邻接矩阵。邻接矩阵又分为有向图邻接矩阵和无向图邻接矩阵。

邻接链表的一个潜在缺陷就是无法快速判断一条边(u,v)是否是图中的某一边,唯一的解决办法就是再A[u]里面搜索节点v。而邻接矩阵就克服了这个缺陷,而付出的代价是更大的存储空间消耗。

深度优先搜索

深度优先搜索总是对最近才发现的点v的出发搜索,直到该点所有的出发边都被发现为止。一旦该节点v所有的出发边都被发现,搜索则回溯到v的前驱结点,来搜索该节点的出发边。该过程一直持续到从源节点可以到达的所有节点都被发现为止。如果存在还未发现的节点,则深度优先搜索将会从这些尚未发现的节点中任选一个作为新的源节点,并重复整个过程,直到所有的节点都被搜索到为止。

DFS实现算法:

  1. 先创建一个visited数组,初始化为false。
  2. 调用遍历函数,实现递归。
  3. 当相邻节点为false时(即没有被访问过),以该节点进行递归。
  4. 否则返回上一节点。

我们上伪代码吧!

n = len(M)
visited = [False]*n    #创建一个长度为n的visited数组def dfs(i):
visited[i] = True  #将visited数组上对应的i设置为True
for i in range(n):
if M[i][j] == 1 and not visited[j]:   #前提是没被访问过dfs(j) #递归count = 0for i in range(n):if visited[i] == False:dfs(i)count += 1return count

深度优先遍历有点像二叉树的前序遍历过程:

  1. 以某一节点开始,访问该节点
  2. 以该节点开始,重复1.(这里需要定义一个节点数大小的bool类型数组visited,来记录哪些节点访问过了,哪些节点没有访问过.)
  3. 当某个节点的邻接节点都访问过了,回退到上一个节点,访问上一个节点的其他相邻节点.
  4. 重复3.直至返回开始节点.

这是连通图的遍历方法,对于非连通图,我们只需循环调用递归,直至所有节点都访问过.

广度优先搜索

如果说,深度优先遍历类似于树的前序遍历,那么广度优先遍历就类似于树的层序遍历了。

广度优先遍历通过队列实现:

  1. 从某一节点开始,将该节点入队,找到该节点的所有相邻节点,将他们入队。
  2. 将该节点出队,再将队头节点的所有相邻节点入队。(这里也需要一个visited数组,已经入队过的节点不再入队.)
  3. 检查队列,对队头元素进行操作,出队。

为了更好的说明,我们上伪代码把!

void BFSTraverse(Graph G, Status(*Visit)(int v))
{/*按广度优先非递归遍历图G。使用辅助队列Q 和访问标志数组visited*/for (v=0;v<G,vexnum;++v)visited[v]=FALSE;InitQueue(Q); /*置空的国债队列Q*/if (!visited[v]) /*v 尚未访问*/{EnQucue(Q,v); /*v 入队列*/while (!QueueEmpty(Q)){ DeQueue(Q,u); /*队头元素出队并置为u*/visited[u]=TRUE; visit(u); /*访问u*/for(w=FistAdjVex(G,u); w; w=NextAdjVex(G,u,w))if (!visited[w]) EnQueue(Q,w); /*u 的尚未访问的邻接顶点w 入队列Q*/}}
}/*BFSTraverse*/

两个算法的总结

遍历的目的是为了找到合适的定点,那么选择哪一种遍历就要仔细斟酌了,深度优先遍历适合于目标明确,以找到目标为主要目的的情况,而广度优先遍历适合于在不断扩大遍历范围时,找到相对最优的情况。

下面是几道真题,我特意把深度优先搜索和广度优先搜索的真题混在一起了,大家在看真题的时候先要自己判断下是哪种类型的搜索哦!~!

Leetcode : 695. Max Area of Island (Easy)

给定一个包含了一些 0 和 1的非空二维数组 grid , 一个 岛屿 是由四个方向 (水平或垂直) 的 1 (代表土地) 构成的组合。你可以假设二维矩阵的四个边缘都被水包围着。

找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为0。)

示例 1:

[[0,0,1,0,0,0,0,1,0,0,0,0,0],

[0,0,0,0,0,0,0,1,1,1,0,0,0],

[0,1,1,0,1,0,0,0,0,0,0,0,0],

[0,1,0,0,1,1,0,0,1,0,1,0,0],

[0,1,0,0,1,1,0,0,1,1,1,0,0],

[0,0,0,0,0,0,0,0,0,0,1,0,0],

[0,0,0,0,0,0,0,1,1,1,0,0,0],

[0,0,0,0,0,0,0,1,1,0,0,0,0]]

对于上面这个给定矩阵应返回 6。注意答案不应该是11,因为岛屿只能包含水平或垂直的四个方向的‘1’。

class Solution(object):def dfs(self,grid,i,j):cnt = 1m = len(grid)n = len(grid[0])grid[i][j] = -1out = [[-1,0],[1,0],[0,-1],[0,1]]for k in range(4):p = i + out[k][0]q = j + out[k][1]if(p>=0 and p<m and q>=0 and q<n and grid[p][q] == 1):cnt += self.dfs(grid,p,q)return cntdef maxAreaOfIsland(self, grid):""":type grid: List[List[int]]:rtype: int"""big = 0m = len(grid)n = len(grid[0])for i in range(0,m):for j in range(0,n):if grid[i][j] == 1:big = max(big,self.dfs(grid,i,j))return big

Leetcode : 547. Friend Circles (Medium)

班上有 N 名学生。其中有些人是朋友,有些则不是。他们的友谊具有是传递性。如果已知 A 是 B 的朋友,B 是 C 的朋友,那么我们可以认为 A 也是 C 的朋友。所谓的朋友圈,是指所有朋友的集合。

给定一个 N * N 的矩阵 M,表示班级中学生之间的朋友关系。如果M[i][j] = 1,表示已知第 i 个和 j 个学生互为朋友关系,否则为不知道。你必须输出所有学生中的已知的朋友圈总数。

示例 :

输入:

[[1,1,0],

[1,1,0],

[0,0,1]]

输出: 2

说明:已知学生0和学生1互为朋友,他们在一个朋友圈。

第2个学生自己在一个朋友圈。所以返回2。

class Solution(object):def findCircleNum(self, M):""":type M: List[List[int]]:rtype: int"""if M == [] or M[0] == []:return 0n = len(M)visited = [False]*ndef dfs(i):visited[i] = Truefor j in range(n):if M[i][j] == 1 and not visited[j]:dfs(j)count = 0for i in range(n):if visited[i] == False:dfs(i)count += 1return count

Leetcode : 130. Surrounded Regions (Medium)

给定一个二维的矩阵,包含 'X' 和 'O'(字母 O)。

找到所有被 'X' 围绕的区域,并将这些区域里所有的 'O' 用 'X' 填充。

示例:

X X X X

X O O X

X X O X

X O X X

运行你的函数后,矩阵变为:

X X X X

X X X X

X X X X

X O X X

class Solution:def solve(self, board):""":type board: List[List[str]]:rtype: void Do not return anything, modify board in-place instead."""if board == None :return []for i in range(len(board)):for j in range(len(board[0])):if board[i][j] == 'O':if i == 0 or i == len(board) -1 or j == 0 or j == len(board[0]) -1:self.robot(board,i,j,len(board),len(board[0]))for i in range(len(board)):for j in range(len(board[0])):if board[i][j] == 'O':board[i][j] = 'X'elif board[i][j] == '*':board[i][j] = 'O'def robot(self,board,i,j,m,n):dx = [0,0,1,-1]dy = [1,-1,0,0]board[i][j] = '*'for idx in range(4):if i + dx[idx] >= 0 and i + dx[idx] < m and j + dy[idx] >= 0 and j + dy[idx] < n and board[i + dx[idx]][j + dy[idx]] == "O":self.robot(board,i+dx[idx],j+dy[idx],m,n)

接下来我们再顺带介绍下拓扑排序和强连通分量吧,之所以把它们和上面两个算法放在一起讲,是因为都属于图的范畴,大家可以了解下。

拓扑排序

对于一个有向无环图G来说,拓扑排序是G中所有节点的一种先行次序,该次序满足以下条件:如果图G包含(u,v),那么节点u处于在节点v的前面。可以将拓扑排序看作是将图的节点在一条水平线上排开,图的所有有向边都从左指向右。

强连通分量

在有向图G中,如果两点互相可达,则称这两个点强连通,如果G中任意两点互相可达,则称G是强连通图。

  1. 一个有向图是强连通的,当且仅当G中有一个回路,它至少包含每个节点一次。
  2. 非强连通有向图的极大强连通子图,称为强连通分量

我们有两种方法来判断是否为强连通图,我们来分别介绍下:

方法1:Korasaju算法

首先理解一下转置图的定义:将有向图G中的每一条边反向形成的图称为G的转置GT 。(注意到原图和GT 的强连通分支是一样的)

算法流程:

  1. 深度优先遍历G,算出每个结点u的结束时间f[u],起点如何选择无所谓。
  2. 深度优先遍历G的转置图GT ,选择遍历的起点时,按照结点的结束时间从大到小进行。遍历的过程中,一边遍历,一边给结点做分类标记,每找到一个新的起点,分类标记值就加1。
  3. 第2步中产生的标记值相同的结点构成深度优先森林中的一棵树,也即一个强连通分量

简而言之,就是先深度优先遍历,再将图转置后进行深度优先遍历,如果标记相同,可以理解为两点可以相互到达,就是强连通分量。

方法二:Tarjan算法

Tarjan算法是基于对图深度优先搜索的算法,每个强连通分量为搜索树中的一棵子树。

算法思想如下:

   dfn[u]表示dfs时达到顶点u的次序号,low[u]表示以u为根节点的dfs树中次序号最小的顶点的次序号,所以当dfn[u] = low[u]时,以u为根的搜索子树上所有节点是一个强连通分量。 先将顶点u入栈,dfn[u] = low[u] = ++idx,扫描u能到达的顶点v,如果v没有被访问过,则dfs(v),low[u] = min(low[u],low[v]),如果v在栈里,low[u] = min(low[u],dfn[v]),扫描完v以后,如果dfn[u] = low[u],则将u及其以上顶点出栈。

简而言之,就是根据深度优先将节点压到栈中,然后观察栈顶的节点能到达的距离栈底最近的那个节点,将其提取出来,就是强连通分量。

差不多就是这样了,希望本文能帮到你!~!

最后打个小广告,我的公众号,喜欢写点学习中的小心得,不介意可以关注下!~!

networkx 有向图强连通_leetcode刷题(四):搜索(深度优先搜索,广度优先搜索)拓扑排序,强连通分量...相关推荐

  1. 深度优先搜索遍历与广度优先搜索遍历

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 深度优先 ...

  2. 数据结构--图--图的数组存储表示,深度优先搜索遍历和广度优先搜索遍历

    图有四种存储结构:数组,邻接表,十字链表,邻接多重表.下面以数组为存储结构来实现图的深度优先搜索遍历和广度优先搜索遍历.其中广度优先搜索遍历中有用到STL中的queue,注意头文件的包含.具体代码如下 ...

  3. 6.1 图的深度优先和广度优先搜索

    图的广度优先搜索 图的的搜索算法主要分为广度优先搜索(breadth-first search或BFS)和深度优先搜索(depth-first search或DFS).首先讨论广度优先搜索算法. 称之 ...

  4. Python 实现图的深度优先和广度优先搜索

    在介绍 python 实现图的深度优先和广度优先搜索前,我们先来了解下什么是"图". 1 一些定义 顶点 顶点(也称为"节点")是图的基本部分.它可以有一个名称 ...

  5. 【Python排序搜索基本算法】之拓扑排序

    [Python排序搜索基本算法]之拓扑排序 版权声明:本文为博主原创文章,未经博主允许不得转载.

  6. 邻接矩阵的深度优先和广度优先搜索

    c语言中图的邻接矩阵的深度优先和广度优先搜索 #include<stdio.h> #include<stdlib.h> typedef struct {int vexs[7]; ...

  7. 深度优先和广度优先搜索

    目录 前言 1 深度优先搜索 2 广度优先搜索 3 深度优先和广度优先的比较 前言 最近面试,被问到了深度优先和广度优先搜索,这个我似曾相识,曾经大学的时候学到过,但是由于这几年的工作都未接触到,所以 ...

  8. 搜索技术【广度优先搜索】 - 嵌套广度优先搜索 【POJ No. 1475】 推箱子 Pushing Boxes

    搜索技术[广度优先搜索] - 嵌套广度优先搜索 [POJ No. 1475] 推箱子 Pushing Boxes 在广度优先搜索里面嵌套广度优先搜索的算法被称为嵌套广度优先搜索(或称双重广度优先搜索) ...

  9. java二叉树转换为链表_leetcode刷题笔记-114. 二叉树展开为链表(java实现)

    leetcode刷题笔记-114. 二叉树展开为链表(java实现) 题目描述 给你二叉树的根结点 root ,请你将它展开为一个单链表: 展开后的单链表应该同样使用 TreeNode ,其中 rig ...

最新文章

  1. 曾被无视多年,却成就19世纪最伟大的一场革命,影响了整个世界!
  2. Android初学者之轻松实现语音识别
  3. 2016年腾讯产品笔试真题
  4. 微信公众账号 token 验证失败 解决办法
  5. windows2003配置
  6. 云服务器 ECS > 块存储 > 加密云盘 > 加密概述
  7. eclipse adt如何切换到设计界面_如何设计出优秀的UI界面?这4个方面帮你快速优化...
  8. 全网首篇用人话讲清楚:光纤中的色散
  9. 新媒体运营的基本方法——四步法让你成为新媒体业内大佬
  10. Sem 2---Web Database---XML学习笔记[2]
  11. cocos渲染引擎分析(五)-----FBO实现多分辨率渲染
  12. 图片加文字用什么软件?推荐这三款软件给你
  13. leetcode题解(含解题思路)(持续更新中)
  14. 【递推】HDU -2018 母牛的故事斐波那契兔子数列
  15. NTLite精简Windows 10 Enterprise LTSC 2019 (1809) x64 - Client 10.0.17763.316
  16. 刺激战场电脑版android,绝地求生刺激战场怎么用电脑玩 绝地求生刺激战场pc版安装攻略...
  17. UVA 1103 - Ancient Messages(古代象形符号) By SuCicada
  18. usb插入检测系统、支持实时日志记录、邮箱报警
  19. 使用HTML+CSS制作加载动画
  20. 金融python有哪些证书可以考_python有证书考吗

热门文章

  1. SSC:面向大规模场景识别的语义扫描context(IROS2021)
  2. 全景图拼接算法实现与改进
  3. 计算机网络连接设备的有什么,常用网络连接设备有哪些?
  4. 图像的大小计算 位深和色深
  5. 常用转录组组装软件集合
  6. 在线作图|如何绘制一张好看的点棒图
  7. Linux学习(3)-常用命令
  8. MPB:林科院袁志林组-​栎类外生菌根形态学特征描述
  9. Gastroenterology:住院期间COVID-19患者肠道菌群的变化
  10. Nature:剖腹产到底好不好?——肠道菌群的视角