数据结构(12)

目录

图的存储结构------图的遍历

[二、广度优先搜索(BFS)------ 仿树的层序遍历](#二、广度优先搜索(BFS)—— 仿树的层序遍历)

[1. 核心思想](#1. 核心思想)

[2. 实现步骤(依赖队列)](#2. 实现步骤(依赖队列))

[3. 示例(无向图)](#3. 示例(无向图))

[4. 关键特性](#4. 关键特性)

[5. BFS 与树的层序遍历的比较](#5. BFS 与树的层序遍历的比较)

[6. BFS算法效率分析](#6. BFS算法效率分析)

[7. DFS与BFS之间的比较](#7. DFS与BFS之间的比较)

图的存储结构------图的遍历

二、广度优先搜索(BFS)------ 仿树的层序遍历

图的广度优先搜索(Breadth-First Search, BFS)是一种按 "层次扩散" 方式遍历图的算法,核心是从起始顶点出发,先访问其所有直接邻接顶点(第一层),再依次访问这些邻接顶点的未访问邻接顶点(第二层),以此类推,直至所有顶点被访问。由于其 "逐层访问" 的特性,BFS 与树的层序遍历(按层次访问节点)在逻辑上高度相似,但因图的复杂性(可能有环、多路径)存在关键差异。

1. 核心思想

"先近后远":从起始顶点开始,优先访问距离最近的顶点(边数最少),再按距离递增顺序访问更远的顶点,确保每个顶点被访问一次。

2. 实现步骤(依赖队列)

  • 步骤 1:初始化一个队列,将起始顶点标记为 "已访问" 并入队。
  • 步骤 2 :当队列非空时,出队一个顶点 u,遍历其所有未访问的邻接顶点 w
    • 标记 w 为 "已访问",访问 w(如输出值)。
    • w 入队(保证后续按层次访问其邻接顶点)。
  • 步骤 3:重复步骤 2,直至队列为空(所有可达顶点均被访问)。
  • 补充:若图为非连通图,需对每个未访问顶点重复上述过程,以覆盖所有连通分量。

3. 示例(无向图)

图结构:顶点 {0,1,2,3,4},边 (0,1), (0,2), (1,3), (1,4), (2,4),从顶点 0 出发的 BFS 过程:

复制代码
初始:队列=[0],visited=[0:True, 1:False, 2:False, 3:False, 4:False]  
1. 出队0 → 访问0 → 遍历邻接顶点1、2(均未访问)→ 标记1、2为True,入队 → 队列=[1,2]  
2. 出队1 → 访问1 → 遍历邻接顶点0(已访问)、3、4(3未访问,4未访问)→ 标记3、4为True,入队 → 队列=[2,3,4]  
3. 出队2 → 访问2 → 遍历邻接顶点0(已访问)、4(已访问)→ 无新顶点入队 → 队列=[3,4]  
4. 出队3 → 访问3 → 遍历邻接顶点1(已访问)→ 无新顶点入队 → 队列=[4]  
5. 出队4 → 访问4 → 遍历邻接顶点1(已访问)、2(已访问)→ 队列空,结束。  
遍历序列:0 → 1 → 2 → 3 → 4  

例题:

算法:

cpp 复制代码
BFS1(List,n,v) //List为邻接表,v 为起点,Q[n]为辅助队列
{
    visit(v);
    visited[v]=1;  //访问顶点 v 并修改标记
    front=n-1;
    rear=0; //队列指针初始化
    q[rear]=v; //起始点入队
    while(rear != front)  //队不空时
    {
        front=(front+1)%n;
        v=q[front];  //访问过的顶点出队
        q=List[v].firstarc; //p指向第1个邻接点
        while(!p) //未到表尾,且邻接域未访问过,则先输出再改标记,最后再入队
        {
            if(!visited[adjvex(p)])
            {
                visited(adjvex(p));
                visited[adjvex(p)]=1;
                rear=(rear+1)%n;
                q[rear]=adjvex(p);
            }
            p=nextarc(p);
        }
    }
    return;  //指向单链表中下一个邻接点
}

4. 关键特性

  • 队列是核心:确保顶点按 "入队顺序"(即层次顺序)被访问。
  • 访问标记不可少 :图可能有环(如顶点 4 连接 1 和 2),visited 数组避免重复访问。
  • 时间复杂度:O(n+e)(n 为顶点数,e 为边数),与存储结构相关(邻接表遍历邻接顶点更快)。
  • 最短路径特性:在无权图中,BFS 得到的顶点访问顺序对应 "从起始顶点到该顶点的最短路径(边数最少)"。
  • 广度优先搜索是一种分层的搜索过程,每向前走一步可能访问一批顶点,不像DFS那样有回退的情况。因此BFS不是递归过程,其算法也不是递归的。

5. BFS 与树的层序遍历的比较

(1) 相同点(核心逻辑一致)

  • 层次访问:均按 "距离起始点的远近" 逐层访问节点,先访问第 k 层节点,再访问第 k+1 层节点。

  • 依赖队列:均通过队列实现 "先进先出" 的访问顺序,确保层次顺序不被打乱。

  • 遍历结果:在无环结构中(如树),两者遍历序列完全一致(见示例)。

    **示例:**树的层序遍历与对应图的 BFS二叉树结构:

    复制代码
        0
       / \
      1   2
     / \
    3   4

    层序遍历序列:0 → 1 → 2 → 3 → 4。将树视为无环图(边为父子关系),从 0 出发的 BFS 序列相同。

(2) 不同点(因图的复杂性导致)

对比维度 图的 BFS 树的层序遍历
环的处理 必须用 visited 数组标记已访问顶点,否则会因环导致无限循环(如顶点 4→1→4)。 无需标记(树无环,子节点仅由父节点指向,不会重复访问)。
"父节点" 的唯一性 图中顶点可能有多个 "前驱"(如示例中顶点 4 的前驱是 1 和 2),BFS 需处理多路径。 树中节点有唯一父节点,层序遍历只需按 "父→子" 顺序访问,无多路径问题。
连通性 若图非连通,需对每个未访问顶点执行 BFS(覆盖所有连通分量)。 树是连通结构,从根节点出发一次遍历即可覆盖所有节点。
适用场景扩展 除遍历外,可用于无权图最短路径、网络爬虫(按链接层次抓取)、社交网络好友推荐(一度、二度好友)等。 主要用于树的层次化访问(如打印二叉树的层、求树的深度)。

图的 BFS 是树的层序遍历在 "带环、多路径、非连通" 结构上的扩展:

  • 继承了 "逐层访问" 和 "队列依赖" 的核心逻辑,因此两者在无环连通结构(如树)上表现一致。
  • 增加了 "访问标记" 和 "多连通分量处理" 机制,以应对图的复杂性。

6. BFS算法效率分析

设图中有 n 个顶点,e 条边:

(1)如果使用邻接表来表示图,则BFS循环的总时间代价为 ,其中的 是顶点 i 的度

(2)如果使用邻接矩阵,则BFS对于每一个被访问到的顶点,都要遵循检测矩阵中的整整一行(n 个元素),总的时间代价为O()

7. DFS与BFS之间的比较

(1)空间复杂度相同,都是O(n)(借用了堆栈或队列)

(2)时间复杂度只与存储结构(邻接矩阵或邻接表)有关,而与搜索路径无关

相关推荐
磊-3 小时前
数组双指针总结
数据结构·算法
Skrrapper3 小时前
【C++】C++11都有什么新特性?
数据结构·c++
.小小陈.4 小时前
链表算法题
数据结构·算法·链表
大飞pkz4 小时前
【算法】排序算法汇总1
开发语言·数据结构·算法·c#·排序算法
前端小L4 小时前
单调栈的“降维打击”:从直方图到矩阵——再探「最大矩形」
数据结构·算法
逐步前行4 小时前
C数据结构--数组|矩阵|广义表
c语言·数据结构·矩阵
迷途之人不知返4 小时前
数据结构初识,与算法复杂度
数据结构
DARLING Zero two♡5 小时前
【优选算法】D&C-Mergesort-Harmonies:分治-归并的算法之谐
java·数据结构·c++·算法·leetcode
louisdlee.6 小时前
树状数组维护DP——前缀最大值
数据结构·c++·算法·dp