Universidade Federal de Itajubá Mestrado em Ciências e Tecnologia da Informação Algoritmos e Estruturas de Dados Prof. Dr. Alexandre C. B. Ramos ramos@unifei.edu.br.

Slides:



Advertisements
Apresentações semelhantes
IFTO ESTRUTURA DE DADOS AULA 05 Prof. Manoel Campos da Silva Filho
Advertisements

Estruturas de Dados Listas Prof. Rodrigo Rocha
Aula T10 – BCC202 Listas – Parte 2 Túlio Toffolo www. decom. ufop
Software Básico Silvio Fernandes
AULA 8 Profa. Sandra de Amo GBC053 – BCC
Estruturas de Dados Árvores Binárias
Estruturas de Dados Arvores
Listas duplamente encadeadas
Software Básico Silvio Fernandes
Organização e Recuperação da Informação
Indices estruturados por B-TREE
Introdução aos Métodos de Acesso AULA 7 – Parte II Profa. Sandra de Amo GBC053 – BCC
Método de Acesso Dinâmico - B-Tree AULA 14 Profa. Sandra de Amo Programa de Pós-Graduação em CC - UFU Sistemas de Banco de Dados
Série de Exercícios.
1 Complexidade de Algoritmos Complexidade de pior caso Complexidade de melhor caso de uso bem menos freqüente em algumas situações específicas Complexidade.
Árvores.

Arquivos Seqüenciais Inhaúma Neves Ferraz
Pesquisa em Memória Primária
Pesquisa em Memória Primária
Árvores Introdução e Aplicações Árvores de Busca Binária Fundamentos
Estruturas de Dados e Algoritmos
Listas Ligadas – Conceitos Avançados
Classificação e Pesquisa de Dados
Árvores Balanceadas (AVL)
AED – Algoritmos e Estruturas de Dados
Estruturas de Dados II Prof.: Sergio Pacheco Prof.: Sergio Pacheco 1 1.
Robson Godoi / Sandra Siebra
Lista Encadeada Circular Lista Duplamente Encadeada
Robson Godoi / Sandra Siebra
Listas Encadeadas.
Indexação de Arquivos Página de rosto.
Árvores Equilibradas Sumário Splay B-tree Vermelho-Preto AA e BB
Árvore Binária de Busca
Algoritmos e Estruturas de Dados II
Pesquisa em Memória Primária
Árvores binárias de pesquisa com balanceamento
Estruturas de Dados com Jogos
Estruturas de Dados com Jogos
Árvore de Pesquisa Memória Secundária Engenharia de Computação - UFG.
Noções de trigonometria e funções trigonométricas
Árvores Binárias de Pesquisa
Lista Encadeada Circular Lista Duplamente Encadeada
Algorítmos e estrutura de dados III
Universidade Católica de Pelotas Mestrado em Ciência da Computação
Operações de Inserção e Remoção – BTree Resumo
Algoritmos e Estrutura de Dados III
Introdução e Busca Cega
Árvores Balanceadas Liliane Rose Benning Salgado.
Rotação Simples e Dupla Katia Guimarães
Listas de Prioridades Em muitas aplicações, dados de uma coleção são acessados por ordem de prioridade A prioridade associada a um dado pode ser qualquer.
© Copyright 2007 Algoritmos e Estruturas de Dados - Todos os direitos reservados Átila Valgueiro Malta Moreira Juliana Medeiros de Lucena Rafael Alberto.
Campus de Caraguatatuba Aula 2: Somatório e Produtório
Árvore Binária de Busca
Educação Profissional Técnica de Nível Médio Curso Técnico de Informática Disciplina: Estrutura de Dados Professor: Cheli dos S. Mendes da Costa Listas.
SISTEMAS OPERACIONAIS I Gerenciamento de Arquivos
Árvores Prof. Dr. rer. nat. Daniel Duarte Abdala DAS 5102 – Fundamentos da Estrutura da Informação 1.
Lógica para Computação Prof. Celso Antônio Alves Kaestner, Dr. Eng. celsokaestner (at) utfpr (dot) edu (dot) br.
Estruturas de Dados Aula 15: Árvores
– Aula 20 Adaptado por Reinaldo Fortes para o curso de
Árvores Vermelho-Preto
Árvores AVL Balanceadas (Árvore Binária Balanceada)
© 2011 Pearson Prentice Hall. Todos os direitos reservados.slide 1 © 2011 Pearson Prentice Hall. Todos os direitos reservados.slide 1 © 2011 Pearson Prentice.
Prof. Hilton Cardoso Marins Junior
Árvores Rubro-Negras São árvores balanceadas segundo um critério ligeiramente diferente do usado em árvores AVL A todos os nós é associada uma cor que.
Estruturas Avançadas de Dados UNISINOS
AULA 21 Profa. Sandra de Amo BCC - UFU
Árvores Balanceadas (AVL)
BANCO DE DADOS II Árvore de Dados. FATEC - GUARATINGUETÁ Professor: Eduardo Barbosa Adiel Lemos Ana Renata Bruno Michael Jéssica Fernanda Layla Monique.
INE5408 Estruturas de Dados Árvores B - Estrutura e peculiaridades -Algoritmos de inserção, pesquisa e deleção.
Transcrição da apresentação:

Universidade Federal de Itajubá Mestrado em Ciências e Tecnologia da Informação Algoritmos e Estruturas de Dados Prof. Dr. Alexandre C. B. Ramos ramos@unifei.edu.br www.ici.unifei.edu.br/ramos 1

Objetivos Mapear o domínio do problema no domínio da solução utilizando estruturas de armazenamento de informações adequadas e algorítimos eficientes. Construir estruturas a partir de abstrações tanto de informações quanto do procedimentos. Construir e analisar algorítimos para manipular e armazenar dados em programas. Critério de avaliação: 70% - Prova bimestral 30% - Trabalhos 2

Programa – 1º. bimestre Introdução Tipos abstratos de dados Registros Procedimentos e funções Recursividade Complexidade de algoritmos Tipos de complexidade Notação O Algoritmos ótimos 3

Programa – 1º. bimestre Listas, filas e pilhas Alocação sequencial Listas encadeadas Listas circulares Listas duplamente encadeadas Filas e pilhas Árvores Percurso em árvores Árvores AVL Árvores rubro-negras Árvores múltiplas 4

Bibliografia Tenenbaum, A. M. et all. Estruturas de Dados Usando C. Makron Books. São Paulo, 1995. Estruturas de dados. Paulo Veloso, Clésio Veloso dos Santos e outros. Ed. Campus. Algorítmos e Estruturas de dados. Guimarães Lages. Ed. LTC. Estruturas de dados Fundamentais: conceitos e aplicações. Silvio do Lago. Ed. Érica 5

Avaliações Trabalhos práticos individuais = 10pts Seminários em grupo = 20pts Avaliação escrita = 70 pts (26/09/2012) 6

Seminários Apresentação e exercícios com correção. Temas e datas: Árvores AVL : Diogenes, Ricardo e Wellison : 12/09 Árvores Rubro-negras : Daniele e Henrique : 12/09 Árvores B e B* : Iuri, Orlando e Rodrigo : 19/09 Árvores de Bit : João e Danillo : 19/09 Árvores R : Felipe e Flávio : 19/09 Avaliação: 26/09 7

Introdução 8

Desenvolvimento de Programas O processo de solução de problemas por meio de computador pode ser descrito a partir das seguintes etapas: 1. Especificação do problema 2. Projeto em alto nível 3. Análise de alternativas 4. Refinamento e codificação 5. Verificação do comportamento 9

Desenvolvimento de Programas Nesta descrição devemos ressaltar 3 pontos: Estruturas de dados, que retratam as relações lógicas existentes entre os dados, de modo análogo ao uso de um modelo matemático para espelhar alguns aspectos de uma realidade física. Operações, manipulam as estruturas de dados transformando-as. Estruturas de Representação, que são formas de armazenar na memória os dados, isto é, as estruturas de dados manipuladas pelas operações, que devem: Preservar as relações lógicas existentes entre os dados. Permitir que as operações sejam descritas por procedimentos simples e eficientes. 10

Exercício 1 Escreva um algoritmo para solucionar o seguinte problema: Três missionários e três canibais se encontram em um lado do rio. Todos concordam que precisam chegar do outro lado. Mas os missionários não confiam nos canibais. Assim, os missionários querem atravessar o rio de modo tal que o número de missionários em qualquer das margens nunca seja inferior ao número de canibais que estiver do mesmo lado. O único barco disponível só carrega 2 pessoas por vez. Como todos poderão chegar ao outro lado, sem que os missionários arrisquem suas vidas? 11

Exercício 1bis Escreva um programa em linguagem C que, de forma interativa, ajude o usuário a resolver o problema. O programa deverá ser apresentado na tela de texto, sendo os canibais representados pela letra “C” e os missionarios pela letra “M”. O usuário deverá escolher o conjunto certo de “C” e “M” para atravessar o rio para o lado desejado. A aparencia do programa podera ser: CC MMM C Vai: C C Volta: 12

Tipos Abstratos de Dados 13

Tipos Abstratos de Dados Para descrever os algorítimos e procedimentos durante o curso, vamos utilizar uma linguagem de descrição algoritmica. A noção do tipo de dados ocorre na maioria das linguagens de programação. O tipo da variável delimita o conjunto de valores que ela pode tomar e as operações que podemos efetuar com elas. Geralmente uma linguagem de programação oferece 2 tipos de Tipos de dados: Tipos Primitivos: São tipos básicos pré definidos Tipos construídos: tipos criados a partir de mecanismos presentes na linguagem. 14

5 2 1 2 5 div 2 = 2 5 mod 2 = 1 Tipos primitivos Operações aceitas inteiro - int + - * div mod real - real + - * / lógico - log e ou não (.e. & .ou. V ) caracter- car =  Exemplos: Tipos inteiro ? Real ? Lógico ? Caracter ? V= F e F = V 15

Funções de Transferência São operações especiais que manipulam variáveis de tipos primitivos, diferentes entre si, por exemplo: Xi sendo X real e inteiro. Comparações também podem ser consideradas funções de transferência, por exemplo:X < i sendo X real e i inteiro e o resultado lógico F(V) Trunc, converte um numero real em inteiro trunc (75.37)=75 Tier, converte inteiro em real tier (75)= 75.0 Ord, transforma car em int que é seu numero de ordem no conjunto de caracteres ASCII ord (‘0’) <...< ord (‘9’)<...< ord(‘A’)<...< ord(‘a’). Mais funções de transferencia podem ser obtidas nos tipos construídos. 16

Mecanismo para construção de tipos Utilizados para construir tipos novos a partir de tipos primitivos, segundo a definição: Tipos nome_do_tipo: definição_do_tipo a ser utilizada com os seguintes mecanismos: Tipos construídos forma Geral Vetor - Vet Vet (limite inferior...limite superior) de tipo registro - reg reg (seletor1: tipo1, seletor2: tipo2,...,seletorn: tipon) seqüência- seq seq de tipo p/ ex: tipo cadeia:: seq car referência - ref ref a tipo p/ ex: tipo matricula:: ref int alternativa - alt alt (tipo1 | tipo2 |...| tipon ) p/ ex: tipo questão:: alt (int | car) string ponteiro 17

Exemplos: Vetor= tipo dia :: vet [0...30] de int [1,2,3,4,...31] onde dia[2] = 3 dia [i] = 31 então i= 30 Registro= tipo inscrição::reg (disciplina: int, turma: car) Se matricula: inscrição “Se matricula é uma variável do tipo inscrição, então matricula (103, ‘a’) daí temos: matricula.turma= a matricula.disciplina= 103 O elemento da turma”a”, quer inscrever-se na disciplina 103 18

Seqüência= tipo cadeia:: seq car Se x: cadeia e x< ‘r’, ‘a’, ‘m’> Seq. De comprimento 3 y: cadeia e y<‘e’, ‘m’, ‘i’, ‘g’, ‘r’, ‘a’> Seq. De comprimento 6 Então princ x = ‘r’ cont x = < ‘a’, ‘m’> yconc x = < ‘e’, ‘m’, ‘i’, ‘g’, ‘r’, ‘a’, ‘r’, ‘a’, ‘m’> Se Z: cadeia e Z< ’a ’> Então princ Z=‘a’ cont z=<> Seqüência nula de comprimento 0 19

Referência Tipo de matricula :: ref int Este mecanismo permite uma modalidade dinâmica de alocação, ao contrário dos mecanismos anteriores (vet, seq, etc.) cuja alocação é contínua. Se geografia: matrícula o espaço necessário para a variável geografia compreende 2 partes: 1° parte do valor: armazena um valor do tipo int; 2° parte de posição: armazena uma indicação de posição da localização da parte de valor. 20

Sejam os comandos: Aloque geografia a) após a declaração da variável b)após a execução do comando aloque Desaloque geografia c)após a execução do comando (Na memória) Parte de posição (geografia) Parte de posição Parte de valor geografia Parte de posição 21

