1,什么是广度优先搜索; 2,广度优先搜索应用场景;

登录以参加训练计划

1,广度优先搜索概念

广度优先搜索(BFS)是一种经典的算法,用于遍历或搜索树或图中的节点。其核心思想是从一个起始节点出发,首先访问所有的邻接节点,然后再依次访问这些邻接节点的邻接节点,形成一个层次结构的遍历方式。BFS 通常使用队列(Queue)数据结构来跟踪待访问的节点,确保按照层次顺序处理节点。

2,算法特点

广度优先搜索具有以下几个显著的特点:

  • 层次遍历:BFS 会从起始节点开始,逐层访问每一层的所有节点,直到所有节点都被访问为止。这使得 BFS 特别适合寻找最短路径问题。(因为最先被找到的节点一定是通过路径最短的。)
  • 使用队列:BFS 使用队列来保持待访问的节点,确保先访问的节点先被处理,这也是 BFS 的核心机制。
  • 访问标记:为了防止重复访问同一节点,BFS 通常会使用一个布尔数组或集合来标记已访问的节点。

3,适用场景

广度优先搜索适合解决许多问题,包括但不限于:

  • 最短路径问题:在无权图中,BFS 可用于寻找从起始节点到目标节点的最短路径。
  • 连通性问题:用于检查图的连通性,例如判断一个图是否是连通的。
  • 层次关系:BFS 可以用于构建或分析数据的层次结构,如树的层次遍历。
  • 寻路问题:在迷宫或地图中找到从起点到终点的路径。

4,数据结构

在实现 BFS 时,通常需要以下几个数据结构:

  • 队列:用于跟踪待访问的节点,FIFO(先进先出)原则确保节点按照层次顺序被访问。
  • 访问标记:布尔数组或集合用于标记已访问的节点,避免重复访问。

5,时间复杂度

广度优先搜索的时间复杂度通常为 O(V + E),其中 V 是图中的顶点数量,E 是边的数量。这个复杂度反映了算法需要访问每个节点和每条边。

6,空间复杂度

BFS 的空间复杂度主要由队列和访问标记结构决定:

  • 队列的大小在最坏情况下可能达到 O(V),因为需要存储所有未访问的节点。
  • 访问标记的空间复杂度也是 O(V),用于存储每个节点的访问状态。

7,优缺点

优点

  • 找到最短路径:在无权图中,BFS 能够找到从起始节点到任何节点的最短路径。
  • 简单明了:BFS 的实现逻辑简单,易于理解,特别适合初学者学习。

缺点

  • 空间复杂度高:与 DFS 相比,BFS 需要更多的内存,因为它同时需要存储大量的节点。
  • 执行效率低:在某些情况下,BFS 可能需要访问比 DFS 更多的节点,尤其在图较为稀疏时。

8,常用几种队列说明:

1,队列(queue)只允许从队尾入队、从队头出队,不允许在中间位置插入和删除,不支持数组表示法和随机访问。使用 queue 时需要引入头文件#include<queue>。队列的基本操作包括入队、出队、取队头、判断队空、求队列大小。

  • queue<int>q:创建一个空队 q,数据类型为 int。
  • q.push(x):x 入队。
  • q.pop():出队。
  • q.front():取队头(未出队)。
  • q.empty():判断队列是否为空,若为空,则返回 true。
  • q.size():求队列大小,返回队列中的元素个数。

2,deque(双端队列)是 C++ 标准模板库 (STL) 中的一个容器,它支持在两端进行高效地插入和删除操作。与队列不同的是,双端队列可以在头部和尾部进行这些操作,并且它还支持随机访问,这意味着你可以像访问数组一样通过索引来访问元素。

下面是 deque 的基本操作说明:

