第九章:广度优先和深度优先搜索的一些总结

 图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)

阅读本文前,请确保你已经掌握了递归、栈和队列的基本知识,如想掌握搜索的代码实现,请确保你能够用代码实现栈和队列的基本操作。

深度优先遍历(Depth First Search, 简称 DFS) 与广度优先遍历(Breath First Search,简称 BFS)是图论中两种非常重要的算法,也是进行更高的算法阶段学习的最后一道门槛。搜索算法频繁出现在算法竞赛题中, 尤其是深度优先搜索,在竞赛中,它是用来进行保底拿分的神器!

本文将会从以下几个方面来讲述深度优先遍历,广度优先遍历,相信大家看了肯定会有收获。

  • 什么是搜索?搜索用来干什么?

  • 深度优先遍历,广度优先遍历介绍

  • DFS vs BFS

  • 搜索的解题流程

  • 搜索中的常用术语

  • 搜索的一些优化

  • 习题演练

什么是搜索?搜索用来干什么?

搜索本质上就是枚举,只不过是一种有策略的枚举

搜索算法是利用计算机的高性能来有目的的穷举一个问题解空间的部分或所有的可能情况,从而求出问题的解的一种方法。现阶段一般有枚举算法、深度优先搜索广度优先搜索A*算法回溯算法、蒙特卡洛树搜索、散列函数等算法。搜索本质上就是枚举,只不过是一种有策略的枚举, 通常在搜索前,根据条件降低搜索规模;根据问题的约束条件进行剪枝;利用搜索过程中的中间解,避免重复计算这几种方法进行优化 。

搜索算法实际上是根据 初始条件 和 扩展规则 构造一棵“解答树”并寻找符合目标状态的节点的过程。所有的搜索算法从最终的算法实现上来看,都可以划分成两个部分——控制结构(扩展节点的方式)和产生系统(扩展节点),而所有的算法优化和改进主要都是通过修改其控制结构来完成的。我们所熟悉的最常用的搜索算法:深度优先搜索和广度优先搜索就是有两种不同的控制结构(策略)的搜索算法。

其实,在这样的思考过程中,我们已经不知不觉地将一个具体的问题抽象成了一个模型——,即搜索算法的使用 第一步在于搜索树的建立 。

深度优先遍历

深度优先遍历

主要思路是从图中一个未访问的顶点 V 开始,沿着一条路一直走到底,然后从这条路尽头的节点回退到上一个节点,再从另一条路开始走到底......,不断递归重复此过程,直到所有的顶点都遍历完成,它的特点是**不撞南墙不回头**,先走完一条路到底,再换一条路继续走。

树是图的一种特例(连通无环的图就是树),

  • 1
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,下面我将为你提供C++实现。首先,我们需要定义一个节点类,用来保存每个状态的信息和路径: ```c++ class Node { public: string state; // 当前状态 int depth; // 路径长度 string path; // 路径 Node(string state, int depth, string path) : state(state), depth(depth), path(path) {} }; ``` 接下来,我们分别实现A*算法,广度优先算法和深度优先算法。 1. A*算法 ```c++ string astar(string start, string end) { unordered_map<string, int> depth; // 用于记录状态的深度 unordered_map<string, string> path; // 用于记录状态的路径 priority_queue<pair<int, Node*>, vector<pair<int, Node*>>, greater<pair<int, Node*>>> pq; // 用于存储待扩展节点 pq.push({manhattan(start, end), new Node(start, 0, "")}); // 将初始状态加入到pq中 depth[start] = 0; // 初始状态的深度为0 while (!pq.empty()) { auto cur = pq.top().second; // 取出f值最小的节点 pq.pop(); if (cur->state == end) return cur->path; // 找到目标状态,返回路径 if (depth[cur->state] < cur->depth) continue; // 已经扩展过的状态不再扩展 for (auto next : get_next_states(cur->state)) { // 扩展当前节点 int d = cur->depth + 1; // 子节点的深度为父节点的深度加1 if (!depth.count(next) || d < depth[next]) { // 如果子节点未被扩展或者新路径更优 depth[next] = d; // 记录子节点的深度 path[next] = cur->path + direction(next, cur->state); // 记录子节点的路径 pq.push({d + manhattan(next, end), new Node(next, d, path[next])}); // 将子节点加入pq中 } } } return ""; // 没有找到目标状态,返回空路径 } ``` 在上面的代码中,我们使用了一个优先队列pq来存储待扩展的节点,其中节点的优先级为f值(即深度加上曼哈顿距离)。对于每个被扩展的节点,我们都生成它的所有合法子节点,并计算它们的f值。如果子节点未被扩展或者新路径更优,就将子节点加入到pq中。 2. 广度优先算法 ```c++ string bfs(string start, string end) { unordered_map<string, int> depth; // 用于记录状态的深度 unordered_map<string, string> path; // 用于记录状态的路径 queue<Node*> q; // 用于存储待扩展节点 q.push(new Node(start, 0, "")); // 将初始状态加入到q中 depth[start] = 0; // 初始状态的深度为0 while (!q.empty()) { auto cur = q.front(); // 取出队列中的第一个节点 q.pop(); if (cur->state == end) return cur->path; // 找到目标状态,返回路径 for (auto next : get_next_states(cur->state)) { // 扩展当前节点 int d = cur->depth + 1; // 子节点的深度为父节点的深度加1 if (!depth.count(next)) { // 如果子节点未被扩展 depth[next] = d; // 记录子节点的深度 path[next] = cur->path + direction(next, cur->state); // 记录子节点的路径 q.push(new Node(next, d, path[next])); // 将子节点加入到q中 } } } return ""; // 没有找到目标状态,返回空路径 } ``` 在上面的代码中,我们使用了一个队列q来存储待扩展的节点。对于每个被扩展的节点,我们都生成它的所有合法子节点,并将未被扩展的子节点加入到q中。 3. 深度优先算法 ```c++ string dfs(string start, string end, int max_depth) { unordered_map<string, int> depth; // 用于记录状态的深度 unordered_map<string, string> path; // 用于记录状态的路径 stack<Node*> s; // 用于存储待扩展节点 s.push(new Node(start, 0, "")); // 将初始状态加入到s中 depth[start] = 0; // 初始状态的深度为0 while (!s.empty()) { auto cur = s.top(); // 取出栈顶的节点 s.pop(); if (cur->state == end) return cur->path; // 找到目标状态,返回路径 if (cur->depth >= max_depth) continue; // 如果达到最大深度,不再扩展 for (auto next : get_next_states(cur->state)) { // 扩展当前节点 int d = cur->depth + 1; // 子节点的深度为父节点的深度加1 if (!depth.count(next)) { // 如果子节点未被扩展 depth[next] = d; // 记录子节点的深度 path[next] = cur->path + direction(next, cur->state); // 记录子节点的路径 s.push(new Node(next, d, path[next])); // 将子节点加入到s中 } } } return ""; // 没有找到目标状态,返回空路径 } ``` 在上面的代码中,我们使用了一个栈s来存储待扩展的节点。对于每个被扩展的节点,我们都生成它的所有合法子节点,并将未被扩展且深度未达到最大深度的子节点加入到s中。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值