Artigo original: Everything you need to know about tree data structures
Quando você aprende a programar pela primeira vez, é comum aprender arrays como a "principal estrutura de dados".
Em algum momento, você também aprenderá sobre tabelas hash
. Se você estiver buscando o diploma em Ciência da Computação, você terá que fazer uma disciplina de estrutura de dados. Você também aprenderá sobre listas vinculadas
, filas
e pilhas
. Essas estruturas de dados são chamadas de estruturas de dados "lineares", pois todas elas têm um início e um fim lógicos.
Quando começamos a aprender sobre árvores
e grafos
, pode ficar bastante confuso. Neles, não armazenamos dados de forma linear. As duas estruturas armazenam dados de um modo específico.
Este artigo é para ajudá-lo a entender melhor a estrutura de dados de árvore e para esclarecer qualquer confusão que você possa ter sobre ela.
Nele, vamos aprender:
- O que é uma árvore
- Exemplos de árvore
- Sua terminologia e como ela funciona
- Como implementar estruturas de árvore em código.
Vamos começar esta jornada de aprendizagem :)
Definição
Ao começar a programar, é comum entender melhor as estruturas de dados lineares do que estruturas de dados como árvores e grafos.
As árvores são conhecidas como uma estrutura de dados não linear. Elas não armazenam dados de modo linear. Elas organizam os dados de modo hierárquico.
Vamos examinar exemplos da vida real!
O que quero dizer quando digo de modo hierárquico?
Imagine uma árvore genealógica com relacionamentos de todas as gerações: avós, pais, filhos, irmãos e assim por diante. Geralmente, organizamos as árvores genealógicas de modo hierárquico.

O desenho acima é a minha árvore genealógica. Tossico, Akikazu, Hitomi e Takemi são meus avós.
Toshiaki e Juliana são meus pais.
TK, Yuji, Bruno e Kaio são os filhos dos meus pais (eu e meus irmãos).
A estrutura de uma organização é outro exemplo de uma hierarquia.

Em HTML, o Modelo de Objeto de Documento (em inglês, DOM, ou Document Object Model) funciona como uma árvore.

A tag HTML contém outras tags. Temos uma tag head
e uma tag body
. Essas tags contêm elementos específicos. A tag head
tem tags meta
e tags title
. A tag body
tem elementos que aparecem na interface do usuário, como h1
, a
, li
, entre outros.
Uma definição técnica
Uma árvore
é um conjunto de entidades chamadas nós
. Os nós são conectados por arestas
. Cada nó
contém um valor
ou dados
e pode ou não ter um nó filho
.

O primeiro nó
da árvore é chamado de raiz
. Se este nó raiz
é conectado por outro nó, a raiz é então um nó pai
e o nó conectado é um nó filho
.

Todos os nós das árvores
são conectados por links chamados arestas
. Essa é uma parte importante das árvores, porque elas gerenciam a relação entre os nós.

As folhas
são os últimos nós de uma árvore. Elas são os nós sem filhos. Como árvores reais, temos a raiz
, os ramos
e, finalmente, as folhas
.

Outros conceitos importantes a serem entendidos são altura
e profundidade
.
A altura
de uma árvore
é o tamanho do caminho mais longo até uma folha.
A profundidade
de um nó
é o tamanho do caminho percorrido do nó até a raiz
.
Resumo da terminologia
- A raiz é o
nó
mais alto daárvore
- A aresta é a ligação entre dois
nós
- O filho é um
nó
que tem umnó pai
- O pai é um
nó
que tem umaaresta
apontando para umnó filho
- A folha é um
nó
que não temnós filhos
naárvore
- A altura é o tamanho do caminho mais longo até uma
folha
- A profundidade é o tamanho do caminho percorrido do
nó
até araiz
Árvores binárias
Agora, vamos discutir um tipo específico de árvore
. Nós a chamamos de árvore binária
.
"Na ciência da computação, uma árvore binária é uma estrutura de dados em árvore na qual cada nó tem, no máximo, dois filhos, que são referidos como o filho da esquerda e o filho da direita". - Wikipédia
Então, vejamos um exemplo de árvore binária.