jan<fev<mar<...<dez Permite que uma mesma variável possa, em momentos diferentes, ter valores e tipos diferentes Alternativa= tipo questão:: alt ( int | car ) Se resposta : questão Então resposta? tem valor igual a 1 (int) ou 2 (car) Enumeração Permite definir tipos de dados por meio dos valores que os dados daquele tipo podem tomar. Tipo mês= (jan,fev,mar,abr,mai,jun,jul,ago,set,out,nov,dez) seja mês_nasc: mês Se mês_nasc= dez Então Processo qualquer jan<fev<mar<...<dez 22

Variáveis, Declarações e Expressões Nomes (identificadores) podem ser usados para denotar variáveis, do seguinte modo: var nome: tipo; ou nome1, nome2,...,nome3: tipo; Todas as variáveis existentes no programa fonte devem ser declaradas uma vez ( e somente uma vez) no seu cabeçalho, exemplos: Tipos Variáveis matr10:: vet [0..9] de vet [0..9] de real; adj, custo: matr10; status:: reg (no: int, ferias: log, dia: int); folha: seq de status; nome:: seq car; alfa: vet [1970...1975] de nome; celula:: ref int; onde : celula 23

Exercício 2 Escreva um algoritmo que leiam duas strings de 0s e 1s representando inteiros não-negativos binários, e imprima a string representando a soma, a diferença e o produto, respectivamente. 24

Comandos Básicos 25

Comandos Básicos São utilizados para manipular as variáveis, são eles: Atribuição variável expressão Entrada Leia ( lista_de_variáveis) Saída Escreva ( lista_de_variáveis) Se condição então comando Condicionais Se condição então comando senão comando Interação Enquanto condição faça comando Repita comando até (que) condição para variável de valor_inicial incr valor_do_incremento até valor_inicial. Faça comando Escape de malha Saída ou escape Seleção Conforme variável, Faça lista_dos_casos 26

Atribuição V  e (e precisa ser do mesmo tipo de V) X  (Y+Z)/2.0 x,y e z: real; “O valor de X depois da avaliação será média aritmética dos valores de Y e Z antes” i  i+1 exemplo comum para contagens Variáveis de tipo contruído: var custo: vet[1..5] de real; custo[1]  custo[2] + 7.8 var matr: reg(disc: int, turma: car); matr  (2015, ‘ a’ ); Exemplos: 27

Entrada de Dados Leia (V1, V2, V3, ... , Vn); Neste caso todas as variáveis listadas recebem vetores do dispositivo de entrada. Por exemplo: var x: real; i: int; a: vet [1..3] de int; Início leia (x,a,i) Fim. Causará a leitura do dispositivo de entrada para x, a e i de um real, um vetor e um inteiro. Exemplos: 28

Saída de Dados Escreva (V1, V2, V3, ...Vn); Neste caso todas as variáveis listadas serão escritas no dispositivo de saída, por exemplo: var i: int; a: vet [1..5] de int; x: real; Início x  2.1; i  i+ 2; a[2]  i; escreva(i,a,x); Fim. Exemplos: 29

Condicional Se L Então A Senão B var x,y,z: real; {Calcula o menor de 2 nos.} Se x < y Então z  x {comando simples} Senão Início {comando composto} z  y; Se x < z Então z  x; Fim; A avaliação da condição não altera o valor de nenhuma variável mesmo que ela contenha expressões como: Se (2.0*y < x/3.2) .e. (z<>0) Então... Exemplos: 30

Iteração - Enquanto Enquanto L Faça A Var x, y: int; Início Enquanto x =< y Faça x  2*y; Enquanto x <> y Faça Se x > y Então x  x - y Senão y  Y - x; Fim. Causa a repetição do comando até que x=y. Caso o sejam no início o programa termina após o primeiro teste. 31

Iteração - Repita Repita A até L Var x,y: int; Início Se x >< y Então Repita Se x > y Então x  x-y Senão y  y-x; Até x=y; Fim. 32

Iteração - Para Para V de i incr P até F Faça A Var i, soma: int; valor: vet [1..10] de int; Início Soma  0.0; Para i  1 até 10 Faça soma  soma+valor[i]; Fim. 33

Seleção Caso V Faça (V1:C1, V2:C2,...,Vn:Cn) ou Senão Faça (V1:C2, V2:C3,...,Vn:Cn+1) Onde Vi são valores e Ci são comandos, exemplo: Var Ch: car; Início Caso Ch Faça ‘A’..’Z’,’a’..’z’: Escreva (‘Letra’); ‘0’..’9’: Escreva(‘Digito’); ‘+’,’-’,’*’,’/’: Escreva (‘Operador’); Senão Escreva (‘Caracter especial’); Fim; Fim. 34

Exercício 3 Escreva um algoritmo para ler uma string de 0s e 1s representando um inteiro positivo em notação binária e imprima uma string de 0s, 1s e 2s representando o mesmo número em notação ternária Escreva um programa em C que realize essa conversão 35

Variáveis Indexadas - Vetores A utilização de vetores em computação é uma técnica muito útil para a otimização de tempo e memória e torna um programa mais elaborado e eficiente. Forma geral: Tipo vetor:: vet[1..3] de int; Var A, B, C: vetorz; 36

Leitura e Escrita de um Vetor Exemplo: Leitura Var x: vet[1..4] de int; Início Para i:= 1 até 4 Faça leia(x[i]); Fim. Exemplo: Escrita Para i:=1 até 4 Faça Escreva(x[i]); É bom lembrar que poderíamos usar os comandos Repita e Enquanto, tanto na leitura quanto na escrita. 37

Variáveis Indexadas - Matrizes Uma matriz é uma variável indexada que possui 2 dimensões e portanto precisa de 2 índices para marcação de valor. Sua representação matemática é: dimensão: m - linhas representação: Amxn n - colunas Forma geral: Tipo matriz:: vetor[1..3,1..3] de int; Var A, B, C: matriz; 38

Leitura e Escrita de uma Matriz Faz-se necessário 2 varreduras, uma com a variável “j” percorrendo as colunas e outra com a variável “i” percorrendo as linhas: Exemplo: Leitura Para i:= 1 até m Faça Para j:= 1 até n Faça Leia(A[i,j]); Exemplo: Escrita Para j:= 1 até n Faça Escreva(A[i,j]); 39

Exemplo Fazer um algoritmo Algoritmo Matriz; para ler e imprimir Var A: vet[1..3,1..3] de int; i,j: int; a matriz: Inicio Para i:= 1 ate 3 Faça Para j:= 1 ate 3 Faça Leia(A[i,j]); Fim. 40

Registros São conjuntos de dados logicamente relacionados, mas de tipos diferentes (inteiro, real, car etc.); Utilizamos registros, quando vetores ou matrizes possuem diferentes componentes dentro de um mesmo campo, por exemplo: Como criar uma matriz desse tipo, onde existem 2 tipos de variáveis (int e car)? Percebe-se que é impossível a utilização de matrizes, pois na mesma declaração só pode conter um tipo de elemento. Por isso utiliza-se “reg”. 41

Forma Geral de Registro Tipo pagantes:: reg (cadast: int, nome: caract, bandeira: caract, pagamento: caract); Pode-se utilizar vetor composto por reg ou matriz composta por reg, exemplo: Escreva um algoritmo que dados 10 codigos de profissão, emita o nome da profissão correspondente. 42

Tipo lista: reg(codigo: int, nome:caract); Algoritmo Profissao; Tipo lista: reg(codigo: int, nome:caract); Var tab: vet [1..100] de lista; cod,i,k: int; Inicio Para i:= 1 ate 10 Faça leia(tab[i].codigo, tab[i].nome); Leia (cod); {recebe o codigo para comparar} Repita i:= 1; Enquanto(tab[i].cod<>cod) e (i=<10) Faça i:=i+1; Se (i>10) Então Escreva(‘Codigo invalido’) Senão Escreva(‘Cod. Desejado ‘,tab[i].nome); Ate(cod=0); Fim. 43

Arquivos São estruturas de dados manipuladas fora do ambiente do programa. Considera-se como ambiente do programa a memória principal, onde nem sempre é possível ou conveniente manter certas estruturas de dados. No ambiente do programa, tem de ser declaradas as estruturas externas de dados: declara-se primeiro o tipo do arquivo e depois os nomes dos arquivos deste tipo. Um exemplo: Algoritmo arquivo; Var dados: text; Início Fim. 44

Abertura e Fechamento de um Arquivo Para abrir um arquivo novo, utiliza-se o comando: Rewrite (NomeArquivo); Este comando ignora os dados existentes e imprime os novos dados no lugar dos antigos. Para a associação desta identificação interna com o nome externo do arquivo usa-se o comando: Assign (NomeArquivo, ‘NomeExtern’); Para abrir um arquivo cujo problema é não perder os dados existentes: Reset(NomeArquivo); Assim, os dados não são perdidos e a impressão é realizada depois da última linha de dados. 45

Associação de Arquivos no Programa Para a associação, também utiliza-se o comando: Assign(dados,’dados.txt’); Exemplo: Algortimo Arquivo; var dados:text; Inicio Reset(dados); Fim. 46

Fechamento de Arquivo Para terminar a geração do arquivo, faz-se necessário o comando de fechamento antes do “End.” final do programa, forma geral: close(NomeArquivo); exemplo: ........ close(dados); Fim. 47

Escrevendo e Lendo de Arquivos Toda vez que não for desejado ler da tela, ou imprimir na tela, uma pequena modificação deve ser feita nos comandos Escreva e Leia, por exemplo: Leia (dados, x, y); Escreva (dados, x,y); Assim a estrutura completa fica: var dados: text; x,y: int; Inicio Assign(dados,’dados.txt’); Reset(dados); leia(dados,x,y); ........ Escreva(dados,x,y);....... Close(dados); Fim. 48

Exercicio 4 Desenvolver um algoritmo que leia os valores de duas matrizes mxn quaisquer e A e B, do arquivo entrada.dat e escreva os valores da matriz C=A+B no arquivo saida.dat. Escreva um programa que realize este algoritmo. 49

Procedimentos São trechos de programa que possuem seus objetos (variáveis, arquivos etc.) e comandos próprios e que para serem executados devem ser ativados por um programa principal ou outro procedimento: Forma Geral: Procedimento nome; declaração dos objetos locais ao procedimento Inicio comandos do procedimento Fim; 50

Exemplo de Procedimento Algoritmo proced; var A,B,C,X,Y,Z: int; teste:log; Procedimento Repete; Inicio x  SQR(A)+B*SQR(B)+SQR(C)*SQR(C); y  A+C; Fim; Teste  false; Leia (A,B,C); Repete; Se x>y Entao teste  true; A  A+1; Leia(z); Repete; Escreva(x,y,z,teste); Fim. 51

Funções Usada para atribuir o resultado da chamada de um procedimento a uma variável. Forma Geral: Função nome: tipo; declaração dos objetos locais a função; Inicio comandos da função; Fim; 52

Exemplo de Função Algoritmo funcao; var A, B, C: real; Inicio Funcao ABS(x): real; Var x: real; Se x>= 0 Entao ABS  X Senao ABS  -X; Fim; A 5; B  -3; C  -10; Escreva (ABS(A)); Escreva (ABS(-3)); E  ABS(E**3) * A+B; Escreva (E); Fim. 53

Ponteiros São utilizados para alocação dinâmica de variáveis. Alocação dinâmica: o programa é capaz de alocar área de memória, que não foram declaradas anteriormente; Para alocar uma variável dinamicamente, deve-se: 1. Criar espaços para as novas variáveis em tempo de execução; e 2. Definir “ligações” entre essas variáveis dinamicamente. A geração de uma variável dinâmica de tipo qualquer é feita pelo operador NEW. 54

Exemplo de Ponteiro New(p); {Gera uma variável de tipo qualquer e a} {coloca em P o endereço da variável } Program Aloca; P Type ptr= integer; Var P:^ptr; {estatica} Memória usada Memória Livre Begin pelo programa do sistema New(P); {dinamica} P $5C2D p^:= 1234; $5C2D 1234 writeln(p^); End. 55

Exemplo de Ponteiro em Pascal 56

Exercício 5 Escrever um algoritmo que leia uma tabela contendo registros de matriculas, nomes e notas (B1, B2 e MF) e permita ao usuário as funções inserir, excluir, alterar e pesquisar registros pelo numero da matricula. Escreva um programa que realize este algoritmo. 57

Recursividade 58

Recursividade Chama-se método recursivo um método que, para ser aplicado a uma estrutura, envolve a aplicação dele mesmo às subestruturas componentes. Todo procedimento recursivo (ou não) deve possuir pelo menos uma chamada proveniente de um local exterior a ele (chamada externa). Em termos de programação, isso implica em procedimentos que chamam a si próprios, exemplo: Proc fat : int (n:int) Suponha k <- fat(5) Se n=0 como n <> 0 Então 5*fat(4) retorne 1 4*fat(3)..... Senão 1*fat(0) retorne n * fat (n – 1) como n=0 a recursividade para e o resultado é calculado 59

Recursividade A todo procedimento recursivo corresponde um outro não recursivo que executa, exatamente, a mesma computação, por exemplo: Fa[0] = 1 Para j = 1,...,n faça fat[j] = j x fat[j-1] 60

