Você está na página 1de 18

LÓGICA DE PROGRAMAÇÃO E

ESTRUTURAS DE DADOS

Professor Marcelo Ferreira Zochio


6 ÁRVORES BINÁRIAS
Neste bloco, você irá aprender os principais conceitos e aplicações de árvores binárias,
saberá como uma máquina decide qual o menor caminho entre um ponto e outro, e
também como representar árvores binárias em Python.

6.1 Árvores de busca

Quando queremos representar hierarquicamente uma estrutura de dados com mais de


uma dimensão, usamos um tipo de estrutura chamada árvore, que consiste em nós e
arcos.

Veja alguns exemplos de árvores na figura a seguir:

Nota que uma árvore é composta pelo nó raiz, do qual saem todos os outros nós e/ou
folhas.

2
Essa outra figura contém um exemplo de uma árvore que representa a hierarquia de
uma empresa:

Podemos representar valores através da estrutura em árvore. Quando uma árvore


possui em todos os nós da subárvore esquerda valores numéricos inferiores ao nó raiz
e todos os nós da subárvore direita, valores superiores ao nó raiz, e os nós possuem no
máximo duas folhas, ela é definida como árvore binária de busca. Veja um exemplo:

A busca em árvore binária normalmente é feita usando um processo recursivo.

Essa busca começa examinando-se o nó raiz. Se o valor é igual ao valor armazenado na


raiz, a busca se encerra. Se o valor é menor do que a raiz, a busca segue pela ramificação
esquerda. Caso o valor seja maior do que a raiz, a busca segue pela ramificação direita.

3
Esse processo é repetido até que o valor seja encontrado. Se o valor não for encontrado,
então o valor não deve estar presente na árvore.

Uma árvore binária de busca aceita outras operações além de busca. Na inserção, a raiz
é verificada e introduz-se um nó novo na subárvore da esquerda, se o novo valor for
menor do que a raiz, ou na subárvore da direita, se o valor novo for maior do que a raiz.

Na operação remoção, você deve considerar algumas regras:

 Se for para remover apenas uma folha, basta removê-la simplesmente;


 Se for para remover um nó com apenas uma folha, a folha passa a ocupar o lugar
desse nó removido;
 Se for para remover um nó com duas folhas, pode-se substituir o nó a ser retirado
pelo nó sucessor (o nó mais à esquerda da ramificação direita) ou pelo nó antecessor
(o nó mais à direita da ramificação esquerda).

Veja exemplos nas figuras a seguir:

Removendo uma folha

4
Removendo um nó com uma folha

Removendo um nó com duas folhas

SAIBA MAIS

Neste vídeo, você aprenderá mais sobre árvores de busca.

Vídeo: Árvore Binária de Busca

GUERRA, Rodrigo. Árvore binária de busca. 2014. Disponível em:


<https://www.youtube.com/watch?v=XZ0MEDhb4oE>. Acesso em: 15 fev. 2019.

5
6.2 Balanceamento de árvores binárias

Dizemos que uma árvore está balanceada quando a altura das duas subárvores da raiz
for igual a 1, -1 ou 0. Veja um exemplo de uma árvore desbalanceada:

Árvore desbalanceada

Note que a profundidade dessa árvore é desigual, medindo 2 na subárvore direita em


relação à esquerda. Veja agora um exemplo de uma árvore balanceada:

Árvore balanceada

Quando uma árvore está desbalanceada, é preciso balanceá-la. Como? Através de


rotações. Podemos definir rotação como operação que influencia no balanceamento de
uma árvore, sem alterar a sequência de percurso.

Há os seguintes tipos de rotações: esquerda simples, direita simples, esquerda dupla e


direita dupla. Vejamos cada um deles:

6
Rotação à esquerda simples

Rotação à esquerda dupla

Rotação à direita simples

7
Rotação à direita dupla

6.3 Algoritmo de Dijkstra

O algoritmo de Dijkstra, desenvolvido pelo cientista da computação E.W. Dijkstra, é um


algoritmo que calcula o caminho mais econômico em questão de custo entre vértices de
um grafo. Escolhido um vértice como ponto de origem, o algoritmo calcula o custo desse
vértice para todos os vértices do grafo, e escolhe o caminho mais curto e com menor
custo.

Veja os grafos a seguir expondo um problema a ser resolvido com esse algoritmo. Qual
o caminho mais curto em ter os pontos A e F?

8
Vértice Passo 1 Passo 2 Passo 3 Passo 4 Passo 5 Passo 6
A 0, A - - - - -
B 5, A 5, B 5, B - - -
C 3, A 3, A - - - -
D ∞ 12, C 11, B 11, B - -
E ∞ 14, C 14, C 14, D 14, D -
F ∞ ∞ ∞ 17, D 17, E 17, E

O melhor caminho, partindo de A para chegar até F, é C, B, D, E, F.

6.4 Aplicações práticas de árvores binárias

Podemos citar duas entre várias: jogos de xadrez e futebol de robôs.

No xadrez, a estrutura em árvore permite analisar as possibilidades de movimentos e


posições do tabuleiro em níveis de profundidade. Quando você escolhe os níveis de
dificuldade para vencer seu adversário, jogando contra o computador, quanto mais
elevado o nível, mais profunda é a análise das jogadas feitas pela máquina, ou seja, ela
consegue prever seus próximos lances com mais precisão. Por isso, é mais difícil vencer
a máquina em níveis mais altos, e mais ela demora para jogar, pois precisa analisar mais
possibilidades.

