Python图算法之深度优先搜索

本文详细介绍了深度优先搜索(DFS)的概念及其在图算法中的应用,包括深度优先森林、拓扑排序、强连通图和强连通分量的识别。同时,讨论了Kosaraju、Tarjan和Gabow算法在寻找有向图强连通分量中的作用,以及启发式规则在优化搜索算法中的重要性,如骑士周游问题中的Warnsdorff规则。
摘要由CSDN通过智能技术生成

深度优先搜索(Depth First Search, DFS):
是一种用于遍历或搜索树或图的算法。沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当节点v的所在边都己被探寻过,搜索将回溯到发现节点v的那条边的起始节点。这一过程一直进行到已发现从源节点可达的所有节点为止。如果还存在未被发现的节点,则选择其中一个作为源节点并重复以上过程,整个进程反复进行直到所有节点都被访问为止。属于盲目搜索。
广度优先搜索算法(BFS)是一次建立搜索树的一层,而DFS算法则是尽可能深的搜索树的一枝。

深度优先森林:
当深度优先搜索算法建立一组树时,称之为深度优先森林。

拓扑排序:
将一个有向无圈图(DAG)转换为一个包含该图所有不重复顶点且路径不可逆的线性排列,称为“拓扑排序”,可以起到指示事件优先级的作用。

拓扑排序是深度优先搜索(DFS)的一个简单却有效的应用。

拓扑排序要满足以下两个条件:
1、每个顶点出现且只出现一次。
2、若A在序列中排在B的前面,则在图中不存在从B到A的路径。

			 ┌连通:又称强连通,只有一个极大连通子图(又称强连通分量),为其本身。
	┌有向图——|			
	|		 └不连通:包含多个极大连通子图。
	|
图——|			   ┌只有一个极大连通子图(又称连通分量),为其本身。
	|		┌连通——|
	|		|	   └极小连通子图为其生成树。
	└无向图——|
			|		 ┌多个极大连通子图。
			└不连通——|
			 		 └多个极小连通子图。

注意:
有向图没有极小连通子图的概念;无向连通图的极小连通子图为无向连通图的生成树。

强连通图:
指每个顶点皆可以经由该图上的边抵达其他每个点的有向图。即对于此图上每个点对(Va, Vb),皆存在路径Va→Vb以及Vb→Va。

强连通分量(Strongly connected component):
指一张有向图G的极大强连通子图G’。如果将每个强连通分量缩成一个点,则原图G将会变成一张有向无环图。有向环是一个强连通分量,而任何强连通分量皆具有至少一个有向环。强连通图只有一个强连通分量,即是其自身;非强连通的有向图有多个强连通分量。

连通分量:
无向图G的一个极大连通子图(即连通分量是图G中不被其他连通子图包含的连通子图,所以图G可以有多个连通分量)称为原图G的一个连通分量(或连通分支)。连通图只有一个连通分量,即其自身;非连通的无向图有多个连通分量。

Kosaraju、Tarjan、Gabow算法皆为寻找有向图强连通分量的有效算法。但是由于在Tarjan和Gabow算法的过程中,只需要进行一次深度优先搜索,因而相对Kosaraju算法较有效率,不过Kosaraju算法的优势是依次求出的强连通分量已经是拓扑排序的。

特殊深度优先搜索:
示例:
骑士周游是以创建深度最深并无分支的优先搜索树为目标。

骑士周游问题:
在国际象棋棋盘上仅用“骑士(马)”这个棋子进行操作,目的是找到一条可以让骑士访问所有格子,并且每个格子只能走一次的走棋序列。一个这样的走棋序列称为一次“周游”。

Warnsdorff规则:
指在所有可走且尚未经过的方格中,马只可能走这样一个方格:从该方格出发,马能跳的方格数最少;如果可跳的方格数相等,则从当前位置看,方格序号小的优先。依照这一规则往往可以找到一条路径,但是并不一定能够成功。

走棋规则:
如下图,骑士起始在36位置,在国际象棋中,骑士走棋(类似中国象棋中的马走日)只能从36跳到19、21、30、46、53、51、42、26。

骑士周游棋盘:
在这里插入图片描述

from pythonds.graphs import Graph

# 建立一个 n*n 棋盘对应的完整骑士周游图
def knightGraph(bdSize):
	ktGraph = Graph()										#实例化图类
	for row in range(bdSize):								
		for col in range(bdSize):							#遍历每个格
			nodeId = posToNodeId(row, col, bdSize)			#遍历生成每个节点的ID值
			# 以下3行可删除,若想显示棋盘,不可删除,若要周游,则需删除,因此处节点ID为str
			# if nodeId < 10:									
			# 	nodeId = '0' + str(nodeId)
			# print('  ' + str(nodeId), end = '')
			# 以上3行可删除,若想显示棋盘,不可删除,若要周游,则需删除,因此处节点ID为str
			newPositions = genLegalMoves(row, col, bdSize)	#单步合法走棋
			for e in newPositions:							#遍历每个可走棋位置
				nid = posToNodeId(e[0], e[1], bdSize)		#遍历生成当前格子可走棋位置的ID值
				# 以下3行可删除,若只想显示棋盘,也可删除,若要周游,则需删除,因此处节点ID为str
				# if nid < 10:									
				# 	nid = '0' + str(nid)
				# print('  ' + str(nid), end = '')
				# 以上3行可删除,若只想显示棋盘,也可删除,若要周游,则需删除,因此处节点ID为str
				ktGraph.addEdge
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值