秒懂拓扑排序

Intorduction:

拓扑排序和普通排序不同,拓扑排序是专门为了解决特定的排序问题。如:排课问题。

在现实生活中,我们往往需要先学一门课,有这门课的基础才能学下一门。比如学了小学数学才能学中学数学,学了中学数学才能学高数。学了c语言才能学c++ 等等。

要学一门课就必须要学这门课的先修课。当然这门课也会构成你后面要学课的先修课。

那么,如何找到一个正确的学习顺序? 这时候就需要用到拓扑排序。当然了,使用拓扑排序之前还需要把这些不同的课,课与课之间的关系抽象成图。

 

 

 

什么样的图有拓扑序呢?

必须是有向无环图。

Why? 试想一下,如果无向就不存在先后顺序了,如果有环意味着这个节点就是这个节点的先驱节点(也就是这门课就是自己的先修课),不符合逻辑。

算法流程:

先推理一下,我们要排课,首先要排什么课?

肯定是没有预修课程的课了。对应成图上的节点就是入度为0的课,没有其他课指向的课。

把这个节点放在排序的最前面,意味着这门课已经被学完了,那对于这张图上的其他课来说(其他节点),你已经有了这门课的基础,那是不是可以把这门课的节点指向其他课节点W 的边去掉,因为这样去掉,意味着学W课程不需要预修课程,这样也是逻辑合理的,因为我们已经学过了W的预修课程,就可以把W看成不需要预修课程的了。

去掉这些边后,又可以在图上找入度为0的节点,重复即可。

简化一下:

1.遍历所有节点,找到入度为0的节点入队。

2.弹出入度为0的节点,后遍历这个节点的邻接点,把他们的入度-1,如果为0,再放入队列中。

3.循环跳出后,还有节点没有被遍历到说明图不是有向无环图。Error

 

代码:

 1 list<Node*> sortedTopology(Graph& graph) {
 2     queue<Node*> zeroDegree;  // queue contains nodes that in degree is zero 
 3     unordered_map<Node*, int> inDegree;  // container collects in degree of all the node in graph
 4     unordered_map<int, Node*>::iterator ite = graph.nodes.begin(); // iterator for while loop
 5     list<Node*> ret; // return value
 6     int cnt = 0; // count if all the nodes are traversed.
 7     while (ite!=graph.nodes.end()) {
 8         // traverse all the node find out which node has zero in degree and push it into queue.
 9 
10         inDegree[ite->second] = ite->second->in;
11         if (ite->second->in == 0) {
12             zeroDegree.push(ite->second);
13         }
14         ite++;
15         
16     }
17 
18     while (!zeroDegree.empty()) {
19         Node* temp=zeroDegree.front();
20         zeroDegree.pop();
21         cnt++;
22         ret.push_back(temp);
23         // pop one
24         for (auto node : temp->next) {
25             // traverse adjacent vertexes.
26             inDegree[node]--;
27             if (inDegree[node] == 0) {
28                 zeroDegree.push(node);
29             }
30         }
31     }
32     if (cnt != graph.nodes.size())
33     {
34         // throw error
35         throw "Graph is not a directed cyclical graph!";
36         list<Node*> empty;
37         return empty;
38     }
39     return ret;
40 
41 }

全部代码:

#include<iostream>
#include<list>
#include<queue>
#include<stack>
#include<unordered_map>
#include<unordered_set>
using namespace std;
//解依赖
class Edge;
class Node {
public:
    int value;
    int in;
    int out;
    list<Node*> next;
    list<Edge*> edges;
    Node(int value) {
        this->value = value;
        in = 0;
        out = 0;
    }
};
class Edge {
public:
    int weight;
    Node* from;
    Node* to;

    Edge(int weight, Node* from, Node* to) {
        this->weight = weight;
        this->from = from;
        this->to = to;
    }
};
class Graph {
public:
    unordered_map<int, Node*> nodes;
    unordered_set<Edge*> edges;

};
class GraphGenerator {
public:
    Graph createGraph(int matrix[][3], int rows, int col) {
        Graph graph;
        for (int i = 0; i < rows; i++) {
            int weight = matrix[i][0];
            int from = matrix[i][1];
            int to = matrix[i][2];
            if (graph.nodes.find(from) == graph.nodes.end()) {
                graph.nodes[from] = new Node(from);
            }
            if (graph.nodes.find(to) == graph.nodes.end()) {
                graph.nodes[to] = new Node(to);
            }
            //以上两个if操作后,必能找到 from 好人to节点
            Node* fromNode = graph.nodes.find(from)->second;
            Node* toNode = graph.nodes.find(to)->second;
            //为 graph 和 from所在的node 准备 一条边
            Edge* newEdge = new Edge(weight, fromNode, toNode);
            //对于新增的一条边, 被指向节点的入度+1
            toNode->in++;
            //对于新增的一条边, 指向节点的出度+1,所指向的节点确定,指向该节点的边确定
            fromNode->out++;
            fromNode->next.push_back(toNode);
            fromNode->edges.push_back(newEdge);
            //两个if会保证建立节点,这里保证 边的存在。
            graph.edges.insert(newEdge);

        }
        return graph;
    }
};


list<Node*> sortedTopology(Graph& graph) {
    queue<Node*> zeroDegree;  // queue contains nodes that in degree is zero 
    unordered_map<Node*, int> inDegree;  // container collects in degree of all the node in graph
    unordered_map<int, Node*>::iterator ite = graph.nodes.begin(); // iterator for while loop
    list<Node*> ret; // return value
    int cnt = 0; // count if all the nodes are traversed.
    while (ite!=graph.nodes.end()) {
        // traverse all the node find out which node has zero in degree and push it into queue.

        inDegree[ite->second] = ite->second->in;
        if (ite->second->in == 0) {
            zeroDegree.push(ite->second);
        }
        ite++;
        
    }

    while (!zeroDegree.empty()) {
        Node* temp=zeroDegree.front();
        zeroDegree.pop();
        cnt++;
        ret.push_back(temp);
        // pop one
        for (auto node : temp->next) {
            // traverse adjacent vertexes.
            inDegree[node]--;
            if (inDegree[node] == 0) {
                zeroDegree.push(node);
            }
        }
    }
    if (cnt != graph.nodes.size())
    {
        // throw error
        throw "Graph is not a directed cyclical graph!";
        list<Node*> empty;
        return empty;
    }
    return ret;

}



int main() {
    GraphGenerator g;
    // int matrix[][3]= {{1,1,2},{2,2,3},{3,3,1}};
    int matrix[][3] = { {0,1,2},{0,1,3},{0,1,4},{0,2,3},{0,2,7},{0,7,3},
                {0,3,5},{0,4,6} };
    int length = sizeof(matrix) / sizeof(matrix[0]);
    Graph graph = g.createGraph(matrix, length, 3);
    Node* node = graph.nodes.find(1)->second;
    list<Node*> list = sortedTopology(graph);
    std::list<Node*>::iterator ite = list.begin();
    while (ite != list.end()) {
        cout << (*ite)->value << endl;
        ite++;
    }
    return 0;
}

 

 

posted @ 2020-12-07 10:39  彭张智写字的地方  阅读(356)  评论(0)    收藏  举报