Vamos programar uma árvore binária
A primeira coisa que precisamos ter em mente quando implementamos uma árvore binária é o fato de ela ser uma coleção de nós. Cada nó tem três atributos: value
, left_child
(o filho da esquerda) e right_child
(o filho da direita).
Como implementamos uma árvore binária simples que inicializa com estas três propriedades?
Vamos dar uma olhada.
class BinaryTree:
def __init__(self, value):
self.value = value
self.left_child = None
self.right_child = None
Aqui está. Esta é a nossa classe de árvore binária.
Quando instanciamos um objeto, passamos value
(os dados do nó) como um parâmetro. Olhe para left_child
e para right_child
. Os dois estão definidos como None
(Nenhum).
Por quê?
Porque quando criamos nosso nó
, ele não tem filhos. Temos apenas os dados do nó.
Vamos testá-lo:
tree = BinaryTree('a')
print(tree.value) # a
print(tree.left_child) # None
print(tree.right_child) # None
É isso.
Podemos passar a string 'a' como o valor para nosso nó da árvore binária. Se imprimirmos value
, left_child
e right_child
, podemos ver os valores.
Vamos para a parte de inserção. O que precisamos fazer aqui?
Vamos implementar um método para inserir um novo nó
à direita e à esquerda.
Aqui estão as regras:
- Se o nó atual não tiver um filho da esquerda, criamos apenas um nó e o configuramos como
left_child
do nó atual. - Se ele tiver o filho da esquerda, criamos um nó e o colocamos no lugar da filho da esquerda atual. Alocamos este nó filho da esquerda como o novo nó
left_child
.
Vamos representar isso com um desenho :)

Aqui está o código:
def insert_left(self, value):
if self.left_child == None:
self.left_child = BinaryTree(value)
else:
new_node = BinaryTree(value)
new_node.left_child = self.left_child
self.left_child = new_node
Novamente, se o nó atual não tiver um filho da esquerda, apenas criamos um nó e o configuramos como left_child
do nó atual. Como alternativa, criamos um nó e o colocamos no lugar do left_child
atual. Alocamos este nó como o novo nó left_child
.
Fazemos a mesma coisa para inserir um nó do filho da direita.
def insert_right(self, value):
if self.right_child == None:
self.right_child = BinaryTree(value)
else:
new_node = BinaryTree(value)
new_node.right_child = self.right_child
self.right_child = new_node
Feito. :)
Porém, temos que testá-lo.
Vamos construir a seguinte árvore
:

Para resumir a ilustração desta árvore:
- o
nó
a
será araiz
da nossaárvore binária
- o
filho da esquerda
dea
é onó
b
- o
filho da direita
dea
é onó
c
- o
filho da direita
deb
é onó
d
(onó
b
não tem umfilho da esquerda
) - o
filho da esquerda
dec
é onó
e
- o
filho da direita
dec
é onó
f
- tanto o
nó
e
como onó
f
não têm filhos
Portanto, aqui está o código para a árvore:
a_node = BinaryTree('a')
a_node.insert_left('b')
a_node.insert_right('c')
b_node = a_node.left_child
b_node.insert_right('d')
c_node = a_node.right_child
c_node.insert_left('e')
c_node.insert_right('f')
d_node = b_node.right_child
e_node = c_node.left_child
f_node = c_node.right_child
print(a_node.value) # a
print(b_node.value) # b
print(c_node.value) # c
print(d_node.value) # d
print(e_node.value) # e
print(f_node.value) # f
A inserção está feita.
Agora temos que pensar na travessia da árvore.
Temos aqui duas opções: a Busca em Profundidade (DFS - Depth-First Search) e a Busca em Largura (BFS - Breadth-First Search).
- DFS "é um algoritmo para fazer uma travessia ou pesquisar a estrutura de dados em árvore. Ele começa pela raiz e explora o máximo possível ao longo de cada ramo antes de retroceder". – Wikipedia
- BFS "é um algoritmo para fazer uma travessia ou pesquisar a estrutura de dados em árvore. Ele começa na raiz da árvore e explora os nós vizinhos primeiro antes de passar para os vizinhos do nível seguinte". – Wikipedia
Portanto, vamos analisar os tipos de travessia de árvores.
Busca em Profundidade (DFS - Depth-First Search)
A DFS explora todo um caminho até uma folha antes de retroceder (backtracking) e explorar outro caminho. Vamos dar uma olhada em um exemplo com este tipo de travessia.

