数据结构和算法 十八、图的结构、最短路径、广度优先搜索、深度优先搜索
一、图的定义和基本术语
二、图的存储结构
1、邻接矩阵
邻接矩阵(Adjacency Matrix)是表示顶点之间相邻关系的矩阵。
用邻接矩阵表示法表示图,除了一个用于存储邻接矩阵的二维数组外,还需要用一个一维数组来存储顶点信息。
2、邻接表
邻接表(Adjacency List)是图的一种链式存储结构。在邻接表中,对图中每个顶点vi建立一个单链表,把与vi相邻接的顶点放在这个链表中。邻接表中每个单链表的第一个结点存放有关顶点的信息,把这一结点看成链表的表头,其余结点存放有关边的信息,这样邻接表便由两部分组成:表头结点表和边表。
3、十字链表
十字链表(Orthogonal List)是有向图的另一种链式存储结构。可以看成是将有向图的邻接表和逆邻接表结合起来得到的一种链表。在十字链表中,对应于有向图中每一条弧有一个结点,对应于每个顶点也有一个结点。
4、邻接多重表
邻接多重表的结构和十字链表类似。在邻接多重表中,每一条边用一个结点表示,它由如下图所示的6个域组成。其中,mark为标志域,可用以标记该条边是否被搜索过;ivex和jvex为该边依附的两个顶点在图中的位置;ilink指向下一条依附于顶点ivex的边;jlink指向下一条依附于顶点jvex的边,info为指向和边相关的各种信息的指针域。
三、图的遍历
1、理解最短路径
路径是介于起点和终点之间的所有节点构成的序列,同一个节点不会在同一条路径上出现两次。也就是说,路径表示起点和终点之间的路线,它是连接起点和终点的顶点集p,在p中没有重复的顶点。
路径的长度是通过对组成路径的边进行计数完成计算的,在所有的路径中,长度最短的路径称为最短路径。最短路径的计算在图论算法中广泛使用,但并不总是直接计算。有多种算法可以用来寻找起始节点和结束节点之间的最短路径,其中一个最流行的算法是狄克斯特拉(Dijkstra)算法。
狄克斯特拉算法用于每条边都有关联数字的图,这些数字称为权重(weight)。
狄克斯特拉算法包含4个步骤
(1) 找出最便宜的节点,即可在最短时间内前往的节点。
(2) 对于该节点的邻居,检查是否有前往它们的更短路径,如果有,就更新其开销。
(3) 重复这个过程,直到对图中的每个节点都这样做了。
(4) 计算最终路径。
以下面的图为例
python代码示例
# 构建这个十分简单的图
graph = {}
graph["start"] = {}
graph["start"]["a"] = 6
graph["start"]["b"] = 2
graph["a"] = {}
graph["a"]["fin"] = 1
graph["b"] = {}
graph["b"]["a"] = 3
graph["b"]["fin"] = 5
graph["fin"] = {}
# 构建花费表
infinity = float("inf")
costs = {}
costs["a"] = 6
costs["b"] = 2
costs["fin"] = infinity
# 构建parent表
parents = {}
parents["a"] = "start"
parents["b"] = "start"
parents["fin"] = None
processed = []
def find_lowest_cost_node(costs):
lowest_cost = float("inf")
lowest_cost_node = None
# 循环每一个节点
for node in costs:
cost = costs[node]
# 如果是目前为止成本最低的,还没有处理...
if cost < lowest_cost and node not in processed:
# 将其设置为新的最低成本节点。
lowest_cost = cost
lowest_cost_node = node
return lowest_cost_node
# 找到您尚未处理的成本最低的节点。
node = find_lowest_cost_node(costs)
# 如果您已经处理了所有节点,那么这个 while 循环结束
while node is not None:
cost = costs[node]
# Go through all the neighbors of this node.
neighbors = graph[node]
for n in neighbors.keys():
new_cost = cost + neighbors[n]
# 如果通过这个节点到达这个邻居更便宜
if costs[n] > new_cost:
# 更新此节点的成本。
costs[n] = new_cost
# 该节点成为该邻居的新父节点。
parents[n] = node
# 标记节点已经被处理了
processed.append(node)
# 寻找下一个没处理的节点
node = find_lowest_cost_node(costs)
print("Cost from the start to each node:")
print(costs)
2、深度优先搜索(DFS)
深度优先查找(Depth-First Search,DFS)算法也称为深度优先搜索算法,有点类似于前序遍历法,因为查找的过程也是遍历的过程,所以也习惯叫深度优先遍历。从图的某一顶点开始遍历,被访问过的顶点就做上已访问的记号,接着遍历此顶点的所有相邻且未访问过的顶点中的任意一个顶点,并做上已访问的记号,再以该点为新的起点继续进行深度优先搜索。
深度优先查找算法(或深度优先遍历)是使用堆栈和递归的技巧来遍历图的。
遍历过程如下示例:
Visited:包含所有已访问的节点。Queue:包含尚未访问的节点。
python代码示例
# 使用字典定义图
graph={ 'Amin' : {'Wasim', 'Nick', 'Mike'},
'Wasim' : {'Imran', 'Amin'},
'Imran' : {'Wasim','Faras'},
'Faras' : {'Imran'},
'Mike':{'Amin'},
'Nick':{'Amin'}}
# 定义深度优先搜索函数
def dfs(graph, start, visited=None):
if visited is None:
visited = set()
visited.add(start)
print(start)
for next in graph[start] - visited:
dfs(graph, next, visited)
return visited
# 从Amin开始搜索
dfs(graph,'Amin')
3、广度优先搜索(BFS)
广度优先查找(Breadth-First Search,BFS)算法则是使用队列和递归的技巧来查找的。当我们所处理的图aGraph存在层次的概念时,BFS的效果最好。
广度优先查找算法(或广度优先遍历)是从图的某一顶点开始遍历,被访问过的顶点就做上已访问的记号,接着遍历此顶点的所有相邻且未访问过的顶点中的任意一个顶点,并做上已访问的记号,再以该点为新的起点继续进行广度优先遍历。
BFS假设在给定图中遍历每条边的代价相同,而在Dijkstra算法中,遍历图中各条边的代价则可以是不同的,这就需要将代价纳入算法设计中,进而将BFS修改为Dijkstra算法。
遍历过程如下示例:
Visited:包含所有已访问的节点。Queue:包含尚未访问的节点。
python代码示例
# 定义图
graph={ 'Amin' : {'Wasim', 'Nick', 'Mike'},
'Wasim' : {'Imran', 'Amin'},
'Imran' : {'Wasim','Faras'},
'Faras' : {'Imran'},
'Mike':{'Amin'},
'Nick':{'Amin'}}
# 定义广度优先搜索
def bfs(graph, start):
visited = []
queue = [start]
while queue:
node = queue.pop(0)
if node not in visited:
visited.append(node)
neighbours = graph[node]
for neighbour in neighbours:
queue.append(neighbour)
return visited
# 从Amin开始搜索
bfs(graph,'Amin')
相关文章
- 算法题——深度优先搜索
- 数据结构与算法-2 最短路径 Dijkstra A*搜索 [MD]
- (算法)二分查找的搜索区间
- python code practice(二):KMP算法、二分搜索的实现、哈希表
- CentOS 文本搜索grep
- CRM webClient UI搜索参数里max hit是怎么被后台服务器处理的
- 基于SA模拟退火算法的多车辆TSP问题matlab仿真,实现多车辆各自搜索最优路径
- 基于形态学处理算法的迷宫路线搜索matlab仿真
- 基于精英混沌搜索策略的交替正余弦算法-附代码
- Python实现直方图梯度提升回归模型(HistGradientBoostingRegressor算法)并基于网格搜索进行优化同时绘制PDP依赖图项目实战
- 数据结构与算法问题 二叉搜索树
- 【Android 逆向】修改运行中的 Android 进程的内存数据 ( 使用 IDA 分析要修改的内存特征 | 根据内存特征搜索修改点 | 修改进程内存 )
- 二叉搜索树的后序遍历序列
- 一个推荐系统,实现完整的设计-在百度搜索关键词推荐案例
- 012-elasticsearch5.4.3【五】-搜索API【一】搜索匹配所有matchAllQuery、全文查询[matchQuery、multiMatchQuery、commonTermsQuery、queryStringQuery、simpleQueryStringQuery]
- 算法导论 文章12章 二叉搜索树
- B-树和B+树的应用:数据搜索和数据库索引
- 如何利用AI识别未知——加入未知类(不太靠谱),检测待识别数据和已知样本数据的匹配程度(例如使用CNN降维,再用knn类似距离来实现),将问题转化为特征搜索问题而非决策问题,使用HTM算法(记忆+模式匹配预测就是智能),GAN异常检测,RBF
- 数据结构和算法 十六、指数查找/跳转搜索/鞍背搜索