Docsity
Docsity

Prepare-se para as provas
Prepare-se para as provas

Estude fácil! Tem muito documento disponível na Docsity


Ganhe pontos para baixar
Ganhe pontos para baixar

Ganhe pontos ajudando outros esrudantes ou compre um plano Premium


Guias e Dicas
Guias e Dicas


Árvores Binárias de Busca e Árvores AVL: Algoritmos e Complexidade Computacional, Resumos de Autômatos e Teoria da Complexidade

Os conceitos fundamentais de árvores binárias de busca e árvores avl, incluindo algoritmos de busca, inserção e remoção. analisa a complexidade computacional desses algoritmos e explora o conceito de balanceamento em árvores, com foco em como transformar árvores desbalanceadas em árvores balanceadas, como as árvores avl, que garantem operações em o(log n). O texto detalha algoritmos e ilustra com exemplos práticos.

Tipologia: Resumos

2025

Compartilhado em 29/04/2025

eliza-leal-suzano
eliza-leal-suzano 🇧🇷

5 documentos

1 / 71

Toggle sidebar

Esta página não é visível na pré-visualização

Não perca as partes importantes!

bg1
DESCRIÇÃO
Apresentação dos conceitos e os principais algoritmos de busca, inserção e remoção para as árvores
binárias de busca, Análise de complexidade computacional dos algoritmos para manipulação das
árvores binárias de busca, Apresentação do conceito de árvore balanceada e um algoritmo estático
capaz de transformar uma árvore binária de busca em uma árvore balanceada, Conceituação de árvores
AVL, Apresentação dos algoritmos dinâmicos para manutenção da propriedade AVL.
PROPÓSITO
A busca, inserção e remoção de informações em uma estrutura de dados é um dos principais problemas
da computação. Ao aprender a utilizar corretamente as estruturas de dados para resolver problemas
específicos, você será capaz de desenvolver sistemas que são muito mais rápidos e eficientes. As
árvores são uma estrutura de dados no qual o problema da busca, inserção e remoção é resolvido em
um tempo proporcional ao logaritmo do número de chaves na estrutura de dados.
OBJETIVOS
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a
pf1b
pf1c
pf1d
pf1e
pf1f
pf20
pf21
pf22
pf23
pf24
pf25
pf26
pf27
pf28
pf29
pf2a
pf2b
pf2c
pf2d
pf2e
pf2f
pf30
pf31
pf32
pf33
pf34
pf35
pf36
pf37
pf38
pf39
pf3a
pf3b
pf3c
pf3d
pf3e
pf3f
pf40
pf41
pf42
pf43
pf44
pf45
pf46
pf47

Pré-visualização parcial do texto

Baixe Árvores Binárias de Busca e Árvores AVL: Algoritmos e Complexidade Computacional e outras Resumos em PDF para Autômatos e Teoria da Complexidade, somente na Docsity!

DESCRIÇÃO

Apresentação dos conceitos e os principais algoritmos de busca, inserção e remoção para as árvores

binárias de busca, Análise de complexidade computacional dos algoritmos para manipulação das

árvores binárias de busca, Apresentação do conceito de árvore balanceada e um algoritmo estático

capaz de transformar uma árvore binária de busca em uma árvore balanceada, Conceituação de árvores

AVL, Apresentação dos algoritmos dinâmicos para manutenção da propriedade AVL.

PROPÓSITO

A busca, inserção e remoção de informações em uma estrutura de dados é um dos principais problemas

da computação. Ao aprender a utilizar corretamente as estruturas de dados para resolver problemas

específicos, você será capaz de desenvolver sistemas que são muito mais rápidos e eficientes. As

árvores são uma estrutura de dados no qual o problema da busca, inserção e remoção é resolvido em

um tempo proporcional ao logaritmo do número de chaves na estrutura de dados.

OBJETIVOS

MÓDULO 1

Reconhecer os tipos de percursos utilizados em árvores binárias

MÓDULO 2

Definir árvores binárias de busca

MÓDULO 3

Definir o conceito de árvore balanceada

MÓDULO 4

Definir árvores AVL

INTRODUÇÃO

Neste tema, aprenderemos a utilizar uma estruturas de dados capaz de realizar busca, inserção e

remoção em complexidade computacional de O ( log n). Para tal, partiremos do conceito de árvores

binárias, explorando o conceito de percursos, em seguida, definiremos as árvores binárias de busca e

seus principais algoritmos, comparando a complexidade computacional com as listas lineares e as

estruturas de dados não organizadas.

