拓扑排序

拓扑排序用来解决什么问题

解决相互依赖的条件下的排序问题。
比如:

  • 早上起来你只穿了内裤
  • 你还需要穿:秋衣、秋裤、毛衣、毛裤、羽绒服、外裤、袜子、鞋子

此时穿衣顺序上就有了两两之间的局部依赖,如:

  • 内裤 --> 秋裤 --> 毛裤 --> 外裤
  • 秋衣 --> 毛衣 --> 羽绒服
  • 袜子 --> 鞋子

当然有的人喜欢先穿毛裤再来秋裤(逃)

最终的穿衣顺序可能为:

  • 内裤 --> 秋裤 --> 毛裤 --> 外裤 --> 球衣 --> 毛衣 --> 羽绒服 --> 袜子 --> 鞋子
  • 内裤 --> 秋衣 --> 毛衣 --> 羽绒服 --> 秋裤 --> 毛裤 --> 外裤 --> 袜子 --> 鞋子

是的,拓扑排序有可能有多种结果

拓扑排序实现

需要用到的数据结构

如果 a 先于 b 执行,也就是说 b 依赖于 a,那么就在顶点 a 和顶点 b 之间,构建一条从 a 指向 b 的边。而且,这个图不仅要是有向图,还要是一个有向无环图,
也就是不能存在像 a->b->c->a 这样的循环依赖关系。因为图中一旦出现环,拓扑排序就无法工作了。实际上,拓扑排序本身就是基于有向无环图的一个算法。

//存储有向图
vector<vector<int>> graph;
//存储入度
vector<int> indegree;

Kahn 算法

如果 s 需要先于 t 执行,那就添加一条 s 指向 t 的边。所以某个顶点入度为 0 就表示没有任何顶点必须先于这个顶点执行,那么这个顶点就可以执行了。我们先从图中,找出一个入度为 0 的顶点,将其输出到拓扑排序的结果序列中(对应代码中就是把它打印出来),并且把这个顶点从图中删除(也就是把这个顶点可达的顶点的入度都减 1)。我们循环执行上面的过程,直到所有的顶点都被输出。最后输出的序列,就是满足局部依赖关系的拓扑排序。

    vector<int> topoSortByKahn(vector<int>& indegree, vector<vector<int>>& graph){
        //将入度为0的顶点入队
        queue<int> myqueue;
        for (int i = 0; i < indegree.size(); i++){
            if (indegree[i] == 0)
                myqueue.push(i);
        }
        vector<int> ans;
        while (!myqueue.empty()){
            //删除当前节点
            int temp = myqueue.front();
            myqueue.pop();
            ans.emplace_back(temp);
            //则对应的节点入度减一
            for (int i = 0; i < graph[temp].size(); i++)
            {
                indegree[graph[temp][i]]--;
                //入度变为0,加到队列后面
                if (indegree[graph[temp][i]] == 0)
                    myqueue.push(graph[temp][i]);
            }		
        }
        //最终的输出数 与 总节点数不同
        if (ans.size() != indegree.size()) {
            return {};
        }
        return ans; 
    }

DFS 算法

遍历图的所有顶点,注意是所有顶点

    vector<int> findOrder(vector<vector<int>>& graph, int n) {
        vector<int> visited(n, 0);
        vector<int> ans;
        //每次找一个未访问的节点dfs
        for(int i = 0; i < n; ++i){
            if(visited[i] != 0) continue;
            if(!topoSortByDfs(ans, visited, graph, i)){
                return {};//有环
            }
        }
        //注意下标0的在栈底
        reverse(ans.begin(), ans.end());
        return ans;
    }
    bool topoSortByDfs(vector<int>& ans, vector<int>& visited, vector<vector<int>>& graph, int u) {
        //visited : 0, 未搜索; 1, 搜索中; 2, 搜索完成;
        // 将节点标记为「搜索中」
        visited[u] = 1;
        // 搜索其相邻节点
        // 只要发现有环,立刻停止搜索
        for (int v: graph[u]) {
            // 如果「未搜索」那么搜索相邻节点
            if (visited[v] == 0) {
                if(!topoSortByDfs(ans, visited, graph, v)){
                    return false;//有环
                }
            }
            // 如果「搜索中」说明找到了环
            else if (visited[v] == 1) {
                return false;
            }
        }
        // 将节点标记为「已完成」
        visited[u] = 2;
        // 将节点入栈
        ans.emplace_back(u);
        return true;
    }

相关题目

207. 课程表
1203. 项目管理

posted @ 2021-01-12 18:24  miyanyan  阅读(106)  评论(0编辑  收藏  举报