Algorithme de tri topologique et exemples

tri topologique

Trier topologiquement un graphe acyclique dirigé (DAG en abrégé) G consiste à organiser tous les sommets de G en une séquence linéaire, de sorte que toute paire de sommets u et v dans le graphe, si l'arête <u, v>∈ E( G), alors u apparaît avant v dans la séquence linéaire. Habituellement, une telle séquence linéaire est appelée une séquence qui satisfait l'ordre topologique (ordre topologique), appelée séquence topologique. En termes simples, un ordre total sur un ensemble est obtenu à partir d'un ordre partiel sur un certain ensemble, et cette opération est appelée tri topologique.

Pas

Pour les graphes acycliques dirigés, l'algorithme de tri topologique pour la construction de séquences topologiques exécute principalement les deux étapes suivantes dans une boucle jusqu'à ce qu'il n'y ait plus de sommet avec un degré de 0.
(1) Sélectionnez un sommet avec un degré d'entrée de 0 et sortez-le ;
(2) Supprimez ce sommet et tous les bords sortants du réseau.
Une fois la boucle terminée, si le nombre de sommets en sortie est inférieur au nombre de sommets dans le réseau, alors sortez les informations de "boucle", sinon la séquence de sommets en sortie est une séquence topologique.

exemple

Calendrier du programme du Rituel 207

largeur première recherche

Générer un tri topologique séquentiellement est une sorte de pensée positive.
Nous utilisons une file d'attente pour la recherche en largeur d'abord. Initialement, tous les nœuds avec un degré de 0 sont placés dans la file d'attente, ce sont les nœuds supérieurs qui peuvent être triés topologiquement, et l'ordre relatif entre eux n'est pas pertinent.
A chaque étape de la recherche en largeur d'abord, on sort le nœud u en tête de file :

  • Nous vous mettons dans la réponse;
  • Supprimez tous les bords sortants de u, c'est-à-dire réduisez les degrés entrants de tous les nœuds adjacents de u de 1. Si le degré d'entrée d'un nœud voisin v devient 0, alors nous mettons v dans la file d'attente.

Une fois le processus de recherche en largeur terminé. Si les n nœuds sont inclus dans la réponse, alors on a trouvé un tri topologique, sinon, il y a un cycle dans le graphe, et il n'y a pas de tri topologique.

class Solution {
    
    
private:
    vector<vector<int>> edges;
    vector<int> indeg;

public:
    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
    
    
        edges.resize(numCourses);
        indeg.resize(numCourses);
        //更新入度表和邻接表
        for (const auto& info: prerequisites) {
    
    
            edges[info[1]].push_back(info[0]);
            ++indeg[info[0]];
        }

        queue<int> q;
        //入度为0的先入队列
        for (int i = 0; i < numCourses; ++i) {
    
    
            if (indeg[i] == 0) {
    
    
                q.push(i);
            }
        }

        int visited = 0;
        while (!q.empty()) {
    
    
            ++visited;
            int u = q.front();
            q.pop();
            for (int v: edges[u]) {
    
    
                --indeg[v];
                if (indeg[v] == 0) {
    
    
                    q.push(v);
                }
            }
        }

        return visited == numCourses;
    }
};

recherche en profondeur d'abord

La recherche en profondeur d'abord est une sorte de réflexion inversée, et le dernier élément du tri topologique est d'abord poussé sur la pile.
Pour tout nœud du graphe, il a trois états pendant le processus de recherche, à savoir :

  • « Non recherché » : Nous n'avons pas recherché ce nœud ;
  • "Recherche" : nous avons recherché ce nœud, mais nous n'avons pas retracé jusqu'à ce nœud, c'est-à-dire que ce nœud n'a pas été poussé dans la pile, et il y a des nœuds adjacents qui n'ont pas été recherchés );
  • "Terminé" : nous avons recherché et reculé ce nœud, c'est-à-dire que ce nœud a été poussé dans la pile, et tous les nœuds adjacents de ce nœud apparaissent en bas de la pile, ce qui répond aux exigences du tri topologique.

À travers les trois états ci-dessus, nous pouvons donner le flux d'algorithme de tri topologique en utilisant la recherche en profondeur d'abord.Au début de chaque cycle de recherche, nous sélectionnons au hasard un nœud "non recherché" pour lancer la recherche en profondeur d'abord.

Nous marquons le nœud u actuellement recherché comme "recherche", et parcourons chaque nœud adjacent v de ce nœud :

  • Si v n'est "pas recherché", alors nous commençons à rechercher v et revenons à u une fois la recherche terminée ;
  • Si v "recherche", alors nous avons trouvé un cycle dans le graphe, donc il n'y a pas de tri topologique ;
  • Si v est "terminé", cela signifie que v est déjà dans la pile, mais u n'est pas encore dans la pile, donc peu importe quand u est mis sur la pile, cela n'affectera pas la relation topologique avant (u, v) , et il n'y a aucune opération à effectuer.

Lorsque tous les voisins de u sont "terminés", nous le plaçons sur la pile et le marquons comme "terminé". Une fois que tout le processus de recherche en profondeur est terminé, si nous ne trouvons pas l'anneau dans le graphique, alors stockons tous les n nœuds dans la pile, et l'ordre du haut de la pile au bas de la pile est un tri topologique .

class Solution {
    
    
private:
    vector<vector<int>> edges;
    vector<int> visited;
    bool valid = true;

public:
    void dfs(int u) {
    
    
        visited[u] = 1;
        for (int v: edges[u]) {
    
    
            if (visited[v] == 0) {
    
    
                dfs(v);
                if (!valid) {
    
    
                    return;
                }
            }
            else if (visited[v] == 1) {
    
    
                valid = false;
                return;
            }
        }
        visited[u] = 2;
    }

    bool canFinish(int numCourses, vector<vector<int>>& prerequisites) {
    
    
        edges.resize(numCourses);
        visited.resize(numCourses);
        for (const auto& info: prerequisites) {
    
    
            edges[info[1]].push_back(info[0]);
        }
        for (int i = 0; i < numCourses && valid; ++i) {
    
    
            if (!visited[i]) {
    
    
                dfs(i);
            }
        }
        return valid;
    }
};

Je suppose que tu aimes

Origine blog.csdn.net/weixin_45184581/article/details/129055772
conseillé
Classement