Exercício 6 1. Imagine A como um vetor de inteiros. Apresente algoritmos recursivos para calcular: O elemento máximo do vetor O elemento mínimo do vetor A soma dos elementos do vetor O produto dos elementos do vetor A média dos elementos do vetor 2. Determine o que a seguinte função calcula: Func(n) int n; { if (n==0) return (0); return (n+func(n-1); } 61

Exercício 6 3. Responder certo ou errado: Todo procedimento recursivo deve incorporar terminações sem chamadas recursivas, caso contrário ele seria executado um número infinito de vezes. O algoritmo que calcula o fatorial de forma recursiva, requer apenas uma quantidade constante de memória O algoritmo fatorial não recursivo, requer o armazenamento do vetor fat, com n+1 elementos 4. Mostrar que o algoritmo para o problema da torre de Hanoi, requer exatamente 2n – 1 movimentos de disco para terminar. 5. Elaborar um programa não recursivo para a torre de Hanoi. 62

Complexidade de Algoritmos 63

Complexidade de Algoritmos A Complexidade de um Algoritmo consiste na quantidade de “trabalho” necessária para a sua execução. A expressão matemática que define a complexidade de um algoritmo é determinada pela contagem de uma grandeza (por exemplo, as operações dominantes realizadas). Operação dominante é o conjunto de operações básicas que são executadas repetidas vezes. 64

Complexidade de Algoritmos Um algoritmo serve para resolver um determinado problema, e todos os problemas têm sempre uma entrada de dados (N) O tamanho desse N afeta sempre diretamente no tempo de resposta de um algoritmo Dependendo do problema, já existem alguns algoritmos prontos, ou que podem ser adaptados O problema é: qual algoritmo escolher? 65

Complexidade de Algoritmos A complexidade de um algoritmo pode ser dividida em: Complexidade Espacial: Quantidade de recursos utilizados para resolver o problema; Complexidade Temporal: Quantidade de Tempo utilizado. Pode ser visto também como o número de instruções necessárias para resolver determinado problema; Em ambos os casos, a complexidade é medida de acordo com o tamanho dos dados de entrada (N) 66

Complexidade de Algoritmos Exemplo 1: inversão de uma sequência Para i = 1,...,[n/2] faça temp = S[i] S[i] = S[n-i+1] S[n-i+1] = temp Nesse algoritmo cada entrada é uma sequencia que se deseja inverter e o algoritmo efetua sempre as mesmas operações para as sequencias de mesmo tamanho n. Cada passo executa uma troca de posições, então o número de passos é igual ao numero de execuções do bloco Para, ou seja, [n/2] sendo n>1. 67

Complexidade de Algoritmos Exemplo 2: Soma de matrizes para i = 1,...,n faça para j = 1,...,n faça cij = aij + bij Qual o numero total de passos? Como no exemplo o algoritmo sempre efetua as mesmas operações sempre que a e b forem matrizes nxn e n é o parâmetro independente. Cada passo desse algoritmo corresponde a execução da soma aij + bij e o número total de passos é igual ao número total de somas, isto é, o algoritmo efetua n2 passos. 68

Complexidade de Algoritmos Exemplo 3: Multiplicação de matrizes para i = 1,...,n faça para j = 1,...,n faça cij = 0 para k = 1,...,n faça cij = cij + aik * bkj Qual o numero total de passos? Como no exemplo o algoritmo sempre efetua as mesmas operações sempre que A e b forem matrizes nxn e n é o parâmetro independente. Cada passo desse algoritmo corresponde a execução do produto aik * bkj e o número total de passos é igual ao número total de somas, isto é, o algoritmo efetua n3 passos. 69

Complexidade de Algoritmos Existem três escalas de complexidade: Melhor Caso - Ω ômega Caso Médio - Θ theta Pior Caso Ο - ômicron Nas três escalas, a função f(N) retorna a complexidade de um algoritmo com entrada de N elementos 70

Melhor Caso Definido pela letra grega Ω (Ômega) É o menor tempo de execução em uma entrada de tamanho N É pouco usado, por ter aplicação em poucos casos. Ex.: Se tivermos uma lista de N números e quisermos encontrar algum deles assume-se que a complexidade no melhor caso é f(N) = Ω (1), pois assume-se que o número estaria logo na cabeça da lista. 71

Caso Médio A complexidade média é P(1) + P(2) + ... + P(N) Definido pela letra grega θ (Theta) Dos três, é o mais difícil de se determinar Deve-se obter a média dos tempos de execução de todas as entradas de tamanho N, ou baseado em probabilidade de determinada condição ocorrer No exemplo anterior: A complexidade média é P(1) + P(2) + ... + P(N) Para calcular a complexidade média, basta conhecer as probabilidades de Pi; Pi = 1/N, 1 <= i <= N Isso resulta em P(1/N) + P(2/N) + ... + P(N/N) Que resulta em 1/N(1+2+...+N) Que resulta em 1 N(N+1) = (N+1) N 2 2 Que resulta em f(N) = θ (N+1) 2 72

Pior Caso Representado pela letra grega O (ômicron) ou O maiúsculo. É o método mais fácil de se obter. Baseia-se no maior tempo de execução sobre todas as entradas de tamanho N Ex.: Se tivermos uma lista de N números e quisermos encontrar algum deles, assume-se que a complexidade no pior caso é O (N), pois assume-se que o número estaria, no pior caso, no final da lista. 73

Complexidade de Algoritmos Mas como saber qual é a complexidade de um determinado algoritmo implementado? Para resolver esse problema, dividiu-se os algoritmos em “Classes de Problemas”, de acordo com o parâmetro que afeta o algoritmo de forma mais significativa 74

Classes de Algoritmos São elas: Complexidade Constante Complexidade Linear Complexidade Logarítmica NlogN Complexidade Quadrática Complexidade Cúbica Complexidade Exponencial 75

Complexidade Constante São os algoritmos de complexidade O(1) Independe do tamanho N de entradas É o único em que as instruções dos algoritmos são executadas num tamanho fixo de vezes Ex.: Function Vazia(Lista: TipoLista): Boolean; Begin Vazia := Lista.Primeiro = Lista.Ultimo; End; 76

Complexidade Linear São os algoritmos de complexidade O(N) Exemplo 4: 77

Complexidade Linear Uma operação é realizada em cada elemento de entrada Exemplo 5: pesquisa de elementos em uma lista Procedure Busca(Lista: TipoLista; x: TipoElem; Var pos: integer) Var i: integer; Begin i:=1; while Lista.Elemento[i] <> x do i := i+1; if i >= Lista.MaxTam then pos := -1 else pos := i; End; 78

Complexidade Linear Exemplo 6: Determinar a complexidade do laço for (i = soma = 0; i < n; i++) soma += a[i]; Primeiro, duas variáveis são inicializadas, então o laço for itera n vezes e, durante cada iteração, executa duas atribuições, uma das quais a soma e a outra atualiza i. Assim, existem 2 +2n atribuições para a rodada completa desse laço for; sua complexidade é O(n). 79

Complexidade de Linear Exemplo 7: Determinar a complexidade do código de inversão de sequências for ( i = 1; i <= n/2; i++) { temp = S[i]; S[i] = S[n-i+1]; S[n-i+1] = temp; } Nesse caso o código apresenta a propriedade de o número de passos manter-se o mesmo quando aplicado a entradas diferentes de mesmo tamanho, isto é, para um mesmo valor de n o resultado permanece constante. A variável independente é o valor de n, então as complexidades de pior, médio e melhor caso são iguais entre si. O código sempre efetua [n/2] passos, logo sua complexidade é O(n) E para o caso de soma e multiplicação de matrizes? 80

Complexidade Logarítmica São os algoritmos de complexidade O(logN) Ocorre tipicamente em algoritmos que dividem o problema em problemas menores Ex.: O algoritmo de Busca Binária 81

Complexidade NlogN Como o próprio nome diz, são algoritmos que têm complexidade O(NlogN) Ocorre tipicamente em algoritmos que dividem o problema em problemas menores, porém juntando posteriormente a solução dos problemas menores A maioria dos algoritmos de ordenação externa são de complexidade logarítmica ou N Log N 82

Complexidade Quadrática São os algoritmos de complexidade O(N²) Itens são processados aos pares, geralmente com um loop dentro do outro 83

Complexidade Quadrática São os algoritmos de complexidade O(N²) Itens são processados aos pares, geralmente com um loop dentro do outro Ex.: Procedure SomaMatriz(Mat1, Mat2, MatRes: Matriz); Var i, j: integer; Begin for i:=1 to n do for j:=1 to n do MatRes[i,j] := Mat1[i, j] + Mat2[i,j]; 84

Complexidade Quadrática Exemplo 8: Determinar a complexidade do código que produz a soma de todas as submatrizes que começam com a posição 0 for (i = 0; i < n; i++) { for (j = 1, soma = a[0]; j < n-i; j++) soma += a[j]; cout<<”soma para submatriz 0 até “<<i<<”e “<<soma<<endl; } Antes que os laços comecem, i é inicializado. O laço mais externo é realizado n vezes, executando em cada iteração um laço for mais interno, uma impressão e as atribuições para 1, j e soma. O laço mais interno é executado i vezes para cada i Є {1,...,n-1} com duas atribuições para cada iteração: uma para soma e outra para j. Assim existem 1+3n+ Σi=1n-1 2i = 1+3n+2(1+2+...+n-1)= 1+3n+n(n-1)= =O(n)+O(n2) = O(n2) atribuições são executadas antes que o programa termine. 85

Complexidade Cúbica São os algoritmos de complexidade O(N³) Itens são processados três a três, geralmente com um loop dentro do outros dois Ex.: Procedure SomaElementos_Vetor_Indices_Matriz (mat: Matriz, vet: Vetor); Var i, j: integer; Begin for i:=1 to n do for j:=1 to n do for k:=1 to n do mat[i, j] := mat[i, j] + vet[k]; 86

Complexidade Exponencial São os algoritmos de complexidade O(2N) Utilização de “Força Bruta” para resolvê-los (abordagem simples para resolver um determinado problema, geralmente baseada diretamente no enunciado do problema e nas definições dos conceitos envolvidos) Geralmente não são úteis sob o ponto de vista prático 87

Ordens mais comuns (exponencial) n2 n log n f (linear) log n (constante) n 88

Cálculo da complexidade Foi visto que, para calcular a complexidade de um algoritmo, deve-se analisar o pior caso A análise deve ser feita no programa todo, de acordo com a tabela a seguir 89

Algumas operações com a notação O 90

Exemplo 9 Procedure Verifica_Item_Lista (Lista: TipoLista; x: TipoItem; pos: integer); Var i: integer; Begin i:=1; achou := false; while (i <= Lista.Tamanho) and not achou do begin inc(i); if Lista.Item[i] = x then achou := true; end; if achou then pos := i else pos := -1; O(1) O(N) O(1) f(N) = O(9 * O(1) + O(N)) = O(O(1) + (O(N)) = O(N) 91

Exemplo 10 O(1) O(N) O(1) O(N) O(1) Procedure Verifica_Item(Lista: TipoLista; x: TipoItem; pos: integer); Var i: integer; Begin i:=1; achou := false; while (i <= Lista.Tamanho) and not achou do if Lista.Item[i] = x then achou := true; if achou then pos := i else for i:= Lista.Tamanho +1 to MaxTam; Lista.Item[i] := x; O(1) O(N) O(1) O(N) O(1) f(N) = O(7 * O(1) + 2*O(N)) = O(O(1) + (O(N)) = O(N) 92

Exercício 7 1. Escrever as seguintes funções em notação O: n3 – 1 n2 + 2 log n 3nn +5 * 2n (n-1)n + nn-1 2. Certo ou errado: E - Se f,g são funções tais que f=O(g) e g= Ω(f) então f = Ɵ(g) E - Se a complexidade de melhor caso de um algoritmo for f, então o número de passos que o algoritmo efetua, qualquer que seja a entrada é Ω(f) E - Se a complexidade de pior caso de um algoritmo for f, então o número de passos que o algoritmo efetua, qualquer que seja a entrada é Ɵ(f) 93

Listas Lineares 94

Listas Lineares São estruturas que permitem representar um conjunto de dados de forma a preservar a relação de ordem linear (ou total) entre eles.* Definição: conjunto de n  0 nós X1, X2, X3, ..., Xn, organizados estruturalmente de forma a refletir as posições relativas dos mesmos: Se n > 0, então X1 é o primeiro nó e Xn é o último nó. Quando n=0 a lista é vazia: 95

Operações com Listas Lineares Acessar o k-ésimo nó para obter ou alterar seu conteúdo Inserir um novo nó após (ou antes) o k-ésimo nó da lista Remover o k-ésimo nó da lista Concatenar duas listas Determinar o número de nós de uma lista Localizar o nó que contém um dado valor 96

Algoritmo para Acessar o K-ésimo Nó de uma Lista Algoritmo AcessarKesimo; var x:vet[1..10] de int; k,fim: int; sinal:log, val: int; Inicio x[2]  3; leia(k); fim  5; {Uso 5 elementos da lista} Se (K=<0) ou (k>fim) {Verifica a validade de K} Entao sinal  false Senao Inicio val  x[k]; sinal  true; Fim; Escreva (‘O k-ésimo vale: ‘,x[k]); Fim. 97

Programa para Acessar o K-ésimo Nó de uma Lista 98

Algoritmo para Alterar o valor do K-ésimo Nó Algoritmo AlterarKesimo; var x:vet[1..10] de int; k,fim: int; sinal:log, val: int; Inicio x[3]  1; leia(k); leia(val); fim  5; {Uso 5 elementos da lista} Se (K=<0) ou (k>fim) {Verifica a validade de K} Entao sinal  false Senao x[k]  val; sinal  true; Fim; Escreva (‘O k-ésimo vale: x[‘,k,‘]= ‘,x[k]); Fim. 99

Algoritmo para Inserir um Novo Nó Algoritmo InserirKesimo; var x:vet[1..10] de int; i,k,fim: int; sinal:log, val: int; Inicio leia(k); leia(val); fim  5; {Uso 5 elementos da lista} Se (K=<0) ou (k>fim) Entao sinal  false {Verifica a validade de K} Senao Para i  fim inc -1 ate K Faça x[i+1]  x[i]; fim  fim+1; x[k]  val; sinal  true; Fim; Escreva (‘O k-ésimo vale: x[‘,k,‘]=‘,x[k]); Fim. Supõe-se que haja ao menos um elemento disponível 100

Programa para inserir um novo nó 101

Algoritmo para Remover o K-ésimo da Lista Algoritmo RemoverKesimo; var x:vet[1..10] de int; i,k,fim: int; sinal:log, val: int; Inicio leia(k); leia(val); fim  10; {Uso todos elementos da lista} Se (K=<0) ou (k>fim) Entao sinal  false {Verifica a validade de K} Senao Para i  k inc 1 ate Fim-1 Faça x[i]  x[i +1]; fim  fim-1; sinal  true; Fim; Escreva (‘O k-ésimo vale: x[‘,k,‘]=‘,x[k]); Fim. 102

Programa para remover um nó 103

Listas Encadeadas Resolvem o problema do comprimento máximo da lista permitindo a alteração dinâmica do tamanho da lista aumentando seu comprimento inicial. Neste tipo de lista, cada nó além de conter o dado propriamente dito, deverá também conter a indicação do nó seguinte, caso existir (contiguidade lógica). X1 X2 Xn-1 Xn ... Representação: referência tipo no:: reg(dado: real, proximo: ref no); dado proximo var primeiro: ref no; p^.dado p^.proximo 104

Obtenção dos Nós A obtenção dos nós para compor a lista é feita por meio do comando new(p) onde p também é uma variável do tipo referência a nó. New(p) : aloca dinamicamente um espaço na memória do computador correspondente a um nó. O endereço deste espaço é atribuído à variável p. Exemplo: Desenvolvimento de um algoritmo para criação de uma lista encadeada com um nó apenas; 105

Algoritmo para a criação de um nó Algoritmo listaUmNo; tipo no:: reg(dado: real, proximo: ref no); {especifica o tipo de no} var lista, p: ref no; valor: real; {lista e p apontam p/no} Inicio lista  nil; { lista recebe nil} leia (valor); {le um dado em coloca em valor} new(p); { aloca 1 no o valor de p e o endereco do no} p^.proximo  lista; { o componente proximo de p recebe lista} lista  p; { lista recebe o valor da variavel p} p^.dado  valor; { ou lista^.dado  valor} Fim; 106

Para incluir um outro nó (1) new(p); (2) p^.proximo  lista; (3) lista  p; 107

Para remover o último nó de uma lista (1) p  lista; (2) lista  p^.proximo; {ou lista  lista^.proximo;} (3) dispose(p); Ao remover o último nó da lista, esta voltará à situação inicial de lista vazia: lista / 108

Exemplo Algoritmo para determinar o comprimento de uma lista encadeada. Algoritmo MedeLista; Tipo no::reg(dado:real; proximo: ref no); Enquanto p^;proximo>< nil Faça Var lista, p: ref no; valor: real; Inicio INICIO q  p; ...... p  p^.proximo; Se lista = nil Entao execute rotinaerro2 fim; Senao Se lista^.proximo= nil Entao q^.proximo  nil; Inicio dispose(p); dispose(lista); Fim; lista  nil; ........... Fim FIM. Senao Inicio p  lista; 109

Listas com Descritor 110

Listas com Descritor Simplificam a representação das listas encadeadas pois reunem em um só elemento as referências do primeiro e do último elemento da lista chamado “nó descritor” ou líder da lista. O nó descritor: tipo descritor :: reg (i: ref nó; n: int; f: ref nó); Neste caso a representação da lista vazia é: 111

Utilizando listas com descritor Nesse tipo de lista deve-se criar uma lista vazia, que consiste em: 1. Alocar um nó do tipo descritor; 2. Tornar suas duas referências nulas; e 3. Fazer a variável que indica o início da lista apontar para o nó. O procedimento ao lado Proc criar (d: ref descritor) implementa início esta operação: aloque(d); d^.i  nil; d^.n  0; d^.f  nil; fim 112

Inserindo um nó à esquerda da lista Inserimos um nó com um dado valor à esquerda da lista cujo descritor é apontado pela variável d. 113

Inserindo um nó à direita da lista Inserimos um nó com um dado valor à direita da lista cujo descritor é apontado pela variável d. 114

Removendo um nó da esquerda O procedimento a seguir remove o primeiro nó da lista, se houver, e retorna o dado que o nó removido continha através do parâmetro valor. Para remover um nó da lista devemos percorrer todos os nós da lista a partir do primeiro (esq) até atingir o nó da direita. 115

Pilhas e Filas 116

Pilhas e filas São utilizadas em aplicações onde são impostos critérios para a inserção e retirada de elementos cuja ordem não depende da ordem natural dos valores dos dados. São 2 os critérios mais usados: LIFO “last in first out”: o primeiro a ser retirado é o último que tiver sido inserido, ex.: pilha de pratos; FIFO “first in first out”: o primeiro elemento a ser retirado é o primeiro que tiver sido inserido, ex.: fila de banco. 117

Pilhas 118

Pilhas São estruturas essencialmente dinâmicas. Implementamos utilizando sequências. Supondo que os elementos do conjunto são do tipo dado (pilhas de dados), já definido, podemos definir: tipo pilha_seq:: seq dado; var p: pilha_seq; i,j: dado; Inicialmente a pilha está vazia: p<- <> 119

Empilhar e Desempilhar Para inserir um elemento i (empilhar): p <- < i > conc p Para retirar um elemento (desempilhar): p <- cont p Obs.: o último elemento inserido da pilha é o primeiro componente da sequencia. Caso a pilha esteja vazia, a retirada de um elemento não implica em erro pois a continuação da sequência nula é a própria sequência nula. 120

Acesso ao topo da pilha Em geral, só se admite acesso ao elemento do “topo” da pilha (o último inserido). Para se obter esse elemento fazemos: Se p =< > entao erro {pilha vazia} senão j<- princ p Por serem estruturas dinâmicas as pilhas podem crescer e decrescer aleatoriamente durante a execução do programa. 121

Definindo o tipo pilha Quando se pode prever o tamanho máximo de uma pilha, é possível implementa-la por meio de registros contendo um n° inteiro e um vetor de dados: tipo pilha_vet :: (topo: int; elementos: vet[1..100] de dado); var p: pilha_vet; i,j: dado; 122

Definindo e inicializando uma pilha em Pascal 123

Inicializando a pilha e empilhando A inicialização como pilha vazia faz-se por: p.topo <- 0; Para o empilhamento, devemos verificar se ainda há espaço no vetor para mais um elemento: Se p.topo = 100 entao erro {excesso de elementos} Senao inicio p.topo <- p.topo + 1; p.elementos[p.topo] <- i; fim 124

Desempilhando Para o desempilhamento, verifica-se se a pilha está vazia: Se p.topo = 0 entao nada {a pilha esta vazia} senao p.topo <- p.topo - 1; Para obter o elemento do topo: Se p.topo = 0 entao erro {a pilha esta vazia} senao j <- p.elementos[p.topo] 125

Programa para empilhar e desempilhar 126

Filas 127

Filas Enquanto Pilhas sofrem inserções e retiradas na mesma extremidade (o topo), as filas exigem acesso às duas extremidades: a) começo - onde é feita a retirada; e b) fim - onde é feita a inserção. Definição de filas - forma geral: tipo fila_vet :: reg (comeco:int; termino:int; elementos: vet [1..10] de dado); var f: fila_vet; i,j: dado; 128

Operações básicas Inicialização f.comeco <- 1; f.termino <- 0; Inserção Se f.termino =10 entao erro {excesso de elementos} Senao inicio f.termino <- f.termino + 1; f.elementos[f.termino] <- i; fim; 129

Definindo e inicializando filas 130

Operações básicas Retirada: Se f.termino < f.comeco entao nada { fila vazia } Senao f.comeco <- f.comeco + 1; Consulta ao elemento no começo da fila Se f.termino < f.comeco entao erro {fila vazia} j <- f.elementos[f.comeco]; 131

Representação na memória Após a inicialização, a fila vazia tem o seguinte aspecto: A atribuição do valor 1 inicial a f.comeco é para garantir que f.termino < f.comeco notando que se f.comeco = f.termino não indica fila vazia e sim uma fila com somente um elemento. A inserção dos elementos: 26,18,12,9,3,11,4,2,13 faz: 132

Representando na memória Havendo 4 retiradas da fila temos: Como a retirada envolve o incremento de f.comeco nota- se que a fila vai se deslocando da esquerda para a direita do vetor. Para inserir mais um elemento (17, por exemplo) soma-se 1 a f.termino, o qual passa a conter o índice 10, posição em que colocamos o valor 17. Se agora quisermos inserir mais um valor, isso não será possível (f.termino = 10 indica erro se for tentada uma inserção) o que de certo modo é absurdo, já que as posições 1 a 4 estão livres 133

Resolvendo inconsistências Para permitir a reutilização de posições já ocupadas, inserimos mais um componente “f.tamanho” que indica quantos elementos existem na fila no momento. Portanto as operações serão executadas conforme os algoritmos: Inicialização: f.comeco <- 1; f.termino <- 0; f.tamanho <- 0; 134

Resolvendo inconsistências Inserção: Se f.tamanho = 10 entao erro {excesso de elementos} Senao inicio f.tamanho <- f.tamanho + 1; f.termino <- (f.termino mod 10) + 1; f.elementos[f.termino] <- i; fim; Antes de incrementar f.termino tomamos o resto de sua divisão pelo n° de elementos do vetor, ex.: para inserir mais um elemento, f.termino será calculado por 10 mod 10 + 1 e o novo elemento será inserido na primeira posição do vetor. 135

Resolvendo inconsistências Retirada: Se f.tamanho = 0 entao nada {fila vazia} Senão inicio f.tamanho <- f.tamanho - 1; f.comeco <- (f.comeco mod 10) + 1; fim; Consulta: Se f.tamanho = 0 entao erro {fila vazia} Senao j <- f.elementos[f.comeco]; 136

Trabalho Individual 1 Desenvolver um programa que seja capaz de avaliar expressões aritméticas inteiras utilizando a estrutura de dados Pilha. Utilizar os operadores: +, -, *, / (div), % (mod), ^ (potência). Supor que as expressões possuem identificadores contendo somente uma única letra em maiúscula, conforme visto em sala de aula. Seu programa deve possibilitar a leitura de expressões do tipo: A*B+C/B-A%D^3 Obs.: A expressão deve ser corretamente parentizada pelo usuário antes de ser analisada. Valor do trabalho: 5.0 pts. Referência Bibliográfica: PEREIRA, S. L. Estrutura de dados fundamentais: conceitos e aplicações. São Paulo: Erica, 1996. 137

Trabalho Individual 2 Um simulador é um programa que modela o funcionamento de algum fenômeno do mundo real. Ele deve tratar eventos que ocorrem no decorrer do tempo. Para isso, ele implementa um motor de simulação, que gerencia o funcionamento do simulador. A passagem do tempo é implementada através de ciclos. Em cada ciclo o motor de simulação deve: • Gerar eventos aleatoriamente; • Tratar o próximo evento. - Funcionamento de um Simulador de Aeroporto: O aeroporto apresenta 3 pistas que podem ser usadas para pousos ou decolagens. Duas pistas comportam aviões de grande e pequeno porte. A terceira pode ser usada apenas por monomotores (pequeno porte). A escolha da pista a ser usada para pouso ou decolagem é realizada pela torre de controle, que tem como objetivo otimizar o uso das pistas sem provocar colisões ou acidentes por falta de combustível. Colisões ocorrem quando dois aviões usam a mesma pista no mesmo Instante. O problema da falta de combustível será descrito a seguir. A cada instante (ciclo do simulador): o Um número variável de aviões pode chegar ao aeroporto. o Os aviões aguardando pouso têm sua quantidade de combustível decrescida de uma unidade. Esta quantidade é atribuída pelo simulador (aleatoriamente) no instante em que o avião chega no aeroporto. o Cabe à torre de controle gerenciar uma fila de pousos de forma que não ocorram quedas de aviões por falta de combustível. o A torre de controle pode autorizar um pouso ou decolagem em cada pista.Ao final, o simulador deve gerar alguns eventos estatísticos, como por exemplo, aquantidade de aviões que pousaram, quantos decolaram, etc 138

Árvores Baseado em: Material do prof. L. Baldochi http://pt.wikipedia.org/wiki/arvore_binaria 139

Árvores Utilizadas em muitas aplicações Modela uma hierarquia entre elementos Árvore genealógica Diagrama hierárquico de uma organização Modelagem de algoritmos O conceito de árvores está diretamente ligado à recursão 140 2

Árvores cada elemento é um nó ou vértice da árvore Um conjunto finito de elementos onde um elemento é chamado de raiz os outros são divididos em subconjuntos disjuntos, onde cada um define uma árvore cada elemento é um nó ou vértice da árvore arcos ou arestas conectam os vértices 141 2

Árvores Uma coleção não vazia de vértices e ramos que satisfazem a certos requisitos vértice (ou nó): é um objeto simples que pode ter um nome e mais alguma outra informação associada arco ou aresta (direcionado ou não): é uma conexão entre dois nós 142 5

Representação 143 7

Terminologia e Propriedades cada vértice (exceto a raiz) tem exatamente um antecessor imediato ou pai cada vértice tem nós sucessores imediatos ou filhos, a não ser: nós sem filhos  terminais ou folhas ou externos filhos de um mesmo pai - irmãos nós com pelo menos um filho  não-terminais ou internos 144 9

Terminologia e Propriedades caminho em uma árvore: é uma lista de vértices distintos e sucessivos, conectados por arcos (arestas) da árvore nó raiz existe exatamente um caminho entre a raiz e cada um dos nós da árvore se existir mais de um caminho ou nenhum grafo 145 6

Terminologia e Propriedades grau é o número de subárvores de um nó no exemplo: grau de A é 3; de N é 4; de J é 1 qualquer nó é a raiz de uma sub-árvore consistindo dele e dos nós abaixo 146 10

Árvores 147 12

Árvores A única diferença entre as duas árvores é a ordem das sub- árvores Uma árvore ordenada é definida como uma árvore onde as sub-árvores formam um conjunto ordenado Em uma árvore ordenada define-se o primeiro, segundo e último irmão, de acordo com alguma propriedade 148 13

Terminologia nível da raiz é zero nível de C é 1 nível de K é 3 os vértices da árvore estão classificados em níveis é o número de nós no caminho entre o vértice e a raiz nível da raiz é zero nível de C é 1 nível de K é 3 nível de um nó = nível de seu pai + 1 nível de P é 5 149 16

Terminologia Altura de uma árvore corresponde ao maior nível maior distância entre a raiz e qualquer nó Floresta um conjunto de árvores se removemos a raiz e os arcos que a ligam às sub- árvores, ficamos com uma floresta 150 17

Árvore Binária as sub-árvores da esquerda e da direita É um conjunto finito de elementos que é ou vazio ou composto de três conjuntos disjuntos (aqueles sem elementos em comum) o primeiro contém um único elemento, a raiz os outros dois subconjuntos são árvores binárias as sub-árvores da esquerda e da direita As sub-árvores da esquerda ou da direita podem estar vazias 151 18

Árvore Binária A B G F E D C H I U A B C D E F G H I 152

Árvores Binárias Considerando que os dois filhos de cada nó interno são ordenados: o filho da esquerda e o filho da direita Cada nó interno tem que ter filho da direita ou da esquerda, sendo que um ou ambos podem ser nós externos 153 21

Árvores Binárias uma árvore binária vazia: consiste de nenhum nó interno e um nó externo uma árvore binária é uma árvore ordenada, na qual cada nó tem 0, 1, ou 2 filhos cada filho corresponde a uma árvore binária árvores estão diretamente ligadas a recursão (veremos a seguir)a forma mais simples de definir árvores é recursivamente como se segue: "uma árvore é um único nó ou um nó raiz conectado a um conjunto de árvores" 154 23

Árvores Binárias O número de sub-árvores a esquerda e a direita vazias em uma árvore binária com n nós é de n+1 se n = 1 então 2 subárvores vazias se n = 2 então 3 subárvores vazias Se vale para n – 1 então, em uma árvore com n nós uma subárvore vazia foi substituída por um vértice interno e 2 subárvores vazias o número de subárvores vazias é então: n -1+2 = n +1 árvores estão diretamente ligadas a recursão (veremos a seguir)a forma mais simples de definir árvores é recursivamente como se segue: "uma árvore é um único nó ou um nó raiz conectado a um conjunto de árvores" 155 24

Árvores Binárias Definição de Árvores uma árvore é um único nó ou um nó raiz conectado a um conjunto de árvores Definição de Árvores Binárias uma árvore binária é um nó externo ou um nó raiz (interno) conectado a esquerda e a direita a árvores binárias 156 25

Árvore Binária Estrita todo nó não folha possui filhos a esquerda e a direita  Árvore Binária Estrita Uma árvore binária estrita com n folhas sempre contém 2n- 1 nós 157 26

Árvore Binária Estrita 158 27

Árvore Binária Estrita 159 28

Árvore Binária Estrita Árvore binária estrita com: 1 folha  um nó 2 folhas  3 nós hipótese: n folhas  2n-1 nós 160 29

Árvore Binária A raiz tem nível 0 A raiz de outro nó é o nível do nó pai +1. A profundidade de uma árvore é o maior nível para todas as folhas Markeson: raiz tem nível 1 Langsan : raiz tem nível 0 161 30

Árvore Binária Árvore binária cheia de nível d: árvore binária estrita com todas as folhas no nível d Árvore binária completa de nível d: uma árvore binária estrita com todas as folhas no nível d ou no nível d-1 162 31

Árvore Binária Completa 163 32

Árvore Binária Cheia 164 33

Exercício 8 Classificar: A) B) 165