Explicaremos o conceito de balanceamento e aprenderemos como transformar árvores não balanceadas

em balanceadas e sobre uma estrutura de dados completamente dinâmica capaz de realizar as

operações de busca, inserção e remoção em O(log n), as árvores AVL.

MÓDULO 1

Td é composta pelos nós: “C” e “G”. “B” e “C” são as raízes de Te e Td respectivamente.

Observe que, na árvore binária, existe, por definição, distinção entre a subárvore esquerda e direita. Por

isso, na representação gráfica sempre fica evidente a posição esquerda ou direita do nó subordinado à

raiz.

Na Figura 1, o nó “G” é o filho direito de “C” e não existe filho esquerdo de “C”, por exemplo.

Representação em memória

A forma mais comum de representar uma árvore em memória é utilizando alocação dinâmica. Na

verdade, não representamos a árvore como um todo, mas, sim, uma referência para sua raiz que guarda

a chave (dado) e uma referência para a raiz das subárvores esquerda e direita.

A abstração mais utilizada para representar o nó de uma árvore é o agregado heterogêneo, comumente

chamado de registro (record) ou estrutura (struct) nas linguagens de programação. O agregado

heterogêneo é capaz de armazenar objetos de dados de tipos diferentes acessando-os através do

mesmo identificador e distinguindo-os através de um seletor.

Para representar o nó da árvore, será construído um agregado heterogêneo contendo a chave

associada ao nó, e ponteiros para as raízes das subárvores esquerda e direita.

Em pseudocódigo, utilizaremos a estrutura sintática abaixo para descrever um nó.

registro no { Inteiro chave; registro no *esq; registro no *dir; }

Assim, a árvore é representada por uma referência para sua raiz, ponteiro para raiz, que,

recursivamente, aponta para as raízes das subárvores esquerda e direita.

Convencionamos que a variável “raiz”, declarada da forma abaixo, é um ponteiro para a raiz da árvore.

registro no *raiz;

A árvore vazia é representada quando o ponteiro raiz aponta para o endereço 0, associado à constante

NULA. Assim, para inicializar uma árvore vazia, basta inicializar o ponteiro raiz como nulo.

raiz = NULA;

Para que o conceito fique claro, será utilizada a árvore da Figura 2 para ilustrar a representação em

memória da estrutura.

Fonte: O Autor, 2020.

 Figura 2 – Árvore Binária Exemplo.

Na árvore da Figura 2, temos cinco nós, identificados pelos inteiros: 10, 8, 4, 2 e 6.

Cada nó fazendo referência à raiz das subárvores esquerda e direita.

Esquematicamente, em memória, a estrutura é representada como na Figura 3.

Fonte: O Autor, 2020.

 Figura 3 – Esquemático da representação de uma árvore em memória.

Observe que o conceito de árvore é recursivo , isto é, a estrutura foi definida em termos recursivos. Por

esta razão, as definições dos percursos também são recursivas.

PERCURSO EM PRÉ-ORDEM

O percurso em pré-ordem é definido recursivamente abaixo.

A partir da raiz r da árvore T , percorre-se a árvore da seguinte forma:

ETAPA 01

Visita-se a raiz.

ETAPA 02

Percorre-se a subárvore esquerda de T , em pré-ordem.

ETAPA 03

Percorre-se a subárvore direita de T , em pré-ordem.

Considerando como a operação de visita a impressão do rótulo contido no nó visitado, veremos o

resultado do percurso em pré-ordem da árvore da Figura 2.

Partindo-se da raiz, nó de rótulo “10”, temos que a primeira operação é a visita do nó, isto é, a impressão do seu rótulo.

Impressão: 10,

Em seguida, visita-se recursivamente a subárvore esquerda, cuja raiz é “8”, e a primeira operação é a visita do nó com rótulo “8”.

Impressão: 10, 8,

Após a visita do nó “8”, percorre-se recursivamente a subárvore esquerda do nó “8”, cuja raiz é o nó com rótulo “2”. A primeira operação deste percurso é a visita, isto é, o rótulo “2” é impresso.

Impressão: 10, 8, 2,

O nó “2” é folha, isto é, não possui subárvores, assim, o percurso da subárvore esquerda do nó “8” é concluído. O próximo passo é percorrer a subárvore direita do nó “8”, cuja raiz é o nó “6”. O primeiro passo é a impressão (visita).

Impressão: 10, 8, 2, 6,

Observe que o nó “6” é folha, o que encerra o percurso da subárvore de raiz “6”. Assim, retornamos ao

