BFS算法篇——打开智慧之门,BFS算法在拓扑排序中的诗意探索(下)


在这里插入图片描述

引言

上篇我们介绍了BFS解决拓扑排序的背景知识,本篇我们将结合具体题目分析,进一步深化对于该算法的理解运用。

一、课程表

1.1 题目链接:https://leetcode.cn/problems/course-schedule/description/

1.2 题目分析:

  • numCourses 表示要学的课程的数量
  • prerequisites[i] = [ai, bi] ,表示如果要学习课程 ai 则 必须 先学习课程 bi 。
  • 如果存在能按照顺序学完的可能,返回true,否则返回false

1.3 思路讲解:

该题为一个明显的拓扑排序问题,根据上篇讲解,我们可以这样子处理

  • 首先构建邻接表,先决条件pre表示的顺序即为b->a,作为边加入其中
  • 同时,由于学习a之前必须要学习b,因此a的入度加1
  • 之后将入度为0的节点入队列,层序遍历即可

1.4 代码实现:

class Solution {
public:
    bool canFinish(int n, vector<vector<int>>& prerequisites) {
        unordered_map<int,vector<int>> edges;//邻接表
        vector<int> in(n);//入度
        //建图
        for(auto e: prerequisites)
        {
            int a=e[0],b=e[1];
            edges[b].push_back(a);
            in[a]++;
        }
        queue<int> q;
        //将入度为0的节点入队列
        for(int i=0;i<n;i++)
        {
            if(in[i]==0)
            {
                q.push(i);
            }
        }
        //层序遍历
        while(q.size())
        {
            int t=q.front();
            q.pop();
            for(int e : edges[t])
            {
                in[e]--;
                if(in[e]==0)
                {
                    q.push(e);
                }
            }
        }
        for(int i=0;i<n;i++)
        {
            if(in[i])
            {
                return false;
            }//说明无法学完所有课程
        }
        return true;
    }
};

二、课程表||

2.1 题目链接:https://leetcode.cn/problems/course-schedule-ii/description/

2.2 题目分析:

该题与上题要求基本相同,只是返回值要求返回可能的一种学习顺序,如果不存在,则返回空数组

2.3 思路讲解:

判断是否可以学习的思路与上题相同,我们只需要在层序遍历时,用一个数组记录当前学习顺序即可。

2.4 代码实现:

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int,vector<int>> edges;//邻接表
        vector<int> in(numCourses);//入度
        vector<int> ret;//返回值
        vector<int> temp;//空数组
        queue<int> q;
        //建图
        for(auto e:prerequisites)
        {
            int a=e[0],b=e[1];
            edges[b].push_back(a);
            in[a]++;
        }
        //入度为0的节点入队列
        for(int i=0;i<numCourses;i++)
        {
            if(in[i]==0)
            {
                q.push(i);
            }
        }
        while(q.size())
        {
            int t=q.front();
            q.pop();
            ret.push_back(t);//更新结果
            for(int e:edges[t])
            {
                in[e]--;
                if(in[e]==0)
                {
                    q.push(e);

                }
            }
        }
        for(int i=0;i<numCourses;i++)
        {
            if(in[i])
            {
                return temp;
            }
        }//存在未完成情况,返回空数组
        return ret;

        
    }
};

三、火星词典

3.1 题目链接:https://leetcode.cn/problems/Jf1JuT/description/

3.2 题目分析:

题目要求一时间难以读懂,我们来简单翻译一下:

  • 给定的word里面已经按一种新的字母顺序排列好

假设 words = [“wrt”,“wrf”,“er”,“ett”,“rftt”],我们可以得到字母之间的一些依赖关系:

  • 比如从 “wrt” 和 “wrf” 可以得出 t 在 f 之前(因为 “t” 是两个单词中的不同字母,且在相同位置上不同)。

  • 从 “er” 和 “ett” 中可以得出 r 在 e 之前。

最终需要返回题目中外星词典的递增顺序,若不存在合法的顺序,则返回空

3.3 思路讲解:

我们通过比较相邻的单词,找出它们的第一个不同字母。这些字母的顺序关系就可以帮助我们构建字母的顺序图。

图的构建:

  • 我们可以通过图来表示字母之间的顺序关系。每个字母是图中的一个节点,而字母之间的顺序关系是边。

拓扑排序:

  • 通过拓扑排序的方法,我们可以得到字母的正确排序。如果有环,则说明字母顺序无法确定,返回空字符串。

3.4 代码实现:

class Solution {
public:
    unordered_map<char,unordered_set<char>> edges;//边
    unordered_map<char,int> in;//入度
    bool check;//处理特殊情况
    void add(string& s1,string& s2)
    {
        int n=min(s1.size(),s2.size());
        int i;
        for( i=0;i<n;i++)
        {
            if(s1[i]!=s2[i])
            {
                char a=s1[i],b=s2[i];
                if(!edges.count(a) || !edges[a].count(b))//如果之前为记录过,则记录这条边a->b
                {
                    edges[a].insert(b);
                    in[b]++;//更新边和入度节点
                }
                break;//注意跳出循环
            }

        }
        if(i==s2.size()&&i<s1.size())//处理abc ab这种特殊情况
        {
            check=true;
        }
    }
    string alienOrder(vector<string>& words) {
        //建图和初始化
        for(auto e: words)
        {
            for(auto ch:e)
            {
                in[ch]=0;
            }//将所有字符的入度都初始化为0
        }
        int n=words.size();
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                add(words[i],words[j]);
                if(check)
                {
                    return "";//存在非法情况
                }
            }
        }
        //拓扑排序
        queue<char> q;
        string ret;
        //将所有入度为0的节点
        for(auto [a,b] :in)
        {
            if(b==0)
            {
                q.push(a);
            }
        }
      
        while(q.size())
        {
            char t=q.front();
            q.pop();
            ret+=t;
            for(auto e:edges[t])
            {
                if(--in[e]==0) q.push(e);
            }
            
        }
        for(auto [a,b] :in)
        {
            if(b!=0)
            {
                return "";

            }
        }//判断是否存在不合法顺序
        return ret;

    }
};

小结

本篇关于BFS解决拓扑排序的讲解就暂告段落啦,希望能对大家的学习产生帮助,欢迎各位佬前来支持斧正!!!

在这里插入图片描述

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值