No futebol de robôs, veja a seguinte situação: a partir da figura apresentada a seguir,


qual a melhor rota que o robô deve seguir para pegar a bola?

9
Transformaremos o cenário em um sistema de grafos:

Para mapear o cenário, traçaremos uma reta entre o ponto inicial (robô) e o ponto final
(bola) e também traçaremos retas perpendiculares à essa reta nos outros grafos:

Traçaremos um círculo em torno de cada grafo com distância de 2r (2 vezes seu raio)
para evitar colisões ao estabelecer a rota do robô. Os pontos de intersecção das retas
perpendiculares e os círculos serão considerados grafos do nosso sistema.

10
Então ligamos os grafos uns com os outros a partir do ponto de origem:

Os pontos que cruzarem a zona de segurança (os círculos) serão desconsiderados:

Então ligamos os outros pontos sem passar pelos círculos:

Este é nosso grafo, já com o cálculo dos custos de percurso entre os pontos,
considerando a distância entre eles:

11
Aplicando o algoritmo de Dijkstra, chega-se a esta solução de percurso:

Mostrando no cenário:

6.5 Exemplos de árvores binárias em Python

Como representar e montar uma árvore binária em Python? Veja a seguir.

Nesse programa, usaremos conceito de orientação a objetos para representar a


seguinte árvore binária:

12
Os valores estão em branco, pois iremos escolhê-los durante a execução do programa.
Eis o código:

# -*- coding: cp1252 -*-

class Tree:
def __init__(self, cargo, left=None, right=None):

self.cargo = cargo

self.left = left

self.right = right

def printTreeIndented(tree, level=0):

if tree == None: return

printTreeIndented(tree.right, level+1)

print ' '*level + str(tree.cargo)

printTreeIndented(tree.left, level+1)

def abertura():

a = input("Digite o valor da raiz da árvore: ")

b = input("Digite o valor de uma das folhas: ")

c = input("Digite o valor da outra folha: ")

if b > a and c < a:

tree = Tree(a, Tree(c), Tree(b))

printTreeIndented(tree, level=0)

13
elif b < a and c > a:

tree = Tree(a, Tree(b), Tree(c))

printTreeIndented(tree, level=0)

else:

print "Os valores devem ser maiores ou menores que a raiz!"

abertura()

abertura()

A saída é mostrada na figura a seguir:

O número mais à esquerda é a raiz; as folhas e nós da ramificação esquerda estão abaixo
do número mais à esquerda; as folhas e nós da ramificação direita estão acima do
número mais à esquerda.

Para criarmos uma árvore com mais ramificações, é só alterarmos o código. Vejamos o
trecho alterado para criarmos uma árvore com uma raiz e quatro elementos, com os
seguintes formatos:

14
(…)

def printTreeIndented(tree, level=0):

if tree == None: return

printTreeIndented(tree.right, level+1)

print ' '*level + str(tree.cargo)

printTreeIndented(tree.left, level+1)

def printTreeIndented1(tree, level=0):

if tree == None: return

printTreeIndented1(tree.left, level+1)

print ' '*level + str(tree.cargo)

printTreeIndented1(tree.right, level+1)

def abertura():

a = input("Digite o valor da raiz da árvore: ")

b = input("Digite o valor de um nó: ")

c = input("Digite o valor de um nó: ")

15
d = input("Digite o valor de uma das folhas: ")

e = input("Digite o valor de uma das folhas: ")

if b > a and c > b > a and d > c > a and e > d > a:

tree = Tree(a, Tree(b, Tree(c, Tree(d, Tree(e)))))

#tree = Tree(a, Tree(c), Tree(b))

printTreeIndented(tree, level=0)

elif b < a and c < b < a and d < c < a and e < d < a:

tree = Tree(e, Tree(d, Tree(c, Tree(b, Tree(a)))))

printTreeIndented1(tree, level=0)

elif b < a and c < b < a and d < c < e and e > c < b and e > c > d and e > d:

tree = Tree(a, Tree(b, Tree(c, Tree(d), Tree(e))))

printTreeIndented(tree, level=0)

elif b > a and c > b > a and d < c < e and e > c > b and e > c > d and e > d:

tree = Tree(a, Tree(b, Tree(c, Tree(d), Tree(e))))

printTreeIndented1(tree, level=0)

elif a > b > d and b > d and a < c < e and c < e:

tree = Tree(a, Tree(c, Tree(e)), Tree(b, Tree(d)))

printTreeIndented1(tree, level=0)

else:

print "Os valores devem ser maiores ou menores que a raiz!"

16
abertura()

abertura()

Conclusão

Neste bloco, você aprendeu sobre como trabalhar com árvores binárias, seus principais
conceitos e aplicações práticas e também como representá-las na linguagem Python.
Por aqui terminamos esta disciplina. Nos vemos nas avaliações!

17
Referências

CAPUTO, Rodrigo. Algoritmo de Dijkstra. 2012. Disponível em:


<https://www.youtube.com/watch?v=J4TZgD 1As0Q>. Acesso em: 20 dez. 2018.

DROZDEK, Adam. Estrutura de Dados e Algoritmos em C++. Tradução de Roberto


Enrique Romero Torrejon. São Paulo: CENGAGE, 2017.

PERKOVIC, Ljubomir. Introdução à computação usando Python: um foco no


desenvolvimento de aplicações. Rio de Janeiro: LTC, 2016.

18

Você também pode gostar