seu pai, o nó “8”, que é raiz da subárvore e que já teve seu percurso completo. O Pai de “8” é o nó “10”

que já foi visitado e que já teve sua subárvore esquerda visitada.

O próximo passo é visitar sua subárvore direita em pré-ordem. A raiz da subárvore direita é “4”, que é folha, sendo assim, o percurso pré-ordem da árvore é:

Impressão: 10, 8, 2, 6, 4

 COMENTÁRIO

Observe que, para cada nó, acessamos seu conteúdo três vezes, porém, somente uma dessas vezes corresponde à visita. No caso do percurso em pré-ordem, a primeira vez.

Graficamente, os momentos de acesso podem ser vistos na Figura 4.

Fonte: O Autor, 2020.

 Figura 4 – Acesso ao nó.

Na Figura 4, considere o contorno gráfico da árvore a partir da raiz no sentido anti-horário.

ALGORITMOS RECURSIVOS PARA OS

PERCURSOS

Os algoritmos recursivos para os percursos em pré-ordem, ordem simétrica e pós-ordem são

consequências diretas das definições dos percursos.

Vejamos o algoritmo da pré-ordem.

função pre-ordem (registro no *p) inicio visita (p); se (p->esq != NULO) pre-ordem (p->esq); se (p->dir != NULO) pre-ordem (p->dir); fim

Algoritmo 1 – Percurso em pré-ordem.

Para realizar o percurso em pré-ordem, são necessários três acessos ao nó.

ETAPA 01

No caso da pré-ordem, no primeiro, executamos a visita.

ETAPA 02

No segundo, chamamos recursivamente o algoritmo para a subárvore esquerda.

ETAPA 03

No terceiro, ocorre a chamada do percurso em pré-ordem do ramo direito.

Se temos n nós em uma árvore, o número de acesso ao nó é 3 n.

Assim, a complexidade computacional do percurso em pré-ordem é O(n).

O algoritmo do percurso em ordem simétrica é semelhante, modificamos somente o momento da visita,

resultando no Algoritmo 2.

função simetrica (registro no *p) inicio se (p->esq != NULO) simetrica (p->esq); visita (p); se (p->dir != NULO) simetrica (p->dir); fim

Algoritmo 2 – Percurso em ordem simétrica.

A análise de complexidade é análoga a feita no algoritmo do percurso em pré-ordem. Observe que a

única diferença é a ordem das visitas.

Sendo assim, a complexidade computacional do algoritmo para percurso em ordem simétrica é O(n).

Finalmente, temos o algoritmo para o percurso em pós-ordem (Algoritmo 3), que é resultado direto da

definição como o da pré-ordem e o da ordem simétrica.

função pos-ordem (registro no *p) inicio se (p->esq != NULO) pos-ordem (p->esq); se (p->dir != NULO) pos-ordem (p->dir); visita (p); fim

Algoritmo 3 – Percurso em pós-ordem.

A análise da complexidade é totalmente análoga à análise feita para pré-ordem e a ordem simétrica, o

que faz com que o algoritmo tenha complexidade O(n).

Algoritmo 4 – Algoritmo de pré-ordem não recursivo.

Para verificarmos o funcionamento do algoritmo, analisemos um exemplo e vejamos como os três

momentos do ábaco de “contorno” da árvore ocorre. Para tal, veja a árvore da Figura 6.

Fonte: O Autor, 2020.

 Figura 6 – Árvore exemplo.

O algoritmo é inicializado com a pilha vazia. O primeiro passo é inserir a raiz “10” na pilha no momento

Sendo assim, o conteúdo da pilha é:

Pilha: (10,1)

Como a pilha não é vazia, o algoritmo entra em seu laço principal, removendo o nó 10 e o momento “1”

da pilha. Como o momento é igual a “1”, visita-se o nó imprimindo seu conteúdo e insere-se (10,2) e

(8,1), nesta ordem, na pilha. O par (8,1) está no topo da pilha.

Assim, a impressão do percurso e o conteúdo da pilha é:

Impressão 10,

Pilha (10,2), (8,1)

Com a repetição, remove-se o par do topo da pilha (8,1). De forma análoga ao primeiro laço do

algoritmo, visita-se o nó “8” e inserimos (8,2) na pilha.

Como o nó “8” não tem filho esquerdo, nada mais ocorre, resultando em:

Impressão 10, 8,

Pilha (10,2), (8,2)

No novo laço, remove-se o par (8,2) da pilha. O acesso ao nó está no momento “2” e existe nó à direita