O resultado para este algoritmo será 1–2–3–4–5–6–7.
Por quê?
Vamos dividi-lo em partes.
- Comece na `raiz` (1). Imprima-a.
2. Vá para o filho da esquerda
(2). Imprima-o.
3. Então, vá para o filho da esquerda
(3). Imprima-o. (Este nó não tem filhos)
4. Retroceda e vá para filho da direita
(4). Imprima-o. (Este nó não tem filhos)
5. Retroceda, vá para nó raiz
e vá para o filho da direita
(5). Imprima-o.
6. Vá para o filho da esquerda
(6). Imprima-o. (Este nó não tem filhos)
7. Retroceda e vá para filho da direita
(7). Imprima-o. (Este nó não tem filhos)
8. Feito.
Quando vamos a fundo até uma folha e retrocedemos, isso é chamado de algoritmo DFS.
Agora que estamos familiarizados com esse algoritmo de travessia, discutiremos os tipos de DFS: pré-ordem, em ordem e pós-ordem.
Pré-ordem
Isso é exatamente o que fizemos no exemplo acima.
- Imprimir o valor do
nó
. - Ir para o
filho da esquerda
e imprimi-lo se, e somente se, tiver umfilho da esquerda
. - Ir para o
filho da direita
e imprimi-lo se, e somente se, tiver umfilho da direita
.
def pre_order(self):
print(self.value)
if self.left_child:
self.left_child.pre_order()
if self.right_child:
self.right_child.pre_order()
Em ordem

O resultado do algoritmo em ordem para este exemplo da árvore é 3–2–4–1–6–5–7.
Começamos da esquerda primeiro, depois pegamos o do meio e, por fim, o da direita.
Agora, vamos transformar isso em código.
def in_order(self):
if self.left_child:
self.left_child.in_order()
print(self.value)
if self.right_child:
self.right_child.in_order()
- Vá para o
filho da esquerda
e imprima-o se, e somente se, tiver umfilho da esquerda
. - Imprima o valor do
nó
- Vá para o
filho da direita
e imprima-o se, e somente se, tiver umfilho da direita
.
Pós-ordem

O resultado do algoritmo de pós-ordem para este exemplo de árvore é 3–4–2–6–7–5–1.
Começamos com a esquerda, seguindo da direita e depois o meio.
Vamos transformar isso em código.
def post_order(self):
if self.left_child:
self.left_child.post_order()
if self.right_child:
self.right_child.post_order()
print(self.value)
- Vá para o
filho da esquerda
e imprima-o se, e somente se, tiver umfilho da esquerda
. - Vá até o
filho da direita
e imprima-o se, e somente se, tiver umfilho da direita
. - Imprima o valor do
nó
Busca em largura (BFS - Breadth-First Search)
O algoritmo BFS atravessa a árvore
nível por nível e profundidade por profundidade.

Aqui está um exemplo que ajuda a explicar melhor este algoritmo:

Portanto, atravessamos nível por nível. Neste exemplo, o resultado é 1–2–5–3–4–6–7.
- Nível/Profundidade 0: somente o
nó
com valor 1 - Nível/Profundidade 1:
nós
com valores 2 e 5 - Nível/Profundidade 2:
nós
com valores 3, 4, 6, e 7
Agora, vamos transformar isso em código.
def bfs(self):
queue = Queue()
queue.put(self)
while not queue.empty():
current_node = queue.get()
print(current_node.value)
if current_node.left_child:
queue.put(current_node.left_child)
if current_node.right_child:
queue.put(current_node.right_child)
Para implementar um algoritmo de BFS, usamos a estrutura de dados da fila para ajudar.
Como funciona?
Aqui está a explicação.