Árvore Binária Para muitas aplicações, é importante a relação entre altura e número de nós 166 35

Árvore Binária de altura máxima Para árvores com n nós: altura máxima: cada nó não folha só possui um filho - ziguezague sua altura é n-1 F E D C B A 167 36

Árvore Binária - altura mínima T’ e T’’ possuem a mesma altura Seja T’ uma árvore binária de altura mínima Se T’ não é completa retira-se uma folha w de seu último nível e coloca-se como filho de uma folha de nível superior (acima do penúltimo) Repete-se a operação até não ser possível mais realizá-la A árvore resultante T’’ é completa Se a altura de T’’ for menor que a de T’  esta não seria mínima. T’’ não pode ser de altura maior  nenhum nó foi retirado T’ e T’’ possuem a mesma altura 168 37

Árvore Binária - altura mínima Nível 0 – (somente a raiz) contém um nó Nível 1 – contém no máximo 2 nós ..... No nível L - pode conter no máximo 2L nós Árvore binária cheia de altura d tem exatamente 2L nós em cada nível 0  L  d 169 38

2d = 2log(n+1)-1 = 2log(n+1) = n+1 Árvore Binária O total de nós n em uma árvore binária cheia (que seria o máximo) de altura d é a soma do número de nós a cada nível d n = 20 + 21 + 22 + ..... + 2d =  2j j=0 n = 2d+1 -1  d = log (n+1) –1 pode-se mostrar também por indução! OBS.: número de folhas de uma árvore cheia com n nós 2d = 2log(n+1)-1 = 2log(n+1) = n+1 2 2 170 39

