Python无向图遍历

bytetoy.cn

无向图的遍历同二叉树类似,也分为广度优先和深度优先两种方式,其逻辑和原理也是一样的。

本次以无向图邻接表为基础,实现无向图的两种遍历方式。

关于无向图的邻接表实现,请看上一节:Python无向图邻接表实现

一、广度优先遍历

bytetoy.cn

广度优先遍历以队列为基础进行实现,在实现的过程中,同二叉树类似,按层进行遍历。

实现步骤为

  • 输入的顶点(起始顶点)开始,从根据顶点的健获取键值,即获取顶点的邻接的全部顶点(按层访问),然后加入队列(append),同时记录已经放问过visited

  • 从队列中弹出一个顶点,重复以上步骤,将邻接的顶点加入队列。注意,这里就需要加入队列前进行一个判断,此顶点是否已经访问过,已经访问的顶点不再加入队列:

    if sub_vert in visited:
        continue
    
  • 直到字典的全部顶点已经访问,即队列长度为0:while len(que) > 0:

广度优先遍历的核心:一是用队列来按层添加后续访问的顶点;二是要记录已经访问的顶点,不再加入队列。

同时要理解set的特性以及用法

# 广度优先遍历,通过队列方式实现
def bfs(graph: GraphList, start_vert: str):
    res = []
    # 集合和队列存储顶点,均为str类型,做个注解
    visited = set[str]([start_vert])
    que = deque[str]([start_vert])
    # 获取顶点的邻接顶点,如果没有被访问,就添加至队列中,直到队列元素全部被访问完毕
    # 前提条件是所有节点之间有相连的边,如果有未连接的节点,则无法遍历。
    while len(que) > 0:
        vert = que.popleft()
        res.append(vert)
        # graph_list就是存储图的字典,根据健获取键值,键值是一个列表
        for sub_vert in graph.graph_list[vert]:
            # 如果该顶点已经放问过,则继续循环不甜加至队列
            if sub_vert in visited:
                continue
            que.append(sub_vert)
            visited.add(sub_vert)
    return res

二、深度优先遍历

bytetoy.cn

深度优先队列在前面二叉树中已经做个简单介绍:Python二叉树的遍历,同样使用递归的方式实现。

递归的使用,也在前面小节:Python递归算法及其简单应用,其应用的核心是要找到边界条件(终止条件),在无向图的森度优先遍历中,字典的键值链表循环遍历结束,即表示达到来终止条件

实现步骤为:

  • 输入顶点获取邻接第一个顶点(根据字典的键值,获取列表的第一个);
  • 判断邻接的顶点是否已经访问visited,如果未访问,则重复以上步骤,即使用递归,此时逻辑是一样的,只是输入的顶点变成来另一个(邻接的顶点)
  • 当遍历至最后一个无邻接顶点时,则返回(),返回上一步,或者继续向上返回。

这里需要注意的是,递归的终止条件是隐性的,for循环执行完毕,即返回。

# 深度优先遍历,使用递归方式实现
# 为列便于理解,为变量加上注解
# graph:邻接表实现的无向图
# visited:访问过顶点的集合
# res:访问的节点,这里要与visited区分,他们顺序不同的。这里注意set的属性,有兴趣可以观察set的内容和顺序
def dfs(graph: GraphList, visited: set[str], res: list[str], start_v):
    visited.add(start_v)
    # 经过节点即记录,前序方式
    res.append(start_v)
    # 递归边界条件(终止条件),这里是一种隐性的写法,for循环终止列,即退回到上一步
    for v in graph.graph_list[start_v]:
        if v in visited:
            continue
        # 对连接的新顶点继续遍历
        dfs(graph, visited, res, v)

三、全部代码

# 无向图遍历
from collections import deque
from graph_list import GraphList


# 广度优先遍历,通过队列方式实现
def bfs(graph: GraphList, start_vert: str):
    res = []
    # 集合和队列存储顶点,均为str类型,做个注解
    visited = set[str]([start_vert])
    que = deque[str]([start_vert])
    # 获取顶点的邻接顶点,如果没有被访问,就添加至队列中,直到队列元素全部被访问完毕
    # 前提条件是所有节点之间有相连的边,如果有未连接的节点,则无法遍历。
    while len(que) > 0:
        vert = que.popleft()
        res.append(vert)
        # graph_list就是存储图的字典,根据健获取键值,键值是一个列表
        for sub_vert in graph.graph_list[vert]:
            # 如果该顶点已经放问过,则继续循环不甜加至队列
            if sub_vert in visited:
                continue
            que.append(sub_vert)
            visited.add(sub_vert)
    return res


# 深度优先遍历,使用递归方式实现
# 为列便于理解,为变量加上注解
# graph:邻接表实现的无向图
# visited:访问过顶点的集合
# res:访问的节点,这里要与visited区分,他们顺序不同的。这里注意set的属性,有兴趣可以观察set的内容和顺序
def dfs(graph: GraphList, visited: set[str], res: list[str], start_v):
    visited.add(start_v)
    # 经过节点即记录,前序方式
    res.append(start_v)
    # 递归边界条件(终止条件),这里是一种隐性的写法,for循环终止列,即退回到上一步
    for v in graph.graph_list[start_v]:
        if v in visited:
            continue
        # 对连接的新顶点继续遍历
        dfs(graph, visited, res, v)


if __name__ == "__main__":
    verts = ['a', 'b', 'c', 'd', 'e']
    edges = [['a', 'b'], ['a', 'd'], ['b', 'c'], ['c', 'd'], ['c', 'e'], ['d', 'e'], ]
    graph = GraphList(verts, edges)
    print(graph.graph_list)

    print(bfs(graph, 'a'))

    dfs_res = []
    visited = set[str]()
    dfs(graph, visited, dfs_res, 'a')
    print(dfs_res,visited)

results matching ""

    No results matching ""

    results matching ""

      No results matching ""