拓扑排序与深度优先遍历

以力扣210题进行分析

  • 题目简介
  • 实现代码
      • 下面是非正常思维

题目简介

下面首先给出题目的简单介绍,笔者有些懒,直接截图。
拓扑排序与深度优先遍历_第1张图片

实现代码

下面给出我实现的代码,说来也惭愧,冷落了拓扑排序了,居然在实现完成后也仅仅认为是普通的DFS加递归问题,没有发现是拓扑排序大哥。所以特地为大哥写一篇文章。

class Solution {
public:
    vector<int> cf;//保存的是节点的状态,默认为-1,表示未开始搜索;0表示正在搜索;1表示搜索完成
    vector<int> seri;//保存的是结果,为了代码书写方便,也在了类中
    unordered_map<int,set<int>> mpre;//保存的是以key为起点的所有其他节点,也就是边,set的大小也就是key这个节点的入度
    vector<int> findOrder(int numCourses, vector<vector<int>>& prerequisites) {
        cf.resize(numCourses,-1);//初始化cf
        for(auto&req:prerequisites) mpre[req[0]].insert(req[1]);//初始化mpre
        //为了后续访问方便
        for(int i=0;i<numCourses;i++){
            if(cf[i]==1) continue;//之前已经处理过了,要选择一个没搜索过的进行搜索
            if(!yesWecan(i)) return {};//如果返回值为false,其实也就意味着找到了图中的一个环,因此拓扑排序失败,返回空数组
        }
        return seri;//所有节点都找到了,正常返回结果
    }

    bool yesWecan(int pre){
        if(cf[pre]!=-1) return cf[pre]==1;
        //cf[pre]!=-1表示要么正在探索中,此时又发现,则出现环,返回false;要么发现已经搜索完成的节点,返回true
        if(mpre.count(pre)==0) {cf[pre]=1;seri.push_back(pre);return true;}
        //mpre.count(pre)==0代表该节点的入度为0,可以直接加到已排序队列seri中,同时标记cf为1,返回true
        cf[pre] = 0;//表示该节点正在搜索中,如果在DFS的过程中再遇到,则出现环
        bool flag = true;
        //下面这一部分:一个节点只有它前面的所有节点都搜索完成时,它才会被加入到排序队列中,其实也可以递减其入度
        //当一个节点的入度为0时,就可以选择该节点了
        for(auto&item:mpre[pre]) flag &= yesWecan(item);
        if(flag) cf[pre] = 1;
        seri.push_back(pre);
        return flag;
    }
};

每一条语句都有注释,相信自己这次能长记性了。

不过这和传统思路下得拓扑排序稍微有点区别,算是逆着来的,下面是从网上找的一张图,很直观拓扑排序与深度优先遍历_第2张图片
以上面这张图为例,正常思维是:
1、先统计各个顶点的入度;
degree[1]=0,degree[2]=1,degree[3]=2,degree[4]=2,degree[5]=2
2、将所有入度为0的顶点入队并输出;
queue = {1},输出1
3、从队列中取出一个顶点
顶点1被取出,同时删掉该顶点和所有以它为起点的有向边;表现在代码中就是:将所有v指向的顶点的入度减1,并将入度减 为0的顶点入队列。
重复上述过程直至队列为空或出现环。

下面是非正常思维

因为之前的代码中有注释,所以简单说一下,如果以入度的角度考虑,则是随机找一个点V,如果V点入度为0则直接输出,如果入度不为0,则V点进入待定状态,递归找指向V节点的所有前驱节点,前驱节点如果顺利输出,则V点入度减一,当减为0时就可以输出,如果在递归过程中又出现V点,则代表环出现,拓扑失败。
下面是拓扑排序解决问题的又一案例,力扣6163题,给定条件下构造矩阵
下面是按照入度思路写下的拓扑排序源代码

class Solution {
public:
    vector<vector<int>> buildMatrix(int k, vector<vector<int>>& rowConditions, vector<vector<int>>& colConditions) {
        int n = rowConditions.size(), m = colConditions.size();
        vector<int> rows(k, -1);
        bool flag = getSortedIndex(rowConditions,rows,k);
        if(!flag) return {};
        vector<int> cols(k, -1);
        flag = getSortedIndex(colConditions,cols,k);
        if(!flag) return {};
        vector<vector<int>> ret(k, vector<int>(k, 0));
        for(int i=0;i<k;i++){
            ret[rows[i]][cols[i]] = i+1;
        }
        return ret;
    }

    bool getSortedIndex(vector<vector<int>>& Conditions,vector<int>& Ind,int k){       
        vector<int> degree(k, 0);//一开始入度皆为0
        set<vector<int>> uniqueConditions(Conditions.begin(),Conditions.end());//条件有些重复,去重
        unordered_map<int,set<int>> edges;//记录有向边
        for(auto&cond:uniqueConditions){
            edges[cond[0]-1].insert(cond[1]-1);//题目的条件中下标以1开始,所以要减一
            degree[cond[1]-1]++;//同上
        }
        queue<int> q;
        for(int i=0;i<k;i++){
            if(degree[i]==0) q.push(i);//将入度为0的点入队列
        }

        int index = 0;
        while(!q.empty()){
            int p = q.front();q.pop();
            Ind[p] = index++;//记录排序后的下标,根据实际情况可以直接压倒vector中,作为排序后输出
            for(auto&e:edges[p]){
                degree[e]--;
                if(degree[e]==0) q.push(e);//将入度为0的点入队列
            }
        }

        for(int i=0;i<k;i++){
            if(degree[i]!=0) return false;//成环
        }

        for(int i=0;i<k;i++){
            if(Ind[i]==-1) Ind[i] = index++;//题目约束不一定够,剩下的随便选
        }
        return true;
    }
};

上面的代码中有一个去重的部分,如果改set为vector可以省去去重的工作量

class Solution {
public:
    vector<vector<int>> buildMatrix(int k, vector<vector<int>>& rowConditions, vector<vector<int>>& colConditions) {
        int n = rowConditions.size(), m = colConditions.size();
        vector<int> rows(k, -1);
        bool flag = getSortedIndex(rowConditions,rows,k);
        if(!flag) return {};
        vector<int> cols(k, -1);
        flag = getSortedIndex(colConditions,cols,k);
        if(!flag) return {};
        vector<vector<int>> ret(k, vector<int>(k, 0));
        for(int i=0;i<k;i++){
            ret[rows[i]][cols[i]] = i+1;
        }
        return ret;
    }

    bool getSortedIndex(vector<vector<int>>& Conditions,vector<int>& Ind,int k){       
        vector<int> degree(k, 0);//一开始入度皆为0
        unordered_map<int,vector<int>> edges;//**更改地方**
        for(auto&cond:Conditions){
            edges[cond[0]-1].push_back(cond[1]-1);
            degree[cond[1]-1]++;
        }
        queue<int> q;
        for(int i=0;i<k;i++){
            if(degree[i]==0) q.push(i);
        }

        int index = 0;
        while(!q.empty()){
            int p = q.front();q.pop();
            Ind[p] = index++;
            for(auto&e:edges[p]){
                degree[e]--;
                if(degree[e]==0) q.push(e);
            }
        }

        for(int i=0;i<k;i++){
            if(degree[i]!=0) return false;//成环
        }

        for(int i=0;i<k;i++){
            if(Ind[i]==-1) Ind[i] = index++;//题目约束不一定够,剩下的随便选
        }
        return true;
    }
};

你可能感兴趣的:(深度优先,算法,排序算法)