——本节内容为Bilibili王道考研《数据结构》P59视频内容笔记。
目录
一、与树的广度优先遍历之间的联系
1.树的广度优先遍历(层次遍历)
(1)算法步骤
①初始化一个辅助队列;
②根结点入队;
③若队列非空,则队头结点出队,并访问该结点,然后将其左、右子树结点插入队尾(如果有的话);
(2)算法演示
(3)特点
不存在“回路”,搜索相邻的结点时,不可能搜到已经访问过的结点。
2.图的广度优先遍历
(1)算法思想
从指定结点出发进行访问,然后找到相邻结点进行访问,循环往复,直到访问完所有的结点;
(2)特点
搜索相邻的结点时,有可能搜到已经访问过的结点;
(3)算法步骤
①找到与一个顶点相邻的所有顶点(用FirstNeighbor(G,x)和NextNeighbor(G,x,y)两个基本操作来实现);
②标记哪些顶点被访问过(定义bool型数组:bool visited[MAX_VERTEX_NUM];来标记每个顶点有没有被访问过);
③需要一个辅助队列;
二、BFS算法
1.代码实现
bool visited[MaxVertexNum]; //访问标记数组
void BFSTraverse(MGraph G) //对图G进行广度优先遍历
{
for (int i = 1; i < G.vexnum; ++i)
visited[i] = false; //访问标记数组初始化
InitQueue(Q); //初始化辅助队列Q
for (int i = 0; i < G.vexnum; ++i) //从1号顶点开始遍历
if (!visited[i]) //对每个连通分量调用一次BFS
BFS(G, i); //vi为访问过,从vi开始BFS
}
void BFS(MGraph G, int v) //从顶点v出发,广度优先遍历图G
{
visit(v); //访问初始顶点v
visited[v] = true; //对v做已访问标记
EnQueue(Q, v); //顶点v入队列Q
while (!isEmpty(Q))
{
DeQueue(Q, v); //顶点v出队
for (w = FirstNeighbor(G, v); w >= 0; w = NextNeighbor(G, v, w)) //检测v所有邻接点
if (!visited[w]) //w为v的尚未访问的邻接顶点
{
visit(w); //访问顶点w
visited[w] = true; //对w做已访问标记
EnQueue(Q, w); //顶点w入队列
}
}
}
2.代码解释
(1)BFS函数
①从顶点v出发,广度优先遍历图G;
②访问结点后标记为true;
③访问完毕后入队;
④while循环:队不空时,队头元素出队,这里注意出队操作第二个形参会接收出队元素,然后将v值进入后面的for循环来访问刚才出队的顶点相邻的顶点;
⑤for循环:w=刚才出队元素的第一个邻接点,执行访问、标记、入队操作,然后找到下一个和刚才出队元素相邻的点,直到没有相邻的点,此时w=NextNeighbor返回的-1<0,退出for循环;
⑥if循环:如果还没被标记为true,就进行访问、标记、入队操作。
(2)BFSTraverse函数
①该函数主要是用于“非连通图”的情况,因为BFS只能遍历完所有与指定结点相连通的结点;如果是非连通图,则需要判断还有没有未被标记为true的元素,对其进行BFS;
②先初始化访问标记数组;
③初始化辅助队列;
④从1号结点开始遍历,每个连通分量(极大连通子图)调用一次BFS;
⑤对于无向图,调用BFS函数的次数=连通分量数;
三、广度优先遍历序列
1.邻接矩阵
同一个图的邻接矩阵表示方式唯一,因此广度优先遍历序列唯一;
2.邻接表
同一个图的邻接表表示方式不唯一,因此广度优先遍历序列不唯一;
四、复杂度分析
1.邻接矩阵
(1)访问|V|个顶点需要O(|V|)的时间;
(2)查找每个顶点的邻接点都需要O(|V|)的时间,而总共有|V|个顶点;
(3)两个加起来是,所以时间复杂度=;
2.邻接表
(1)访问|V|个顶点需要O(|V|)的时间;
(2)查找各个顶点的邻接点共需要O(|E|)的时间;
(3)所以时间复杂度=O(|V|+|E|);
五、广度优先生成树
1.每次访问邻接点时,第一次访问的结点作为孩子结点组成的树;
2.广度优先生成树由广度优先遍历过程确定。由于邻接表的表示方式不唯一,因此基于邻接表的广度优先生成树也不唯一;
3.对于非连通图的广度优先遍历,可得到广度优先生成森林。