Baixe o app para aproveitar ainda mais
Prévia do material em texto
Estrutura de Dados 1 Conceitos de Programação Orientada a Objetos(POO) e Tipos abstratos de Dados(TAD) Programação Orientada a Objetos -> é a programação por meio de envio de mensagens a objetos. Soluções de Problemas OO Identificar os objetos Mensagens Envio de sequencias de mensagens p/ obter a solução Conceitos Fundamentais em POO Abstração Encapsulamento Herança Polimorfismo Objetivos principais da POO Construção de sofware: Mais rápido Mais barato Flexível(fácil de alterar) Abstração – é o processo de observar uma realidade e extrair informações relevantes a uma determinada aplicação. São operações mentais tais como classificação, instanciação, generalização, especialização, etc. Classificação(ou categorização) : o agrupamento em classes , com base em um conjunto de atributos comuns a todos. Generalização/Especialização: Observamos várias classes e abstraímos delas uma classe mais genérica. Hierarquia de Abstrações: Composta por classes (ou categorias) da mais genérica para a mais particular. Herança – A classe veículos possui atributos comuns a todos os veículos. As classes aéreos, terrestres e aquáticos, herdam os atributos de veículos, além de possuírem atributos particulares. Encapsulamento – As classes encapsulam (mantém) dados e comportamentos e deixam visível á outras classes somente serviços que são relevantes á outras partes do programa. Polimorfismo – (várias formas) – no paradigma O.O podemos definir o mesmo comportamento em objetos de classes diferentes. Ex: o comportamento mover em uma peça de xadrez e, também em uma janela de uma tela de computador. 2 TAD e Linguagem de Programação TAD é um conceito importante em projetos de programas. O conceito tipo abstrato de Dados: apareceu pela 1º vez no trabalho de Parnas: “Criterio a serem utilizados para decompor sistemas em módulos” (1972) Ideia Principal: Ocultamento de informações: módulos devem expor a outros módulos somente a informação necessária para que operem corretamente. Tipo de Dado: em linguagem de programação, refere-se ás formas de dados que as variáveis podem assumir. São os tipos primitivos característicos das linguagens. Ex: real, inteiro, caractere, booleano, etc. Tipo de Dados Compostos: tipos de dados definidos pelo usuário usando os tipos primitivos de dados. Ex: O tipo de dados Funcionário, constituído de tipos tais como caracteres, real, inteiro, etc. Em c: O tipo funcionário seria definido como um tipo chamado de struct Funcionário. Com os campos: nome, RG, cargo, salário, nº de dependentes, etc. Tipo abstrato de Dados: composto pelos tipos de dados + operações associadas. TAD é um tipo de dado definido pelo programador cujas funções especificam como um cliente(outros módulos, ou o programa principal) pode manipular dados. Definição formal de TAD: é um modelo matemático que pode ser definido pelo par (V,θ), onde V é o conjunto de valores e θ é um conjunto de operações definidas sobre esses valores. EX: 1. Tipo real(float) associado aos seus valores + operações válidas.(*,/,+,-,menos unitário(-)) 2. Grafos+operações associadas Propriedades Encapsulamento(dados+operações) Ocultamento de informação : termo utilizado para descrever a habilidade de separar o conceito de sua implementação (característica essencial de um TAD) Ao usuário são fornecidos a descrição dos valores e o conjunto de operações, mas a implementação é invisível e inacessível. Levam a projetos o Reutilizáveis o Portáveis o Modulares Estrutura de Dados: implementação dos TADs em uma linguagem de programação. As diferentes linguagens de programação tem maneiras diferentes de implementar um TAD (algumas de forma mais direta do que outras). C++ ( e outras LP00) implementa TAD por meio de classes, objetos e métodos , onde são utilizados os conceitos de abstração e ocultamento de informação. Dados Operações Associadas TAD 3 Introdução ao C++ A orientação a objetos surgiu para suprir a grande demanda por software, o qual precisava ser construído rápido, correta e economicamente. Fortemente baseada no principio de reutilização de componentes, a orientação a objetos permite uma redução do tempo de implementação, e por meio da organização modular e independente, melhora o tempo de manutenção e aumenta a confiabilidade dos programas. 1. Primeiros Programas Entrada e saída por meio de cin e cout. (substituindo o scanf e o printf, respectivamente, da linguagem c) *abrir Ex1.cpp* 2. Namespace No programa anterior, prefixamos as funções cin, cout e endl por std:: (as 3 funções ao mesmo namespace std). Para omitir o std na frente de cada função precisamos avisar o compilador usando a seguinte declaração: using namespace std; antes da função main *abrir Ex2.cpp* 3. Uma visão geral de orientação a objetos em c++ C++ implementa os 3 conceitos fundamentais de O.O: Encapsulamento Herança Polimorfismo 4 *abrir Ex3.cpp* *abrir Ex4.cpp* 5 *abrir Ex5.cpp* 6 22/10/2014 Exemplo 2: (*Ex6.cpp*) Projetar um TAD para um circulo. O circulo é definido por meio do raio e as operações incluem: o construtor de um objeto circulo, a função que calcula a área de um circulo e a função que calcula o perímetro do circulo. 7 Herança: Exemplo(*Ex7.cpp*) Vamos supor o TAD cilindro. Dados incluem: raio e a altura(h). Operações incluem: construtor de cilindro, área do cilindro, volume do cilindro. Área = Perímetro da Base * (Altura+raio) Volume = Área da Base * Altura 8 A classe Cilindro: pode ser derivada da classe circulo, herdando suas propriedades. Classe circulo: passar a visibilidade dos atributos privados(private) para as classes derivadas, fazendo-os protected. Forma Geral para a definição de uma classe derivada class Derivada::modificador_acesso Base{ ... }; Modificador_acesso: pode ser public ou private. Possui o seguinte comportamento: Acesso na classe Base Modificador de Acesso Acesso herdado da Base Private Public Não acessível public Public Public Protected Public Protected Private Private Não acessível Public Private Private protected private Private 9 Listas lineares: Pilhas, filas, listas, listas circulares, filas de propriedades(estruturas lineares) 1. Introdução Listas lineares: é um conjunto de n (N>0) elementos de informação ( em que: é o primeiro elemento Para 1<k<n, é precedido por e é seguido de é o último elemento do conjunto Operações comuns 1. Ter acesso a , k qualquer, para examinar ou alterar o seu conteúdo. 2. Inserir um elemento novo antes ou depois de . 3. Remover 4. Combinar 2 ou mais listas lineares em uma só lista linear 5. Quebrar uma lista linear em duas ou mais listas 6. Copiar uma lista linear em um outro espaço 7. Determinar o número de elementos de uma lista linear, e outras... Nomes especiais, como pilha ou fila, são dados para as listas lineares, de acordo com a maneira que essas operações são realizadas. Amaneira de representar listas depende das operações mais frequentes. Não existe, em geral, uma única representação para a qual todas as operações são eficientes. EX: Como atender de maneira eficiente as duas operações: 1. Ter acesso fácil(direto) ao k-ésimo elemento da lista ( 2. Inserir ou remover elementos em qualquer posição da lista Pilha Definição: é uma lista linear em que todas as inserções e remoções são feitas em uma mesma extremidade da lista linear. Esta extremidade é chamada de topo da pilha. É conhecida como uma estrutura do tipo LIFO(Last In First Out). Operações definidas para uma pilha 1. Verificar se a pilha está vazia (EMPTY) 2. Inserir um elemento na pilha (empilhar ou push) no lado do topo 3. Remover um elemento (desempilhar ou pop) do lado do topo 4. Consultar ou mostrar o elemento que se encontrar no topo da pilha (TOP) 5. Mostrar quanto elementos estão na pilha Exemplo de aplicação de pilhas 1. Na execução de um programa: é usada na chamada de funções para armazenar o endereço de retorno. 10 2. Na avaliação de expressões aritméticas: a pilha pode ser usada para transformar expressões em notação polonesa reversa e, também, é usada na avaliação da expressão em notação polonesa reversa. Alocação sequencial Os elementos da pilha ocupam posições consecutivas na memória (vetor contendo os elementos da pilha) Vetor de inteiros (reais, caracteres, ou um outro tipo definido pelo usuário) Topo – valor inteiro: indicando a posição do elemento que está no topo da lista. Especificação do TAD Pilha Usando classes em C++ *abrir Ex8.cpp* 11 12 28/10/2014 Uso do tipo de dados genérico template para o TAD pilha Template : especifica um nome genérico para o tipo de dado manipulado pela classe, função, ou mesmo uma coleção(vetor, matriz, etc). Por exemplo, pode-se dar o nome genérico Tobjeto para definir o tipo de dados manipulado por uma classe. Ex: class colecao{ ... int A[100]; ... }; Declaração do objeto: Colecao obj; Usando Template: template<class Tobjeto> class coleção{ ... Tobjeto; ... public: coleção(); Tobjeto metodo1(Tobj); }; Declaração de Objeto colecao<int>obj1; colecao<float>obj2; colecao<char>obj3; Obs: 1. Tobjeto representa o nome genérico, o qual pode ser um tipo primitivo da linguagem C ou um tipo definido pelo usuário. 2. A lista de padrões template <class T1, class T2,..., class Tn> deverá precede a especificação da classe template. 3. Os métodos da classe que usa templates devem ser tratados como funções template. Na implementação dos métodos: template<class Tobjeto> Coleção::Coleção(){ ... } Template<class Tobjeto> Tobjeto Coleção::modelo1(Tobjeto E){ .... } 4. Todas as referencias á classe como um tipo de dado deve incluir o tipo do template entre “<” e “>” Template<class Tobjeto> Tobjeto Coleção<TObjeto>::modelo1(Tobjeto E){ .... } 5. Templates também podem ser usados para definir funções templates, permitindo que sejam utilizados parâmetros genéricos. Ex: 13 template<class Tgenerico> int BuscaSequencia(Tgenerico x[], Tgenerico y){ ... } Redefinição do TAD pilha usando template *abrir ex9.cpp* 14 Obs: Os argumentos da definição template podem incluir expressões constantes, além de nomes de tipos de dados genéricos. Ex: template<class TPILHA, int MAX> class pilha{ ... TPILHA S[MAX]; ... }; Na implementação dos métodos template<class TPILHA, int MAX> … Topo=-1; … } (fazer de forma semelhante para os demais métodos) Na declaração de objetos da classe pilha pilha<int, 100> P1; //pilha p/ armazenar inteiros pilha<char, 200> P2; //pilha p/ armazenar caracteres Implementação Dinâmica do TAD Pilha a) Ponteiros em C Int *p, *q; //ponteiros para inteiros Int a=5,b; P=&b;//p recebe o endereço de b *p=a;//b=5. Isso significa que o conteúdo da variável apontada por p b) Ponteiros para classes 15 Objetos que apresentam uma referencia para alguma classe são tratados de forma semelhante aos ponteiros para estruturas(struct) em c. class *p; { pilha *p; c) Representação encadeada de pilha Antes de especificar o TAD pilha, que será definido por meio de nós encadeado, vamos definir inicialmente o nó da pilha como sendo uma classe. d) Definição da classe Nó da pilha *abrir ex11.cpp* e) Implementação da classe Nó *abrir ex11.cpp* 16 05/11/14 17 18 Aplicações do TAD Pilha a) Avaliação do balanceamento de expressões matemáticas O TAD pilha pode ser usado para avaliar o balanceamento de parênteses, colchetes e chaves em uma expressão. O programa deve verificar se existe um número igual de abre e fecha parênteses (chaves, colchetes, etc.). EX: {(A-[B-C]+D)-[E+F]} Solução: utiliza-se uma estrutura de pilha: I. A cada símbolo {([ encontrado na expressão, coloca-o na pilha II. A cada símbolo })] encontrado a pilha será examinada. Se estiver vazia, a expressão é considerada inválida. Senão retira-se o elemento da pilha e compara-o com o símbolo atual: Se for o símbolo correspondente, continua-se a análise. Senão a expressão está inválida III. No final, a pilha deve estar vazia, do contrário a expressão é mal formada. *abrir ex12.cpp* 19 20 21 b) Notação de Expressões Forma infixa: A+B Forma pré-fixa: +AB Forma pós-fixa: AB+ Transformação de expressões na forma infixa para forma pós fixa 1. Parentizar a expressão de acordo com as regras de precedência das operações. 2. Converte a expressão parentizada 3. Retirar os parênteses c) Avaliação da Pós-fixa Uso de uma pilha de operandos Processo de Avaliação Dado uma expressão matemática na notação posfixa(notação polonesa reversa). 1. Criar uma pilha de operandos 2. Ler símbolos Se operandos empilhá-lo Se operador Desempilhar os dois últimos operandos, efetuar a operação e, colocar o resultado na pilha 3. Continuar a leitura de símbolos enquanto expressão não terminar 4. O resultado está no topo da pilha 12/11/2014 Ex: 623+-382/+*2^3+= 52 Algoritmo para Avaliação *abrir ex13.cpp* A+B*C = (A+(B*C)) = (A+(BC*) = (A(BC*)+) = ABC*+ Ex1 1 2 2 3 A/B^C+D*E-A/C = (((A/(B^C))+(D*E))-(A/C)) = (((A/(BC^))+(DE*))-(AC/)) = (((A(BC^)/)+(DE*)-(AC/)) = -> Ex2 1 2 2 (((A(B^C)/)(DE*)+)-(AC/)) = ABC^/DE*+AC/- 2 3 22 23 24 Processo de Transformação da notação infixa para a posfixa Exemplo: A*(B+C)/D= Símbolo lido Pilha de Operadores Posfixa - vazia - A vazia A * * A ( -> sempre empilha *( A B *( AB + *(+ AB C *(+ ABC ) ->desempilhar operadores ate ) * ABC+ / / ABC+* D / ABC+*D = Vazia ABC+*D/ 25 Definição das prioridades dos operadores Prioridade 1 Prioridade 2 Operador Prioridade dentro da Pilha Prioridade de chegada ^ 3 4 * 2 2 / 2 2 + 1 1 - 1 1 ( 0 5 Processo de Conversão para Posfixa 1. Constroi-se uma pilha de operadores 2.Ler símbolo Se for operando, coloca-o na posfixa Se for operador, verificar sua prioridade (prioridade 2) com a prioridade do elemento que está no topo da pilha(prioridade 1): Enquanto a prioridade do operador lido for menor ou igual a prioridade do elemento do topo, desempilhar o operador e coloca-lo na posfixa Empilhar o símbolo lido Se simb é um ‘)’, desempilhar todos os operadores da pilha até encontrar o ‘(‘ e coloca-los na posfixa 3. Até que expressão não terminar 4. Esvaziar a pilha, colocando os operadores na posfixa Algoritmo para conversão *abrir ex14.cpp* 26 Avaliação de Expressões matemáticas (conversão + avaliação) Ex: 12/3*2+6*2-12*2= 123/2*62*+122*- Processo de conversão e avaliação 1. Constroe-se 2 pilhas: uma de operando e outra de operadores 2. Ler símbolos Se operando, coloca-o na pilha de operandos Se operador, verificar sua prioridade sobre o ultimo operador. Colocar no topo da pilha Enquanto a prioridade 2(símbolo) for maior ou igual a prioridade 1 do elem do topo da pilha: Resolver a operação removendo-se 2 elementos da pilha de operando com o operador que está no topo da pilha e coloca o resultado na pilha de operando Empilha-se o símbolo lido na pilha de operadores Se operador for ‘)’, resolve-se as operações que estão na pilha, colocando-se os resultados na pilha de operando ate encontrar o ‘(‘ correspondente. 3. Até que símbolo seja igual a ‘=’ 4. Esvaziar a pilha de operadores, resolvendo-se as operações e colocando os resultados na pilha de operadores Algoritmo em c++ *abrir ex15.cpp* 12/3*2+6*2-12*2 = ((((12/3)*2)+(6*2))-(12*2)) = ((((123/)*2)+(62*))-(122*)) = ((((123/)*2)+(62*))-(122*)) = 1 2 2 ((((123/)2*)+(62*))-(122*)) = ((((123/)2*)(62*)+)-(122*)) = ((((123/)2*)(62*)+)(122*)-) = 123/2*62*+122*- 2 2 2 3 27 Fazer Lista de exercícios 1 sobre classes e pilhas 28 Fila É uma estrutura linear do tipo FIFO(First In First Out) no qual todas as inserções são feitas em uma extremidade (fim da fila) e as remoções são feitas da outra extremidade (o início da fila). 1. Operações que são realizadas em uma fila Criar uma fila vazia Inserir um elemento na fila Eliminar um elemento da fila Verificar se a fila está vazia Verificar se a fila está cheia 2. Representação sequencial de uma Fila Convenção Fim – indica a posição onde se encontrar o elemento que está no final da fila. Inicio – indica a posição anterior ao elemento que está no ínicio da fila. Definição do TAD fila em C++ usando classes: *abrir ex16.cpp* 29 30 19/11/14 O TAD fila circular e sua representação Convenções 1. Representação por um vetor, cujos índices estão no intervalo [0, N-1] 2. Ponteiros de controle: inicio e fim 3. Inicialmente inicio=fim=0(fila vazia) 4. Condição de fila vazia: inicio=fim 5. Quando fim=N-1, o próximo elemento será inserido na posição 0 Ex: Suponha que N=10, o cálculo das posições de inserção na fila será: 0 módulo 10=0 1 módulo 10=1 2 módulo 10=2 ... 9 módulo 10=9 10 módulo 10=0 O TAD fila circular Definição : É exatamente a mesma vista anteriormente( Representação Sequencial) Implementação de alguns Métodos *abrir ex17.cpp* Fim=(fim+1) módulo N e não fim=fim+1 Se fim=9 e N=10 => fim=(9+1) módulo 10 = 0 31 32 O TAD fila e a sua representação encadeada (alocação Dinâmica de Memória) A definição da classe Nó da fila Igual á classe Nó de pilha *Abrir Ex12.cpp* Definição da classe fila A estrutura da fila terá informação sobre: inicio e fim da fila. E fornecerá os seguintes métodos: Construtor Método p/ inserir elementos Método p/ remover elementos Método p/ verificar se fila vazia Método p/ exibir o primeiro elemento da fila Método p/ destruir a estrutura encadeada 33 Definição da classe fila em C++ *abrir ex18.cpp* Inserção 1º caso: Fila Vazia inicio = novo; fim = novo; 2º caso: Fila com elementos fim -> SetProx(novo); fim = novo; Remoção 1º caso: Fila Vazia : não faz nada 2º caso: Fila com um único elemento incio = NULL; fim = NULL; 3º caso: Fila com mais de um elemento aux = inicio; inicio = inicio->GetProx(); delete aux; 34 35 O TAD fila com prioridades Em algumas aplicações deseja-se ordenar a fila não apenas pela ordem de chegada, mas também através da associação de prioridades a cada elemento. =>Neste caso a fila é ordenada por prioridades. =>Alterações á implementação anterior: Nó da Fila: acrescentar mais um campo para representar o nível de prioridade Modificação do algoritmo de inserção (para considerar não só a ordem de chegada, mas também a prioridade do elemento. Ou então a modificação do algoritmo de remoção.) 26/11/2014 Nó da Fila com Prioridade *abrir ex19.cpp* 36 implementação do método Insert na classe fila com prioridade Inserção 1º caso: Fila Vazia inicio = novo; fim = novo; 2º caso: Inserção no inicio da Fila Nesse caso q vale null e faz-se Novo->setProx(p); Inicio=null; 3º caso: Inserção no final da Fila Nesse caso p vale null q->setProx(nvovo); novo->setProx(null); fim=novo; 4º caso: Inserção no meio da Fila q->setProx(Novo); novo->setProx(p); 37 38 39 Fazer lista de Exercicios 2 sobre Fila 40 Lista Encadeadas Definição : é uma coleção de objetos, os quais possuem tipos de dados iguais. Operações que podem ser realizadas: 1. Inserção de um elemento na lista: Inserção no Inicio Inserção entre 2 elementos quaisquer Inserção no final da lista Inserção após um determinado elemento 2. Localização de um elemento na lista 3. Exibição de um elemento corrente 4. Verificação de lista vazia 5. Concatenação de uma lista com outra lista 6. Intercalação de 2 listas, gerando-se uma única lista 7. Inversão de uma lista(inversão dos ponteiros de uma lista) 8. Intercalação de 2 listas, criando-se uma terceira lista 9. Etc... Lista simplesmente encadeada Definição da classe lista com alocação dinâmica e implementação de alguns métodos OBS: uso da classe genérica No<T> para definir a lista encadeada. *abrir Ex20.cpp* Inserção 1º caso: Fila Vazia 2º caso: Inserção no inicio da Fila 3º caso: Inserção no meio da Fila 41 42 43 10/12/14 A implementação do método Find() O método Find() retorna o endereço do nó anterior ao nó que contém o elemento procurado. Find() retornará NULL caso o elemento esteja no primeiro, ou então não esteja na lista. Dessa forma será necessário retornar também uma segunda variável indicando se achou ou não o elemento na lista. *abrir ex20.cpp* 44 Remoção 1º caso: Fila Vazia: Não há o que remover da lista 2º caso: Remoção do primeiro elemento da lista P=L; L=L->GetProx(); Delete p; 3º caso: Remoção no meio e no fim da lista P=ant.getProx(); Ant->SetProx(p->GetProx()); Delete p;45 Concatenar L<- L->Front(); //obter o nó inicial de L P<-inicio; While(p->GetProx()!=NULL) p=p- >GetProx(); p->SetProx(l);//ligando o ultimo nó da lista corrente com a lista l 46 Lista Duplamente Encadeada Implementação da classe NoDuplo *abrir ex21.cpp* 47 A classe Lista Duplamente Encadeada 48 Inserção 1º caso: Lista Vazia 2º caso: Inserção no inicio da Lista Novo->SetDir(I); I->SetEsq(Novo); I=Novo; 3º caso: Inserção no final da Lista Novo->SetEsq(F); F->SetDir(Novo); F=Novo; 4º caso: Inserção no meio da Lista Q=P->GetEsq(); P->SetEsq(Novo); Novo->SetDir(P); Q->SetDir(Novo); Novo->SetEsq(Q); 49 Remoção 1º caso: Fila Vazia: Não há o que remover da lista 2º caso: Remoção do primeiro nó da lista I=I->GetDir(); I->SetEsq(NULL); Detele p; 3º caso: Remoção do ultimo nó da lista F=f->GetEsq(); F->SetDir(NULL); Delete p; 4ºcaso: Remoção no meio da lista O=p->GetEsq(); Q=p->GetDir(); O ->SetDir(Q); Q->SetEsq(O); Delete p; Fazer Lista de Exercícios 3 sobre Listas 50 17/12/2014 Arvores São estruturas não lineares, onde um elemento da estrutura pode ter múltiplos sucessores. Definição Árvore é um conjunto finito de um ou mais nós de tal forma que: Existe um nó especialmente chamado de raiz Os nós restantes são desdobrados em , onde cada um destes conjuntos constituem uma árvore. são denominados subarvores da raiz. Terminologia Nó raiz: origem da árvore. Nó raiz:A. Grau de um nó: número de subárvores que partem do nó. Grau(A)=3, Grau(B)=2, Grau(I)=0 Nós folhas ou nós terminais: são os nós que possuem grau 0. Ex: {k, L, F, G, M, I, J} Grau de uma árvore: é o grau máximo de seus nós. Na árvore exemplo, o grau da árvore é 3. Altura ou profundidade de uma árvore: é o nível máximo da árvore. Na árvore exemplo, o nível da árvore é 4. As raízes das subarvores de um nó x são chamados de filhos de x. x é chamado pai desses filhos. Os filhos de um mesmo pai são denominados irmãos. No exemplo: A é pai de {B, C, D}, e B,C e D são irmãos. 51 Árvores Binárias Definição: uma árvore binária é: I. Um conjunto vazio de nós II. Um nó raiz com duas árvores binárias separadas, denominadas subárvore esquerda e subárvore direita. Nós da Árvore: *abrir ex22.cpp* 52 53 Árvore Binária de Busca Nesta árvore todos os elementos presentes na subárvore esquerda são menores do que o conteúdo do nó raiz e todos os elementos da subárvore á direita são maiores ou iguais ao conteúdo do nó raiz. *abrir ex23.cpp* 54 Busca e Inserção Se a árvore for vazia, criar o nó raiz com o elemento a ser inserido(elem) Senão percorrer a árvore e localizar a posição de inserção: Enquanto (existe nó) faça Se (elem<conteúdo do nó) Acessar a subárvore á esquerda Senão acessar a subárvore a direita Acessar o nó folha e inserir o novo elem 55 56 Travessia ou percurso em Árvore Binária Consiste em “visitar” todos os nós de uma árvore uma única vez, realizando uma operação qualquer sobre o seu conteúdo. Existem três maneiras diferentes de percorrer uma árvore binária: Pré-Ordem: visita inicialmente o nó e depois percorre-se as subárvores esquerda e direita. 1. Visitar a raiz 2. Percorrer a subárvore esquerda em pré-ordem 3. Percorrer a subárvore direita em pré-ordem Ex: In-Ordem: percorre-se a subárvore esquerda do nó, e depois visita o nó, e passa-se á subárvore direita percorrendo-a. Ex: para escrever em ordem. 1. Percorrer em In-Ordem a subárvore esquerda 2. Visitar a raiz 3. Percorrer em In-Ordem a subárvore direita 57 Pos-Ordem: primeiro percorre-se as subárvores esquerda e direita e depois, visita-se o nó. Ex: para apagar. 1. Percorrer em Pós-Ordem a subárvore esquerda 2. Percorrer em Pós-Ordem a subárvore direita 3. Visitar a raiz Depende do que queremos fazer, determinada maneira de percorrer é mais correta ou necessária. 07/01/2015 Exemplos 1. Percorrer a árvore 58 Pré-ordem: *+a/BC-d*ef: notação pré-fixa da expressão matemática In-ordem: a+b/c*d-e*f: notação infixa Pós-ordem: abc/+def*-*:notação pós-fixa 2. Construir a árvore binária que originou os percursos: a) Pré-ordem: ABDCEGFHI b) In-Ordem: DBAEGCHFI 3. Construir a árvore binária que originou os percursos: a) In-ordem: HDBIEAJFCG b) Pós-Ordem: HDIEBJFGCA 4. Construir a árvore binária que originou os percursos: a) Pré-ordem: ABCDE b) In-Ordem: ABCDE 59 versão não recursiva para o percurso em Pré-Ordem Processar a raiz de A Guardar A para poder percorrer a sua subárvore C. Passar á subárvore esquerda (B) e processar usa subárvore Idem a subárvore D Retornar a A(pilha) para acessar C. Algoritmo Inicio Inicializar uma pilha com os endereços dos nós da árvore Enquanto existir nós a serem processados Percorrer a subárvore esquerda, mostrando o conteúdo do nó e empilhando-o até chegar a um nó folha. (repetição) Se pilha não estiver vazia então Acessar o topo, eliminando-o da pilha Posicionar na subárvore a direita Senão acabaram-se os nós a serem percorridos Fim enquanto Fim *abrir ex23.cpp* 60 14/01/2015 1. Escrever uma função na classe Árvore Binária para retornar o menor elemento da árvore. Neste caso considerar que a árvore não está ordenada. Desta forma, deve-se percorrer a árvore toda para encontrar tal elemento. Ex: 61 *abrir ex23.cpp* Obs: Foi utilizado percurso em pós-ordem no método Menor. 2. Escrever uma função na classe Árvore Binária para calcular a quantidade de nós da árvore até o nível n que será fornecido, sabendo-se que o nó raiz está no nível 0, os filhos da raiz estão no nível 1 e assim por diante. Se p==NULL => return 0 Se n==0 => return 1 Se n>0 => return 1 + Contar(p->Esquerda(), n-1) + Contar(p->Direita(), n-1) *abrir ex23.cpp* 62 3. Escrever uma função na classe Árvore Binária para contar quantos nós não folhas existem na árvore. *abrir ex23.cpp* Função Busca Algoritmo modificado, onde a função retorna o nó onde o elemento foi encontrado e também o pai do nó. Se o elemento encontrado não tem pai(==NULL), significa que ele é a raiz da árvore. Caso o elemento não seja encontrado, a função retorna NULL. 63 *abrir ex23.cpp* Operação de Remoção em uma Árvore Binária de Busca Encontrar o nó a ser removido D e o pai dele p. Encontrar um nó substituto (nó a ser conectado ao pai p e que toma o lugar do nó a ser removido). Eliminar o nó A Árvore deve se manter ordenada Casos de Remoção Dependentes do número de filhos que o nó a ser removido possui. 1º caso – o nó D não possui filhos(é um nó folha) => atualizar o pai p com uma subárvore vazia R – nó substituição R=NULL; p->Esquerda(R); 2º caso – o nó D a ser removido possui um filho esquerdo, mas não um direito. 64 R = D->Esquerda(); p->Direita(R);3º caso – o nó D a ser removido possui um filho direito, mas não um esquerdo. R = D->Direita(); p->Esquerda(R); 4º caso – O nó D a ser removido possui ambos os filhos esquerdo e direito. *O nó de substituição R deve ser o maior nó da subárvore esquerda. 65 ** O nó de substituição R deve ser o menor nó da subárvore direita. Selecionar o nó R, o mais a direita na subárvore esquerda. Desligar esse nó da Árvore: -Conectar sua subárvore esquerda ao seu pai(PR) -Conectar R ao nó removido Algoritmo para encontrar o nó de substituição R que está mais á direita na subárvore esquerda de D 1. Como o nó R é menor do que o nó D, vamos descer á subárvore esquerda de D. 2. Uma vez que R é o maior dos nós á esquerda, ele será localizado percorrendo o caminho das subárvores á direita. Durante o percurso, deve-se guardar o nó predecessor de R, é o pai de R(PR) No percurso das subárvores á direita, tem-se 2 casos: 1º caso: A subárvore direita é nula(vazia) R = Direita(D->Direita()); p->Esquerda(R); 2º caso: A subárvore direita não é vazia e a) R é um nó folha => desliga-lo da árvore PR = Direita(NULL); //R->Esquerda()==NULL R->Esquerda(D->Esquerda()); R->Direita(D->Direita()); p->Esquerda(R); 66 b) R não é um nó folha => o nó R tem uma subárvore á esquerda PR = Direita(R->Esquerda()); R->Esquerda(D->Esquerda()); R->Direita(D->Direita()); p->Esquerda(R); Finalização: Depois de ter encontrado o nó R O nó D é substituído pelo nó R: Ligar os filhos de D como filhos de R: R->Esquerda(D->Esquerda()); R->Direita(D->Direita()); Completar a ligação com o nó pai p Se (p==NULL) => raiz = R Senão Se(D->GetDado()<p->GetDado()) P->esquerda(R); Senão p->Direita(R); O método Remove Dado elem, se elem for encontrado na árvore, remove-lo. *abrir ex23.cpp* 67 68 21/01/2015 Árvore de Busca Balanceada(AVL) A.Adilson V.Volsky L.Landis Definição de Árvore Balanceada Uma árvore é considerada balanceada se e somente se para cada nó, a altura da subárvore esquerda e da subárvore á direita diferem em no máximo 1. Associado a cada nó da árvore AVL, existe um peso indicando o seu balanceamento, cujo valor deve ser necessariamente 0, 1 ou -1, indicando a diferença a altura da subárvore á esquerda e da subárvore a direita. Estrutura de um nó da árvore AVL Especificação da classe No_AVL(ex24.cpp) 69 Especificação da classe AVL Método para calcular a altura da árvore AVL 70 Busca e Inserção em uma Árvore AVL Estudar os casos de desbalanceamento e o rebalanceamento de árvore AVL. Fator de Balanceamento de um nó(FB): é a altura da subárvore esquerda do nó menos a altura da subárvore direita do nó. Inserção na árvore AVL : pode acarretar o desbalanceamento da árvore AVL. Podem ocorrer diversos casos de desbalanceamento. A inserção dos nós 9 e 11 não causa desbalanceamento na árvore. A árvore ficou melhor balanceada. Por outro lado, a inserção dos nós 3, 5 ou 7 requerem que a árvore seja rebalanceada. Para fazer o rebalanceamento deve-se analisar 2 casos de desbalanceamento. 1º caso de Desbalanceamento(caso 1) O nó x foi inserido á esquerda de p e causou desbalanceamento FB(p)=2(ficou mais pesado á esquerda) e: 1.1 Tem um filho q com FB(q)=1(sinais iguais e positivos) => fazer rotação á direita Ex: 71 Rotação a direita Seja q o filho á esquerda de p Torna-se p como filho direito de q Torna-se o filho direito de q como filho esquerdo de p 1.2 Tem um filho q com FB(q)=-1(sinais diferentes – raiz p é positiva e o filho q é negativo) => fazer duas rotações: Rotação_Esquerda_Direita Roda-se o nó q (FB=-1) á esquerda Roda-se o nó p (FB=2) a direita Rotação do nó q a esquerda Seja q o filho esquerdo de p 72 Seja np o filho direito de q Torna-se q o filho esquerdo de np Torna-se o filho esquerdo de np como o filho direito de q Rotação do nó p a direita Faça p como filho direito de np Faça o filho direito de np como filho esquerdo de p 28/01/2014 2ºcaso de desbalanceamento: x foi inserido a direita e causou desbalanceamento em P, em que FB(P) = -2 (-1<=FB<=1) e: 2.1 FB(P) = -2 e possui um filho q com FB(q) = -1 (sinais iguais e negativos) 73 Rotação á esquerda Seja q o filho direito de p Torne o p filho esquerdo de q Torne o filho esquerdo de q como filho direito de p 2.2 FB(P) = -2 possui um filho q com FB(q) = 1 (sinais diferentes->filho positivo, rotação a direita – pai negativo rotação a esquerda) Rotação do nó q a direita(FB(q) = 1 – sinal positivo) Rotação do nó p a esquerda(FB(p) = - 2 – sinal negativo) 74 Implementação separada dos métodos caso1 e caso2 de desbalanceamento Inserção em uma árvore AVL Processo de inserção: Seja T uma árvore AVL e um elemento item a ser inserido em algum novo nó x. Busca-se o item 1. Se item estiver na árvore, nada a ser feito 2. Senão, a busca determina a posição de inserção 3. Insere-se o novo nó na árvore 4. Verifica-se se a inserção ocasionou desbalanceamento em algum nó 5. Caso não tenha desbalanceado, fim da inserção 6. Senão fazer o rebalanceamento de T, por meio das rotações 7. Fim da inserção Verificação se algum nó de T ficou desbalanceado Para cada nó v, o fator B armazena o valor hE(v) – hD(v). O nó v está balanceado se -1<=FB(v)<=1. Se x for inserido na subárvore esquerda de v, e esta inclusão ocasionar um aumento na altura desta subárvore, soma-se 1 ao fator B de v. Se esse valor ficar igual a 2, v ficou desbalanceamento. 75 Verificar os casos em que a inclusão de x provoca um aumento na altura h(v) da subárvore: A inserção do nó x acarreta alteração na subárvore esquerda no seu nó pai w. O campo FatorB permite avaliar se essa alteração pode ou não se propagar aos outros nós v que estão no caminho entre w e a raiz da árvore. Senão, se x for inserido na subárvore direita de v, casos simétricos deverão ser considerados. EX: suponha que o nó x foi inserido na subárvore esquerda de v. A analise inicia no nó w e propaga-se em seus nó ancestrais, de forma recursiva. O processo é encerrado quando se acha a raiz de uma subárvore Tv, que não foi modificada. Três situações distintas podem ocorrer: Caso a: FatorB(v) = -1 antes da inclusão do nó x(mais pesado à direita) FB(v) fica 0 e a altura da subárvore v não foi modificada. Os balanços dos nós no caminho entre v e a raiz não foram alterados. (se v é a raiz – nada fazer também) Caso b: FB(v) = 0 antes da inclusão de x. O fatorB(v) = 1, e a altura da subárvore foi modificada. Analisar os demais nós no caminho até a raiz da árvore. Se v é a raiz, encerrar a analise, senão repetir o processo. Caso c: FB(v) = 1 antes da inclusão de x. O FatorB(v) = 2, o nó v ficou desbalanceado: Aplicar a rotação correta. Qualquer rotação implica que a subárvore resultante tenha a mesma altura da subárvore antes da inclusão: as alturas dos nós ancestrais não precisam ser avaliados. 76 Implementação do algoritmo de inserção
Compartilhar