Árvore Binária Cheia Árvore Binária Cheia de altura 3  23+1-1 = 15 nós 171 42

Árvore Binária Cheia É a árvore binária com o máximo de nós para uma dada altura, mas a distância da raiz é pequena Lema: Seja T uma árvore binária completa com n>0 nós. Então, T possui altura h mínimo. Além disso, h = log n 172 43

Árvore Binária Completa Seja T uma árvore binária completa com n nós, então sua altura h = log2n Seja T’ a árvore obtida pela remoção dos k nós do último nível T’ é cheia já que T só tinha folhas no último e no penúltimo nível (definição de completa) nós de T’ n’ = n - k = 2d +1 -1 , onde d é a altura de T’ 173 46

Árvore Binária Completa h(T’) = d e h(T) = d+1 d = log2(n’+1) -1 h(T) = log2(n’+1) -1 +1 = log2(n’+1) e k? (no máximo  número de folhas da árvore cheia  2)  1  k  n’ +1 h(T) = log2(n’+1) = log2(n’ + k)  = log2(n)  174 47

Exercício 8 a 1. Uma árvore cheia tem 1023 nós. Qual sua profundidade e o número de nós terminais? Resposta: 9 e 512. 2. Uma árvore completa tem 37 nós. Qual sua profundidade e o número de nós terminais? Resposta: 5 e 19. 3. Uma árvore completa tem 37 nós terminais. Qual sua profundidade e número total de nós? Resposta: 6 e 73. 175

Representando Árvores Binárias typedef struct { int info; tipo_no * esq; tipo_no * dir; } tipo_no; A C B 176 59

Representando Árvores Binárias Typedef struct { char info; tipo_no * esq; tipo_no * dir; tipo_no * pai; } tipo_no A C B 177 60

Representando Árvores Binárias Na representação nós externos podem ser NULL ponteiro para o próprio nó A C B 178 61

Representando Árvores Binárias 179 62

Representando Florestas Representação depende da Aplicação Árvores binárias possuem dois ponteiros em cada nó interno, um para cada filho, e portanto sua representação é imediata O que fazer para árvores gerais ou florestas, com um número arbitrário de filhos por nó, que requerem um número arbitrário de ponteiros Representação depende da Aplicação 180 63

Representando Florestas Se não é necessário caminhar para os níveis de baixo da árvore mas só para os de cima Percorre-se a ávores dos nós terminais para os não terminais e por último a raiz B C A 181 64

Representando Florestas typedef struct { char info; tipo_no * filho; tipo_no * irmão; } tipo_no A B C D E 182 65

Representando Florestas Se é necessário caminhar para os níveis mais altos Empregar uma lista encadeada conectando o nó com seus irmãos e outra com seus filhos Ao invés de empregar um nó dummy para terminar cada lista pode-se apontar de volta para seu pai permitindo mover para cima ou para baixo Esses ponteiros para pai tem que estar marcados para poder ser distingui-los dos ponteiros para irmãos Alternativamente pode-se armazenar o nome do pai de forma que a busca pára quando o nó for revisitado para representar uma floresta com processamento de cima para baixo, necessita-se de uma forma de tratar filhos de cada nó sem prealocar um número específico de nós esse é exatamente a restrição das listas encadeadasjá estudadas. Pode-se usar uma lista encadeada para os filhos de um dado nó. uma lista conectando o nó com seus irmãos e outra com seus filhos (ver fig 4.7) ao invés de um nó dummy para terminar cada lista, fazemos ela apontar de volta para seu pai para poder se mover para cima ou para baixo (esses links para pai tem que estar marcados para poder ser distiguidos dos links para irmãos) Reconhecer fim da lista de irmãos alternativamente pode-se armazenar o nome do pai de forma que a busca pára quando o nó for revisitado 183 66

Representando Florestas Essa representação cada nó possui exatamente dois ponteiros 1 para filho e 1 para um irmão Onde está a diferença entre essa estrutura e uma árvore binária?? não existe 184 67

Operações em Árvores Binárias 185 68

Operações em Árvores Binárias 186 69

Aplicações Com Árvores Binárias É uma estrutura útil quando uma de duas decisões devem ser tomadas no decorrer do processo. Encontrar todas as duplicatas em uma lista de números Uma forma de fazer isso é comparar o número com todos os que o precedem isto não é uma solução eficiente 187 70

Aplicações Com Árvores Binárias Solução: empregar uma árvore binária Armazenam-se os números na árvore de forma a: O 1º número é armazenado na raiz de uma árvore com apenas um nó interno Cada um dos próximos números na lista é comparado com a raiz: caso seja igual é uma duplicata caso seja menor, é armazenado na sub-árvore da direita seguindo-se recursivamente o mesmo procedimento caso seja maior, é armazenado na sub-árvore da esquerda seguindo-se recursivamente o mesmo procedimento 188 71

Aplicações Com Árvores Binárias 14, 18, 4, 9, 7, 15, 3, 5, 17, 4, 20, 9, 5 3 5 7 9 4 17 15 20 18 14 189 72

Aplicações com Árvores Binárias Outra aplicação comum é atravessar a árvore binária, visitando cada nó como sistematicamente visitaremos cada nó? Operação é trivial para listas lineares Para árvores, existem diferentes formas de proceder os métodos diferem conforme a ordem em que se visitam os nós 190 73

Atravessando Árvores Binárias Métodos Pré-ordem:visite a raiz, então visite a subárvore da esquerda, depois a subárvore da direita Em-ordem ou ordem simétrica: visite a subárvore da esquerda, então visite a raiz, depois a subárvore da direita Pós-ordem: visite a subárvore da esquerda, então visite a subárvore da direita, depois a raiz 191 74

Atravessando Árvores Binárias pré-ordem: - * a b * + f g e em-ordem: a*b - f+g * e pós-ordem: a b * f g + e * - 192 75

Atravessando Árvores Binárias Implementação simples dos métodos - recursiva Como se visita uma subárvore de cada vez, seguindo-se a regra recursiva , cada subárvore é visitada começando pela raiz 193 76

