Encontrar uma fonte de um gráfico acíclico direcionado em tempo linear

10

Dado um gráfico acíclico direcionado , um vértice é uma fonte se seu indegree for zero, significando que ele possui apenas arcos de saída.D=(V,UMA)vV

Existe um algoritmo de tempo linear para encontrar uma fonte em um determinado gráfico acíclico direcionado?

Pergunta de acompanhamento: Alguém em tempo linear pode encontrar todas as fontes?

breezeintopl
fonte
2
Por raiz, você quer dizer um nó com todas as arestas de saída e sem arestas de entrada? Nesse caso, pode haver mais de uma raiz.
Paresh
Sim você está certo. É por isso que digo "uma raiz", mas não "a raiz".
Br
2
Nesse caso, a definição não é suficiente para um algoritmo de tempo linear?
Paresh
3
Qual é a estrutura de dados? Você recebe uma matriz de adjacência, listas de vizinhança, lista de adjacência ou o quê? Você pode verificar a vizinhança (de entrada) de um vértice no tempo proporcional ao seu tamanho?
GD
5
Se você quer dizer linear no número de vértices, deve indicar isso. Além disso, as listas de adjacência são ambíguas para os gráficos direcionados - você está listando as arestas de entrada e de saída, em listas separadas ou juntas?
Yuval Filmus

Respostas:

20

Como Yuval menciona, a estrutura de dados é importante aqui. Vou tentar dar uma solução para alguns dos tipos de listas de adjacência:

  1. Lista de arestas de entrada : para cada nó, há uma lista de vértices a partir dos quais há uma aresta de entrada nesse nó. Você pode simplesmente varrer todos os vértices e verificar se o tamanho da lista de adjacências é0 0 ou não. Um tamanho0 0lista significa que não há arestas recebidas; portanto, o nó é uma fonte ou está desconectado. Assumindo um gráfico conectado, essa varredura de cada vértice fornecerá uma lista de todas as fontes (ou você pode parar depois de encontrar uma) emO(|V|)tempo - linear no número de vértices .
  2. Lista de arestas de saída : para cada nó, existe uma lista de vértices para os quais existe uma aresta direcionada a partir desse nó. Mantenha uma cadeia de bits com cada bit representando um vértice, inicializado como 0. A partir do primeiro nó, comece a varrer sua lista em busca de vértices nos quais existe uma borda de saída. Como esse nó (vizinho) não pode ser uma fonte, continue definindo o bit correspondente na cadeia de bits. No final, todos os vértices cujos bits correspondentes ainda não estão definidos, são os vértices de origem. Você pode fazer isso no tempo linear no tamanho do gráfico -O(|V|+|E|).
  3. Ambas as listas juntas : Para cada vértice, há uma lista mista de vértices que têm uma aresta para ou a partir desse vértice, com algum outro atributo indicando qual dos dois é realmente o caso. A abordagem é semelhante à 2 acima, com a adição de que qualquer borda de entrada exclui imediatamente o vértice atual (e você pode marcar seu conjunto de bits). Diferente do ponto 2, onde você precisa passar por todos os vértices, aqui, você pode encontrar alguma fonte mais cedo. Se você não parar, você terá todas as fontes. Nos dois casos, o tempo é novamente linear no tamanho do gráfico -O(|V|+|E|).
  4. Ambas as listas separadamente : basta escolher a lista de arestas recebidas e seguir 1.

Como observação lateral, se a escolha da estrutura de dados estiver em suas mãos, convém analisar o que todas as operações você pretende executar e com que frequência e escolher uma estrutura de dados apropriada.

Edit: Para o caso 1, se você tem um dag em que o número de fontes é muito pequeno em comparação com|V|(por exemplo, em uma árvore com uma fonte) e onde a distância média de qualquer vértice a uma fonte é pequena em comparação com|V| e você deseja apenas uma fonte, pode usar um algoritmo mais rápido, em média (embora a pior complexidade assintótica seja a mesma). Selecione qualquer vértice aleatoriamente e vá para qualquer um de seus pais (na lista de arestas recebidas) e depois para seu pai, etc., até chegar a um nó que não tem pai - uma fonte. Esse pequeno ganho de eficiência é para tipos muito limitados de gráficos com um algoritmo um pouco mais complexo.

