想要了解dfs与bfs,就得了解队列和栈。
一、栈与队列
1.栈
栈说白了就是先入后出。把栈类比为一个容器。只有一个口,所以如果我们想要取出最底层也就是最先放入的元素,只能最后取出它。
栈基础操作有如下几种:
- push 放入
- pop 拿出
- empty 是否为空
- size 栈的大小
- top 获取栈顶元素
下面将用两种方式实现:
- 用数组或者链表模拟栈
#include <iostream> using namespace std; const int N = 100; int stk[N], top = 0; int main() { //使用数组模拟栈 int x; cin >> x; //push放入栈中 stk[++top] = x; //输出栈顶元素 cout << stk[top] << endl; //pop拿出(不管top元素删不删,top--就行) top--; // 常用的就是,获取栈顶元素的同时,弹出栈顶元素 int u = stk[top--]; return 0; }
以上是普通静态数组。但是考虑到现实生活由于入栈的元素可能会源源不断地增加,因此我们可以使用动态数组或者链表,这样就无须自行处理数组扩容问题。这里贴出hello算法里对这个的网址。https://www.hello-algo.com/chapter_stack_and_queue/stack/#1https://www.hello-algo.com/chapter_stack_and_queue/stack/#1
- stl库写栈
首先就是 #include <stack>
定义:stack<数据类型> stk;
使用:
- stk.push(x);
- stk.pop();
- stk.top();
- stk.empty();//true表示空
- stk.size();//返回正整数
#include <iostream>
#include <stack>
using namespace std;
int main()
{
stack<int>stk;
int x;
cin >> x;
//放入栈中
stk.push(x);
//输出栈顶
cout << stk.top() << endl;
//弹出栈顶
stk.pop();
return 0;
}
2.队列
队列则是:“先入先出”。「队列 queue」是一种遵循先入先出规则的线性数据结构。顾名思义,队列模拟了排队现象,即新来的人不断加入队列尾部,而位于队列头部的人逐个离开。
队列基础操作有如下几种:
- push 放入(从队尾)
- pop 拿出(从队首)
- empty 是否为空
- size 栈的大小
- front
#include <iostream>
using namespace std;
const int N = 100;
int q[N], front = 0, tail = 0;
int main()
{
//使用数组模拟队列
int x;
cin >> x;
//push 放入队列中
q[++tail] = x;
//pop 把元素移除(只能从队首)
front++;
//size 队列长度
int size = tail - front + 1;
//empty 看size是否为0
//front
return q[front];
return 0;
}
/* 初始化队列 */
queue<int> queue;
/* 元素入队 */
queue.push(1);
queue.push(3);
queue.push(2);
queue.push(5);
queue.push(4);
/* 访问队首元素 */
int front = queue.front();
/* 元素出队 */
queue.pop();
/* 获取队列的长度 */
int size = queue.size();
/* 判断队列是否为空 */
bool empty = queue.empty();
额外:优先队列,双向队列
http://t.csdnimg.cn/KpFjNhttp://t.csdnimg.cn/KpFjN5.2 队列 - Hello 算法 (hello-algo.com)https://www.hello-algo.com/chapter_stack_and_queue/queue/#5215.3 双向队列 - Hello 算法 (hello-algo.com)https://www.hello-algo.com/chapter_stack_and_queue/deque/
/* 初始化双向队列 */
deque<int> deque;
/* 元素入队 */
deque.push_back(2); // 添加至队尾
deque.push_back(5);
deque.push_back(4);
deque.push_front(3); // 添加至队首
deque.push_front(1);
/* 访问元素 */
int front = deque.front(); // 队首元素
int back = deque.back(); // 队尾元素
/* 元素出队 */
deque.pop_front(); // 队首元素出队
deque.pop_back(); // 队尾元素出队
/* 获取双向队列的长度 */
int size = deque.size();
/* 判断双向队列是否为空 */
bool empty = deque.empty();
双向队列兼具栈与队列的逻辑,因此它可以实现这两者的所有应用场景,同时提供更高的自由度。
我们知道,软件的“撤销”功能通常使用栈来实现:系统将每次更改操作
push
到栈中,然后通过pop
实现撤销。然而,考虑到系统资源的限制,软件通常会限制撤销的步数(例如仅允许保存 50 步)。当栈的长度超过 50 时,软件需要在栈底(队首)执行删除操作。但栈无法实现该功能,此时就需要使用双向队列来替代栈。请注意,“撤销”的核心逻辑仍然遵循栈的先入后出原则,只是双向队列能够更加灵活地实现一些额外逻辑。
二、DFS深度优先搜索-----栈
1.指数型搜索
模板:
void dfs(int step){
判断边界
尝试每一种可能 for(i=1;i<=n;i++){
继续下一步 dfs(step+1)
}
返回
}
#include <iostream>
using namespace std;
const int N = 100;
int n;//输入的节点数
int st[N] = {}; //决策数组 0 表示没有搜索过, 1表示选择 , 2表示没有选择
// 对于每一个点,都需要有一次决策,无论是1还是2
// 搜索是把所有的情况都考虑
// u表示对u进行抉择,已经抉择了u个数
void dfs(int u) {
// 递归出口,当所有节点都决策完毕时输出结果
if (u > n){
for (int i = 0; i < n; i++) {
// 只有当第i个节点被选择时才输出
if (st[u] = 1){
cout << i << " ";
}
}
}
// 选择第u个节点(标记为1)并继续搜索下一个节点
st[u] = 1;
dfs(u + 1);
// 恢复第u个节点的状态(标记为0)并选择不选择第u个节点(标记为2)并继续搜索下一个节点
st[u] = 0;
st[u] = 2;
dfs(u + 1);
// 继续恢复第u个节点的状态(标记为0)以备下次决策使用
st[u] = 0;
}
int main()
{
cin >> n;
dfs(1);
return 0;
}
Python Tutor code visualizer: Visualize code in Python, JavaScript, C, C++, and Javahttps://pythontutor.com/render.html#code=%23include%20%3Ciostream%3E%0A%0Ausing%20namespace%20std%3B%0A%0Aconst%20int%20N%20%3D%20100%3B%0A%0Aint%20n%20,%20st%5BN%5D%20%3D%20%7B%7D%3B%20//%200%20%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E6%90%9C%E7%B4%A2%E8%BF%87%EF%BC%8C%201%E8%A1%A8%E7%A4%BA%E9%80%89%E6%8B%A9%20%EF%BC%8C%202%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E9%80%89%E6%8B%A9%0A%0A//%20%E5%AF%B9%E4%BA%8E%E6%AF%8F%E4%B8%80%E4%B8%AA%E7%82%B9%EF%BC%8C%E9%83%BD%E9%9C%80%E8%A6%81%E6%9C%89%E4%B8%80%E6%AC%A1%E5%86%B3%E7%AD%96%EF%BC%8C%E6%97%A0%E8%AE%BA%E6%98%AF1%E8%BF%98%E6%98%AF2%20%0A//%20%E6%90%9C%E7%B4%A2%E6%98%AF%E6%8A%8A%E6%89%80%E6%9C%89%E7%9A%84%E6%83%85%E5%86%B5%E9%83%BD%E8%80%83%E8%99%91%20%0A%0A//%20u%E8%A1%A8%E7%A4%BA%E5%AF%B9u%E8%BF%9B%E8%A1%8C%E6%8A%89%E6%8B%A9%EF%BC%8C%E5%B7%B2%E7%BB%8F%E6%8A%89%E6%8B%A9%E4%BA%86u%E4%B8%AA%E6%95%B0%20%0Avoid%20dfs%28%20int%20u%20%29%20%7B%0A%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E5%87%BA%E5%8F%A3%20%EF%BC%8C%20%E6%AF%8F%E4%B8%80%E4%B8%AA%E7%82%B9%E9%83%BD%E6%8A%89%E6%8B%A9%E8%BF%87%E4%BA%86%20%0A%20%20%20%20if%28u%20%3E%20n%20%29%7B%0A%20%20%20%20%20%20%20%20for%28int%20i%20%3D%201%3B%20i%20%3C%3D%20n%20%3B%20i%2B%2B%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20//%20%E6%88%91%E4%BB%AC%E5%8F%AA%E6%9C%89%E5%9C%A8%E9%80%89%E6%8B%A9%E7%AC%ACi%E4%B8%AA%E7%82%B9%E7%9A%84%E6%97%B6%E5%80%99%E8%BE%93%E5%87%BA%20%0A%20%20%20%20%20%20%20%20%20%20%20%20if%28st%5Bu%5D%20%3D%3D%201%20%29%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20cout%20%3C%3C%20i%20%3C%3C%20%22%20%22%20%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%20%20return%20%3B%0A%20%20%20%20%7D%0A%20%20%20%20%0A%20%20%20%20%0A%20%20%20%20//%20%E9%80%89%E6%8B%A9%0A%20%20%20%20//%20st%5Bu%5D%20%3D%200%3B%20%E8%BF%99%E5%8F%A5%E8%AF%9D%E9%80%9A%E5%B8%B8%E7%9C%81%E7%95%A5%20%0A%20%20%20%20st%5Bu%5D%20%3D%201%3B%0A%20%20%20%20dfs%28u%20%2B%201%29%3B%0A%20%20%20%20%0A%20%20%20%20//%20%E6%81%A2%E5%A4%8D%E7%8E%B0%E5%9C%BA%EF%BC%8C%E6%8E%A7%E5%88%B6%E5%8F%98%E9%87%8F%20%EF%BC%9A%20%E5%8D%95%E4%B8%80%E5%8F%98%E9%87%8F%E5%8E%9F%E5%88%99%20%0A%20%20%20%20st%5Bu%5D%20%3D%200%3B%20%0A%20%20%20%20//%20%E4%B8%8D%E9%80%89%E6%8B%A9%20%0A%20%20%20%20st%5Bu%5D%20%3D%202%3B%0A%20%20%20%20dfs%28u%20%2B%201%29%3B%0A%20%20%20%20%0A%20%20%20%20//%20%E7%BB%A7%E7%BB%AD%E6%81%A2%E5%A4%8D%E7%8E%B0%E5%9C%BA%0A%20%20%20%20st%5Bu%5D%20%3D%200%3B%20%0A%7D%0A%0Aint%20main%28%29%7B%0A%20%20n%3D3%3B%0A%20%20%20%20dfs%281%29%3B%0A%20%20%20%20%0A%20%20%20%20return%200%3B%0A%7D&cppShowMemAddrs=true&cumulative=false&curInstr=38&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp_g%2B%2B9.3.0&rawInputLstJSON=%5B%5D&textReferences=false
例题1 P2036 Perket
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
// 全局变量声明
int n; // n代表数字的个数
int mindif = 999999999; // mindif用于存储最小差值,初始化为一个非常大的数
int s[10], b[10]; // s数组和b数组分别用于存储输入的数字和它们对应的值
// perket函数是一个递归函数,用于计算所有可能的组合
// u代表当前处理的数字的索引
// sp代表当前选择的s数组中数字的乘积
// bs代表当前选择的b数组中数字的和
void perket(int u, int sp, int bs) {
// 递归终止条件:当u大于n时,表示所有数字都已经处理完毕
if (u > n) {
// 如果bs为0,表示没有选择任何b数组中的数字,直接返回
if (!bs) {
return;
}
// 更新最小差值
mindif = min(mindif, abs(sp - bs));
return;
}
// 递归调用1:不选择当前数字
perket(u + 1, sp, bs);
// 递归调用2:选择当前数字
perket(u + 1, sp * s[u], bs + b[u]);
}
int main() {
// 输入数字的个数
cin >> n;
// 输入每个数字及其对应的值
for (int i = 1; i <= n; i++) {
cin >> s[i] >> b[i];
}
// 调用perket函数开始计算
perket(1, 1, 0);
// 输出最小差值
cout << mindif << endl;
return 0;
}
Python Tutor code visualizer: Visualize code in Python, JavaScript, C, C++, and Javahttps://pythontutor.com/render.html#code=%23include%20%3Ciostream%3E%20%20%0A%23include%20%3Calgorithm%3E%20%20%0A%23include%20%3Ccstdio%3E%20%20%0A%23include%20%3Ccmath%3E%20%20%0Ausing%20namespace%20std%3B%20%20%0A%20%20%0A//%20%E5%85%A8%E5%B1%80%E5%8F%98%E9%87%8F%E5%A3%B0%E6%98%8E%20%20%0Aint%20n%3B%20//%20n%E4%BB%A3%E8%A1%A8%E6%95%B0%E5%AD%97%E7%9A%84%E4%B8%AA%E6%95%B0%20%20%0Aint%20mindif%20%3D%20999999999%3B%20//%20mindif%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%EF%BC%8C%E5%88%9D%E5%A7%8B%E5%8C%96%E4%B8%BA%E4%B8%80%E4%B8%AA%E9%9D%9E%E5%B8%B8%E5%A4%A7%E7%9A%84%E6%95%B0%20%20%0Aint%20s%5B10%5D,%20b%5B10%5D%3B%20//%20s%E6%95%B0%E7%BB%84%E5%92%8Cb%E6%95%B0%E7%BB%84%E5%88%86%E5%88%AB%E7%94%A8%E4%BA%8E%E5%AD%98%E5%82%A8%E8%BE%93%E5%85%A5%E7%9A%84%E6%95%B0%E5%AD%97%E5%92%8C%E5%AE%83%E4%BB%AC%E5%AF%B9%E5%BA%94%E7%9A%84%E5%80%BC%20%20%0A%20%20%0A//%20perket%E5%87%BD%E6%95%B0%E6%98%AF%E4%B8%80%E4%B8%AA%E9%80%92%E5%BD%92%E5%87%BD%E6%95%B0%EF%BC%8C%E7%94%A8%E4%BA%8E%E8%AE%A1%E7%AE%97%E6%89%80%E6%9C%89%E5%8F%AF%E8%83%BD%E7%9A%84%E7%BB%84%E5%90%88%20%20%0A//%20u%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E5%A4%84%E7%90%86%E7%9A%84%E6%95%B0%E5%AD%97%E7%9A%84%E7%B4%A2%E5%BC%95%20%20%0A//%20sp%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E9%80%89%E6%8B%A9%E7%9A%84s%E6%95%B0%E7%BB%84%E4%B8%AD%E6%95%B0%E5%AD%97%E7%9A%84%E4%B9%98%E7%A7%AF%20%20%0A//%20bs%E4%BB%A3%E8%A1%A8%E5%BD%93%E5%89%8D%E9%80%89%E6%8B%A9%E7%9A%84b%E6%95%B0%E7%BB%84%E4%B8%AD%E6%95%B0%E5%AD%97%E7%9A%84%E5%92%8C%20%20%0Avoid%20perket%28int%20u,%20int%20sp,%20int%20bs%29%20%7B%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E7%BB%88%E6%AD%A2%E6%9D%A1%E4%BB%B6%EF%BC%9A%E5%BD%93u%E5%A4%A7%E4%BA%8En%E6%97%B6%EF%BC%8C%E8%A1%A8%E7%A4%BA%E6%89%80%E6%9C%89%E6%95%B0%E5%AD%97%E9%83%BD%E5%B7%B2%E7%BB%8F%E5%A4%84%E7%90%86%E5%AE%8C%E6%AF%95%20%20%0A%20%20%20%20if%20%28u%20%3E%20n%29%20%7B%20%20%0A%20%20%20%20%20%20%20%20//%20%E5%A6%82%E6%9E%9Cbs%E4%B8%BA0%EF%BC%8C%E8%A1%A8%E7%A4%BA%E6%B2%A1%E6%9C%89%E9%80%89%E6%8B%A9%E4%BB%BB%E4%BD%95b%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E6%95%B0%E5%AD%97%EF%BC%8C%E7%9B%B4%E6%8E%A5%E8%BF%94%E5%9B%9E%20%20%0A%20%20%20%20%20%20%20%20if%20%28!bs%29%20%7B%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20return%3B%20%20%0A%20%20%20%20%20%20%20%20%7D%20%20%0A%20%20%20%20%20%20%20%20//%20%E6%9B%B4%E6%96%B0%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%20%20%0A%20%20%20%20%20%20%20%20mindif%20%3D%20min%28mindif,%20abs%28sp%20-%20bs%29%29%3B%20%20%0A%20%20%20%20%20%20%20%20return%3B%20%20%0A%20%20%20%20%7D%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E8%B0%83%E7%94%A81%EF%BC%9A%E4%B8%8D%E9%80%89%E6%8B%A9%E5%BD%93%E5%89%8D%E6%95%B0%E5%AD%97%20%20%0A%20%20%20%20perket%28u%20%2B%201,%20sp,%20bs%29%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E9%80%92%E5%BD%92%E8%B0%83%E7%94%A82%EF%BC%9A%E9%80%89%E6%8B%A9%E5%BD%93%E5%89%8D%E6%95%B0%E5%AD%97%20%20%0A%20%20%20%20perket%28u%20%2B%201,%20sp%20*%20s%5Bu%5D,%20bs%20%2B%20b%5Bu%5D%29%3B%20%20%0A%7D%20%20%0A%20%20%0Aint%20main%28%29%20%7B%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%85%A5%E6%95%B0%E5%AD%97%E7%9A%84%E4%B8%AA%E6%95%B0%20%20%0A%20%20%20%20n%3D4%3B%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%85%A5%E6%AF%8F%E4%B8%AA%E6%95%B0%E5%AD%97%E5%8F%8A%E5%85%B6%E5%AF%B9%E5%BA%94%E7%9A%84%E5%80%BC%20%20%0A%20%20%20%20s%5B0%5D%3D1,s%5B1%5D%3D2,s%5B2%5D%3D3,s%5B3%5D%3D4%3B%0A%20%20%20%20b%5B0%5D%3D7,b%5B1%5D%3D6,b%5B2%5D%3D8,b%5B3%5D%3D9%3B%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%B0%83%E7%94%A8perket%E5%87%BD%E6%95%B0%E5%BC%80%E5%A7%8B%E8%AE%A1%E7%AE%97%20%20%0A%20%20%20%20perket%281,%201,%200%29%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20//%20%E8%BE%93%E5%87%BA%E6%9C%80%E5%B0%8F%E5%B7%AE%E5%80%BC%20%20%0A%20%20%20%20cout%20%3C%3C%20mindif%20%3C%3C%20endl%3B%20%20%0A%20%20%20%20%20%20%0A%20%20%20%20return%200%3B%20%20%0A%7D&cppShowMemAddrs=true&cumulative=false&curInstr=0&heapPrimitives=nevernest&mode=display&origin=opt-frontend.js&py=cpp_g%2B%2B9.3.0&rawInputLstJSON=%5B%5D&textReferences=false
在给定的代码中,每次递归的两种选择(选择当前数字和不选择当前数字)可能会产生重复的结果。这意味着,递归函数perket
可能会计算出重复的组合。
为了解决这个问题,可以在递归过程中添加一些逻辑来跳过已经计算过的组合,或者使用其他方法来避免重复计算。例如,可以维护一个集合或哈希集合来跟踪已经计算过的组合,并在递归函数中进行检查。
如果不采取措施避免重复计算,则可能会导致递归函数的时间复杂度增加,因为重复计算相同的组合会导致更多的递归调用。在处理大量数据时,这可能会导致性能问题。
2.组合型搜索
例题2 全排列
【题意】
先给一个正整数 ( 1 < = n < = 10 ),输出所有全排列。
什么是全排列,例如n=3,输出所有组合,并且按字典序输出:
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
每个全排列一行,相邻两个数用空格隔开(最后一个数后面没有空格)
【输入格式】
一行一个整数n。
【输出格式】
输出1~n的所有全排列。
【样例输入】
3
【样例输出】
1 2 3
1 3 2
2 1 3
2 3 1
3 1 2
3 2 1
#include <iostream> // 引入输入输出流库
#include <algorithm> // 引入算法库
#include <cstdio> // 引入标准输入输出库
#include <cmath> // 引入数学库
using namespace std; // 使用标准命名空间
int n, ans[10], used[10]; // 定义全局变量,n表示要选取的数字个数,ans数组用于存放结果,used数组用于标记数字是否被使用过
void dfs(int cnt){ // 定义深度优先搜索函数,cnt表示当前已经选取的数字个数
if (cnt > n) { // 如果已经选取的数字个数大于n,说明已经选取了所有数字,输出结果并返回
for (int i = 1; i <= n; i++) { // 遍历所有数字并输出
cout << ans[i] << " ";
}
cout << endl;
return;
}
for (int i = 1; i <= n; i++){ // 枚举所有数字
if (used[i] == 0) { // 如果数字i还没有被使用过
ans[cnt] = i; // 将数字i放入当前位置
used[i] = 1; // 标记数字i为已使用
dfs(cnt + 1); // 递归搜索下一个位置
ans[cnt] = 0; // 撤销当前位置的选择,即清空当前位置
used[i] = 0; // 撤销对数字i的选择,即将其标记为未使用
}
}
}
int main() { // 主函数
cin >> n; // 输入要选取的数字个数n
dfs(1); // 从第一个位置开始搜索
return 0; // 程序正常结束
}
这段代码中的回溯是通过以下步骤实现的:
1. 在dfs函数中,首先检查是否已经选取了所有数字(即`cnt`是否大于`n`)。如果是,则输出当前结果并返回。
2. 然后,函数使用一个循环来枚举所有数字。如果某个数字还没有被使用过(即`used[i]`等于0),则进行以下操作:
- 将该数字放入当前位置(即`ans[cnt] = i`)。
- 标记该数字为已使用(即`used[i] = 1`)。
- 递归调用`dfs(cnt + 1)`来搜索下一个位置。
3. 在递归调用返回后,执行回溯操作:
- 撤销当前位置的选择,即将`ans[cnt]`重置为0,表示该位置还没有被选择。
- 撤销对数字`i`的选择,即将`used[i]`重置为0,表示数字`i`现在可以再次被选择。
4. 重复上述过程,直到所有可能的排列都被探索完为止。
通过这种方式,代码能够撤销之前的决策,尝试其他可能的路径,从而全面地探索所有可能的解空间。
还有几个题,以后补补(太赶了qaq
三、BFS广度优先搜索-----队列
bfs个人感觉就是,同时往每个方向都试试,最先得到的就是最好的。
它是一种盲目搜寻法,目的是系统地展开并检查图中的所有节点,以找寻结果。并不考虑结果的可能位置,彻底地搜索整张图,直到找到结果为止。宽度优先搜索是一种对图进行搜索的算法。假设我们一开始位于某个顶点(即起点),此 时并不知道图的整体结构,而我们的目的是从起点开始顺着边搜索,直到到达指定顶点(即终 点)。在此过程中每走到一个顶点,就会判断一次它是否为终点。广度优先搜索会优先从离起点 近的顶点开始搜索。
b站有个视频非常清晰,这里贴下链接
【广度优先搜索BFS遍历-动画演示过程,希望对大家有所帮助~】
https://www.bilibili.com/video/BV1bP4y1K7Tr?vd_source=7ccfb0770f378b7710213a50fbacfc17https://www.bilibili.com/video/BV1bP4y1K7Tr?vd_source=7ccfb0770f378b7710213a50fbacfc17
这个大佬写的也很清晰:
http://t.csdnimg.cn/sw4pZhttp://t.csdnimg.cn/sw4pZ
int BFS(Node start, Node target) {
入队(初始状态);
visited[初始状态] = true;
while(!空队) {
for() { // 状态转换
Node node = 队首元素;
对node的处理,生成新node状态;
if (visited[node] == true)
continue;
if (node == target) {
输出答案;
return 0;
}
v[node] = true;
入队(node);
}
出队();
}
}
例题3 P1135 奇怪的电梯
#include <iostream> // 引入输入输出流
#include <cstring> // 引入C风格的字符串处理函数库,用于memset
#include <queue> // 引入队列容器
using namespace std;
const int N = 201; // 定义常量N为201,表示图的最大节点数
int n, a, b; // n为图的节点数,a为起点,b为终点
int k[N], vis[N], ans[N]; // k数组存储每个节点的关联值,vis数组标记节点是否被访问过,ans数组存储从起点到每个节点的最短路径长度
// BFS函数,从节点a开始搜索
void bfs(int a) {
queue<int> Q; // 定义一个队列Q
Q.push(a); // 将起点a加入队列
ans[a] = 0; // 设置起点a到自身的距离为0
// 当队列不为空时继续循环
while (!Q.empty()) {
int u = Q.front(); // 取出队首元素
Q.pop(); // 弹出队首元素
// 如果当前节点是目标节点b,则结束搜索
if (u == b) {
break;
}
// 如果当前节点已经被访问过,则跳过
if (vis[u] == 1) {
continue;
}
vis[u] = 1; // 标记当前节点为已访问
// 计算通过当前节点的关联值k[u]可以到达的两个节点v1和v2
int v1 = u + k[u];
// 如果v1是一个有效的节点且未被访问过,则将其加入队列,并更新ans[v1]
if (v1 >= 1 && v1 <= n && vis[v1] == 0) {
Q.push(v1);
ans[v1] = min(ans[v1], ans[u] + 1);
}
int v2 = u - k[u];
// 如果v2是一个有效的节点且未被访问过,则将其加入队列,并更新ans[v2]
if (v2 >= 1 && v2 <= n && vis[v2] == 0) {
Q.push(v2);
ans[v2] = min(ans[v2], ans[u] + 1);
}
}
}
int main() {
cin >> n >> a >> b; // 输入节点数n,起点a和终点b
// 初始化ans数组为一个很大的数(表示无穷大)
memset(ans, 0x3f, sizeof(ans));
// 输入每个节点的关联值k[i]
for (int i = 1; i <= n; i++) {
cin >> k[i];
}
bfs(a); // 从起点a开始BFS搜索
// 如果从a到b没有路径,则输出-1,否则输出最短路径长度
if (ans[b] == 1e9) {
cout << -1 << endl;
} else {
cout << ans[b] << endl;
}
return 0;
}
四、BFS与DFS区别
bfs 遍历节点是先进先出,dfs遍历节点是先进后出;
bfs是按层次访问的,dfs 是按照一个路径一直访问到底,当前节点没有未访问的邻居节点时,然后回溯到上一个节点,不断的尝试,直到访问到目标节点或所有节点都已访问。
bfs 适用于求源点与目标节点距离最近的情况,例如:求最短路径。dfs 更适合于求解一个任意符合方案中的一个或者遍历所有情况,例如:全排列、拓扑排序、求到达某一点的任意一条路径。
————————————————版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/aliyonghang/article/details/128724989