- Primeiro adicione o
nó raiz
nafila
(Queue) com o métodoput
. - Itere enquanto a
fila
não estiver vazia. - Obtenha o primeiro
nó
nafila
, depois imprima o seu valor. - Acrescentar o
filho da esquerda
e ofilho da direita
na fila (se o nó atual tiver filhos). - Feito. Imprimiremos o valor de cada
nó
, nível por nível, com nosso ajudante defila
.
Árvore binária de busca
"Uma árvore binária de busca é às vezes chamada de árvore binária ordenada ou classificada, e mantém seus valores em ordem, para que a busca e as outras operações possam usar o princípio da busca binária" - Wikipedia
Uma propriedade importante de uma árvore binária de busca
é que o valor de um nó da árvore binária de busca é maior que o valor dos descendentes do seu filho da esquerda
, mas menor que o valor dos descendentes de seu filho da direita
".

Aqui está uma descrição da ilustração acima:
- A está invertido. A
subárvore
7-5-8-6 precisa estar do lado direito e asubárvore
2-1-3 precisa estar do lado esquerdo. - B é a única opção correta. Ela satisfaz a propriedade da
árvore binária de busca
. - C tem um problema: o
nó
com o valor 4. Ele precisa estar do lado esquerdo daraiz
porque é menor do que 5.
Vamos programar uma árvore de busca binária!
Agora é hora de programar!
O que veremos aqui? Vamos inserir novos nós, procurar um valor, deletar nós e o equilíbrio da árvore
.
Vamos começar.
Inserção: adicionar novos nós à nossa árvore
Imagine que temos uma árvore
vazia e que queremos adicionar novos nós
com os seguintes valores nesta ordem: 50, 76, 21, 4, 32, 100, 64, 52.
A primeira coisa que precisamos saber é se 50 é a raiz
de nossa árvore.

Podemos agora começar a inserir nó por nó.
- 76 é maior que 50, portanto insira 76 no lado direito.
- 21 é menor do que 50, portanto insira 21 no lado esquerdo.
- 4 é menor do que 50. O
nó
com valor 50 tem umfilho da esquerda
21. Como 4 é menor do que 21, insira-o no lado esquerdo destenó
. - 32 é menor do que 50. O
nó
com valor 50 tem umfilho da esquerda
21. Como 32 é maior que 21, insira 32 no lado direito destenó
. - 100 é maior que 50. O
nó
com valor 50 tem umfilho da direita
76. Como 100 é maior que 76, insira 100 no lado direito destenó
. - 64 é maior que 50. O
nó
com valor 50 tem umfilho da direita
76. Como 64 é menor que 76, insira 64 no lado esquerdo destenó
. - 52 é maior que 50. O
nó
com valor 50 tem umfilho da direita
76. Como 52 é menor do que 76, o nó com valor 76 tem umfilho da esquerda
64. 52 é menor do que 64, portanto insira 54 no lado esquerdo destenó
.

