拓补排序-专题

目录

1.课程表1

2.课程表2

3.火星词典


 

拓补排序的核心找入度为0的点,然后擦除跟该点相连的边。直到所有点都被找过(假设此时所有点都是入度为0的点,即有向无环图)或者入度为0的点没了(假设此时是有向有环图)

拓补排序大多用来看有向图有没有环

核心代码思路:

初始化:入度为0的点丢入队列。

1.把队头元素取出,加入答案

2.删除跟该元素相连的边

3.判断删除前跟该元素相连的元素,在删除了边后,入度是否为0,为0加入队列。

1.课程表1

 207. 课程表 - 力扣(LeetCode)

思路就是有向图是否有环

class Solution {
public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
        unordered_map<int, vector<int>> graph;
        int m = prerequisites.size();
        if(m==0)return true;
        unordered_map<int, int> input;
        queue<int> mp;
        for (int i = 0; i < m; i++) {
            input[prerequisites[i][0]]++;
            graph[prerequisites[i][1]].push_back(prerequisites[i][0]);
        }
        for (int i = 0; i < numCourses; i++) {
            if (input[i] == 0)
            {
                mp.push(i);
                input[i]=-1;
            }
        }
        int cnt = 0;
        while (mp.size()) {
            int tmp = mp.front();
            cout<<tmp<<endl;
            mp.pop();
            cnt++;
            for (auto& e : graph[tmp]) {
                input[e]--;
                if(input[e]==0)mp.push(e);
            }
        }
        if (cnt == numCourses)
            return true;
        return false;
    }
};

2.课程表2

 210. 课程表 II - 力扣(LeetCode)

思路没有太大变化,记得加进去,最后判断下即可

class Solution {
public:
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        vector<int>ret(0);
        unordered_map<int,int>input;
        queue<int>mp;
        unordered_map<int,vector<int>>graph;
        for(auto &e:prerequisites)
        {
            input[e[0]]++;
            graph[e[1]].push_back(e[0]);
        }
        for(int i=0;i<numCourses;i++)
        {
            if(input[i]==0)
            {
                mp.push(i);
            }
        }
        while(mp.size())
        {
            int tmp=mp.front();
            ret.push_back(tmp);
            mp.pop();
            for(auto &e:graph[tmp]){
                input[e]--;
                if(input[e]==0)mp.push(e);
            }
        }
        if(ret.size()!=numCourses)return {};
        return ret;
    }
};

3.火星词典

LCR 114. 火星词典 - 力扣(LeetCode)

非常恶心,思路是把先把各个字符的大小关系,用图的形式存储,然后转化为拓补排序的问题。

写起来细节很多,我的写法又比较粗糙,提交了7、8次,按照反馈改代码,最后终于过了,但我个人觉得不太行,这样写不适合其他赛制,所以我试着简化我的代码,让我代码更加清晰,先把第一次过的放上来

class Solution {
public:
    string alienOrder(vector<string>& words) {
        unordered_map<char,unordered_set<char>>graph;
        int n=words.size();
        unordered_map<char,int>input;
        unordered_set<char>num;
        if(n==1)return words[0];
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                
                int left=0,right=0,na=words[i].size(),nb=words[j].size();
                while(left<na&&right<nb&&words[i][left]==words[j][right]){
                    num.insert(words[i][left]);
                    num.insert(words[j][right]);
                    left++,right++;
                }
                if(left<na&&right<nb)
                {
                    num.insert(words[i][left]);
                    num.insert(words[j][right]);
                    if(graph[words[i][left]].count(words[j][right])==0)input[words[j][right]]++;
                    graph[words[i][left]].insert(words[j][right]);
                    if(graph[words[j][right]].count(words[i][left])!=0)return "";
                }
                else{
                    if(left<na)
                    {
                        return "";
                    }
                }
                while(left<na)
                {
                    num.insert(words[i][left]);
                    
                    left++;
                }
                while(right<nb)
                {
                    num.insert(words[j][right]);
                    right++;
                }
            }
        }
        string ret="";
        queue<char>mp;
        for(auto&e:num)
        {
            if(input[e]==0)
            {
                mp.push(e);
            }
        }
        while(mp.size())
        {
            char tmp=mp.front();
            mp.pop();
            ret+=tmp;
            for(auto &e:graph[tmp])
            {
                input[e]--;
                if(input[e]==0)mp.push(e);
            }
        }
        return ret;
    }
};

比较有条理的版本

class Solution {
public:
    unordered_map<char,int>input;//统计入度
    unordered_map<char,unordered_set<char>>graph;//图
    bool add(string &a,string &b)//增加关系
    {
        int n=min(a.size(),b.size());//取小的,因为如果字典中,如果前面的字符串长度>后面的字符串,说明无法还原。
        //而在可以还原的情况下,取小的,以免下标越界即可,至于不合法判断在最后
        int i=0;
        for(;i<n;i++)
        {
            if(a[i]!=b[i])
            {
                if(!graph.count(a[i])||!graph[a[i]].count(b[i]))
                {
                    graph[a[i]].insert(b[i]);
                    input[b[i]]++;
                }
                break;//在第一个不同字母处,如果 s 中的字母在这门外星语言的字母顺序中位于 t 中字母之前,那么 s 的字典顺序小于 t 
            }
        }
        if(i==b.size()&&i<a.size())return false;
        //不合法,原因在上面有写
        return true;
        //如果前面 min(s.length, t.length) 字母都相同,那么 s.length < t.length 时,s 的字典顺序也小于 t 
    }
    string alienOrder(vector<string>& words) {
        for(auto &e:words)
        {
            for(auto &a:e)
            {
                input[a]=0;
            }
        }//初始化,因为这题有可能出现,有字符虽然在字典里,但在比较过程中,因为巧合,没有被加入input
        int n=words.size();
        for(int i=0;i<n;i++)
        {
            for(int j=i+1;j<n;j++)
            {
                if(!add(words[i],words[j]))return "";
            }
        }
        //拓补排序
        queue<char>mp;
        for(auto&e:input){
            if(e.second==0)mp.push(e.first);
        }
        string ret;
        while(mp.size())
        {
            char tmp=mp.front();
            mp.pop();
            ret+=tmp;
            for(auto &e:graph[tmp])
            {
                input[e]--;
                if(input[e]==0)mp.push(e);
            }
        }
        for(auto&e:input)
        {
            if(e.second!=0)return "";
        }
        return ret;
    }
};

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值