de “8”, por isso insere-se (8,3) e (12,1) na pilha, resultando em:

Impressão 10, 8,

Pilha (10,2), (8,3), (12,1)

Na nova iteração, remove-se o par (12,1). Como é o momento “1”, visita-se o nó “12”, inserindo (12,2) na

pilha somente (“12” é folha), resultando em:

Impressão 10, 8, 12,

Pilha (10,2), (8,3), (12,2)

Na próxima iteração, remove-se o par (12,2), como “12” é folha, somente (12,3) é inserido na pilha.

Impressão 10, 8, 12,

Pilha (10, 2), (8, 3), (12,3)

No próximo loop, remove-se (12,3) da pilha e nada ocorre, resultando em:

Impressão 10, 8, 12,

Pilha (10, 2), (8, 3),

Na próxima iteração, remove-se (8,3) da pilha e nada ocorre, resultando em:

Impressão 10, 8, 12,

Pilha (10, 2),

Na próxima iteração, remove-se (10,2) da pilha. Como “10” tem filho direito, será inserida na pilha a

sequência de inserções de (10,3) e (7,1), resultando em:

Impressão 10, 8, 12,

Pilha (10, 3), (7, 1)

Próximo laço de repetição, remove-se (7,1). Como é o acesso no momento “1”, visita-se o nó “7”, insere-

se na pilha (7,2) somente uma vez que o nó “7” não tem filho esquerdo, resultando em:

Impressão 10, 8, 12, 7

Pilha (10, 3), (7,2),

No próximo passo, remove-se (7,2) e insere-se (7,3) na pilha. Como “7” não tem filho direito, o passo

termina, resultando em:

Impressão 10, 8, 12, 7

Pilha (10, 3), (7,3),

No próximo laço, remove-se (7,3), resultando em:

Impressão 10, 8, 12, 7

Pilha (10, 3),

No próximo laço, remove-se a dupla (10,3), resultando em:

Impressão 10, 8, 12, 7

fim

Algoritmo 5 – Algoritmo de ordem simétrica não recursivo.

função pos-ordem-não-recursiva (registro no *p) inicio registro no *aux; inteiro momento; push (p,1); //insere a raiz de T, no momento 1 enquanto (pilha não vazia) inicio pop (aux,momento); if (momento == 1) inicio push (aux,2); //push aux na pilha, momento 2 se (aux->esq != NULO) push (aux->esq,1); fim if (momento == 2) inicio push (aux,3); se (aux->dir != NULO) push (aux->dir,1); fim if (momento == 3) visitar (aux); fim fim

Algoritmo 6 – Algoritmo pós-ordem não recursivo.

VERIFICANDO O APRENDIZADO

1. DADA A ÁRVORE ABAIXO, IDENTIFIQUE O PERCURSO EM PRÉ-ORDEM:

A) 23,24,25,26,27,28,
B) 23,24,26,29,28,27,
C) 25,24,27,23,26,29,
D) 25,24,23,27,26,28,
E) 23,24,25,27,26,28,

2. DADA A ÁRVORE ABAIXO, IDENTIFIQUE O PERCURSO EM ORDEM-

SIMÉTRICA:

O percurso é definido pela recursão, visita raiz, percorrer recursivamente esquerda e direita da raiz

considerada.

2. Dada a árvore abaixo, identifique o percurso em ordem-simétrica:

A alternativa "B " está correta.

O percurso é definido pela recursão, percorrer recursivamente esquerda, visitar a raiz e percorrer

recursivamente a direita da raiz considerada.

MÓDULO 2

Definir árvores binárias de busca

O problema da busca, inserção e remoção é um dos principais objetivos do estudo de estruturas de

dados.

autor/shutterstock

Esta situação lúdica nos remete a algumas conclusões. Para tal, vamos relembrar alguns conceitos.

No estudo de complexidade, sempre levamos em consideração o pior caso, isto é, a análise de

complexidade é feita com a visão do pessimista.

A situação lúdica nos mostra que, na visão do pessimista, sem organização alguma, teremos que tirar n

bolinhas do saco para encontrar a bolinha vermelha.

Isso mostra que, sem nenhuma organização da informação, é possível realizar uma busca com

complexidade computacional de O(n).

 COMENTÁRIO

Outra conclusão importante é que, para melhorar a busca, isto é, para conseguir um algoritmo mais eficiente, precisamos perseguir a complexidade de O(log n). As árvores são as estruturas de dados que vão viabilizar este objetivo.

ÁRVORES BINÁRIAS DE BUSCA