Ошибка в топологической сортировке при представлении графа как unordered_map - PullRequest
1 голос
/ 24 января 2020

Когда я пытаюсь реализовать топологическую сортировку в C ++ с unordered_map<string, vector<string>>, который представляет график, я сталкиваюсь с необъяснимой ошибкой (с моей стороны). В частности, это происходит только , когда «текущий узел», который посещается, не существует в качестве ключа в unordered_map (т. Е. У него нет исходящих ребер). Вместо возврата «правильного» порядка, он полностью завершает вызов функции topSort и возвращает только небольшое подмножество порядка.

Код возвращает: ML, AML, DL

Вместо этого возможно правильное решение: LA, MT, MA, PT, ML, AML, DL

Кто-нибудь может объяснить, почему это происходит?

Ниже приведен небольшой фрагмент кода, в котором возникает проблема:

// 0 -> white (node has not been visited)
// 1 -> grey (node is currently being visited)
// 2 -> black (node is completely explored)
bool topSortVisit(unordered_map<string, vector<string>>& graph,
        unordered_map<string, int>& visited, string node, vector<string>& result){

    if(visited[node] == 1) return false;
    if(visited[node] == 2) return true;

    // Mark current node as being visited.
    visited[node] = 1;
    // node might not have outgoing edges and therefore not in the
    // unordered_map (graph) as a key.
    for(auto neighbor : graph[node]){
        if(!topSortVisit(graph, visited, neighbor, result)) return false;
    }

    result.push_back(node);
    visited[node] = 2;
    return true;
}

vector<string> topSort(unordered_map<string, vector<string>>& graph){

    unordered_map<string, int> visited;
    vector<string> result;

    // Should visit all nodes with outgoing edges in the graph.
    for(auto elem : graph){
        string node = elem.first;
        bool acyclic = topSortVisit(graph, visited, node, result);
        if(!acyclic){
            cout << "cycle detected\n";
            return vector<string>{};
        }

    }

    reverse(result.begin(), result.end());
    return result;
}

А вот код для воспроизведения всего:

#include<iostream>
#include<vector>
#include<unordered_map>
#include<algorithm>

using namespace std;

bool topSortVisit(unordered_map<string, vector<string>>& graph,
        unordered_map<string, int>& visited, string node, vector<string>& result){

    if(visited[node] == 1) return false;
    if(visited[node] == 2) return true;

    visited[node] = 1;
    for(auto neighbor : graph[node]){
        if(!topSortVisit(graph, visited, neighbor, result)) return false;
    }

    result.push_back(node);
    visited[node] = 2;
    return true;
}

vector<string> topSort(unordered_map<string, vector<string>>& graph){

    unordered_map<string, int> visited;
    vector<string> result;

    for(auto elem : graph){
        string node = elem.first;
        bool acyclic = topSortVisit(graph, visited, node, result);
        if(!acyclic){
            cout << "cycle detected\n";
            return vector<string>{};
        }

    }

    return result;
}


unordered_map<string, vector<string>> makeGraph(vector<pair<string, string>> courses){
    unordered_map<string, vector<string>> graph;

    for(auto p : courses){
        graph[p.first].push_back(p.second);
    }
    return graph;
}

int main(){

    vector<pair<string, string>> pairs;
    pairs.push_back(make_pair("LA", "ML"));
    pairs.push_back(make_pair("MT", "ML"));
    pairs.push_back(make_pair("MA", "PT"));
    pairs.push_back(make_pair("PT", "ML"));
    pairs.push_back(make_pair("ML", "DL"));
    pairs.push_back(make_pair("ML", "AML"));

    auto graph = makeGraph(pairs);
    vector<string> result = topSort(graph); // ML, AML, DL
    // A possible correct solution could be: LA, MT, MA, PT, ML, AML, DL


    for(string s : result){
        cout << s << " ";
    }
    cout << "\n";
}

1 Ответ

1 голос
/ 25 января 2020

Вставка в unordered_map делает недействительными итераторы в карте , если она повторяет . Это разбивает ваш l oop на auto elem : graph (что, кстати, копирует ваши vector<string> объекты; вместо этого используйте auto &elem). Передайте свой график как const&, чтобы избежать таких махинаций; Затем компилятор мягко предложит вам использовать at вместо operator[].

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...