#include <deque>:包含 deque 的头文件。

  • deque<T> dq:创建一个空的 deque 对象 dq,其中 T 是 deque 中存储的元素类型。

  • dq.push_front(x):将元素 x 插入到 deque 的前端。

  • dq.push_back(x):将元素 x 插入到 deque 的后端。

  • dq.pop_front():移除 deque 的前端元素。

  • dq.pop_back():移除 deque 的后端元素。

  • dq.front():返回 deque 的前端元素(但不移除)。

  • dq.back():返回 deque 的后端元素(但不移除)。

  • dq.empty():如果 deque 是空的则返回 true,否则返回 false。

  • dq.size():返回 deque 中元素的数量。

  • dq.clear():移除 deque 中的所有元素。

  • dq.at(index) 或 dq[index]:通过索引访问 deque 中的元素。注意使用 at() 方法时,如果索引超出范围会抛出异常,而使用 [] 运算符不会检查边界。 这里是一个简单的示例来演示 deque 的使用:

这个程序创建了一个 deque,然后向其前端和后端添加了几个元素,并展示了如何移除它们以及获取 deque 的状态信息。


#include <iostream>
#include <deque>
using namespace std;

int main() {
    deque<int> dq;
    // 向 deque 的前后添加元素
    dq.push_front(5);
    dq.push_back(10);
    dq.push_front(3);

    // 输出 deque 的前端和后端元素
    cout << "Front: " << dq.front() << ", Back: " << dq.back() <<endl;

    // 移除前端和后端元素
    dq.pop_front();
    dq.pop_back();

    // 输出 deque 的大小
    cout << "Size after pop operations: " << dq.size() <<endl;

    // 检查 deque 是否为空
    if (dq.empty()) {
        cout << "Deque is empty." << endl;
    } else {
        cout << "Deque is not empty." <<endl;
    }

    return 0;
}

//bfs模板
struct Node {
	int x,y,step;
  .....
}
queue<Node> q;
void bfs() {
	标记起点 
	起点入队列 
	while(!q.empty()) {//队列不为空 
		ed nw=q.front();//返回队首
		for(拓展出接下来可能的状态) {
			ed nxt;
			记录这一状态
			判断状态是否合法 
			标记状态 
			q.push_back(nxt);//状态入队列 
		}
		q.pop_front();//弹出队首 
	}
}


9,BFS与DFS使用场景的区别

(1)核心区别:

dfs:

像探险家,一条路走到黑,走不通再回头换路,适合遍历所有可能。

dfs:

像雷达扫苗,一层一层向外扩散,适合找最短路径或最近解。

(2)什么时候用DFS?

典型场景:

1,要枚举所有可能的解(如排列组合、子集、拆分数)。 2,问题需要回溯(如迷宫的所有路径、数独填充)。 3,路径的深度最大多少,但不需要求最短路径。

题目:

1,数的全排列 2,n皇后问题 3,自然数的拆分 4,树的遍历

(3)什么时候用BFS?

典型场景:

1,需要找最短路径、最少操作次数(如迷宫最短路径,社交网络的最短关系链)。 2,问题的解在较浅的层次(靠近起点)。

题目:

1,走出迷宫(bfs会一层层扩散,第一次到达终点的一定是最短路径。) 2,二叉树的层次遍历。

(4)判断技巧?

三问法: 1,问题是否要求所有可能解?(是,DFS) 2,问题是否要求最短路径或最少多少步?(是,BFS) 3,问题是否可能在浅层?(是,BFS)

(5)有些情况两者均可?

比如岛屿数量问题等相关类型的题。两种解法都可。

章节 1. 新手

开放

题目 尝试 AC 难度
T1254  走出迷宫 26 4 9
P377  【例65.3】 细胞个数 3 2 10
P378  练65.1 水洼个数 11 2 10
T1330  【例8.3】最少步数 9 2 10
T1252  走迷宫 26 6 8
T1253  抓住那头牛 9 3 10

章节 2. 师傅

开放

题目 尝试 AC 难度
T1248  Dungeon Master 7 2 10
T1249  Lake Counting 1 1 10
T1250  The Castle 1 1 10
T1251  仙岛求药 1 1 10
T1256  献给阿尔吉侬的花束 1 1 10
T1257  Knight Moves 1 1 10

章节 3. 大神

开放

题目 尝试 AC 难度
P529  字串变换 0 0 (无)
 
参加人数
9
创建人