Paresh
fonte
1
Para listas de arestas recebidas, caso você precise apenas encontrar uma única fonte, não seria mais rápido apenas seguir uma aresta arbitrária para obter o predecessor, até chegar a uma fonte? Especialmente se o gráfico for plano, ou seja, a distância média a cada fonte de cada vértice é muito menor que|V|.
Simon S
@SimonS Mesmo que a pior complexidade seja a mesma (por exemplo, uma cadeia linear), você poderá torná-la mais rápida se o gráfico tiver muito poucas fontes (em comparação com |V|) e a distância média de qualquer vértice a uma fonte é muito pequena - por exemplo, um gráfico em estrela com o centro como fonte. Só ter a condição mencionada não é suficiente - por exemplo, 1-> 2, 1-> 3, 4-> 2, 4-> 3 - (você pode ter um gráfico com distância média de 0,5 e | V | / 2) A distância média é pequena, mas ambos os algoritmos fornecerão o tempo esperado igual / semelhante. Vou adicioná-lo à minha resposta embora.
Paresh
Obrigado pela sua resposta! Eu acho que o que quero dizer é o segundo caso, lista de saída. BTW, por que é O (| V | + | E |)? Eu sei | E |, porque você precisa escanear todas as bordas, mas onde | V | de? Obrigado!
breezeintopl
@breezeintopl É uma forma de representação. Você verifica cada aresta uma vez e cada vértice também um número constante de vezes. No mínimo, no final, você precisa verificar todos os vértices uma vez para ver quais bits estão desmarcados. Outra maneira de ver é que uma lista de adjacência usaΘ(|V|+|E|)space - armazena um vértice e sua lista de arestas. E você está percorrendo toda a lista. Claro que desde|E| pode variar de O(|V|) para O(|V|2), você pode pular o |V|parte. Veja um exemplo para o BFS .
Paresh
0

Vamos considerar uma pergunta mais simples. Suponha que você saiba que seu gráfico é uma árvore. Então você pode encontrar o nó de origem em tempo linear. Basta selecionar um nó aleatório, se é a raiz, então você tem a sua resposta; caso contrário, deve ser filho ou pai e, em seguida, retroceder até chegar à raiz. Isso pode ser feito emO(|V|-1).

Reza
fonte
3
Se sua estrutura de dados não incluir uma lista de bordas para um nó, a localização do pai já leva O (m) (nas árvores, portanto, O (n) tempo. Como o caminho para a raiz da árvore pode ser linearmente longo, esta é apenas uma solução quadrática Mas se você tem em bordas, em seguida, encontrar algum nó com 0 no bordas é trivial..
adriann
-1

Supondo que você tenha um gráfico G = (V, E) fornecido no formato de lista de adjacências. (para ficar claro aqui, a lista contém todas as arestas de saída da fonte). Você pode construir o inverso do gráfico G em tempo linear. Em seguida, você pode percorrer o gráfico inverso e coletar todos os vértices que possuem a lista de adjacências vazia. Esses vértices não têm arestas de saída no gráfico inverso, o que significa que não têm arestas de entrada no gráfico original; portanto, esses são os vértices de origem.

O tempo de execução é linear. Construir o inverso do gráfico leva no máximo O (| V | + | E |). A iteração sobre o inverso do gráfico leva tempo O (| V |).

MGB
fonte
1
Honestamente, calcular o inverso do gráfico parece ser um problema mais difícil do que encontrar as fontes. Duvido que alguém que não consiga descobrir como obter as fontes possa descobrir como reverter todas as arestas.
David Richerby
@DavidRicherby você disse que calcular o inverso do gráfico é mais difícil. Como você define mais difícil? Se for a complexidade do tempo, isso pode ser feito em tempo linear. você pode procurar a solução aqui [link] ( stackoverflow.com/questions/40378152/… ). A questão é sobre uma solução de tempo linear que encontra a fonte de um gráfico e minha solução proposta faz isso. Se você acha que não, pode ser específico e me dizer como meu algoritmo não atende ao requisito de tempo linear?
MGB
Quero dizer conceitualmente mais difícil. Você está dizendo que, para resolver este exercício, o solicitante só precisa resolver outro exercício, mas esse outro exercício parece mais difícil que o primeiro.
David Richerby