Digamos que você deseje implementar uma pesquisa pela primeira vez de uma árvore binária de forma recursiva . Como você faria isso?
É possível usar apenas a pilha de chamadas como armazenamento auxiliar?
Digamos que você deseje implementar uma pesquisa pela primeira vez de uma árvore binária de forma recursiva . Como você faria isso?
É possível usar apenas a pilha de chamadas como armazenamento auxiliar?
Respostas:
(Suponho que este seja apenas algum tipo de exercício de pensamento, ou mesmo uma pergunta de lição de casa / entrevista de truque, mas suponho que eu possa imaginar um cenário bizarro em que não seja permitido nenhum espaço de pilha por algum motivo [algum costume muito ruim gerenciador de memória? alguns problemas bizarros de tempo de execução / sistema operacional?] enquanto você ainda tem acesso à pilha ...)
A travessia da primeira largura tradicionalmente usa uma fila, não uma pilha. A natureza de uma fila e uma pilha são praticamente opostas, portanto, tentar usar a pilha de chamadas (que é uma pilha, daí o nome) como o armazenamento auxiliar (uma fila) está fadada ao fracasso, a menos que você esteja fazendo algo estupidamente ridículo com a pilha de chamadas que você não deveria ser.
Da mesma forma, a natureza de qualquer recursão não-cauda que você tenta implementar é basicamente adicionar uma pilha ao algoritmo. Isso deixa de ser a primeira pesquisa em uma árvore binária e, portanto, o tempo de execução e outros enfeites para o BFS tradicional não se aplicam mais completamente. Obviamente, você sempre pode transformar trivialmente qualquer loop em uma chamada recursiva, mas isso não é nenhum tipo de recursão significativa.
No entanto, existem maneiras, como demonstrado por outros, de implementar algo que segue a semântica do BFS a algum custo. Se o custo da comparação for caro, mas a travessia de nó for barata, como o @Simon Buchan , você pode simplesmente executar uma pesquisa iterativa em profundidade, processando apenas as folhas. Isso significaria nenhuma fila crescente armazenada no heap, apenas uma variável de profundidade local e pilhas sendo construídas repetidamente na pilha de chamadas à medida que a árvore é percorrida repetidamente. E como o @Patrick observou, uma árvore binária apoiada por uma matriz é normalmente armazenada na ordem da primeira travessia de largura de qualquer maneira, portanto, uma pesquisa de largura seria trivial, também sem a necessidade de uma fila auxiliar.
fonte
Se você usar uma matriz para fazer backup da árvore binária, poderá determinar o próximo nó algebricamente. se
i
é um nó, seus filhos podem ser encontrados em2i + 1
(para o nó esquerdo) e2i + 2
(para o nó direito). O próximo vizinho de um nó é dado pori + 1
, a menos quei
seja um poder de2
Aqui está o pseudocódigo para uma implementação muito ingênua da primeira pesquisa de largura em uma árvore de pesquisa binária baseada em array. Isso pressupõe uma matriz de tamanho fixo e, portanto, uma árvore de profundidade fixa. Ele examinará os nós sem pai e poderá criar uma pilha incontrolavelmente grande.
fonte
Não consegui encontrar uma maneira de fazê-lo completamente recursivo (sem nenhuma estrutura de dados auxiliar). Mas se a fila Q for passada por referência, você poderá ter a seguinte função recursiva boba:
fonte
O método a seguir usou um algoritmo DFS para obter todos os nós em uma profundidade específica - o mesmo que fazer BFS para esse nível. Se você descobrir a profundidade da árvore e fazer isso para todos os níveis, os resultados serão os mesmos de um BFS.
Encontrar a profundidade de uma árvore é um pedaço de bolo:
fonte
level
seja zero.Uma recursão simples de BFS e DFS em Java:
basta pressionar / oferecer o nó raiz da árvore na pilha / fila e chamar essas funções.
fonte
Eu encontrei um algoritmo relacionado à travessia de largura-primeiro recursivo (até funcional) muito bonito. Não é minha ideia, mas acho que deve ser mencionado neste tópico.
Chris Okasaki explica seu algoritmo de numeração em primeiro lugar no ICFP 2000 em http://okasaki.blogspot.de/2008/07/breadth-first-numbering-algorithm-in.html muito claramente com apenas três figuras.
A implementação Scala do Debasish Ghosh, encontrada em http://debasishg.blogspot.de/2008/09/breadth-first-numbering-okasakis.html , é:
fonte
O jeito idiota:
fonte
Aqui está a solução Scala curta :
A idéia de usar o valor de retorno como acumulador é bem adequada. Pode ser implementado em outros idiomas da mesma maneira, apenas certifique-se de que sua função recursiva processe a lista de nós .
Listagem de código de teste (usando a árvore de teste @marco):
Resultado:
fonte
Aqui está uma implementação python:
fonte
Aqui está uma implementação do Scala 2.11.4 do BFS recursivo. Eu sacrifiquei a otimização da chamada de cauda por brevidade, mas a versão do TCOd é muito semelhante. Veja também a publicação de @snv .
fonte
O seguinte me parece bastante natural, usando Haskell. Itere recursivamente sobre os níveis da árvore (aqui coleciono nomes em uma grande sequência ordenada para mostrar o caminho através da árvore):
fonte
Aqui está uma implementação transversal Python recursiva do BFS, trabalhando para um gráfico sem ciclo.
fonte
Eu gostaria de adicionar meus centavos à resposta principal , pois se o idioma suportar algo como gerador, o bfs poderá ser executado de forma co-recursiva.
Para começar, a resposta de @ Tanzelax diz:
De fato, a pilha de chamadas de funções comuns não se comportará como uma pilha normal. Mas a função de gerador suspenderá a execução da função, dando-nos a chance de gerar o próximo nível de filhos dos nós sem nos aprofundar nos descendentes mais profundos do nó.
O código a seguir é bfs recursivo em Python.
A intuição aqui é:
fonte
Eu tive que implementar uma passagem heap que sai em uma ordem BFS. Na verdade, não é BFS, mas realiza a mesma tarefa.
fonte
Seja v o vértice inicial
Seja G o gráfico em questão
A seguir está o pseudo-código sem usar a fila
fonte
O BFS para uma árvore binária (ou n-ária) pode ser feito recursivamente sem filas, como a seguir (aqui em Java):
Um exemplo de impressão transversal números 1 a 12 em ordem crescente:
fonte
fonte
Aqui está uma implementação JavaScript que falsifica a amplitude da primeira travessia com a recursão da profundidade primeiro. Estou armazenando os valores do nó em cada profundidade dentro de uma matriz, dentro de um hash. Se um nível já existe (temos uma colisão), apenas pressionamos a matriz nesse nível. Você também pode usar uma matriz em vez de um objeto JavaScript, já que nossos níveis são numéricos e podem servir como índices de matriz. Você pode retornar nós, valores, converter em uma lista vinculada ou o que quiser. Estou apenas retornando valores por uma questão de simplicidade.
Aqui está um exemplo de amplitude real da primeira largura usando uma abordagem iterativa.
fonte
A seguir, é apresentado meu código para implementação completamente recursiva da primeira pesquisa de largura de um gráfico bidirecional sem usar loop e fila.
fonte
Implementação em C # do algoritmo de pesquisa recursiva da primeira largura para uma árvore binária.
Visualização de dados em árvore binária
Se você deseja que o algoritmo funcione não apenas com a árvore binária, mas com gráficos, o que pode ter dois ou mais nós que apontam para o mesmo outro nó, você deve evitar o ciclo próprio mantendo a lista dos nós já visitados. A implementação pode ser assim.
Visualização de dados do gráfico
fonte
Eu criei um programa usando c ++ que também está trabalhando em gráfico conjunto e separado.
fonte