Академический Документы
Профессиональный Документы
Культура Документы
Xavier Muiocha
Universidade Licungo
Maio de 2020
Beira
Abede Isaquiel Cugulo
Xavier Muiocha
Docente:
Alfredo Maleca
Universidade Licungo
Maio de 2020
Beira
Indice
Introdução..................................................................................................................................1
Árvore........................................................................................................................................2
Definição de árvore....................................................................................................................2
Representacao............................................................................................................................2
Algoritmos..................................................................................................................................3
Arvore binaria............................................................................................................................3
Percursos em árvore...................................................................................................................6
Ordem simétrica.....................................................................................................................6
Arvore AVL...............................................................................................................................9
Estrutura...............................................................................................................................10
Balanceamento.........................................................................................................................10
Busca........................................................................................................................................11
Algoritmo de busca...............................................................................................................11
Inserção.................................................................................................................................12
Aplicações............................................................................................................................12
Dicionários...........................................................................................................................12
Geometria Computacional....................................................................................................12
Conjuntos..............................................................................................................................12
Arvore de pesquisa...................................................................................................................13
Operacoes.............................................................................................................................13
Busca....................................................................................................................................13
Aplicações............................................................................................................................13
Estrutura de Dados...............................................................................................................15
Execução do Trabalho..........................................................................................................16
Altura Negra.........................................................................................................................16
rbCheck....................................................................................................................................17
Árvores de Decisão..................................................................................................................17
Árvores de decisão...............................................................................................................18
Implementação.....................................................................................................................18
Ambiente computacional......................................................................................................19
Java.......................................................................................................................................19
Conclusão.................................................................................................................................21
Bibliografia..............................................................................................................................22
Introdução
As árvores são estruturas de dados baseadas em listas encadeadas que possuem um nó
superior também chamado de raiz que aponta para outros nós, chamados de nós filhos, que
podem ser pais de outros nós. Uma árvore de busca binária tem as seguintes propriedades
todos os elementos na subárvore esquerda de um determinado nó n são menores que n; todos
os elementos na subárvore direita de um determinado nó n são maiores ou iguais a n.
1
Árvore
No contexto da programação, engenharia de software e ciência da computação, é uma das
mais importantes estruturas de dados não lineares. Herda as características das topologia em
árvore. Conceitualmente diferente das listas, em que os dados se encontram numa sequência,
nas árvores os dados estão dispostos de forma hierárquica, seus elementos se encontram
"acima" ou abaixo de outros elementos da árvore.
Uma árvore é formada por um conjunto de elementos que armazenam informações chamados
nodos (ou nós). Toda a árvore possui o elemento chamado raiz, que possui ligações para
outros elementos denominados ramos ou filhos. Estes ramos podem estar ligados a outros
elementos que também podem possuir outros ramos. O elemento que não possui ramos é
conhecido como nó folha, nó terminal ou nó externo.
Definição de árvore
Formalmente, definimos uma árvore {\displaystyle T}T como um conjunto finito de zero ou
mais nodos tal que[3]:
O número máximo de filhos em um nodo é chamado ordem da árvore. Uma árvore binária é
aquela de ordem 2, i.e., em que cada elemento possui no máximo 2 filhos.
Representacao
Há diversas formas de representação de uma árvore: hierárquica, diagrama de inclusão,
diagrama de barras, numeração por níveis, por aninhamento.
A hierárquica é parecida com um organograma de uma empresa, linhas unem dois nodos e
indicam o relacionamento lógico entre eles. Tradicionalmente desenha-se a raiz na parte
superior e todos os nodos subordinados na parte inferior, mas o contrário também é possível.
Na figura ao lado, o item (a) é um exemplo desta representação.
2
Diagrama de inclusão, um círculo representa cada nodo e seus nodos descendentes são
inseridos dentro do círculo de seus pais. Também conhecida como diagrama de Venn, é
muito utilizada na representação de conjuntos. O item (c) da figura ao lado mostra a árvore
do item (a) usando diagrama de inclusão.
Algoritmos
Uma das operações importantes consiste em percorrer cada elemento da árvore uma única
vez. Esse percurso, também chamado de travessia da árvore, pode ser feito em pré-ordem (os
filhos de um nó são processados após o nó) ou em pós-ordem (os filhos são processados antes
do nó). Em árvores binárias é possível ainda fazer uma travessia em-ordem, em que se
processa o filho à esquerda, o nó, e finalmente o filho à direita. O algoritmo abaixo descreve
uma travessia pré-ordem: PercursoPreordem(nó): Processa nó Para cada filho de nó (se
houver) Executa recursivamente PercursoPreordem(filho).
Arvore binaria
Uma árvore binária é uma estrutura de dados caracterizada por:
Ou não tem elemento algum (árvore vazia). Ou tem um elemento distinto, denominado raiz,
com dois ponteiros para duas estruturas diferentes, denominadas subárvore esquerda e
subárvore direita. Perceba que a definição é recursiva e, devido a isso, muitas operações
sobre árvores binárias utilizam recursão. É o tipo de árvore mais utilizado na computação. A
principal utilização de árvores binárias são as árvores binárias de busca.
3
Definições em teoria dos grafos
Em teoria dos grafos, uma árvore binária é definida como um grafo acíclico, conexo, dirigido
e que cada nó não tem grau maior que 2. Assim sendo, só existe um caminho entre dois nós
distintos. E cada ramo da árvore é um vértice dirigido, sem peso, que parte do pai e vai para o
filho.
Algoritmo da inserção em C:
if(*pRaiz == NULL){
(*pRaiz)→pEsquerda = NULL;
(*pRaiz)→pDireita = NULL;
(*pRaiz)→numero = numero;
}else{
if(numero <(*pRaiz)→numero)
inserir(&(*pRaiz)→pEsquerda, numero));
else
inserir(&(*pRaiz)→pDireita, numero));
public No raiz;
class No {
Integer valor;
No filhoEsquerdo;
4
No filhoDireito;
this.valor = valor;
if (raiz == null) {
raiz = novo;
return raiz;
if (anterior != null) {
} else {
return null;
} else {
anterior = novo;
return anterior;
5
}
Percursos em árvore
Existem três tipos de percursos: Percurso em ordem simétrica(em-ordem), pré-ordem e pós-
ordem.
Ordem simétrica
O algoritmo recursivo desse percurso em C é:
if(pNo != NULL) {
emOrdem(pNo→pEsquerda);
visita(pNo);
emOrdem(pNo→pDireita);
if (no != null) {
emOrdem(no.filhoEsquerdo);
System.out.println(no.valor);
emOrdem(no.filhoDireito);
6
Pré-ordem
if(pNo != NULL){
visita(pNo);
preOrdem(pNo→pEsquerda);
preOrdem(pNo→pDireita);
if (no != null){
System.out.println(no.valor);
preOrdem(no.filhoEsquerdo);
preOrdem(no.filhoDireito);
Pós-ordem
if(pNo != NULL){
posOrdem(pNo→pEsquerda);
posOrdem(pNo→pDireita);
visita(pNo);
7
}
if (no != null) {
posOrdem(no.filhoEsquerdo);
posOrdem(no.filhoDireito);
System.out.println(no.valor);
Em Java
Em C
8
Métodos para representação de árvores binárias
Uma das maneiras mais simples de representar árvores binárias em linguagens de
programação é por meio de arranjos unidimensionais (vetores). Caso a raiz esteja na posição
zero, dado um nó de índice i qualquer, os seus filhos terão índices {\displaystyle 2i+1}
{\displaystyle 2i+1} e {\displaystyle 2i+2}{\displaystyle 2i+2} e o seu pai terá índice piso((i
- 1)/2). Caso a raiz esteja na posição um, os filhos terão índices {\displaystyle 2i}
{\displaystyle 2i} e {\displaystyle 2i+1}{\displaystyle 2i+1} e o pai terá índice piso(i/2). Essa
implementação é utilizada para representar árvores completas ou quase completas. Heaps,
que são árvores binárias quase completas são implementadas na forma de um vetor de uma
maneira bastante eficiente.
Arvore AVL
Uma árvore binária T é denominada AVL quando, para qualquer nó de T, as alturas de suas
duas subárvores, esquerda e direita, diferem em módulo de até uma unidade.
O valor hd(u) - he(u) é denominado fator de balanço do nó. Quando um nó possui fator de
balanço com valor -1, 0 ou 1 então o mesmo é um nó regulado. Todos os nós de uma árvore
AVL são regulados, caso contrário a árvore não é AVL.
9
Estrutura
Proposta de estrutura dos nós de uma árvore AVL básica, com chave do tipo inteiro:
chave: inteiro;
fim;
O campo chave armazena o valor da chave. Os campos esq e dir são ponteiros para as
subárvores esquerda e direita, respectivamente. O campo fb armazena o fator de balanço.
raiz: ^No_AVL;
fim;
Balanceamento
Toda árvore AVL é balanceada, isto é, sua altura é O(log n).
Para garantir essa propriedade, a cada inserção ou remoção o fator de balanço deve ser
atualizado a partir do pai do nó inserido até a raiz da árvore. Na inserção basta encontrar o
primeiro nó desregulado (fb= -2 ou fb= 2), aplicar o operação de rotação necessária, não
havendo necessidade de verificar os demais nós. Na remoção a verificação deverá prosseguir
até a raiz, podendo requerer mais de uma rotação.
10
Busca
A busca é a mesma utilizada em árvore binária de busca. A busca pela chave de valor K
inicia sempre pelo nó raiz da árvore. Seja pt_u um ponteiro para o nó u sendo verificado.
Caso o pt_u seja nulo então a busca não foi bem sucedida (K não está na árvore ou árvore
vazia). Verificar se a chave K igual pt_u->chave (valor chave armazenado no nó u), então a
busca foi bem sucedida. Caso contrário, se K < pt_u->chave então a busca segue pela
subárvore esquerda; caso contrário, a busca segue pela subárvore direita.
Algoritmo de busca
busca_AVL(@pt_u:^no_AVL, K:inteiro):logico;
inicio
fim.
// O método de procura numa AVL é semelhante ao busca binária de uma árvore binária de
busca comum.
11
if (node.isEmpty() || node.getData().equals(element)) {
return node;
} else {
}}
Inserção
Para inserir um novo nó de valor K em uma árvore AVL é necessária uma busca por K nesta
mesma árvore. Após a busca o local correto para a inserção do nó K será em uma subárvore
vazia de uma folha da árvore. Depois de inserido o nó, a altura do nó pai e de todos os nós
acima deve ser atualizada. Em seguida o algoritmo de rotação simples ou dupla deve ser
acionado para o primeiro nó pai desregulado.
Aplicações
A árvore AVL é muito útil pois executa as operações de inserção, busca e remoção em tempo
O(log n) sendo inclusive mais rápida que a árvore rubro-negra para aplicações que fazem
uma quantidade excessiva de buscas, porém esta estrutura é um pouco mais lenta para
inserção e remoção. Isso se deve ao fato de as árvores AVL serem mais rigidamente
balanceadas.
Dicionários
Árvore AVL pode ser usada para formar um dicionário de uma linguagem ou de programas,
como os opcodes de um assembler ou um interpretador.
Geometria Computacional
Árvore AVL pode ser usada também na geometria computacional por ser uma estrutura
muito rápida. Sem uma estrutura com complexidade O(log n) alguns algoritmos da geometria
computacional poderiam demorar dias para serem executados.
Conjuntos
Árvore AVL podem ser empregadas na implementação de conjuntos, principalmente aqueles
cujas chave não são números inteiros. A complexidade das principais operações de conjuntos
usando árvore AVL:
12
Inserir - O(log n);
Interseção - O(n.log n)
Arvore de pesquisa
Em Ciência da computação, uma árvore binária de busca (ou árvore binária de pesquisa) é
uma estrutura de dados de árvore binária baseada em nós, onde todos os nós da subárvore
esquerda possuem um valor numérico inferior ao nó raiz e todos os nós da subárvore direita
possuem um valor superior ao nó raiz (esta é a forma padrão, podendo as subárvores serem
invertidas, dependendo da aplicação). O objetivo desta árvore é estruturar os dados de forma
a permitir busca binária.
Operacoes
Busca
A busca em uma árvore binária por um valor específico pode ser um processo recursivo ou
iterativo. Será apresentado um método recursivo. A busca começa examinando o nó raiz. Se a
árvore está vazia, o valor procurado não pode existir na árvore. Caso contrário, se o valor é
igual a raiz, a busca foi bem sucedida. Se o valor é menor do que a raiz, a busca segue pela
subárvore esquerda. Similarmente, se o valor é maior do que a raiz, a busca segue pela
subárvore direita. Esse processo é repetido até o valor ser encontrado ou a subárvore ser nula
(vazia). Se o valor não for encontrado até a busca chegar na subárvore nula, então o valor não
deve estar presente na árvore.
Aplicações
Percursos em ABB
Em uma árvore binária de busca podem-se fazer os três percursos que se fazem para qualquer
árvore binária (percursos em inordem, pré-ordem e pós-ordem). É interessante notar que,
quando se faz um percurso em ordem em uma árvore binária de busca, os valores dos nós
aparecem em ordem crescente. A operação "Percorre" tem como objetivo percorrer a árvore
numa dada ordem, enumerando os seus nós. Quando um nó é enumerado, diz-se que ele foi
"visitado".
13
Pré-ordem (ou profundidade):
Visita a raiz
Percorre a subárvore esquerda em pré-ordem
Percorre a subárvore direita em pré-ordem
Ordem Simétrica:
Pós-ordem:
Uma árvore vermelho-preto é uma árvore de busca binária que contém uma informação extra
por nó, que é sua cor, que pode ser vermelha ou preta. Na implementação, cada nó possui os
14
atributos cor, chave, esquerda, direita e pai. Se um filho de um nó ou seu pai não existe, o
ponteiro correspondente aponta para uma variável sentinela, chamada “nil”.
2. O nó raiz é preto.
5. Para cada nó, todos os caminhos de um nó até seus descendentes possuem o mesmo
número de nós pretos.
Estrutura de Dados
A árvore implementada em Java contém 2 atributos, uma referência ao nó raiz, e uma
referência sentinela aos nós folha e pai do raiz, que simboliza um nó nulo. A árvore está
descrita no arquivo RBTree.java
nil.setColor(Color.BLACK);
root = nil;
// ...
Na instanciação da árvore, o nó raiz aponta para nil, simbolizando uma árvore vazia. Para
implementar a lógica da árvore binária, que identifica se um elemento deve ser adicionado
como filho à esquerda ou filho da direita, utilizamos uma referência genérica T que deve
15
implementar a interface Comparable. Sendo assim, nossa árvore poderá conter números,
strings ou qualquer outra informação que seja possível comparar.
enum Color {
RED, BLACK;
protected RBElement<T> p;
protected T key;
public RBElement() {
public RBElement(T z) {
key = z;
A classe contém a informação das referências aos elementos pai, filhos da esquerda e direita e
sua cor. Os elemento da árvore está descrito no arquivo RBElement.java
Execução do Trabalho
Um arquivo Main.java foi criado para realizar a interação descrita no trabalho. Uma leitura de
arquivo que define as operações de inserção e remoção de palavras em uma árvore.
Altura Negra
A altura negra de é definida como sendo o número de nós pretos de um nó X até suas folhas,
sem incluir o próprio nó X. Pela propriedade 5 da árvore vermelho-preto a definição de altura
negra é satisfeita
16
Para cada nó, todos os caminhos de um nó até seus nós descendentes possuem o mesmo
número de nós pretos.
Sendo assim, foi definida uma função que recupera a altura negra da raiz da árvore. Esta
função é utilizada como base para implementação do método rbCheck.
if(isNil(root)) {
return 0;
int blackHeight = 0;
RBElement<T> e = root.left;
while ( !isNil(e) ) {
if(e.color.equals(Color.BLACK)) {
blackHeight++;
e = e.left;
return blackHeight;
rbCheck
A função rbCheck é uma chamada que ocorre em uma instância da árvore. O procedimento
realiza uma chamada recursiva no método printRbCheck, que recebe o nó que está sendo
visitado e a sua altura negra. Em sua chamada inicial, definimos a altura negra da árvore, que
é atualizada a cada chamada recursiva, se necessário.
Árvores de Decisão
Uma árvore de decisão pode ser vista como um fluxograma que representa de forma gráfica o
processo de tomada de decisão. Empresas podem utilizá-la diante de situações problema do
17
dia a dia, como conceder um empréstimo a um potencial cliente ou realizar análises
financeiras, por exemplo.
Por outro lado, os indivíduos comuns podem necessitar delas para serem ajudados em algum
processo do dia a dia, como a compra de um carro. O processo de decisão faz parte da vida
do ser humano, entretanto, para analisar grandes volumes de dados são necessários modelos
computacionais que possam fornecer certa precisão e agilidade.
Árvores de decisão
Árvores de decisão são modelos estatísticos utilizados em problemas de predição
supervisionada, onde um conjunto de atributos é utilizado para predizer o valor de um
atributo de saída (resultado), sendo o mapeamento destas entradas para a saída denominado
modelo preditivo. Os dados usados para estimar um modelo preditivo são um conjunto de
casos (observações, exemplos) que contém valores das entradas e do resultado. Este modelo é
aplicado em novos casos onde o resultado é desconhecido. Uma árvore de decisão possui este
nome pois o modelo preditivo pode ser representado numa estrutura semelhante a uma
árvore. A árvore de decisão é sempre lida de forma descendente, iniciando-se pelo nó raiz.
Cada nó interno representa uma quebra baseada nos valores de um atributo de entrada, o qual
pode aparecer em outras quebras na árvore. Os nós terminais de uma árvore são chamados
folhas, que representam o resultado predito. Quando o resultado é discreto, o modelo é
chamado de árvore de classificação, onde as folhas fornecem a classe predita e a sua
probabilidade. Quando o resultado é contínuo, o modelo é chamado de árvore de regressão.
Neste caso, as folhas fornecem apenas uma predição de valor do resultado. O método padrão
usado no crescimento da árvore de decisão é baseado em partições recursivas, que é um
algoritmo guloso descendente. Começando no nó raiz, um número de quebras pertinentes a
um atributo de entrada é examinado. Para entradas contínuas, as divisões são segmentos
disjuntos dos valores de entrada, enquanto que para entradas discretas as divisões são
subconjuntos disjuntos das categorias de entrada.
Implementação
Existem vários classificadores que constroem árvore de decisão, sendo que geralmente eles
geram toda a árvore para depois podá-la. Esta fase de poda serve para melhorar a precisão e
prevenir contra o overfitting. Entretanto, a geração de uma árvore de decisão em duas fases
distintas pode resultar num desperdício de esforço considerado se uma subárvore inteira
18
construída na primeira fase for podada na segunda fase. Se durante a fase de construção,
antes de dividir um nó, puder ser concluído que ele será podado na próxima fase, este esforço
pode ser evitado. Desta forma, como as leituras dos dados são repetidas várias vezes para
gerar uma subárvore, pode-se conseguir reduções significativas de I/O e melhorar o
desempenho do processo.
Ambiente computacional
Com o intuito de avaliar o desempenho do algoritmo PUBLIC implementado em Java
utilizou-se o Oracle 8i v.8.1.7 (release 3) configurado em um microcomputador Pentium II
350Mhz com 128MB de memória principal e executando o Windows NT Server 4.0, onde foi
instalado o pacote Java SDK 2 v.1.3.0. O acesso da aplicação Java ao banco de dados Oracle
é realizado através do driver JDBC Thin. Com o objetivo de ter uma referência para
comparação de desempenho, este algoritmo PUBLIC também foi implementado numa
arquitetura fortemente acoplada, onde os procedimentos foram escritos em Oracle PL/SQL,
que é uma extensão do SQL, e armazenados dentro do dicionário de dados do Oracle. A
utilização da linguagem PL/SQL só foi possível devido à sua capacidade de trabalhar com
SQL dinâmico, não possuindo similar em outros bancos de dados. Uma característica
operacional do PL/SQL é que, por ser uma linguagem de programação proprietária, sua
utilização fica restrita aos bancos de dados da Oracle. Uma avaliação de desempenho de uma
abordagem fortemente acoplada com PL/SQL em ambiente Oracle paralelo é mostrada em
SOUSA et al. [7]. Observa-se que, em ambas as implementações (Java e PL/SQL), os
comandos SQL executados são os mesmos.
Java
Em três anos, o Java passou de uma linguagem de programação usada no desenvolvimento de
simples programas com interface gráfica que podiam ser enviados pela Web para uma
plataforma para desenvolvimento e disponibilização de aplicações corporativas e de Internet.
O Java tornou-se muito popular entre os desenvolvedores de aplicação porque os deixou mais
produtivos e por ser uma linguagem moderna, robusta e orientada a objeto. Além disso,
segundo ORACLE, a popularidade sem precedentes do Java é decorrente de três benefícios:
19
2. As aplicações são independentes de plataforma: o mesmo código binário Java pode ser
executado em qualquer plataforma que suporta uma máquina virtual Java, incluindo
mainframes;
20
Conclusão
Neste artigo procurou-se demonstrar alguns conceitos e como essa importante estrutura de
dados é implementada na prática na linguagem de programação Java. As suas vantagens,
peculiaridades que lhe fazem ser uma árvore binária, sua implementação e detalhes da sua
execução foram demonstradas neste artigo.
21
Bibliografia
Cormen, Thomas; Leiserson, Charles; Rivest, Ronald; Stein, Clifford (2001). «18».
Introduction to Algorithms (em inglês) 2 ed. [S.l.]: MIT Press and McGraw-Hill.
Folk, Michael; Zoellick, Bill (1992). File Structures (em inglês) 2 ed. [S.l.]: Addison-
Wesley. 590 páginas.
Knuth, Donald (1998). The Art of Computer Programming. Sorting and Searching
(em inglês). 3 2 ed. [S.l.]: Addison-Wesley.
22