Fork me on GitHub

深度优先与广度优先算法

深度优先与广度优先算法应用于图的遍历,当我们需要生成一个图G的生成树T的时候,可以使用深度优先或广度优先算法。
以下是我构造的一个无向连通图,用于下面的实例说明

# 顶点与边
vertexs = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
edges = [('A', 'C'), ('A', 'B'), ('A', 'D'), ('B', 'D'), ('B', 'G'), ('C', 'E'), ('C', 'F'), ('D', 'E'), ('D', 'G'),
('E', 'F'), ('E', 'H'), ('F', 'H'), ('G', 'H')]

深度优先遍历

算法思想:尽可能先对纵深方向的顶点进行访问

伪代码

DFS(v)
(7) num(v)=i++;
(8) for 所有与v邻接的顶点u
(9)     if num(u)是0
(10)       将edge(uv)连接到edges中;
(11)       DFS(u);

depthFirstSearch()
(1) for 所有向量v
(2)    num(v)=0;
(3) edges=null;
(4) i=1;
(5) while 有一个向量v使得num(v)是0
(6) DFS(v);
输出edges;

伪代码步骤说明:
depthFirstSearch函数,步骤(1)~(2):循环所有的顶点,将每个顶点初始标记设置为0
步骤(3):初始化edges为null
步骤(5)~(6):循环顶点的标记,只要存在一个顶点标记为0(0代表未访问,1代表已访问),则继续循环,调用DFS()函数,将顶点v传递进去
DFS函数,步骤(7):将当前的顶点的标记递增,设置为已访问
步骤(8)-(9)-(10):循环与v邻接的顶点u,如果邻接的顶点标记为0(未访问)则将uv结合的边加入到edges列表中
步骤(11):进行深度(递归)查找,将顶点u传入DFS函数

步骤说明


以上是以A为出发点的深度遍历的步骤图示,最终结果并不唯一,但是核心是在于纵深遍历

上述实例的Python代码

点击查看代码
# 顶点与边
vertexs = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
edges = [('A', 'C'), ('A', 'B'), ('A', 'D'), ('B', 'D'), ('B', 'G'), ('C', 'E'), ('C', 'F'), ('D', 'E'), ('D', 'G'),
         ('E', 'F'), ('E', 'H'), ('F', 'H'), ('G', 'H')]

# 顶点标记,0-代表未访问,1代表已访问
vertexs_mark = {}
# 最后产出的生成树的边
tree_edges = []
# 每个顶点的邻接顶点
adjacency_vertexs = {}

# 计算出每个顶点的邻接顶点
for edge in edges:
    if edge[0] in adjacency_vertexs.keys():
        adjacency_vertexs[edge[0]].append(edge[1])
    else:
        adjacency_vertexs[edge[0]] = [edge[1]]
    if edge[1] in adjacency_vertexs.keys():
        adjacency_vertexs[edge[1]].append(edge[0])
    else:
        adjacency_vertexs[edge[1]] = [edge[0]]

print(adjacency_vertexs)

def depth_first_search():
    # 顶点标记初始化为0
    for vertex in vertexs:
        vertexs_mark[vertex] = 0

    # 循环所有顶点
    for vertex in vertexs:
        if vertexs_mark[vertex] == 0:
            dfs(vertex)
    print(tree_edges)


def dfs(vertex):
    vertexs_mark[vertex] = 1
    for u in adjacency_vertexs[vertex]:
        if vertexs_mark[u] == 0:
            # 将此条边加入tree_edges
            tree_edges.append((vertex, u))
            # 递归调用,纵深查找
            dfs(u)

depth_first_search()

广度优先遍历

算法思想:尽可能先对横向的顶点进行访问

伪代码

