深度优先搜索(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