Pré-ordem visite(raiz); pre_ordem (pt->esq); { if (pt == NULL) return (); visite(raiz); pre_ordem (pt->esq); pre_ordem (pt-> dir); } 194

em-ordem em_ordem (pt->esq); visite(raiz); em_ordem (pt-> dir); { if (pt == NULL) return (); em_ordem (pt->esq); visite(raiz); em_ordem (pt-> dir); } outra forma de visitar é inorder traversal (serve para escrever as exp. de forma infixa) => de forma similar a preorder a inorder é definida recursivamente: "visite a subárvore da esquerda, visite a raiz e depos a subárvore da direita" também é chamada de ordem simétrica a implementação baseada em pilha é quase idêntica ao acima (guardo link para a raiz , isso se a implementação de árvore não fornecer) 195 77

Pós-ordem pos_ordem (pt->esq); pos_ordem (pt-> dir); { if (pt == NULL) return (); pos_ordem (pt->esq); pos_ordem (pt-> dir); visite(raiz); } 196 78

Exercício 9 Representar, através de uma árvore, a seguinte expressão aritmética: [(a+b)(c+d)/e]-[(f+g)h] Justificar! A partir dos procedimentos anteriores montar um programa completo para atravessamento em-ordem 197

Árvore Binária Completa Representação em lista sequencial Cada nó pode receber um número de 1 a N Um nó de número i está na posição i da lista Seus filhos da esquerda e da direita nas posições 2i+1 e 2i+2, respectivamente Não necessita ponteiros Restringe um tamanho para a árvore Também chamada de representação seqüencial Como foi visto, cada nó em uma árvore quase completa pode ser numerao de 1 a n raiz - 1 filho da esquerda 2xnúmero do pai filho da direita 2xnúmero do pai +1 array 1 - N representa uma árvore de N nós 198 80

Árvore Binária quase Completa D E F G H I 199 81

Exercícios 10 1. Implementar os procedimentos pré-ordem, em- ordem e pós-ordem de forma não recursiva 2. Calcular a altura de cada nó de uma árvore binária 200 82

