Bottom View of Binary Tree
O array root contém uma árvore binária: a raiz no índice 0, os filhos do índice i em 2i+1 (esquerda) e 2i+2 (direita), -1 para uma posição vazia e, possivelmente, entradas extras -1 no final.
A raiz está na coluna 0; um passo para a esquerda move uma coluna à esquerda, e um passo para a direita move uma coluna à direita. Visto de baixo, cada coluna mostra seu nó mais profundo e, em caso de empate, o último nesse nível quando lido da esquerda para a direita (o índice maior). Retorne os valores que você vê, começando pela coluna mais à esquerda.
Função
- rootinteger-array
- a árvore em ordem de heap, com -1 para um espaço vazio
- Retornainteger-array
- o valor visto de baixo em cada coluna, começando pela coluna mais à esquerda
Restrições
1 ≤ root.length ≤ 32767- Cada
root[i]é-1ou um valor com0 ≤ root[i] ≤ 104, e nenhum valor aparece duas vezes. root[0]nunca é-1, então a árvore tem pelo menos um nó.- The array pode terminar com entradas
-1extras após o último nó. - Ambos os filhos de um ponto vazio também estão vazios, e a árvore tem no máximo
14níveis.
Exemplos
- Entrada
- root = [5, 2, 9, 4, 7, 1, 6]
- Saída
- [4, 2, 1, 9, 6]
- Explicação
- A coluna 0 contém
5e, dois níveis abaixo,7(índice 4) e1(índice 5).1aparece depois no nível compartilhado, então é exibido. As colunas -2, -1, 1 e 2 contêm4,2,9e6.
- Entrada
- root = [1, 2, 3, -1, 4, -1, -1, -1, -1, 5]
- Saída
- [5, 4, 3]
- Explicação
- A coluna -1 contém
2e, dois níveis abaixo,5(índice 9), então você vê5. Na coluna 0,4fica abaixo da raiz. A coluna 1 contém apenas3.
- Entrada
- root = [8, -1, -1, -1]
- Saída
- [8]
- Explicação
- Todos os espaços após a raiz são
-1, então8é o único nó e a coluna 0 é a única coluna.
Para ir além
Se um empate no nível mais profundo de uma coluna deve retornar todos os nós empatados, da esquerda para a direita, como a versão em largura primeiro mudaria? O que a coluna 0 do exemplo 1 retornaria?
Dicas
Abra uma de cada vez. Cada uma revela um pouco mais.
Numere as colunas: a raiz é 0, um passo para a esquerda subtrai 1 e um passo para a direita soma 1. Olhando de baixo, qual nó de uma coluna você vê e o que acontece se dois nós dessa coluna estiverem no mesmo nível?
Visite os nós nível por nível, da esquerda para a direita dentro de cada nível, e escreva o valor de cada nó em sua coluna, substituindo o que estava lá. O que resta em cada coluna no final?
Use uma fila de pares (índice, coluna) e enfileire o filho esquerdo antes do direito. Sobrescreva o valor da coluna para cada nó, acompanhe a menor e a maior coluna e leia as colunas em ordem no final.
Solução
Cada coluna mostra seu nó mais profundo, e, em caso de empate, vence o nó que aparece depois na ordem por nível. Uma busca em largura visita os nós em ordem por nível, então o último nó que ela visita em uma coluna é a resposta: deixe cada nó sobrescrever a posição da sua coluna.
Busca em profundidade, nó mais profundo por coluna
Intuição
Percorra a árvore em profundidade, atribuindo a cada nó sua coluna e profundidade, e mantenha, por coluna, a profundidade e o valor do nó mais profundo encontrado até então.
Visitar a subárvore esquerda antes da direita encontra os nós de um nível da esquerda para a direita, então o nó desejado é o último encontrado: substitua quando a profundidade for maior ou igual. No exemplo 1, o percurso encontra 7 e depois 1, ambos na profundidade 2 e na coluna 0, e 1 fica com a coluna.
As colunas formam um intervalo contínuo, então acompanhe a menor e a maior e leia todas as colunas entre elas. Uma visita por nó resulta em tempo O(n), e a recursão tem profundidade de h chamadas para uma árvore de h níveis.
Algoritmo
- Visite a raiz com a coluna 0 e a profundidade 0.
- Pule o índice
iquando ele estiver além do fim do array ouroot[i]for-1. - Se ainda não houver um nó na coluna, ou se essa profundidade for maior ou igual à armazenada, armazene essa profundidade e o valor.
- Visite
2i+1com a colunacol-1e, em seguida,2i+2com a colunacol+1, ambos um nível mais profundos. - Retorne os valores armazenados, começando pela menor coluna.
def bottomView(root):
n = len(root)
best = {} # column -> (depth, value) of the deepest node found so far
def visit(i, col, depth):
if i >= n or root[i] == -1:
return
# Replace on a greater or equal depth: on a tie, the node
# met last is the one further right, which comes later in level order.
if col not in best or depth >= best[col][0]:
best[col] = (depth, root[i])
visit(2 * i + 1, col - 1, depth + 1)
visit(2 * i + 2, col + 1, depth + 1)
visit(0, 0, 0)
# The columns that hold a node form one unbroken range.
return [best[col][1] for col in range(min(best), max(best) + 1)]Busca em largura, último nó por coluna
Intuição
Uma fila em que cada nó que sai adiciona seu filho esquerdo e, em seguida, seu filho direito, entrega os nós na ordem por nível: todos os nós de profundidade d antes de qualquer nó de profundidade d+1, e cada nível da esquerda para a direita. Entre os nós de uma coluna, o mais profundo sai por último e, em caso de empate, sai por último o que estiver mais à direita. Assim, quando cada nó sobrescreve a posição de sua coluna, cada posição acaba contendo o nó visto de baixo, sem necessidade de comparar profundidades.
No exemplo 1, 7 grava na coluna 0 e 1 sobrescreve esse valor.
Cada nó exige uma visita à fila e uma gravação no mapa: tempo O(n). A fila pode conter um nível inteiro, cerca de metade dos nós, então o espaço é O(n).
Algoritmo
- Coloque o par (índice 0, coluna 0) em uma fila.
- Retire um par (
i,col) e armazeneroot[i]na colunacol, substituindo qualquer valor anterior. - Adicione (
2i+1,col-1) e, em seguida, (2i+2,col+1), para cada filho que esteja dentro do array e não seja-1. - Repita até que a fila esteja vazia, acompanhando a menor e a maior coluna.
- Retorne os valores do mapa, começando pela menor coluna.
from collections import deque
def bottomView(root):
n = len(root)
bottom = {} # column -> value of the last node seen in that column
queue = deque([(0, 0)]) # (node index, column); the root is never empty
while queue:
i, col = queue.popleft()
# The queue hands out deeper nodes later, and right after left
# on one level, so the last node to reach a column is the one seen from below.
bottom[col] = root[i]
left, right = 2 * i + 1, 2 * i + 2
if left < n and root[left] != -1:
queue.append((left, col - 1))
if right < n and root[right] != -1:
queue.append((right, col + 1))
# The columns that hold a node form one unbroken range.
return [bottom[col] for col in range(min(bottom), max(bottom) + 1)]
Armadilhas e casos extremos
- Substituir apenas em uma profundidade estritamente maior na versão em profundidade. O exemplo 1 então mantém
7em vez de1. - Manter o primeiro nó que uma busca em largura encontra para cada coluna. Essa é a vista superior.
- Adicionar o filho direito à fila antes do esquerdo. Assim, cada nível sai da direita para a esquerda, e os empates favorecem o nó errado.
- Usar as folhas como resposta. No exemplo 1, a folha
7fica oculta por1, e no exemplo 2 o nó4tem um filho, mas está na parte inferior da coluna 0. - Ordenar as colunas como texto. Como strings,
"-10"fica entre"-1"e"-2".
Perguntas frequentes3
O que é a visão inferior de uma árvore binária?
Atribua a coluna 0 à raiz, uma coluna a menos a cada passo à esquerda e uma a mais a cada passo à direita. A vista inferior lista o nó mais profundo de cada coluna, da esquerda para a direita. Este problema desempata nós na mesma profundidade pela ordem dos níveis, mantendo o nó que aparece depois.
Qual é a complexidade de tempo da vista inferior de uma árvore binária?
O(n) para n nós: a busca em largura percorre cada nó uma vez, com uma escrita em um mapa hash. Um mapa ordenado, como um mapa de árvore, adiciona um fator de log w para w colunas.
Qual é a diferença entre a vista superior e a vista inferior?
Ambas escolhem um nó por coluna. A vista superior escolhe o nó mais raso e a vista inferior, o mais profundo. Na versão em largura, a vista superior mantém o primeiro valor recebido por uma coluna; a vista inferior o sobrescreve a cada vez.
Problemas parecidos
Problemas que usam as mesmas ideias. Resolver dois ou três é o que fixa um padrão.
def bottomView(root):
# Escreva o código aquiTeste #1
Teste #2
Teste #3
Argumento
root = [5, 2, 9, 4, 7, 1, 6]
Saída Esperada
[4, 2, 1, 9, 6]