Você percebe um padrão aqui?
Vamos analisar.
- O valor do novo
nó
é maior ou menor que o donó
atual? - Se o valor do novo
nó
for maior que onó
atual, vá para asubárvore
à direita. Se onó
atual não tiver umfilho da direita
, insira-o lá ou retroceda para o passo nº 1. - Se o valor do novo
nó
for menor do que onó
atual, vá para asubárvore
à esquerda. Se onó
atual não tiver umfilho da esquerda
, insira-o lá ou retroceda para o passo nº 1. - Não tratamos de casos especiais aqui. Quando o valor de um novo
nó
for igual ao valor atual donó
, use a regra número 3. Considere a inserção de valores iguais no lado esquerdo dasubárvore
.
Agora vamos transformar isso em código.
class BinarySearchTree:
def __init__(self, value):
self.value = value
self.left_child = None
self.right_child = None
def insert_node(self, value):
if value <= self.value and self.left_child:
self.left_child.insert_node(value)
elif value <= self.value:
self.left_child = BinarySearchTree(value)
elif value > self.value and self.right_child:
self.right_child.insert_node(value)
else:
self.right_child = BinarySearchTree(value)
Parece muito simples.
A parte poderosa desse algoritmo é a parte de recursão, que está nas linhas 9 e 13. As duas linhas de código chamam o método insert_node
e o utilizam para seus filhos à esquerda
e filhos à direita
, respectivamente. As linhas 11 e 15 são as que fazem a inserção para cada filho
.
Vamos procurar o valor do nó... ou não...
O algoritmo que construiremos agora é o de buscas. Para um determinado valor (número inteiro), diremos se nossa árvore de busca binária tem ou não esse valor.
Um item importante a ser observado é como definimos o algoritmo de inserção de árvores. Primeiro, temos nosso nó raiz
. Todos os nós da subárvore
da esquerda terão valores menores que o nó raiz
. Todos os nós da subárvore
da direita terão valores maiores que o nó raiz
.
Vamos dar uma olhada em um exemplo.
Imagine que temos esta árvore
.

Agora, queremos saber se temos um nó baseado no valor 52.