Em-ordem p = raiz; do { /* segue pelo ramo da esq. até NULL */ H I D E B C A p = raiz; do { /* segue pelo ramo da esq. até NULL */ while (p != NULL) { push(p); p=p->esq; } /* verifica se já processou toda árvore */ if (pilha_não_vazia()){ p = pop(); visite(p); p = p->dir; } while(pilha_não_vazia() || p != NULL); 201 83

Árvore Binária de Busca Construída de tal forma que, para cada nó: Nós com chaves menores estão na sub-árvore esquerda Nós com chaves maiores (ou iguais) estão na sub- árvore direita A inserção dos nós da árvore deve satisfazer a essa propriedade 202 84

Árvore Binária de Busca Para a busca de uma chave v na árvore binária de busca: Primeiro compare com a raiz Se menor, vá para a sub-árvore esquerda se maior, para a sub-árvore direita Aplique o método recursivamente 203 85

Exercício 10a Dos exemplos abaixo quais são árvores binárias de busca: Sim sim não 204 85

Árvore Binária de Busca 205 86

Árvore Binária de Busca 206 86

Árvore Binária de Busca 207 86

Árvore Binária de Busca A cada passo, garante-se que nenhuma outra parte da árvore contém a chave sendo buscada O procedimento pára quando O nó com v é encontrado Senão, chega-se a NULL na busca binária nós usamos uma árvore binária para descrever a seq. de comparações feitas buscando em um array ==/== aqui nós construimos uma estrutura de dados de registros, conectados com ponteiros e usamos isso para a busca 208 87

Árvore Binária de Busca busca_arvore_nao_recursivo (v, pt) { do { if (v < pt->info) pt = pt-> esq; else pt = pt-> dir; }while (pt != NULL) && (v != pt->info); return(pt); } 209 88

Inserindo em Árvore Binária de Busca Para inserir um nó na árvore: fazer uma busca com insucesso alocar um novo nó é necessário saber por qual nó se chegou a NULL será o pai do novo nó 210 90

Inserindo em Árvore Binária de Busca 211 91

Inserindo em Árvore Binária de Busca 212 91

Inserindo em Árvore Binária de Busca 213 91

Inserindo em Árvore Binária de Busca 214 91

Inserindo em Árvore Binária de Busca 215 92

Inserindo em Árvore Binária de Busca 216 92

Inserindo em Árvore Binária de Busca 217 92

Inserindo em Árvore Binária de Busca 218 92

Inserindo em Árvore Binária de Busca 219 92

Inserção Árvore Binária de Busca insere_árvore (int valor, tipo_nó * pt) { tipo_nó * pai; do{ pai = pt ; if (valor < pt->chave) pt = pt ->esq ; else pt = pt->esq; } while(pt != NULL); if (pt == NULL){ pt = aloca(); pt ->chave = valor; pt->esq = NULL; pt->dir = NULL; if (v < pai->chave) pai ->esq = pt ; else pai ->dir = pt ; return(pt); } 220 93

Inserção Árvore Binária de Busca A árvore está classificada se percorrida da forma correta (pre, pos ou em-ordem?) as chaves aparecem em ordem se lidas da esquerda para a direita Podemos ordenar uma sequência como se fosse uma série de inserções o programa tem apenas os ponteiros para se preocupar tempo de execução dos algoritmos Þ dependente da forma da árvore em média para chaves inseridas de forma randômica em torno de 2 lnN comparações e no pior caso N comparações 221 94

Remoção em Árvore Binária de Busca Até então, vimos que a implementação da operação de inserção é simples A remoção de um elemento já é mais complexa remoção de um nó folha os ponteiros esquerdo e direito do pai são setados para NULL se possui apenas um filho o ponteiro apropriado do pai passa a apontar para o filho se o nó possui dois filhos se um desses dois filhos não possui filhos, use esse nó para substituir o nó removido Considere a árvore mostrada do lado esquerdo da figura 14.12 Þ deletar um nó é fácil se: terminalÞ ponha o link correspondente do pai com null ou se possui apenas um filho como A, H ou R Þ mova o link na criança para o link do pai apropriado ou mesmo se o nó possui dois filhos mas um desses dois filhos não possui filhos como N Þ use esse nó para substituir o pai. 222 95

Remoção em Árvore Binária de Busca Senão: substituir o valor do nó a ser removido substitua este com o elemento cuja chave é imediatamente maior (ou menor) é sempre folha? senão for folha – vá repetindo o procedimento algoritmo? 223 96

Remoção em Árvore Binária de Busca 2 3 5 4 8 7 10 9 6 Removendo 5: basta que o ponteiro a direita de 4 aponte para NULL 224 97

Remoção em Árvore Binária de Busca 2 3 4 8 7 10 9 6 Removendo 5: basta que o ponteiro a direita de 4 aponte para NULL 225 97

Remoção em Árvore Binária de Busca 2 3 5 4 8 7 10 9 6 Removendo 3: basta substituir o nó 3 pelo nó 2 continua valendo a regra de formação da árvore 226 98

Remoção em Árvore Binária de Busca 2 5 4 8 7 10 9 6 Removendo 3: asta substituir o nó 3 pelo nó 2 continua valendo a regra de formação da árvore 227 98

Remoção em Árvore Binária de Busca 2 3 5 4 8 7 10 9 6 Removendo 4: basta substituir 4 pelo 5 continua valendo a regra de formação da árvore 228 99

Remoção em Árvore Binária de Busca 2 3 5 8 7 10 9 6 Removendo 4: Basta substituir 4 pelo 5 continua valendo a regra de formação da árvore 229 99

Remoção em Árvore Binária de Busca 2 3 5 4 9 8 12 11 10 7 Removendo 7 (raiz): substituir o 7 pelo imediatamente maior: 8 – como determinar? resulta na remoção do elemento de chave 8 230 100

Remoção em Árvore Binária de Busca 2 3 5 4 9 8 12 11 10 Removendo 7 (raiz): substituir o 7 pelo imediatamente maior: 8 – como determinar? resulta na remoção do elemento de chave 8 231 100

Remoção em Árvore Binária de Busca 2 3 5 4 9 12 11 10 7 Removendo 7 (raiz): substituir o 7 pelo imediatamente maior: 8 – como determinar? resulta na remoção do elemento de chave 8 232 100

Árvore Binária de Busca A árvore obtida depende da seqüência de inserção de nós Para que a árvore binária de busca seja completa completa tem altura mínima o conjunto das chaves deve ser reordenado árvore comum - O (n) árvore completa - O (log n) 233 104

Árvore Binária de Busca Uma árvore binária de busca completa o conjunto das chaves deve ser re-ordenado sejam so e s n+1 duas chaves fictícias e já inseridas a cada passo inserir em T uma nova chave que seja de índice médio entre i e j - duas chaves já inseridas 234 105

Árvore Binária de Busca Árvore completa: ótima para a busca quando a freqüência de acesso aos nós é igual Normalmente estas freqüências são diferentes É interessante construir uma árvore binária que seja a melhor possível no que diz respeito à busca para freqüências conhecidas 235 107

Eficiência da Árvore de Busca Depende da ordem original dos dados Se o array original está ordenado (ascendente ou descendente), as árvores resultantes só tem filhos a direita ou a esquerda a inserção do 1o. nó - 0 comparações a inserção do 2o. nó - 2 comparações a inserção do 3o. nó - 3 comparações 2 + 3 +....+n = n*(n+1)/2 -1 Complexidade - O(n2) - para inserir n nós 236 109

Eficiência da Árvore de Busca Se a lista original estiver organizada, e se uma árvore completa (parecida com completa) for se formando: complexidade da inserção = O( n log n ) 237 110

Eficiência da Árvore de Busca 12, 8, 17, 4, 26 A árvore é balanceada 238 111

Árvores AVL Baseado em: Material do prof. D. Lobato http://pt.wikipedia.org/wiki/arvore_binaria Baseado em: Material do prof. D. Lobato http://pt.wikipedia.org/wiki/arvore_binaria 239

Árvores AVL Os algoritmos convencionais podem gerar árvores degeneradas ou ótimas Árvores de crescimento irrestrito Árvores AVL (Adelson-Velskki & Landis) Árvores de busca binária auto-balanceada Nesse tipo de árvore, al alturas das duas sub-árvores a partir de cada nó difere no máximo em uma unidade. Mesmo depois de inclusões e exclusões o custo se mantem O (log n) onde n é o numero de nós Inserções e eliminações podem requerer o rebalanceamento da árvore, exigindo uma ou mais rotações. AVL é uma ABB com |h|  1 240

AVL Observe as árvores abaixo: Todas armazenam os Mesmos dados. Qual delas é a melhor? E a pior? No pior caso, quantas Comparações são Necesssárias para Encontrar um elemento Na árvoce a) e na Árvore b)? 241

AVL Balanceamento: Dizemos que uma árvore está balanceada em altura (ou profundidade) quando, para cada nó, a diferença entre as alturas de suas sub-árvores é igual a -1, 0 ou 1. 242

AVL Árvore não AVL A mesma árvore após balanceamento por altura, agora AVL 243

AVL Fator de equilíbrio de um nó Fe(A) = hEsq – hDir Fe(C) = 2 – 1 = 1 AVL  Fe(N)  {-1, 0, +1}; N Fe()??? 244

AVL Fator de equilíbrio dos nós Fe(A) = 2 – 3 = -1 Fe(B) = 1 – 1 = 0 Fe(C) = 2 – 1 = 1 Fe(D) = 0 – 0 = 0 Fe(E) = 0 – 0 = 0 Fe(F) = 1 – 1 = 0 Fe(G) = 0 – 0 = 0 Fe(H) = 0 – 0 = 0 Fe(I) = 0 – 0 = 0 245

AVL Calcular o fator de equilíbrio dos nós 246

AVL Calcular o fator de equilíbrio dos nós Resposta 247

AVL Vantagem da árvore balanceada: Com base na tabela acima, se tivéssemos 1.048.575 elementos, poderíamos armazená-los em uma árvore binária perfeitamente balanceada com 20 níveis. Em outras palavras, poderíamos localizar um elemento qualquer dentro dessa árvore com apenas 20 comparações. 248

AVL O que pode acontecer quando um novo nó é inserido numa árvore balanceada ? Dada uma raiz r com subárvores L (left) e R (right), e supondo que a inserção deve ser feita na sub-árvore da esquerda. Podemos distriguir 3 casos: Se hL = hR, então L e R ficam com alturas diferentes mas continuam balanceadas. Se hL < hR, então L e R ficam com alturas iguais e balanceamento foi melhorado. Se hL > hR, então L fica ainda maior e balanceamento foi violado. 249

Exemplo Inseridos normalmente Requer rebalanceamento Dada a árvore: Inserir o nó 9 ou 11 Inseridos normalmente Inserir o nó 3 5 ou 7 Requer rebalanceamento 250

AVL Uma árvore AVL é balanceada quando para cada nó da árvore, a diferença entre as alturas das suas sub-árvores (D e E) não é maior que 1. Onde não inserir nós??? H->(dir|esq) I->(dir|esq) Nos dois casos Fe(C) = 3-1= 2 Todas as outras levam algum | Fe | para além de 1 Tente... 251

AVL Inserindo em H ou I, dois casos Em H: Fe(F) = 1, Fe(C) = 2, F(A) = -2 Em I: Fe(F) = -1, Fe(C) = 2, F(A) = - 2 Inserir e depois rotacionar visto ser necessário alguns ajustes, tal que: Continue ABB Continue balanceada 252

AVL Rotação simples Ocorre devido ao seu desbalanceamento, uma rotação simples ocorre quando um nó está desbalanceado e seu filho estiver no mesmo sentido da inclinação, formando uma linha reta. Rotação dupla Ocorre quando um nó estiver desbalanceado e seu filho estiver inclinado no sentido inverso ao pai, formando um “joelho”. 253

AVL Rotação simples do nó com |Fe| = 2 na direção correta Rebalanceamento mapeado em dois casos Raíz (2|-2) e filho (1|-1) com o mesmo sinal Rotação simples do nó com |Fe| = 2 na direção correta Raíz (2|-2) com um sinal e filho(-1|1) com outro Rotação do nó com |Fe| = 1 na direção correta Rotação do nó que tinha |Fe| = 2 na direção oposta 254

AVL Rotação à direita Numa árvore binária basta empurrar o nodo N para baixo e para a direita. O filho à esquerda de N o substitui, e o filho à direita do filho à esquerda vem a ser o novo filho à esquerda de N. Segue pseudocódigo: Seja Y o filho à esquerda de X Torne X o filho à direita de Y Torne o filho à direita de Y o filho à esquerda de X. É interessante observar que as rotações duplas nada mais são que duas rotações simples seguidas, independentes se à direita ou à esquerda. 255

AVL Rotação à esquerda Em uma árvore binária, basta empurrar o nó N para baixo e para a esquerda. O filho à direita de N o substitui, e o filho à esquerda do filho à direita vem a ser o novo filho à direita de N. Segue pseudocódigo: · Seja Y o filho à direita de X · Torne X filho à esquerda de Y · Torne o filho à esquerda de Y o filho à direita de X. 256

AVL Exemplo Rotação Simples (à direita) A altura da sub-árvoce de raiz 4 difere de mais de 1 unidade da de chave 7. Como a altura da sub-árvore à esquerda de 6 difere de 2 em relação à sua sub-árvore direita, rotaciona-se em torno do 4. 257

AVL Exemplo Rotação Simples (continuação) Então 4 passa a ser a nova raiz A sub-árvore com raiz 6 vira filho direito de 4 5 passa a ser filho esquerdo de 6 Balanceada! 258

AVL – Procedimentos Rotação a esquerda ou direita p void roda_dir(ARVORE p) { ARVORE q, temp; q = p->esq; temp = q->dir; q->dir = p; p->esq = temp; p = q; } 8 8 q q 4 10 temp temp p 2 6 q 9 14 3 temp Raíz (2|-2) e filho (1|-1) com o mesmo sinal Rotação simples do nó com |Fe| = 2 na direção correta Fe(8) = 3-1 = 2 Fe(4)= 2-1 = 1 12 259

AVL – Procedimentos Rotação a esquerda ou direita p void roda_dir(ARVORE p) { ARVORE q, temp; q = p->esq; temp = q->dir; q->dir = p; p->esq = temp; p = q; } 8 q 4 10 temp 2 6 3 260

AVL – Procedimentos Rotação a esquerda ou direita p void roda_dir(ARVORE p) { ARVORE q, temp; q = p->esq; temp = q->dir; q->dir = p; p->esq = temp; p = q; } 8 q 4 10 temp 2 6 3 261

AVL – Procedimentos Rotação a esquerda ou direita p void roda_dir(ARVORE p) { ARVORE q, temp; q = p->esq; temp = q->dir; q->dir = p; p->esq = temp; p = q; } 8 q 4 10 temp 2 6 3 262

AVL – Procedimentos Rotação a esquerda ou direita p void roda_dir(ARVORE p) { ARVORE q, temp; q = p->esq; temp = q->dir; q->dir = p; p->esq = temp; p = q; } 4 2 8 3 10 6 263

AVL – Procedimentos Rotação a esquerda ou direita A esquerda é análogo... Tente! 264

AVL Exemplo Rotação Simples (à esquerda) A altura da sub-árvore de raiz 7 difere de mais de 1 unidade da de chave 3. 265

AVL Rotação simples do nó com |Fe| = 2 na direção correta Rebalanceamento mapeado em dois casos Raíz (2|-2) e filho (1|-1) com o mesmo sinal Rotação simples do nó com |Fe| = 2 na direção correta Raíz (2|-2) com um sinal e filho(-1|1) com outro Rotação do nó com |Fe| = 1 na direção correta Rotação do nó que tinha |Fe| = 2 na direção oposta 266

AVL - Procedimentos E aí, o que fazer? Rotacionar “4” para a esquerda Rotacionar “8” para a direita 8 4 10 2 6 Raíz (2|-2) com um sinal e filho(-1|1) com outro Rotação do nó com |Fe| = 1 na direção correta Rotação do nó que tinha |Fe| = 2 na direção oposta Fe(8) = 3 – 1 = 2 Fe(4) = 1 – 2 = -1 5 267

AVL - Procedimentos E aí, o que fazer? Rotacionar “4” para a esquerda Rotacionar “8” para a direita 8 6 10 Raíz (2|-2) com um sinal e filho(-1|1) com outro Rotação do nó com |Fe| = 1 na direção correta Rotação do nó que tinha |Fe| = 2 na direção oposta Fe(8) = 3 – 1 = 2 Fe(4) = 1 – 2 = -1 4 2 5 268

AVL - Procedimentos E aí, o que fazer? Rotacionar “4” para a esquerda Rotacionar “8” para a direita 6 8 4 10 2 5 269

AVL Exemplo rotação dupla Rotação dupla: Se um nó estiver desbalanceado e seu filho estiver inclinado no sentido inverso ao pai. Nesse caso faz-se duas rotações simples seguidas! Seu filho, o 6, está no sentido inverso do 7, seu pai. 270

AVL Exemplo rotação dupla à direita: Rotação simples à esquerda Rotação simples à direita 271

AVL Exemplo rotação dupla à direita: 5 será a nova raiz da sub-árvore de raiz 7 Como a nova raiz passa a ser o 5, o nó 3 passa a não ser mais filho de 7 Além disso, as arestas de 4 e 6 com 5 são desfeitas 272

AVL Exemplo rotação dupla à direita (continuação): As sub-árvores de raizes 3 e 7 se ligam a 5 4 se liga a 3 e 6 se liga a 7 Balanceada! 273

Exercícios 11 Balancear: 274

Exercícios 11 Inserir: 5 3 7 10 33 15 17 (pag 66) Excluir: 3 17 Excluir: 10 (considerando a árvore inicial) 275

Exercícios 11 Na árvore AVL abaixo: inserir 15, 7 e 28 Excluir 4 e 8 6 10 2 5 276

Árvores B e B+ 277

Árvore B ou B-Tree É um tipo de árvore muito utilizada em banco de dados e sistema de arquivos. Para inserir ou remover variáveis de um nó, o nó não poderá ultrapassar sua ordem e nem ser menor que sua ordem dividida por dois. Árvores B não precisam ser rebalanceadas como são freqüentemente as árvores de busca binária com Árvore AVL. Árvores B têm vantagens substanciais em relação a outros tipos de implementações quanto ao tempo de acesso e pesquisa aos nós. O criador das árvores B, Rudolf Bayer, não definiu claramente de onde veio o B das árvores B. Ao que parece, o B vem de balanceamento onde todos os nós da árvore estão em um mesmo nível. Também é possível que o B tenha vindo de seu sobrenome Bayer, ou ainda do nome da empresa onde trabalhava Boeing, no Boeing Scientific Research Labs. 278

Estrutura do Nó Nós em árvores B, geralmente são representados por um conjunto de elementos apontando para seus filhos. Alguns autores consideram a ordem de uma árvore B como sendo a quantidade de registros que a página pode suportar. Outros consideram a ordem como a quantidade de campos apontadores. Todo nó da árvore tem um mínimo de elementos definido pela sua ordem, que é a metade da ordem, arredondando-se para baixo, caso a árvore seja de ordem ímpar, exceto a raiz da árvore, que pode ter um mínimo de um registro. Por exemplo, os nós de uma árvore de ordem 5, devem ter, no mínimo 5 / 2 registros, ou seja, dois registros. A quantidade de filhos que um nó pode ter é sempre a quantidade de registros do nó mais 1 (V+1). Por exemplo, se um nó tem 4 registros, este nó terá obrigatoriamente 5 apontamentos para os nós filhos. 279

Inserção Primeiro, pesquise a posição onde este nó será incluído. Então, insira o valor dentro do nó. Se nenhum nó ficou errado, acima ou abaixo da ordem div 2, o processo é terminado. Se algum nó ficar acima da ordem, dividimos o nó, o valor central do nó dividido sobe para a posição do pai, continuando assim recursivamente por toda a árvore. Se o nó estourar na raiz, então é criado um novo nó raiz ((podendo ter um único elemento)). 280

Exclusão Primeiro, busque um valor a ser excluído. Então, remova-o de dentro de um nó. Se nenhum nó teve problema, o processo é terminado. Se algum nó estiver errado, então há duas possibilidades: Se o nó ao lado do nó errado pode transferir um ou mais de seus nós filho ao nó atual, então o nó atual voltará ao normal. Após ter atualizado os valores da separação do pai e dos dois filhos, o processo é terminado. Se o nó ao lado do nó errado não tem um filho extra porque está no limite mais baixo, então ambos os nós serão fundidos em um único nó. Continuando até que o nó atual esteja normal. 281

Exercícios 12 Na árvore B da figura: Inserir 5 e 6 na árvore da figura Remover 2 e 3. 282

Árvores-B Objetivo principal Minimizar o número de acessos ao disco para recuperar um registro 25 10 20 30 40 2 5 7 8 22 24 26 27 28 41 42 45 46 13 14 15 18 32 35 38 283

Árvores-B Ordem da árvore-B: m Número de chaves: N Devido a organização, altura da árvore logmN Ordem da árvore-B: m Número de chaves: N Altura da árvore AVL  melhor caso Aplicações Sistema de arquivos: ponteiros para clusters 284

Árvores-B Como representar em C? #define ORDEM 2 #define TAMANHO (ORDEM*2)-1 typedef struct Pagina *PonteiroPagina; typedef struct { int chave; PonteiroPagina p; } item; int m; // numero de itens na pagina PonteiroPagina p0; item e[TAMANHO]; } Pagina; 285

Busca em árvores-B Continuar a busca na página pi Busca binária na página atual Não encontrando Ki < x < Ki+1 com 1  i < m Continuar a busca na página pi Km < x Continuar a busca na página pm x < K1 Continuar a busca na página p0 Página atual == NULL  não existe 286

Busca em árvores-B Implemente uma função de busca em árvore-B com o protótipo Int Pesquisa (int, PonteiroPagina, int *) 287

Busca em árvore-B Possível implementação int Pesquisa(int x, PonteiroPagina pag, int *achou) { int i; *achou = 0 if (pag == NULL) return 0; i = 1; while ((i <= TAMANHO) && (x > pag->e[i-1].chave)) i++; if (x == pag->e[i-1].chave) { *achou = 1; return x; } else if (x < pag->e[i-1].chave) Pesquisa (x, pag->e[i-1].p, achou); else Pesquisa (x, pag->e[i].p, achou); } 288

Inserção em árvores-B A criação de uma árvore-B é trivial Inserir é que é o problema... Respeitar as limitações de ocupação dos nós não-raiz (entre m e 2m)! Crescimento “das folhas para a raíz” Se folhas não acomodarem, aumentar a altura da árvore e manter folhas no mesmo nível 289

Inserção em árvores-B Considerar uma árvore-B ordem 2 Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 290

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 291

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 40 292

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 10 20 40 293

Inserção em árvores-B 10, 15, 20, 30, 40  Mediana é promovida Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 10 20 30 40 15 10, 15, 20, 30, 40  Mediana é promovida 294

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 10 15 30 40 295

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 10 15 30 35 40 296

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 7 10 15 30 35 40 297

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 7 10 15 26 30 35 40 298

Inserção em árvores-B 22, 26, 30, 35, 40  Mediana é promovida Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 7 10 18 15 26 30 35 40 22 22, 26, 30, 35, 40  Mediana é promovida 299

Inserção em árvores-B Inserir as chaves abaixo na ordem dada 20, 40, 10, 30, 15, 35, 7, 26, 18, 22 20 30 7 10 15 18 22 26 35 40 300

Exercícios 13 Na árvore anterior, continuar inserindo chaves 5, 42, 13, 46, 27, 8, 32, 38, 24, 45, 25 Cuidado com as divisões de página! 301

Remoção em árvores-B Remover se não violar condições Algumas situações podem ocorrer Chave na página folha Remover se não violar condições Chave em página intermediária Trocar a chave pelo seu sucessor in-ordem da D  E (maior chave menor que a chave) em uma página folha e remova a chave na folha Se ocorrer violação (página com menos de m chaves), ajustar Balancear (página vizinha) Intercalação (acaba removendo uma página) 302

Remoção em árvores-B Troca com intercalação à direita Algumas situações podem ocorrer Ajuste de violação Troca com intercalação à direita Troca com intercalação à esquerda Balanceamento à direita/esquerda se número de chaves das páginas mais pai  2m+1 Intercalação com direita/esquerda, se número de chaves das páginas mais pai  2m 303

Remoção em árvores-B 5  2m+1  Balanceamento Eliminar chave 24 22 34 6 11 14 19 24 26 38 42 46 26, 34, 38, 42, 46  Promove mediana! 304

Remoção em árvores-B Eliminar chave 24 E para eliminar 26??? 4  2m  Intercalação 22 38 6 11 14 19 26 34 42 46 305

Remoção em árvores-B Eliminar chave 26 E para eliminar 22??? 22 6 11 14 19 34 38 42 46 306

Exercício 14 Considerando inseridas todas as chaves da árvore B do exercício anterior, mostrar as arvores intermediárias das exclusões de 20, 25, 38 e 40. 307

Árvore B+ Uma árvore B+ é uma variação da árvore B. Representa a ordenação de dados de uma maneira que permita uma inserção e remoção eficiente de elementos. É um índice dinâmico de multi-níveis com ligações máximas e mínimas no número de chaves em cada nodo. Os sistemas de arquivos NTFS para o Microsoft Windows, o sistema de arquivos ReiserFS para Unix, o XFS para IRIX e Linux, e o JFS2 para AIX, OS/2 e Linux, usam este tipo de árvore. Numa árvore-B+, contrastando com uma árvore-B, todos dos dados são gravados nas folhas. Os nodos internos contêm apenas chaves e apontadores da árvore. Todas as folhas estão no mesmo nível mais baixo. Os nodos das folhas também estão ligados entre si como uma lista para efetuar consultas facilmente. 308

Conceitos O número máximo de apontadores num registro é chamado de ordem da árvore B+. O número mínimo de chaves por registro é metade do número máximo de chaves. Por exemplo: Se a ordem de uma árvore B+ for n+1, cada nodo (exceto o da raiz) deverá ter entre (n+1)/2 e n chaves. Se n for um número primo, o número mínimo de chaves pode ser (n+1)/2 ou (n-1)/2, mas terá de ser o mesmo em toda a árvore. Um exemplo simples de uma árvore B+ ligando as chaves 1-7 aos valores de dados d1-d7. Notar a lista ligada (em vermelho) permitindo uma atualização ordenada rápida. 309

Conceitos O número máximo de nodos é nh O número de chaves que poderá ser indexado ao usar a árvore B+ é uma função da ordem da árvore e da sua altura. Para uma árvore B+ de ordem n com uma altura h: O número máximo de nodos é nh O número mínimo de chaves é 2(n / 2)h − 1. A árvore B+ foi descrita pela primeira vez em "Rudolf Bayer, Edward M. McCreight: Organization and Maintenance of Large Ordered Indices. Acta Informatica 1: 173-189 (1972)". Uma extensão de uma árvore B+ é chamada de Árvore B# que usa a estrutura da árvore B+ e adiciona mais restrições. 310

Exercícios 15 1. Inserir as chaves abaixo em uma árvore B+ na ordem dada 20, 40, 10, 30, 15, 35, 7 2. Excluir 30 e 15 311

Variantes de árvores-B Todas as chaves estão nas folhas, e nós intermediários são, apenas, índices Folhas encadeadas para permitir acesso seqüencial Usada em bancos de dados para aumentar a concorrência Inserção em página segura com até 2m registros Remoção em páginas seguras com pelo menos m registros 312

Árvores Rubro-Negras É um tipo de árvore criada em 1972 com o nome de árvores binárias simétricas. Assim como as árvores binárias comuns, as árvores rubro-negras possuem um conjunto de operações (tais como inserção, remoção e busca), porém são geralmente mais eficientes devido ao fato de estarem sempre balanceadas. Este balanceamento se dá justamente pela característica que dá nome à árvore, que vem de um bit extra em cada nó que determina se este é "vermelho" ou "preto" dentro do conjunto de regras que rege a árvore. Além deste bit, cada nó também conta com os campos dados do nó, filho esquerdo do nó, filho direito do nó e pai do nó. 313

Definição do nó struct node * grandparent(struct node *n) { if ((n != NULL) && (n->parent != NULL)) return n->parent->parent; else return NULL; } uncle(struct node *n) struct node *g = grandparent(n); if (g == NULL) return NULL; // No grandparent means no uncle if (n->parent == g->left) return g->right; return g->left; 314

Regras Uma árvore rubro-negra estará sempre balanceada pois segue o seguinte conjunto de regras: cada nó da árvore possui um valor a cada novo nó inserido na árvore obedecerá o esquema de menor para o lado esquerdo e maior para o lado direito. a cada nó é associada uma cor: vermelha ou preta. o nó raiz é sempre preto. nós vermelhos que não sejam folhas possuem apenas filhos pretos. para cada nó, todos os caminhos do nó até qualquer folha passa pelo mesmo número de nós pretos. 315

Regras - Inserção A cada vez que uma operação é realizada na árvore, testa-se o conjunto de propriedades descritos anteriormente e são efetuadas rotações e ajuste de cores até que a árvore satisfaça todas estas regras. Uma rotação é uma operação realizada na árvore para garantir seu balanceamento. Na rubro-negra pode ser feita a direita e a esquerda, onde são alterados os ponteiros dos nós rotacionados. Ao inserir um elemento em uma árvore rubro-negra, esta é comparada com os elementos e alocada em sua posição conforme a regra 2. Ao inserir um elemento ele é sempre da cor vermelha (exceto se for o nodo raiz). A seguir a árvore analisa o antecessor da folha. Se este for vermelho será necessário alterar as cores para garantir a regra 5. 316

Regras - Inserir Existem três casos para corrigir as cores após uma inserção: Caso 1: O tio do elemento inserido é VERMELHO. Caso 2: O tio do elemento inserido é PRETO e o elemento inserido é um filho da direita. Caso 3: O tio do elemento inserido é PRETO e o elemento inserido é um filho da esquerda. 317

O nó raiz Ao inserir o primeiro nó na árvore (raiz) este será preto. void insert_case1(struct node *n) { if (n->parent == NULL) n->color = BLACK; else insert_case2(n); } 318

Inserção caso 2 void insert_case2(struct node *n) { if (n->parent->color == BLACK) return; /* Tree is still valid */ else insert_case3(n); } 319

Inserção caso 3 void insert_case3(struct node *n) { struct node *u = uncle(n), *g; if ((u != NULL) && (u->color == RED)) { n->parent->color = BLACK; u->color = BLACK; g = grandparent(n); g->color = RED; insert_case1(g); } else { insert_case4(n); } 320

Inserção caso 4 void insert_case4(struct node *n) { struct node *g = grandparent(n); if ((n == n->parent->right) && (n->parent == g->left)) { rotate_left(n->parent); n = n->left; } else if ((n == n->parent->left) && (n->parent == g->right)) { rotate_right(n->parent); n = n->right; } insert_case5(n); 321

Inserção caso 5 void insert_case5(struct node *n) { struct node *g = grandparent(n); n->parent->color = BLACK; g->color = RED; if ((n == n->parent->left) && (n->parent == g->left)) { rotate_right(g); } else { /* * Here, (n == n->parent->right) && (n->parent == g->right). */ rotate_left(g); } 322

Exemplo Inserir 11,2,14,1,7,13,15,5,8 323

Exemplo Inserir 11,2,14,1,7,13,15,5,8 O nó raiz é sempre preto! 324

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Na inserção o nó de menor valor vai para o lado esquerdo e o de maior para o lado direito. 325

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Ao inserir um elemento ele é sempre da cor vermelha, exceto o nó raiz. 326

Exemplo Inserir 11,2,14,1,7,13,15,5,8 327

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Na inserção o nó de menor valor vai para o lado esquerdo e o de maior para o lado direito. 328

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Na inserção o nó de menor valor vai para o lado esquerdo e o de maior para o lado direito. 329

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Após inserir um elemento da cor vermelha, a árvore analisa o antecessor da folha. Se este for vermelho será necessário alterar as cores para garantir a regra 6 (nós vermelhos que não sejam folhas possuem apenas filhos pretos). 330

Exemplo Inserir 11,2,14,1,7,13,15,5,8 14 é o irmão de 2 e é vermelho. O preto pode ser transferido do pai a seus dois filhos vermelhos (o pai, em seguida, toma a cor vermelha), sem afetar a contagem do nodo preto por qualquer caminho. 331

Exemplo Inserir 11,2,14,1,7,13,15,5,8 Desde que o pai 11 é a raiz, o processo pode ser interrompido neste ponto porque... 332

Exemplo Inserir 11,2,14,1,7,13,15,5,8 A raiz sempre deve ser preta, sem afetar a contagem de nodos pretos em qualquer caminho. 333

Exemplo Inserir 11,2,14,1,7,13,15,5,8 334

Exemplo Inserir 11,2,14,1,7,13,15,5,8 335

Exemplo Inserir 11,2,14,1,7,13,15,5,8 336

Exemplo Inserir 11,2,14,1,7,13,15,5,8 337

Exemplo Inserir 11,2,14,1,7,13,15,5,8 338

Exemplo Inserir 11,2,14,1,7,13,15,5,8 339

Exemplo Inserir 11,2,14,1,7,13,15,5,8 340

Exemplo Inserir 11,2,14,1,7,13,15,5,8 341

Exemplo Inserir 11,2,14,1,7,13,15,5,8 342

Exemplo Inserir 11,2,14,1,7,13,15,5,8 343

Exemplo Inserir 11,2,14,1,7,13,15,5,8 344

Exemplo Inserir 11,2,14,1,7,13,15,5,8 345

Exemplo Inserir 11,2,14,1,7,13,15,5,8 O irmão 1 de 7 é vermelho, deve-se puxar o preto do 2 para baixo. 346

Exemplo Inserir 11,2,14,1,7,13,15,5,8 O preto pode ser transferido de um pai de seus dois filhos vermelhos (o pai, em seguida, toma a cor vermelha), sem afetar o numero de nodos pretos de qualquer caminho. No entanto, uma dupla violação vermelha poderá ser resultante dessa mudança. 347

Exemplo Inserir 11,2,14,1,7,13,15,5,8 348

Exemplo Inserir 11,2,14,1,7,13,15,5,8 349

Exemplo Inserir 11,2,14,1,7,13,15,5,8 350

Exemplo Inserir 11,2,14,1,7,13,15,5,8 351

Exemplo Inserir 4 352

Exemplo Inserir 4 353

Exemplo Inserir 4 354

Exemplo Inserir 4 355

Exemplo Inserir 4 O irmão 8 do 5 é vermelho, puxar o preto para baixo a partir de 7 do conjunto atual de 2 356

Exemplo Inserir 4 O preto pode ser transferido de um pai para seus dois filhos vermelhos (o pai, em seguida, toma a cor vermelha), sem afetar a contagem do caminho do nodo preto. No entanto, uma dupla violação vermelha poderá ocorrer. Portanto, definir 2 para a raiz da sub-árvore. 357

Exemplo Inserir 4 O pai 11 de 2 é do lado direito e seu filho 7 é vermelho e está à direita: girar no sentido anti-horário em torno de 2, fixando no 7 358

Exemplo Inserir 4 Desde que 11 é pai à direita de 2, se o filho vermelho 7 ficou a esquerda, pode-se girar o 11 preto e então mudar de volta para eliminar a dupla violação vermelha (isto é explicado com mais detalhes no Próximo passo). 359

Exemplo Inserir 4 Então, nós usamos este passo para configurar essa possibilidade por rotação no sentido anti-horário em torno de 2. O resultado é vermelho 7 à esquerda de 11 e vermelho preto 2, à esquerda do vermelho 7. 360

Exemplo Inserir 4 O pai 11, de 7 está do lado direito e seu filho 2 é vermelho à esquerda: girar no sentido horário em torno de 11 361

Exemplo Inserir 4 Desde que 11 é preto pai, à direita do vermelho 7 e vermelho 2 é filho e está à sua esquerda, uma rotação no sentido horário por 11 vai subtrair um negro do caminho através da 2. Mas este desequilíbrio pode ser corrigido por troca de cores, de 11 e 7. 362

Exemplo Inserir 4 Ao fazê-lo (na próxima etapa), também elimina a dupla violação vermelha. 363

Exemplo Inserir 4 Mudança de cores. O 2 é filho vermelho à esquerda de 7 tem um nó preto contar que é muito baixo por 1. Trocando as cores entre os atuais e seu filho negro à direita (11) elimina a conta do déficit e da dupla violação vermelho. O nodo preto à direita do filho atual não é afetado. 364

Exemplo Inserir 4 365

Regras - Remoção Existem dois tipos de remoção em uma árvore: De acordo com a remoção efetiva, com as operações de rotação e alteração de cor, remove-se o nodo e estabelece-se as propriedades da árvore. De acordo com a remoção preguiçosa, marca-se um nodo como removido, mas efetivamente não o retira. Sendo desta maneira nenhuma alteração é efetuada na árvore, porém são necessários novos mecanismos de busca e inserção para que reconheçam o nodo como "ausente". 366

Exercício 16 Excluir 7 367