Algoritmo de classificação topológica e exemplos

classificação topológica

Classificar topologicamente um Grafo Acíclico Direcionado (DAG para abreviar) G é organizar todos os vértices em G em uma sequência linear, de modo que qualquer par de vértices u e v no gráfico, se a aresta <u, v>∈ E( G), então u aparece antes de v na sequência linear. Normalmente, essa sequência linear é chamada de sequência que satisfaz a ordem topológica (Ordem Topológica), referida como sequência topológica. Simplificando, uma ordem total em um conjunto é obtida a partir de uma ordem parcial em um determinado conjunto, e essa operação é chamada de classificação topológica.

Passos

Para grafos acíclicos direcionados, o algoritmo de classificação topológica para a construção de sequências topológicas executa principalmente as duas etapas a seguir em um loop até que não haja vértice com um grau de entrada de 0.
(1) Selecione um vértice com grau de entrada 0 e envie-o;
(2) Exclua este vértice e todas as arestas de saída da rede.
Depois que o loop terminar, se o número de vértices de saída for menor que o número de vértices na rede, emita a informação do "loop", caso contrário, a sequência de vértices de saída é uma sequência topológica.

exemplo

Currículo do Ritual 207

largura primeira pesquisa

Gerar classificação topológica sequencialmente é um tipo de pensamento positivo.
Usamos uma fila para busca em largura. Inicialmente, todos os nós com grau de entrada 0 são colocados na fila, eles são os primeiros nós que podem ser classificados topologicamente e a ordem relativa entre eles é irrelevante.
A cada passo da busca em largura, retiramos o nó u no início da fila:

  • Colocamos u na resposta;
  • Remova todas as arestas de saída de u, ou seja, reduza os graus de entrada de todos os nós adjacentes de u em 1. Se o grau de entrada de um nó vizinho v se tornar 0, então colocamos v na fila.

Após o término do processo de pesquisa em largura. Se os n nós forem incluídos na resposta, então encontramos uma classificação topológica, caso contrário, há um ciclo no grafo e não há classificação topológica.

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;
    }
};

pesquisa em profundidade

A pesquisa em profundidade é um tipo de pensamento reverso, e o último elemento da classificação topológica é primeiro colocado na pilha.
Para qualquer nó do grafo, ele possui três estados durante o processo de busca, a saber:

  • "Não pesquisado": não pesquisamos este nó;
  • "Pesquisando": Pesquisamos este nó, mas não rastreamos até este nó, ou seja, este nó não foi colocado na pilha e existem nós adjacentes que não foram pesquisados);
  • "Concluído": Pesquisamos e retrocedemos este nó, ou seja, este nó foi empurrado para a pilha e todos os nós adjacentes desse nó aparecem na parte inferior da pilha, o que atende aos requisitos de classificação topológica.

Através dos três estados acima, podemos dar ao algoritmo fluxo de classificação topológica usando busca em profundidade.No início de cada rodada de busca, selecionamos aleatoriamente um nó "não pesquisado" para iniciar a busca em profundidade.

Marcamos o nó atualmente pesquisado u como "procurando" e percorremos cada nó adjacente v deste nó:

  • Se v for "não pesquisado", então começamos a procurar por v e voltamos para u após a conclusão da pesquisa;
  • Se v estiver "procurando", então encontramos um ciclo no grafo, portanto não há ordenação topológica;
  • Se v estiver "concluído", significa que v já está na pilha, mas u ainda não está na pilha, portanto, não importa quando u for colocado na pilha, isso não afetará o relacionamento topológico antes de (u, v) , e não há necessidade de fazer qualquer operação.

Quando todos os vizinhos de u estiverem "concluídos", colocamos u na pilha e o marcamos como "concluído". Depois que todo o processo de busca em profundidade terminar, se não encontrarmos o anel no grafo, então armazene todos os n nós na pilha, e a ordem do topo da pilha para a parte inferior da pilha é uma classificação topológica .

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;
    }
};

Acho que você gosta

Origin blog.csdn.net/weixin_45184581/article/details/129055772
Recomendado
Clasificación