Vamos analisar.
- Começamos com o
nó raiz
como nossonó
atual. O valor dado é menor do que o valor donó
atual? Se sim, buscaremos esse valor nasubárvore
da esquerda. - O valor dado é maior do que o valor do
nó
atual? Se sim, buscaremos esse valor nasubárvore
da direita. - Se as regras nº 1 e 2 forem falsas, podemos comparar o valor do
nó
atual e o valor dado se forem iguais. Se a comparação retornarTrue
, podemos dizer: "Sim! Nossa árvore tem o valor dado", caso contrário, dizemos: "Não, não tem".
Agora, vamos transformar isso em código.
class BinarySearchTree:
def __init__(self, value):
self.value = value
self.left_child = None
self.right_child = None
def find_node(self, value):
if value < self.value and self.left_child:
return self.left_child.find_node(value)
if value > self.value and self.right_child:
return self.right_child.find_node(value)
return value == self.value
Vamos detalhar o código:
- As linhas 8 e 9 estão sob a regra nº 1.
- As linhas 10 e 11 estão sob a regra nº 2.
- A linha 13 está sob a regra nº 3.
Como testamos isso?
Vamos criar a nossa árvore binária de busca
, inicializando o nó raiz
com o valor 15.
bst = BinarySearchTree(15)
Agora, vamos inserir muitos nós novos.
bst.insert_node(10)
bst.insert_node(8)
bst.insert_node(12)
bst.insert_node(20)
bst.insert_node(17)
bst.insert_node(25)
bst.insert_node(19)
Para cada nó inserido, testaremos se o nosso método find_node
realmente funciona.
print(bst.find_node(15)) # True
print(bst.find_node(10)) # True
print(bst.find_node(8)) # True
print(bst.find_node(12)) # True
print(bst.find_node(20)) # True
print(bst.find_node(17)) # True
print(bst.find_node(25)) # True
print(bst.find_node(19)) # True
Sim, ele funciona para esses valores dados! Vamos testar para um valor que não existe em nossa árvore de busca binária.
print(bst.find_node(0)) # False
Sim. Nossa busca está feita.
Eliminação: remoção e organização
A eliminação é um algoritmo mais complexo porque precisamos tratar de casos diferentes. Para um determinado valor, precisamos remover o nó
com este valor. Imagine os seguintes cenários para este nó
: ele não tem filhos
, tem um único filho
ou tem dois filhos
.
- Cenário nº 1: um
nó
semfilhos
(nó folha
).
# |50| |50|
# / \ / \
# |30| |70| (DELETE 20) ---> |30| |70|
# / \ \
# |20| |40| |40|
Se o nó
que queremos excluir não tem filhos, simplesmente o excluímos. O algoritmo não precisa reorganizar a árvore
.
- Cenário nº 2: um
nó
com apenas um filho (filho da esquerda
oufilho da direita
).
# |50| |50|
# / \ / \
# |30| |70| (DELETE 30) ---> |20| |70|
# /
# |20|
Neste caso, nosso algoritmo precisa fazer com que o pai do nó
aponte para o nó filho
. Se o nó for o filho da esquerda
, fazemos com que o pai do filho da esquerda
aponte para o filho
. Se o nó
for o filho da direita
do seu pai, fazemos com que o pai do filho da direita
aponte para o filho
.
- Cenário nº 3: uim
nó
com dois filhos.
# |50| |50|
# / \ / \
# |30| |70| (DELETE 30) ---> |40| |70|
# / \ /
# |20| |40| |20|
Quando o nó
tem dois filhos, precisamos encontrar o nó
com o valor mínimo, a começar pelo filho da direita
do nó
. Colocaremos este nó
com o valor mínimo no lugar do nó
que queremos remover.
É hora de transformar isso em código.
def remove_node(self, value, parent):
if value < self.value and self.left_child:
return self.left_child.remove_node(value, self)
elif value < self.value:
return False
elif value > self.value and self.right_child:
return self.right_child.remove_node(value, self)
elif value > self.value:
return False
else:
if self.left_child is None and self.right_child is None and self == parent.left_child:
parent.left_child = None
self.clear_node()
elif self.left_child is None and self.right_child is None and self == parent.right_child:
parent.right_child = None
self.clear_node()
elif self.left_child and self.right_child is None and self == parent.left_child:
parent.left_child = self.left_child
self.clear_node()
elif self.left_child and self.right_child is None and self == parent.right_child:
parent.right_child = self.left_child
self.clear_node()
elif self.right_child and self.left_child is None and self == parent.left_child:
parent.left_child = self.right_child
self.clear_node()
elif self.right_child and self.left_child is None and self == parent.right_child:
parent.right_child = self.right_child
self.clear_node()
else:
self.value = self.right_child.find_minimum_value()
self.right_child.remove_node(self.value, self)
return True
- Primeiro: observe o
valor
dos parâmetros e opai
. Queremos encontrar onó
que tem estevalor
. Onó pai
é importante para a remoção donó
. - Segundo: observe o valor de retorno. Nosso algoritmo retornará um valor booleano. Ele retorna
True
se encontrar o nó e o remove. Caso contrário, ele retornaráFalse
- Da linha 2 à linha 9: começamos a procurar o
nó
que tem ovalor
que estamos procurando. Se ovalor
for menor que ovalor atual
, vamos para asubárvore da esquerda
, recursivamente (se, e somente se, onó atual
tiver umfilho da esquerda
). Se ovalor
for maior, vamos para asubárvore da direita
, recursivamente. - Linha 10: começamos a pensar sobre o algoritmo de
remoção
. - Da linha 11 à linha 13: cobrimos o
nó
semfilhos
. É ofilho da esquerda
do seupai
. Removemos onó
definindo ofilho da esquerda
dopai
comoNone
. - Linhas 14 e 15: cobrimos o
nó
sem filhos. É ofilho da direita
do seupai
. Removeremos onó
definindo ofilho da direita
dopai
comoNone
. - Método de limpeza do nó: mostrarei o código de
clear_node
abaixo. Ele define os nós dofilho da esquerda
, dofilho da direita
e o valor donó
paraNone
. - Da linha 16 à linha 18: cobrimos o
nó
com apenas umfilho
(filho da esquerda
). É ofilho da esquerda
do seu pai. Ajustamos ofilho da esquerda
dopai
para ofilho da esquerda
donó
(o único filho que ele tem). - Da linha 19 à linha 21: cobrimos o
nó
com apenas umfilho
(filho da esquerda
), e ele é ofilho da direita
do seupai
. Configuramos ofilho da direita
dopai
para ofilho da esquerda
donó
(o únicofilho
que ele tem). - Da linha 22 à linha 24: cobrimos o
nó
com apenas umfilho
(filho da direita
), e ele é ofilho da esquerda
do seupai
. Configuramos ofilho da esquerda
dopai
para ofilho da direita
donó
(o únicofilho
que ele tem). - Da linha 25 à linha 27: cobrimos o
nó
com apenas umfilho
(filho da direita
), e ele é ofilho da direita
do seupai
. Colocamos ofilho da direita
dopai
nofilho da direita
donó
(o únicofilho
que ele tem). - Da linha 28 à linha 30: cobrimos o
nó
com ofilho da esquerda
e ofilho da direita
. Obtemos onó
com o menorvalor
(o código é mostrado abaixo) e o configuramos com ovalor
do nó atual. Termine removendo o menornó
. - Linha 32: se encontrarmos o
nó
que estamos procurando, ele precisa retornarTrue
. Da linha 11 à linha 31, tratamos desse caso. Então, basta retornarTrue
e pronto.
- Para usar o método
clear_node
: configure o valorNone
para todos os três atributos — (value
,left_child
eright_child
)
def clear_node(self):
self.value = None
self.left_child = None
self.right_child = None
- Para usar o método
find_minimum_value
: desça para a esquerda. Se não conseguirmos encontrar mais nós, encontramos o menor.
def find_minimum_value(self):
if self.left_child:
return self.left_child.find_minimum_value()
else:
return self.value
Agora vamos testá-lo.
Usaremos esta árvore
para testar nosso algoritmo remove_node
.
# |15|
# / \
# |10| |20|
# / \ / \
# |8| |12| |17| |25|
# \
# |19|
Vamos remover o nó
com o valor
8. É um nó
sem filho.
print(bst.remove_node(8, None)) # True
bst.pre_order_traversal()
# |15|
# / \
# |10| |20|
# \ / \
# |12| |17| |25|
# \
# |19|
Agora, vamos remover o nó
com o valor 17. É um nó
com apenas um filho.
print(bst.remove_node(17, None)) # True
bst.pre_order_traversal()
# |15|
# / \
# |10| |20|
# \ / \
# |12| |19| |25|
Finalmente, removeremos um nó com dois filhos. Esta é a raiz da nossa árvore.
print(bst.remove_node(15, None)) # True
bst.pre_order_traversal()
# |19|
# / \
# |10| |20|
# \ \
# |12| |25|
Os testes agora estão prontos. :)
Isso é tudo por enquanto!
Parabéns por terminar este conteúdo denso. É realmente difícil entender um conceito que não conhecemos, mas você conseguiu. :)
Este é mais um passo adiante na minha jornada de aprendizagem e domínio de algoritmos e estruturas de dados. Você pode ver a documentação de minha jornada completa aqui na minha publicação Renaissance Developer (em inglês).
Divirta-se, continue aprendendo e programando.
Recursos adicionais (em inglês)
- Introdução à estrutura de dados em árvore, da mycodeschool
- Como não ser atropelado pelas árvores, do talentoso Vaidehi Joshi
- Introdução às árvores, palestra do professor Jonathan Cohen
- Introdução às árvores, palestra do professor David Schmidt
- Introdução às árvores, palestra do professor Victor Adamchik
- Árvores com Gayle Laakmann McDowell
- Implementação de árvores binárias e testes, por TK
- Curso do Coursera: Estruturas de dados, da Universidade da Califórnia, San Diego
- Curso do Coursera: Estruturas de Dados e desempenho, da Universidade da Califórnia, San Diego
- Conceitos e implementação da árvore binária de busca, de Paul Programming
- Implementação da árvore binária de busca e testes, por TK
- Algoritmo de remoção de nó da árvore binária de busca, de GeeksforGeeks
- Algoritmo de remoção de nó da árvore binária de busca, de Algolist
- Aprendendo Python do zero ao herói
- Travessia de árvores, da Wikipédia