breadthFirstSearch()
    for 所有顶点u                     # 循环所有顶点
        num(u)=0;                   # 将所有顶点初始标记设置为0
    edges=null;                     # 初始化edges,edges为存储的遍历结果
    i=1;
    while 存在一个顶点v使得num(v)==0   # 只要有一个顶点的标记为0,则循环继续
        num(v)=i++;                 # 将顶点的标记递增
        enqueue(v);//进入队列        # 将顶点放入队列
        while 队列非空               # 如果队列非空
            v=dequeue();            # 如果队列非空则将一个顶点出队
            for 所有和v邻接的顶点u             # 找到与当前顶点v邻接的所有顶点,并循环处理
                if num(u)是0                # 如果邻接的顶点的标记为0
                    num(u)=i++;             # 将顶点的标记递增
                enqueue(u);                 # 将顶点放入循环队列
                将edge(vu)连接到edges中       # 将顶点v与顶点u连接的顶点放入遍历结果edges中
            输出edges;

步骤说明


以上是以A为出发点的广度遍历的步骤图示,最终结果并不唯一,核心在于先对横向的顶点进行访问

上述实例的Python代码

点击查看代码
class Queue:
    """ 队列 """
    def __init__(self):
        self.__data = []

    def enqueue(self, data):
        """往队列头中添加一个新元素"""
        self.__data.insert(0, data)

    def travel(self):
        """遍历所有元素"""
        for i in self.__data:
            print(i, end='')
        print('')

    def dequeue(self):
        """从队列尾删除一个元素"""
        return self.__data.pop()

    def is_empty(self):
        """判断队列是否为空"""
        return self.__data == []

    def size(self):
        """返回队列的元素个数"""
        return len(self.__data)

# 顶点与边
vertexs = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
edges = [('A', 'C'), ('A', 'B'), ('A', 'D'), ('B', 'D'), ('B', 'G'), ('C', 'E'), ('C', 'F'), ('D', 'E'), ('D', 'G'),
         ('E', 'F'), ('E', 'H'), ('F', 'H'), ('G', 'H')]

# 顶点标记,0-代表未访问,1代表已访问
vertexs_mark = {}
# 最后产出的生成树的边
tree_edges = []
# 每个顶点的邻接顶点
adjacency_vertexs = {}

# 计算出每个顶点的邻接顶点
for edge in edges:
    if edge[0] in adjacency_vertexs.keys():
        adjacency_vertexs[edge[0]].append(edge[1])
    else:
        adjacency_vertexs[edge[0]] = [edge[1]]
    if edge[1] in adjacency_vertexs.keys():
        adjacency_vertexs[edge[1]].append(edge[0])
    else:
        adjacency_vertexs[edge[1]] = [edge[0]]

print(adjacency_vertexs)
vertex_queue = Queue()

# 广度优先搜索
def breadth_first_search():
    # 顶点标记初始化
    for vertex in vertexs:
        vertexs_mark[vertex] = 0
    # 循环所有顶点,循环所有顶点是因为图可能是非连通图
    for vertex in vertexs:
        if vertexs_mark[vertex] == 0:
            # 进入队列
            vertex_queue.enqueue(vertex)
            while not vertex_queue.is_empty():
                # 出队列
                v = vertex_queue.dequeue()
                bfs(v)

def bfs(vertex):
    # 当前顶点的标记设置为1
    vertexs_mark[vertex] = 1
    for u in adjacency_vertexs[vertex]:
        if vertexs_mark[u] == 0:
            # 当前顶点的邻接顶点
            vertexs_mark[u] = 1
            # 顶点u进入队列
            vertex_queue.enqueue(u)
            # 将此条边加入tree_edges
            tree_edges.append((vertex, u))

breadth_first_search()

### 总结 > 1、图的遍历算法有深度优先与广度优先两种算法,深度优先是先对纵深的顶点进行遍历,广度优先是先对横向的顶点进行遍历 2、深度优先遍历与广度优先遍历的时间复杂度为O(|V| + |E|)或O(|V|^2),V代表顶点,E代表边
posted @   三脚半猫  阅读(166)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 【.NET】调用本地 Deepseek 模型
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· DeepSeek “源神”启动!「GitHub 热点速览」
· 我与微信审核的“相爱相杀”看个人小程序副业
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库
点击右上角即可分享
微信分享提示