济南网站建设培训班,平面设计培训班教程,网站建设为中心,经典 wordpress主题下载算法沉淀——BFS 解决拓扑排序 01.课程表02.课程表 II03.火星词典 Breadth-First Search (BFS) 在拓扑排序中的应用主要是用来解决有向无环图#xff08;DAG#xff09;的拓扑排序问题。拓扑排序是对有向图中所有节点的一种线性排序#xff0c;使得对于每一条有向边 (u, v)DAG的拓扑排序问题。拓扑排序是对有向图中所有节点的一种线性排序使得对于每一条有向边 (u, v)节点 u 在排序中都出现在节点 v 的前面。如果图中存在环路则无法进行拓扑排序。 BFS 解决拓扑排序的步骤如下
统计每个节点的入度in-degree即指向该节点的边的数量。将所有入度为 0 的节点加入队列。对于每个入度为 0 的节点依次出队更新其相邻节点的入度将入度变为 0 的节点加入队列。重复步骤 3 直到队列为空。
如果最终遍历过的节点数等于图中的节点数说明图是有向无环图可以得到一个拓扑排序。
01.课程表
题目链接https://leetcode.cn/problems/course-schedule/
你这个学期必须选修 numCourses 门课程记为 0 到 numCourses - 1 。
在选修某些课程之前需要一些先修课程。 先修课程按数组 prerequisites 给出其中 prerequisites[i] [ai, bi] 表示如果要学习课程 ai 则 必须 先学习课程 bi 。
例如先修课程对 [0, 1] 表示想要学习课程 0 你需要先完成课程 1 。
请你判断是否可能完成所有课程的学习如果可以返回 true 否则返回 false 。
示例 1
输入numCourses 2, prerequisites [[1,0]]
输出true
解释总共有 2 门课程。学习课程 1 之前你需要完成课程 0 。这是可能的。示例 2
输入numCourses 2, prerequisites [[1,0],[0,1]]
输出false
解释总共有 2 门课程。学习课程 1 之前你需要先完成课程 0 并且学习课程 0 之前你还应先完成课程 1 。这是不可能的。提示
1 numCourses 20000 prerequisites.length 5000prerequisites[i].length 20 ai, bi numCoursesprerequisites[i] 中的所有课程对 互不相同
思路
这里我们可以采用容器模拟邻接矩阵或者邻接表来进行拓扑排序判断这个图是否有环的方式来解决这个问题
代码
class Solution {
public:bool canFinish(int numCourses, vectorvectorint prerequisites) {unordered_mapint,vectorint edges;vectorint in(numCourses,0);for(vectorint e:prerequisites){int ae[0],be[1];edges[b].push_back(a);in[a];}queueint q;for(int i0;inumCourses;i) if(in[i]0) q.push(i);while(!q.empty()){int tq.front();q.pop();for(int e:edges[t]){in[e]--;if(in[e]0) q.push(e);}}for(int i:in) if(i) return false;return true;}
};使用一个哈希表 edges 存储有向图的边其中 edges[b] 表示节点 b 指向的所有节点。使用数组 in 记录每个节点的入度。初始化时将所有节点的入度设为 0。遍历先修课程的关系构建有向图并更新入度数组。将入度为 0 的节点加入队列 q。使用 BFS 进行拓扑排序从入度为 0 的节点开始依次出队并将其邻接节点的入度减 1。如果邻接节点的入度减为 0将其加入队列。如果最终所有节点的入度都为 0说明图中不存在环可以完成所有课程返回 true否则返回 false。
02.课程表 II
题目链接https://leetcode.cn/problems/course-schedule-ii/
现在你总共有 numCourses 门课需要选记为 0 到 numCourses - 1。给你一个数组 prerequisites 其中 prerequisites[i] [ai, bi] 表示在选修课程 ai 前 必须 先选修 bi 。
例如想要学习课程 0 你需要先完成课程 1 我们用一个匹配来表示[0,1] 。
返回你为了学完所有课程所安排的学习顺序。可能会有多个正确的顺序你只要返回 任意一种 就可以了。如果不可能完成所有课程返回 一个空数组 。
示例 1
输入numCourses 2, prerequisites [[1,0]]
输出[0,1]
解释总共有 2 门课程。要学习课程 1你需要先完成课程 0。因此正确的课程顺序为 [0,1] 。示例 2
输入numCourses 4, prerequisites [[1,0],[2,0],[3,1],[3,2]]
输出[0,2,1,3]
解释总共有 4 门课程。要学习课程 3你应该先完成课程 1 和课程 2。并且课程 1 和课程 2 都应该排在课程 0 之后。
因此一个正确的课程顺序是 [0,1,2,3] 。另一个正确的排序是 [0,2,1,3] 。示例 3
输入numCourses 1, prerequisites []
输出[0]思路
总体思路和上面一致我们只需要在每次将入度为0的点顺序保存即为拓扑排序的顺序。
代码
class Solution {
public:vectorint findOrder(int numCourses, vectorvectorint prerequisites) {unordered_mapint,vectorint edges;vectorint in(numCourses,0);for(vectorint e:prerequisites){int ae[0],be[1];edges[b].push_back(a);in[a];}queueint q;vectorint ret;for(int i0;inumCourses;i) if(in[i]0){q.push(i);ret.push_back(i);} while(!q.empty()){int tq.front();q.pop();for(int e:edges[t]){in[e]--;if(in[e]0){ q.push(e);ret.push_back(e); }}}for(int i:in) if(i) return {};return ret;}
};使用一个哈希表 edges 存储有向图的边其中 edges[b] 表示节点 b 指向的所有节点。使用数组 in 记录每个节点的入度。初始化时将所有节点的入度设为 0。遍历先修课程的关系构建有向图并更新入度数组。将入度为 0 的节点加入队列 q同时将这些节点加入结果数组 ret 中。使用 BFS 进行拓扑排序从入度为 0 的节点开始依次出队并将其邻接节点的入度减 1。如果邻接节点的入度减为 0将其加入队列和结果数组。如果最终所有节点的入度都为 0说明图中不存在环返回拓扑排序结果否则返回空数组表示无法完成拓扑排序
03.火星词典
题目链接https://leetcode.cn/problems/Jf1JuT
现有一种使用英语字母的外星文语言这门语言的字母顺序与英语顺序不同。
给定一个字符串列表 words 作为这门语言的词典words 中的字符串已经 按这门新语言的字母顺序进行了排序 。
请你根据该词典还原出此语言中已知的字母顺序并 按字母递增顺序 排列。若不存在合法字母顺序返回 。若存在多种可能的合法字母顺序返回其中 任意一种 顺序即可。
字符串 s 字典顺序小于 字符串 t 有两种情况
在第一个不同字母处如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前那么 s 的字典顺序小于 t 。如果前面 min(s.length, t.length) 字母都相同那么 s.length t.length 时s 的字典顺序也小于 t 。
示例 1
输入words [wrt,wrf,er,ett,rftt]
输出wertf示例 2
输入words [z,x]
输出zx示例 3
输入words [z,x,z]
输出
解释不存在合法字母顺序因此返回 。提示
1 words.length 1001 words[i].length 100words[i] 仅由小写英文字母组成
思路
将题意搞清楚之后这道题就变成了判断有向图时候有环可以用拓扑排序解决。 如何搜集信息如何建图 a. 两层for循环枚举出所有的两个字符串的组合 b. 然后利用指针根据字典序规则找出信息。
使用哈希表 edges 存储字母之间的顺序关系其中 edges[a] 表示字母 a 后面可以跟随的字母集合。使用哈希表 in 记录每个字母的入度即有多少字母在它之前。使用布尔变量 cheak 标记是否出现了无效的字母顺序。定义 add 函数该函数比较两个单词 s1 和 s2找到它们第一个不相同的字母然后将这个字母的顺序关系添加到 edges 中。如果 s2 是 s1 的前缀则将 cheak 设置为 true。在构建字母之间的顺序关系时遍历相邻的两个单词调用 add 函数如果 cheak 为 true则直接返回空字符串。使用队列 q 存储入度为 0 的字母初始化队列时将所有入度为 0 的字母加入。使用 BFS 进行拓扑排序不断将入度为 0 的字母出队并将其后面可以跟随的字母的入度减 1。将入度为 0 的字母加入结果字符串 ret 中。最后检查所有字母的入度是否都为 0如果不为 0则说明有环返回空字符串否则返回结果字符串 ret。
代码
class Solution {unordered_mapchar,unordered_setchar edges;unordered_mapchar,int in;bool cheakfalse;void add(string s1,string s2){int nmin(s1.size(),s2.size());int i0;while(in){if(s1[i]!s2[i]){char as1[i],bs2[i];if(!edges.count(a)||!edges[a].count(b)){edges[a].insert(b);in[b];}break;}i;}if(is2.size()is1.size()) cheaktrue;}
public:string alienOrder(vectorstring words) {for(auto s:words)for(auto ch:s)in[ch]0;int nwords.size();for(int i0;in;i)for(int ji1;jn;j){add(words[i],words[j]);if(cheak) return ;}queuechar q;for(auto [a,b]:in)if(b0) q.push(a);string ret;while(!q.empty()){char tq.front();q.pop();rett;for(char ch:edges[t])if(--in[ch]0) q.push(ch);}for(auto [a,b]:in) if(b) return ;return ret;}
};