Baixe o app para aproveitar ainda mais
Prévia do material em texto
Conceito de Lista [SCE182] Página 1 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 ESTRUTURAS DE DADOS I PROFESSOR: MARCO AURÉLIO SPOHN 1. Lista Uma lista, como array, pode conter uma sequência ordenada de registros (records) com elementos disponíveis de forma consecutiva -Lista Estática Sequencial- ou não consecutiva -Lista Estática Encadeada. O Pascal permite construir estruturas de dados avançadas -Listas Dinâmicas-, mais versáteis utilizando ponteiros e variáveis dinâmicas. Um ponteiro é uma variável que contém o endereço de memória de uma outra variável ou estrutura de dados. Uma variável declarada como ponteiro é alocado durante a execução real de um programa. Uma variável dinâmica é a única estrutura de dados do Turbo Pascal que tem de estar identificada numa declaração Var antes de ser utilizada num programa. O Turbo Pascal armazena as variáveis dinâmicas numa área especial da memória chamada Heap (um tipo de pilha, não confundir com stack). Um programa pode criar qualquer número de variáveis dinâmicas, enquanto existir espaço disponível no Heap. 1.1 Lista Estática Sequencial Uma lista estática sequencial é um arranjo de registros onde estão estabelecidos regras de precedência entre seus elementos ou é uma coleção ordenada de componentes do mesmo tipo. O sucessor de um elemento ocupa posição física subsequente. Ex: lista telefônica, lista de alunos A implementação de operações pode ser feita utilizando array (vetor) e record(registro), onde o vetor associa o elemento a(i) com o índice i (mapeamento sequencial). 1.1.1 Características de Lista Estática Sequencial � elementos na lista estão ordenados; � armazenados fisicamente em posições consecutivas; � inserção de um elemento na posição a(i) causa o deslocamento a direita do elemento de a(i) ao último; � eliminação do elemento a(i) requer o deslocamento à esquerda do a(i+1) ao último; Mas, absolutamente, uma lista estática sequencial ou é vazia ou pode ser escrita como ( a(1), a(2), a(3), ... a(n) ) onde a(i) são átomos de um mesmo conjunto S. Além disso, a(1) é o primeiro elemento, a(i) precede a(i+1), e a(n) é o último elemento. Conceito de Lista [SCE182] Página 2 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Assim as propriedades estruturadas da lista permitem responder a questões como: � qual é o primeiro elemento da lista � qual é o último elemento da lista � quais elementos sucedem um determinado elemento � quantos elementos existem na lista � inserir um elemento na lista � eliminar um elemento da lista Consequência: As quatros primeiras operações são feitas em tempo constante. Mas, as operações de inserção e remoção requererão mais cuidados. Vantagem: � acesso direto indexado a qualquer elemento da lista � tempo constante para acessar o elemento i - dependerá somente do índice. Desvantagem: � movimentação quando eliminado/inserido elemento � tamanho máximo pré-estimado Quando usar: � listas pequenas � inserção/remoção no fim da lista � tamanho máximo bem definido 1.1.2 Definição da ED (Estrutura de Dados) Const MAX=100; Type lista=record A:array[1..MAX] of integer; n:0..MAX; End; Var L: lista; 1) Acesso a um elemento Writeln (L.A[i]); 2) Atualização L.A[i]:= 'Valor' 3) Tamanho da Lista Conceito de Lista [SCE182] Página 3 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Begin tamanho:=L.n; End; 4) Inserção de um elemento na posição i Requer o deslocamento à direita dos elementos a(i+1)...a(n) { considerando que a posição foi obtida em um procedimento de consulta executado préviamente } { também é necessário verificar se a lista não está cheia } Begin For j:=L.n+1 downto i+1 do lista.A[j]:=L.A[j-1]; L.A[i]:='novo valor'; L.n:=L.n+1; End; 5) Remoção do i-ésimo elemento Requer o deslocamento à esquerda dos elementos a(i+1)...a(n) Begin For j:=i to L.n-1 do L.A[j]:=L.A[j+1]; L.n:=L.n-1; End; 1.1.3 Exercícios Dada uma lista sequecial ordenada L1, escreva procedimentos Pascal que: � verifique se L1 está ordenada ou não (a ordem pode ser crescente ou decrescente) � faça uma cópia da lista L1 em uma outra lista L2; � faça uma cópia da Lista L1 em L2, eliminando elementos repetidos; � inverta L1 colocando o resultado em L2; � inverta L1 colocando o resultado na própria L1; � intercale L1 com a lista L2, gerando a lista L3. considere que L1, L2 e L3 são ordenadas. � gere uma lista L2 onde cada registro contém dois campos de informação: elem contém um elemento de L1, e count contém quantas vezes este elemento apareceu em L1. � elimine de L1 todas as ocorrências de um elemento dado, L1 ordenada. � assumindo que os elementos da lista L1 são inteiros positivos, forneça os elementos que aparecem o maior e o menor número de vezes (forneça os elementos e o número de vezes correspondente) 1.2 Lista Estática Encadeada Os elementos da lista são registros com um dos componentes destinado a guardar o endereço do registro sucessor. Ex: L = anta, cabra, macaco, pato, rato Conceito de Lista [SCE182] Página 4 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Cada registro é: ou Há duas alternativas para implementação de operações de listas encadeadas: utilizando arrays ou variáveis dinâmicas. Encadeamento em arrays Eliminando o elemento "cobra" teremos: O registro 2 tornou-se disponível para as próximas inserções... Implementação utilizando array Após sucessivas inserções e eliminações como descobrir quais registros estão disponíveis? Juntá-los numa lista DISPO. Assim, os registros 6, 7, ... m estariam inicialmente na lista DISPO. Como deverá ser Dispo ? Sequencial? Ela deve ser capaz de anexar os registros eliminados da lista principal L. Suponha que queremos inserir algum elemento. Isso implica que : � a eliminação de um elemento da lista principal causa a inserção de um registro na lista Dispo � a inserção de um elemento na lista principal causa a utilização de um dos registros da Dispo No exemplo dado, ao eliminar "cobra" anexamos esse registro à dispo. Conceito de Lista [SCE182] Página 5 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 A princípio podemos utilizar qualquer posição (todos são vazios mesmos!!). A posição mais conveniente é a do primeiro elemento do Dispo - uma vez que requer o acesso a poucos ponteiros. Se a próxima operação é a inserção do elemento ovelha temos: Com várias inserções e eliminações, os registros da lista principal ficariam espalhados pelo vetor, intermediados por registros disponíveis. Vantagens: � não requer mais a movimentação de elementos na inserção e eliminação (como na lista sequencial); � apenas os ponteiros são alterados (lembre que cada registro pode conter elementos muito complexos); Desvantagens: � necessário prever espaco máximo da lista; � necessário gerenciar a Dispo. � o acesso é não indexado, para acessar a(i) temos que percorrer a(1) ... a(i -1) pois o endereço de a(i) está disponível apenas em a(i-1); � aumento do tempo de execução, o qual é gasto obtenção de espaço de memória; � reserva de espaço para a Dispo; � tamanho máximo pré-definido. Quando usar: � quando for possível fazer uma boa previsão do espaço utilizado (lista principal + Dispo) e quando o ganho dos movimentos sobre a perda do acesso direto a cada elemento for compensador. 1.2.1 Implementação de Operações Supondo apenas um campo de informação do tipo T: Const N=_____; {número máximo de elementos} Conceito de Lista [SCE182] Página 6 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html15/8/2001 Type endereço= 0..N; { elementos armazenados nas posições de 1 a N do array; o valor 0 indica fim da lista } Type Rec = record info: T lig: endereço; End; Lista = record A: array[1..N] of Rec; Prim, Dispo: endereco; End; Var L: lista; 1) Qual é o primeiro elemento da lista ? Function Primeiro (L: lista): T; Begin If L.Prim <> 0 Then Primeiro := L.A[L.Prim].info; End; 2) Qual é o último ? Function Ultimo(L: Lista): T; Var p: endereco; Begin If L.Prim = 0 Then {Retorna lista vazia } Else Begin p := L.Prim; While L.A[p].lig <> 0 do p := L.A[p].lig; {L.A[p].info é o último elemento} End; Ultimo := L.A[p].info; End; 3) Quantos elementos tem a lista ? Function No_elementos(L: Lista): integer; Var Nelem: integer; p: endereco; Begin If L.Prim = 0 Then Nelem := 0 {lista vazia} Else Begin p := L.Prim; Nelem := 1; While L.A[p].lig <> 0 do Begin Nelem := Nelem + 1; {Nelem contém o Número de elementos} p := L.A[p].lig; End; End; No_elementos := Nelem; End; Algoritmos para inserção e eliminação de elementos Os algoritmos requerem a mudança de vários ponteiros: do antecessor do registro na lista, do ponteiro da lista Dispo, e do registro a ser inserido/eliminado. Conceito de Lista [SCE182] Página 7 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Temos ObterNo(j) e DevolverNo(j), as quais permitem obter um registro de índice j da Dispo, ou devolver o registro de indice j, respectivamente. Na inserção podemos contar com o registro j como sendo disponível, e na eliminação podemos contar com o registro j como a ser deixado disponível. Para inserir ou eliminar um elemento da lista, temos que saber do endereço do predecessor (lembre que a busca é guiada pelo conteúdo do registro, no caso de listas ordenadas). Este predecessor é quem contém o endereço daquele que será o sucessor do elemento inserido/eliminado. inserção eliminação 1) Inserção após o registro de endereço k Procedure Insere(var L: Lista; k: endereco, valor: T); Var j: endereco; Begin If L.Dispo <> 0 Then {se a Dispo está vazia, nao pode inserir!} Begin ObterNo(j); L.A[j].info := valor; L.A[j].lig := L.A[k].lig; L.A[k].lig := j; End Else {não pode inserir registro} ... End; 2) Eliminação do registro de endereço j precedido por k Procedure Remover(var L: Lista; k,j: endereco); Begin L.A[k].lig := L.A[j].lig; DevolverNo(j); End; 3) Casos especiais Inserção antes do primeiro elemento Conceito de Lista [SCE182] Página 8 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Procedure Insere_prim(var L: Lista; valor: T); Var j: endereco; Begin If L.Dispo <> 0 Then Begin ObterNo(j); L.A[j].info:= valor; L.A[j].lig := L.Prim; L.Prim := j; End Else { não pode inserir } End; OBS: No caso da lista estar vazia, Prim passa a apontar o único elemento da lista. Inserção em lista vazia Procedure Insere_ListaVazia(var L: Lista; valor: T); Var j: endereco; Begin If L.Dispo <> 0 Then { lista vazia ou prim = 0 } Begin Obter_No(j); L.A[j].info:=valor; L.A[j].lig:=0; {null} L.Prim:=j; End; End; Eliminação do primeiro elemento Procedure Remove_Primeiro(var L: Lista); Var j: endereco; Begin j := L.Prim; L.Prim := L.A[L.Prim].lig; DevolverNo(j); End; 4) Acesso ao i-ésimo elemento A[i].info Function Acessa_iesimo(L: Lista; Var dado: T): boolean; Var p,j: endereço; Begin p := L.Prim; { começa do primeiro elemento } j := 1; While (j < i) and (p <> 0) do Begin Conceito de Lista [SCE182] Página 9 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 p := L.A[p].lig; { fim := (p=0) } j := j + 1; { achou := (j=i) } End; If p = 0 Then Begin Acessa_iesimo:=false;{ lista não possui i elementos } dado:=0; End Else Begin Acessa_iesimo:=true; dado:=L.A[p].info; { A[p] é o elemento procurado} End; End; 5) Eliminar o registro de conteúdo dado pela variável valor Procedure Remove_elem(var L: lista; valor: T); Var pa, p, endereco; acabou: boolean; Begin pa := 0; { ponteiro anterior } p := L.Prim; { busca a partir do primeiro } acabou := (p=0); { termina quando fim da lista ] While not (acabou) do Begin If L.A[p].info <> valor Then Begin pa := p; p := L.A[p].lig; acabou := (p=0); End Else acabou := true; End; If p=0 Then { não existe o conteúdo `valor' na lista } Else Begin If pa = 0 Then L.Prim := L.A[L.Prim]lig; { eliminar o primeiro elemento } Else L.A[pa].lig := L.A[p].lig; DevolverNo(p); End; End; 1.2.2 Alocação de Nó em Lista Estática Encadeada Inicialização da Lista Inicialmente todas as posições do vetor A estão disponíveis, portanto fazem parte de "Dispo". Procedure Init(L: Lista); Begin L.Dispo:=1; {primeiro elemento} Conceito de Lista [SCE182] Página 10 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 L.Prim:=0; {lista principal está vazia} For i:=1 to n-1 do L.A[i].lig:=i+1; L.A[n].lig:=0; {receber 0 corresponde ao nil} End; Obter_No(j): obtém um registro de índice j da Dispo. Dispo contém pelo menos nil. Procedure Obter_No(var j: endereco); Begin j:= L.Dispo; { A dispo passa a apontar para quem ela apontava } L.Dispo:= L.A[L.Dispo].lig; End; OBS: A procedure Obter_No oferece uma "posição vazia", devemos portanto, determinar os campos do registro. Devolver_No(j): devolve um registro de índice j à Dispo. Procedure Devolver_No(j:endereco); Begin L.A[j].lig := L.Dispo; L.Dispo := j; End; OBS: � Dispo está vazia quando a lista está cheia � Dispo está cheia quando a lista está vazia 1.2.3 Exercícios 1) Dada uma lista encadeada ordenada L1, escreva procedimentos Pascal que: � verifique se L1 está ordenada ou não (a ordem pode ser crescente ou decrescente) � faça uma cópia da lista L1 em uma outra lista L2; � faça uma cópia da Lista L1 em L2, eliminando elementos repetidos; � inverta L1 colocando o resultado em L2; � inverta L1 colocando o resultado na própria L1; � intercale L1 com a lista L2, gerando a lista L3. considere que L1, L2 e L3 são ordenadas. � gere uma lista L2 onde cada registro contém dois campos de informação: elem contém um elemento de L1, e count contém quantas vezes este elemento apareceu em L1. � elimine de L1 todas as ocorrências de um elemento dado, L1 ordenada. � assumindo que os elementos da lista L1 são inteiros positivos, forneça os elementos que aparecem o maior e o menor número de vezes (forneça os elementos e o número de vezes correspondente) 2) Escreva os seguintes algoritmos que implementem Listas Encadeadas Estáticas (em array) com sentinela: � criação de lista; � busca em lista ordenada e não ordenada � inserção e eliminação de elementos Conceito de Lista [SCE182] Página 11 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 1.3 Lista Dinâmica As linguagens de programação modernas tornaram possível explicitar não apenas o acesso aos dados, mas também aos endereços desses dados. Isso significa que ficou possível a utilização de ponteiros explicitamente implicando que uma distinção notacional deve existir entre os dados e as referências (endereços) desses dados. A notação introduzida por Wirth, com a introdução de Pascal, é: Type Tp = ^T Essa declaração expressa que valores do tipo Tp são ponteiros para dados do tipo T. Portanto, lemos o simbolo ^ como sendo ponteiro para... e na declaração acima lemos Tp é um ponteiro para variáveis do tipo T. O fato de que o tipo dos elementos apontados ser evidente na declaração do ponteiro tem importância fundamental. Isso distingue ponteiros de linguagens de alto nível de endereços em Assembly. Valores para ponteiros são gerados quando dados correspondentes a seus tipos são alocados/desalocadosdinamicamente. Em Pascal, os procedimentos new e dispose existem com esse propósito. Portanto, deixa-se a cargo do programa (via linguagem de programação), e não do programador, prover e devolver espaço para inserções e eliminações em tempo de execução. Custo: Tempo de execução comprometido. Idéia: O programador declara apenas o tipo de registro que contém um elemento da lista, e avisa que a alocação será dinâmica. Sempre que requisitar um registro novo para a inserção, ou liberar um registro a ser eliminado, o programador lança mão de duas rotinas pré-definidas para este fim. 1) Registros com ponteiros Dizemos que uma variável do tipo prec aponta para, ou é um ponteiro para um registro do tipo rec . Ponteiro é o único tipo que pré-referencia outro em Pascal. Type prec = ^rec; Lista = prec; rec = record info: T; {seu tipo preferido} lig: Lista; End; Var p, L: Lista; {nossos ponteiros } Um ponteiro do tipo Lista pode assumir o conjunto de valores que correspondem a endereços reais de memória. Por exemplo, sendo var p: Lista podemos ter: onde o conteúdo de p corresponderia ao endereço do objeto. Esses endereços serão as ligações das listas encadeadas Conceito de Lista [SCE182] Página 12 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 dinâmicas. Sendo o registro: O acesso ao registro depende do tipo da alocação. Se compararmos alocação em array com alocação dinâmica com ponteiros, temos que para acessar o conteúdo de uma variável fazemos: alocação alocação array L.A[p] dinâmica p^ Para designar ponteiro, objeto e campos, a notação utilizada é: ponteiro: p objeto: p^ campos: p^.info p^.lig 2) Endereço nulo (terra) Pascal provê uma constante pré-definida para denotar o endereço nulo nil. Podemos utilizá-la para atribuições e testes, como nos exemplos abaixo: 1: L := nil; 2: If (p = nil) Then ... Ponteiro x Objeto Apontado Nos exemplos abaixo, é ilustrada a diferença entre as operações de atribuição entre ponteiros (por exemplo, p : = q ) e a atribuição entre o conteúdo dos registros apontados pelos ponteiros (isto é: p^ : = q^). var p,q: Lista; Dada a situação abaixo, chamada de (a): Dada a situação (a), após a atribuição p : = q temos a representação abaixo (b). Esta atribuição só é válida para ponteiros do mesmo tipo e é a única operação entre ponteiros. Dada a situação (a), após a atribuição p^ : = q^ temos a representação abaixo (c), onde o conteúdo é atribuido (lembre que p^ equivale a L.A[p]). Conceito de Lista [SCE182] Página 13 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 1.3.1 Manipulação de Registros 1) Declaração de Variável Registro do tipo rec var p: ^rec; 2) Criação de um registro Substitui o procedimento ObterNo(j) dada uma variável ponteiro do tipo ^rec. new(p); a) efetivamente aloca uma variável do tipo rec b) gera um ponteiro do ^rec apontando para aquela variável c) atribui o ponteiro à variável p A partir daí: a) o ponteiro pode ser referenciado como p b) variável referenciada por p é denotada por p^ 3) Atribuição de conteúdo ao registro: p^.info := valor; 4) Liberação de um registro Substitui o DevolverNo(j) - dispose(p) a) operação libera o espaço apontado por p b) p passa a ter valor indefinido Conceito de Lista [SCE182] Página 14 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 1.3.2 Definição da ED Type prec = ^rec; Lista = prec; rec = record info: T; {seu tipo preferido} lig: Lista End; Var p: Lista; {ponteiro para qualquer elemento da lista} L: Lista; {ponteiro para o primeiro elemento da lista } � Operações sobre Listas Dinâmicas 1.3.3 Implementação das Operações 1) Criação da lista vazia Procedure Create (Var L : Lista); Begin L:= nil; End; 2) Inserção do primeiro elemento Procedure Insere_Prim(Var L: Lista; valor: T); Var p: Lista; Begin new(p); p^.info := valor; p^.lig := nil; L := p; End; 3) Inserção no início de uma lista Procedure Insere_Inic(Var L: Lista; valor: T); Conceito de Lista [SCE182] Página 15 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Var p: Lista; Begin new(p); p^.info := valor; p^.lig := L; L := p; End; 4) Acesso ao primeiro elemento da lista Function Prim(L: Lista): T; Begin Prim:= L^.info; End; 5) Acesso ao último elemento da lista Function Ultimo(L: Lista): T; Begin If L = nil Then { lista vazia} Else Begin p := L; While p^.lig <> nil do p := p^.lig; Ultimo := p^.info; {p^.info é último elemento} End; {p^ é o último registro} End; 6) Qual o número de elementos da lista ? Function Nelem(L: Lista): integer; Begin If L = nil Then Nelem := 0 Else Begin p := L; Nelem := 1; While p^.lig <> nil do Begin Nelem := Nelem + 1; p := p^.lig; End; End; End; 7) Inserção do valor v depois do elemento apontado por k Procedure Insere_depois(Var L: Lista; v: T; k: Lista); Var j: Lista; Begin new(j); j^.info := v; Conceito de Lista [SCE182] Página 16 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 j^.lig := k^.lig; k^.lig := j; End; OBS: Funciona para inserção após último elemento. 8) Remoção do elemento apontado por j, que segue k Procedure Elimina_depois(Var L: Lista; k, j: Lista); Begin k^.lig := j^.lig; dispose(j); End; 9) Remoção do primeiro elemento Procedure Remove_Prim(Var L: Lista); Begin p := L; L:= L^.lig; dispose(p) End; OBS: funciona no caso de remocao em lista com um único elemento. 10) Eliminar um valor v de uma lista ordenada L Procedure Elimina(v: T; Var L: Lista); Var p, pa: Lista; acabou: boolean; Begin pa := nil; p := L; acabou := (p = nil); While (not acabou) do If p^.info < v Then Begin pa := p; p := p^.lig; acabou := (p = nil); End Else acabou := true; {While acaba aqui!} { p = nil ou p^.info = v ou p^.info > v } If (p = nil) Then "lista não contém v" Else If p^.info > v Then Conceito de Lista [SCE182] Página 17 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 "lista não contém v" Else Begin If pa = nil Then L := p^.lig Else pa^.lig := p^.lig; dispose(p); End; End; 11) Inserção do valor v antes do elemento apontado por p Uma alternativa para evitarmos percorrer a lista para encontrar o antecessor de p é inserirmos o valor dado, de fato, após o elemento apontado por p, após realizar uma cópia de conteúdo entre ponteiros.... Procedure Insere_antes(Var L: Lista; v: T; p: Lista); Var q: Lista; Begin new(q); q^ := p^; p^.info := v; p^.lig := q; End; 12) Criação de uma lista L com registros numerados de 1 .. n Procedure Cria_Lista_num(Var L: Lista); Var L, q: Lista; Begin L := nil; { começa com lista vazia } While n > 0 do Begin new(q); q^.lig := L; q^.info := n; L := q; n := n-1; End; End; 13) Remoção do registro sucessor de p e inserção do mesmo como cabeça em uma lista apontada por q Procedure Remove_Insere(Var q: Lista; p: Lista); Begin r := p^.next; {r aponta o sucessor de p^} p^.lig := r^.lig; {r^ sai da lista} r^.lig := q; {r^ passa a apontar q^ } q := r; {r^ passa a ser o primeiro elemento da lista End; apontada por q} 14) Impressão recursiva de uma lista Procedure Printlist(p: Lista); Begin If (p <> nil) Then Begin write(p^.info); Printlist(p^.lig); End; End; 1.3.4 Exemplo de Uso de Ponteiros Conceito de Lista [SCE182] Página 18 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Este programa cria uma lista de N elementos cujo conteúdo é o quadrado da sua posição e a impressão inclui o sinal negativo. Program Listas; Uses Crt; Type prec = ^fred; lista= prec; fred = record info: integer; lig: prec; End; Var L, p,q,r: lista; N,i: 0..10; Begin ClrScr; write('Dê o número de elementos da lista [1..10]:'); readln(N); L := nil; new(p); p^.info := 1; L := p; For i := 2 to N do Begin new(p^.lig); p := p^.lig; p^.info := i*i; End; p^.lig := nil; writeln('*** Impressão de sua lista ***'); p := L; While (p <> nil) do Begin write(' - ',p^.info); p := p^.lig; End; writeln; writeln('*** Fim ***'); readln; End. 1.3.5 Busca em Lista Dinâmica 1) Problema com nil ? Se estivermos buscando um elemento x em uma lista encadeada, podemos ter algo similar a: while (p <> nil) and (p^.info <> x) do p := p^.lig; Que problema temos no comando acima ? No caso de p = nil o comando pode ser inválido pois p^.info não existe! OBS.: Se o compilador não realiza o segundo teste no caso do primeiro falhar, não haveria problema. Em nível de algoritmo, é melhor resolver o problema evitando a possibilidade desse tipo de erro ocorrer, ao inv és de usar soluções que dependem do compilador. Conceito de Lista [SCE182] Página 19 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 O que fizemos nos nossos algoritmos foi algo similar a: acabou := false; While (p <> nil) and (not acabou) do If p^. info = x Then acabou := true Else p := p^.lig; 2) Uso de sentinela na busca em lista encadeada Um elemento sentinela pode ser utilizado na busca em lista linear encadeada: usamos para isso um registro no final da lista. Supondo que inicializamos uma lista apontada por L com a lista vazia, faremos: Procedure Create(var L: Lista); Var sentinela: Lista; Begin new(sentinela); L := sentinela; End; O algoritmo seguinte implementa uma busca por um elemento x na lista apontada por L não ordenada. Retorna p = nil se o elemento não for encontrado; caso contrário p aponta o registro que contém o elemento. Procedure busca(x: T; var L, p: Lista); Begin p := L; sentinela^.info := x; While p^.info <> x do p := p^.lig; If (p = sentinela) Then p := nil; {elemento não encontrado} End; 3) Busca com sentinela e inserção na cabeça da lista não ordenada Caso o elemento não seja encontrado e quisermos inseri-lo no início da lista, teremos o procedimento abaixo: Procedure busca_insere(x: T; Var L, p: Lista); var p: Lista; Begin p := L; sentinela^.info := x; While p^.info <> x do p := p^.lig; If (p = sentinela) Then Begin { inserção no começo da lista} p := L; {aponta primeiro} new(L); {novo registro é primeiro} L^.info := x; L^.lig := p; End; End; 4) Sentinela em busca com lista encadeada ordenada No algoritmo acima, ao realizar uma busca sequencial numa lista encadeada anulamos a vantagem de utilizar listas Conceito de Lista [SCE182] Página 20 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 encadeadas. Neste caso, esta implementação só pode ser considerada para listas com um número pequeno de elementos. No caso da lista estar ordenada, a busca termina assim que encontrarmos um elemento maior que aquele que buscamos. A ordenação é obtida ao inserirmos cada novo elemento na sua posição correta, ao invés de na cabeça da lista. Essa "ordenação" na realidade não tem custo algum, porque fazemos uso das características da lista encadeada. Isso não ocorre com estruturas estáticas como o array e os arquivos sequenciais. O procedimento abaixo elimina um registro que contém o elemento v de uma lista apontada por L. O bloco de busca pelo elemento realiza dois testes para cada elemento da lista. Esse esforço pode ser reduzido introduzindo o recurso de sentinela. Depois da busca, testes devem ser efetuados para verificar se: � o elemento foi encontrado ou não � se encontrado, se ele era o primeiro da lista ou não Procedure elimina(v: integer; Var Prim: Lista); Var p, pa: Lista; acabou: boolean; Begin pa := nil; p := L; acabou := (p = nil); While (not acabou) do If p^.info < v Then Begin pa := p; p := p^.lig; acabou := (p = nil); End Else acabou := true; { p = nil ou p^.info = v ou p^.info > v } If (p = nil) Then "lista não contém v" Else If p^.info > v Then "lista não contém v" Else Begin If pa = nil Then L := p^.lig Else pa^.lig := p^.lig; dispose(p); End; End; Dada a seguinte inicialização: new(root); new(sentinel); root^.lig := sentinel; Temos acima um lista com dois elementos: o elemento apontado por root vai ser sempre um dummy (elemento sem Conceito de Lista [SCE182] Página 21 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 conteúdo), e o sentinela sempre marca o final da lista. O algoritmo abaixo realiza a busca de um elemento e o insere na posição correta caso ele não seja encontrado. Procedure busca_insere(x: T; Var root: Lista); Var w1, w2, w3: Lista; Begin w2 := root; w1 := w2^.lig; sentinel^.info := x; While w1^.info < x do Begin w2 := w1; w1 := w1^.lig; End; If (w1^.info = x) and (w1 <> sentinel) Then {elemento já existe} Else Begin new(w3); {insere w3 entre w1 e w2} w3^.info := x; w3^.lig := w1; w2^.lig := w3; End; End; 1.3.6 Exercício Reescreva os algoritmos de inserção e eliminação de um elemento em uma lista duplamente encadeada de forma a incluir uma chamada à Função Busca_Dup_Ord(x). 1.4 Lista Duplamente Encadeada Características � Listas foram percorridas do início ao final. � Ponteiro "anterior" necessário para muitas operações. � Em alguns casos pode-se desejar percorrer uma lista nos dois sentidos indiferentemente. � Nestes casos, o gasto de memória imposto por um novo campo de ponteiro pode ser justificado pela economia em não reprocessar a lista toda. Type tpont = ^ trec; trec = record info:Tipoelem; esq, dir: tpont; End; Lista = tpont; Var pont: Lista; � Como consequência, podemos realizar as operações de inserção e eliminação à esquerda ou à direita de um campo no interior de uma lista sem a necessidade de ponteiros "anteriores". Conceito de Lista [SCE182] Página 22 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Implementação de lista duplamente encadeada com alocação dinâmica 1) Inserção à direita de pont Procedure ins_dir (Var pont: lista; x: Tipoelem); Var j: Lista; Begin new(j); j^.info:=x; j^.dir:=pont^.dir; j^.dir^.esq:=j; j^.esq:=pont; pont^.dir:=j; End; 2) Inserção à esquerda de pont Procedure ins_esq (Var ptlista: Lista; x: Tipoelem); Var j: Lista; Begin new(j); j^.info:=x; j^.dir:=pont; j^.esq:=pont^.esq; j^.esq^.dir:=j; pont^.esq:=j; End; 3) Eliminação à direita de pont Procedure elim_dir (Var pont: Lista); Var j: Lista; Begin j:=pont^.dir; pont^.dir:=j^.dir; j^.dir^.esq:=pont; dispose(j); End; 4) Eliminação do próprio pont Procedure elim (Var pont: Lista); Begin pont^.dir^.esq:=pont^.esq; pont^.esq^.dir:=pont^.dir; dispose(pont); End; 5) Busca em uma lista circular Conceito de Lista [SCE182] Página 23 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Funtion Busca_Dup_Ord(Var ptlista: Lista; x: Tipoelem):Lista; { Lista Duplamente Encadeada Ordenada com sentinela apontado por ptlista } Var pont, ultimo: Lista; Begin ultimo:=ptlista^.esq; If x<=ultimo^.info then Begin pont:=ptlista^.dir; While pont^.info < x do pont:=pont^.dir; Busca_Dup_Ord:=pont; End Else Busca_Dup_Ord:=ptlista; End; 1.4.1 Exercício Reescreva os algoritmos de inserção e eliminação de um elemento em uma lista duplamente encadeada de forma a incluir uma chamada à Função Busca_Dup_Ord(x). 1.5 Listas Encadeadas Circulares A implementação de listas circulares significaram uma melhoria na utilização do espaço liberado pelos elementos que são retirados da fila. O conceito de anel (circular)pode ser aplicado também às listas comuns, como por exemplo, listas encadeadas. Nestas listas, um procedimento comum que devemos fazer é o de busca. Veremos a seguir como realizar buscas em listas circulares encadeadas. O conceito de sentinela é também utilizado. O trecho do algoritmo abaixo é o de busca e inserção em lista encadeada ordenada que utiliza dois registros auxiliares: o sentinela e o "dummy". Procedure busc_enc (Var root: Lista; Var sentinela: Lista; x: T); {w2 - anterior; w1 - elemento se existir} Var w1, w2: Lista; Begin w2:= root; {dummy} w1:= w2^.lig; { primeiro elemento } sentinela^.chave:= x; While w1^.chave < x do Begin w2:=w1; w1:=w1^.lig; End; If (w1^.chave = x) and (w1 <> sentinela) then {elemento já existe} Else {inserir entre w1 e w2} End; Conceito de Lista [SCE182] Página 24 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Quando consideramos uma lista circular encadeada, podemos utilizar apenas um registro auxiliar, como indicado no algoritmo de busca abaixo: { Busca em uma lista circular encadeada ordenada com sentinela. O sentinela é o primeiro elemento da lista circular apontado por ptlista } Procedure busc_circ (Var ptlista: Lista; x: T); Var ant, pont: Lista; Begin ant:=ptlista; {sentinela} ptlista^.chave = x; pont:=ptlista^.lig; {primeiro elemento} While pont^.chave < x do Begin ant:=pont; pont:=pont^.lig; End; If pont^.chave = x and (pont<>ptlista) then {elemento já existe} Else {inserir entre ant e pont} End; 1.5.1 Exercícios 1) Como a lista seria inicializada nesse caso ? 2) Como seriam os algoritmos de inserção e eliminação ? 3) Qual alteração devemos fazer no caso das listas não serem ordenadas ? 1.6 Listas Generalizadas Uma lista generalizada é aquela que pode ter como elemento ou um átomo ou uma outra lista (sub-lista). Toda lista pode ser representada usando-se a estrutura de nó onde: � CABEÇA (CAR) é um atomo ou um ponteiro para uma outra lista � CAUDA (CDR) ligação para a cauda da lista ('próximo elemento') � TAG é 0 (CABEÇA é atomo) ou é 1 (CABEÇA é ponteiro) Conceito de Lista [SCE182] Página 25 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Exemplo: L1 = (a, (b, c) ) L2 = (a, b, c) L3 = ( (a, b), (c, d), e) 1.6.1 Implementação de Listas Generalizadas Utilizam o Recurso do Pascal (record variante) Type pont_no = ^no; no = record cauda: pont_no; case tag: boolean of true: (pont: pont_no); false: (atomo: tipo_elem); End; Lista = pont_no; 1.6.2 Variações de Listas Generalizadas Listas Compartilhadas Exemplo: L4 = (L3, L3, ( ) ) onde: � L3 é a lista do exemplo anterior � ( ) é lista vazia Conceito de Lista [SCE182] Página 26 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Listas Recursivas Exemplo: L5 = (a, L5) 1.6.3 Exercícios 1) Sobre Listas Generalizadas � Quais suas vantagens � Quais suas desvantagens � Dê exemplos de uso � Dê um exemplo de uma lista generalizada a qual contém, no total, pelo menos 25 átomos e 10 cabeças (em vários níveis). Escreva sua lista em notação de parênteses (LISP). � Reescreva a sua lista do item acima para incluir listas compartilhadas. Escreva sua lista em notação de parênteses (LISP). � Reescreva a sua lista do item acima para incluir listas recursivas. Escreva sua lista em notação de parênteses (LISP). 2) Implemente a função abaixo, a qual determina se duas listas são iguais, assumindo que listas são não recursivas e compartilhadas. Teste o programa incluindo para as listas: A = B = ( ( a, b), (c, d) ) A = (a, (b, c) ) e B = (a, b, (c)) Function Igual (S,T: Lista) : boolean Var resp:boolean; Begin resp:=false; If (S=nil ) and (T=nil) Then resp:=true; Else If (S<>nil) and (T<>nil) Then If ((S^.tag = T^.tag) Begin If (S^.tag=false) Then resp:=(S^.atomo, T^.atomo) Else resp:= igual(S^.pont, T^.pont); If resp Conceito de Lista [SCE182] Página 27 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Then resp:=igual(S^.cauda, T^.cauda); End; igual:=resp; End; 1.7 Matriz Esparsa Problema: Representação de matrizes contendo grande parte de seus elementos nulos. Por exemplo seja a matriz abaixo, a qual contém 5 linhas e 6 colunas. Apenas 5 de seus 30 elementos são não nulos. Precisamos buscar uma representação que evite o armazenamento de tantas posicões nulas. Veremos uma solução que utiliza, as listas cruzadas como estruturas de dados. Estrutura de Dados de Listas Cruzadas Numa matriz genérica, para cada elemento temos as informacões de: � Linha, � Coluna e � Valor Para não representarmos os valores nulos, fazemos listas de linhas e listas de colunas tais que o elemento a(ij) diferente de 0 pertence: � à lista dos elementos da linha i � à lista dos elementos da coluna j Então se a matriz tem nl linhas e nl colunas, temos nl listas de linhas e nc listas de colunas. Graficamente podemos ter algo como: Conceito de Lista [SCE182] Página 28 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Para o exemplo anterior, temos: 1.7.1 ED e Operações Definição da ED Type Preg = ^Reg; Lista = Preg; Reg = record linha: 1..nl; coluna: 1..nc; valor: TipoElemento; PL,PC: Preg; { pont p/ prox registro } end; var L: array[1..nl] of Lista; C: array[1..nc] of Lista; 1) Acesso ao primeiro elemento não nulo da linha i: � L[i] ^ Conceito de Lista [SCE182] Página 29 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 2) Acesso ao elemento i,j: � percorrer a lista L[i] até encontrar registro com campo de coluna = j ou � percorrer a lista C[j] até encontrar registro com campo de linha = i Operações com matrizes esparsas Além de armazenar matrizes esparsas, as aplicações normalmente exigem a realização de operações sobre essas matrizes, como por exemplo: � multiplicar uma dada linha ou coluna por uma constante � somar uma constante a todos os elementos de uma linha ou coluna � somar duas matrizes esparsas de igual dimensão Como consequência dessas operacões, alguns elementos podem deixar de ser nulos, enquanto que outros podem se tornar nulos. Por exemplo, ao se somar -4 a coluna 5 do exemplo temos: Esse exemplo ilustra que, quando um elemento a(i,j) deve ser eliminado, ele deve ser eliminado, de fato, de duas listas: L[i] e L[j]. Algoritmo: Somar k aos elementos da coluna j Procedure Soma (k: TipoElemento); Var p: Lista; begin p := C[j]; for i := 1 to nl do if p = nil then insere(i,j,k) else begin Conceito de Lista [SCE182] Página 30 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 if p^.linha <> i then { valor era nulo} inserir(i,j,k) {insere antes de p} else begin p^.valor := p^.valor + k; if p^.valor = 0 then begin p := p^.pc; eliminar(i,j); end else p := p^.pc; end; end; end; 1.7.2 Usabilidade Quando a representação de listas cruzadas é vantajosa em relação à representação convencional (bidimensional) ? Fator Espaço Suponhamos o caso de: � uma matriz esparsa que armazena inteiros � um ponteiro que ocupa o mesmo espaço que um inteiro Matriz Esparsa Espaço ocupado por uma matriz esparsa de nl linhas, nl colunas e n valores não-nulos: � 5 * n espaços para ponteiros para os registros (um para cada campo do registro: linha,coluna, valor, PL, PC) � nl espaços para ponteiros para o vetor L � nc espaços para ponteiros para o vetor C � espaço total de 5n + nl + nc Representação bidimensional � o espaço ocupado seria nl * nc Conclusão: Em termos de espaço ocupado, há vantagem em utilizar-se a representação de listas cruzadas quando: 5n + nl + nc < nl * nc ou seja, quando: n < [(nl - 1) * (nc - 1) -1] / 5 Como (nl - 1) * (nc - 1) é aproximadamente o tamanho da matriz, pode-se dizer, de uma maneira geral que, há ganho em termos de espaço, quando um número inferior a 1/5 dos elementos da matriz forem não nulos. Conceito de Lista [SCE182] Página 31 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Fator Tempo As operações sobre listas cruzadas podem ser mais lentas e complicadas que para o caso bidimensional. Portanto, para algumas aplicacões, deve ser feita uma reavaliação de tempo-espaço. 1.7.3 Exercícios 1) Sobre Matrizes Esparsas (ME) � Quais suas vantagens � Quais suas desvantagens � Dê exemplos de uso � Defina vetor esparso, de tanto sua declaração como sua funcionalidade � Quando usar uma ME 2) Faça algorítmos que: 1. leia e imprima um matriz esparsa 2. some os elementos da linha i a uma constante k 3. some duas matrizes esparsas 4. verifique se uma matriz esparsa é a identidade 5. multiplique um vetor esparso por uma ME 6. multiplique duas matrizes esparsas 7. calcule a inversa de uma matriz esparsa 2. Pilhas 2.1 Conceito de Pilhas Pilhas são listas onde a inserção de um novo item ou a remoção de um item já existente se dá em uma única extremidade, no topo. Pilha vazia Insere(A) Insere(B) Retira(B) Conceito de Lista [SCE182] Página 32 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Insre(C) Retira(C) Retira(A) Definição Dada uma pilha P=( a(1), a(2), ..., a(n) ), dizemos que a(1) é o elemento da base da pilha; a(n) é o elemento topo da pilha; e a(i+1) está acima de a(i). Pilhas são também conhecidas como listas LIFO (last in first out). Operações Associadas: 1. criar (P) - criar uma pilha P vazia 2. inserir (x, P) - insere x no topo de P (empilha): push(x,P) 3. vazia (P) - testa se P está vazia 4. topo (P) - acessa o elemento do topo da pilha (sem eliminar) 5. elimina (P) - elimina o elemento do topo de P (desempilha): pop(P) 2.2 Implementação de Pilhas Como lista Sequencial ou Encadeada ? No caso geral de listas ordenadas, a maior vantagem da alocação encadeada sobre a sequencial - se a memória não for problema - é a eliminação de deslocamentos na inserção ou eliminação dos elementos. No caso das pilhas, essas operações de deslocamento não ocorrem. Portanto, podemos dizer que a alocação sequencial é mais vantajosa na maioria das vezes. Um outro modo de se implementar pode ser feito utilizando pilhas múltiplas. 2.3 Exemplo do Uso de Pilhas Chamadas de procedimentos Suponha a seguinte situação: Conceito de Lista [SCE182] Página 33 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Quando o procedimento A1 é executado, ele efetua uma chamada a A2, que deve carregar consigo o endereço de retorno e1. Ao término de A2, o processamento deve retornar ao A1, no devido endereço. Situação idêntica ocorre em A2 e A3. Assim, quando um procedimento termina, é o seu endereço de retorno que deve ser consultado. Portanto, há uma lista implícita de endereços (e0, e1, e2, e3) que deve ser manipulada como uma pilha pelo sistema, onde e0 é o endereço de retorno de A1. No caso de processamento recursivo - por exemplo uma chamada a A2 dentro de A4 - o gerenciamento da lista como uma pilha resolve automaticamente a obtenção dos endereços de retorno na ordem apropriada (e0, e1, e2, e3, e4). 2.4 Alocação Sequencial de Pilhas Definição da Estrutura de Dados Type pilha = array [1..maxp] of TipoElem; indice = 0 .. maxp; Var P: pilha; topo: indice; Operações 1. criar (P) - criar uma pilha P vazia procedure criar (Var topo:indice); begin topo := 0; end; 2. inserir (x, P) - insere x no topo de P(empilha): push (x, P). procedure push (x:TipoElem; var P: pilha; Var topo: indice); begin Conceito de Lista [SCE182] Página 34 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 if topo = maxp then "PILHA CHEIA" else begin topo := topo + 1; P[topo] := x; end end; 3. vazia (P) - testa se P está vazia function vazia (topo: indice): boolean; begin vazia := (topo = 0); end; 4. topo (P) - acessa o elemento do topo da pilha (sem eliminar) procedure top (Var topo_p: TipoElem; P:pilha; topo:indice); begin if topo = 0 then "PILHA VAZIA" else topo_p := P[topo]; end; 5. elimina (P) - elimina o elemento do topo de P (desempilha): pop (P) procedure pop (var P:pilha; Var topo:indice); begin if topo = 0 then "PILHA VAZIA" else topo := topo-1; end; Devolve elemento eliminado procedure pop_up (var topo_p:TipoElem; var P:pilha; var topo:indice); begin if topo = 0 then "PILHA VAZIA" else begin topo_p := P[topo]; {no caso de acesso ao elemento} topo := topo-1; end; end; 2.5 Alocação Encadeada de Pilhas Definição da Estrutura de Dados Type tpont = ^reg_pilha; pilha = tpont; reg_pilha = record info: TipoElem; lig: tpont; Conceito de Lista [SCE182] Página 35 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 End; Var p: pilha; Operações 1. criar (P) - criar uma pilha P vazia procedure criar (var p: pilha); begin p := nil; end; 2. inserir (x, P) - insere x no topo de P (empilha): push(x,P) procedure push (x:TipoElem; var p: pilha); var pont: pilha; begin new(pont); pont^.info := x; pont^.lig := p; p := pont; end; 3. vazia (P) - testa se P está vazia function vazia (var p: pilha): boolean; begin vazia := (p = nil); end; 4. topo (P) - acessa o elemento do topo da pilha (sem eliminar) function top (var p: pilha): TipoElem; begin if (p <> nil) then top := p^.info end; 5. elimina (P) - elimina o elemento do topo de P (desempilha) : pop(P) procedure pop (var p: pilha); var aux:pilha; begin if (p <> nil) then begin Conceito de Lista [SCE182] Página 36 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 aux := p; p:= p^.lig; dispose (aux); end end. 2.6 Aplicação de Pilha: Notação Polonesa Uma representação para expressões aritméticas que seja conveniente do ponto de vista computacional é assunto de interesse, por exemplo, na área de compiladores. A notação tradicional é ambígua e, portanto, obriga o pré-estabelecimento de regras de prioridade. Isso torna a tarefa computacional menos simples. Outras notacões são apresentadas a seguir, considerendo-se apenas operações binárias (com dois operandos): Notação completamente Parentizada: acrescenta-se sempre um parênteses a cada par de operandos e seu operador. Exemplo: tradicional: A * B - C / D parentizada: ((A*B)-(C/D)) Notação Polonesa: os operadores aparecem imediatamente antes dos operandos. Esta notação especifica quais operadores, e em que ordem, devem ser calculados. Por esse motivo dispensa o uso de parênteses, sem ambiguidades. Exemplo: tradicional: A * B - C / D polonesa: - * A B / C D Notação Polonesa Reversa (ou posfix): é como a polonesa na qual os operandos aparecem após os operadores. Exemplo: tradicional: A * B - C / D polonesa reversa: A B * C D / - Avaliação de expressões aritméticas programa fonte - notação infix: x := A / B + D * E - Aobjetivo- notação posfix: x := A B / D E * + A - Um algoritmo para a avaliação de Expressões PosFix: empilha operandos até encontrar um operador retira o número de operandos; calcula e empilha o valor resultante até que chegue ao final da expressão Exemplo: A B / D E * + A - Conceito de Lista [SCE182] Página 37 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 function valor ( E: expressão): TipoValor; var x : TipoOperador; begin topo := 0; while not acabou(E) do begin x := proxsimb(E); if x é operando then push(x,pilha) else begin remove o número de operandos (dois) para o operador x da pilha; calcule o resultado da operação; empilhe resultado; end; valor := P[topo]; end; 2.7 Exercícios 1) Seja a função esvazie( ) tal que, recebendo uma pilha como entrada, esvazie a pilha descartando todos os seus elementos. Escreva a função esvazie( ) 2) Escreva um programa que verifique que expressões aritméticas estão com a parentização correta. Guarde o resultado numa pilha também. Seu programa deve checar expressões para ver se cada "abre parênteses" tem um "fecha parênteses" correspondente. 3) Escreva um algoritmo que converta uma expressão escrita na notação parentizada no seu equivalente na notação polonesa Conceito de Lista [SCE182] Página 38 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 reversa. 4) Uma palavra é uma palíndrome se a seqüência de letras que a forma é a mesma seja ela lida da esquerda para a direita ou vice-versa. Exemplos: arara, rairar, hanah. Escreva a função palíndrome que, dada uma palavra, retorne true caso a palavra seja uma palíndrome, e false caso contrário. 3 Fila 3.1 Conceito É uma lista linear em que a inserção é feita numa extremidade e a eliminação na outra. (FIFO: first in, first out). ( a1, a2 , ..., an) eliminações no inserções início no final Exemplo Escalonamento de "Jobs": fila de processos aguardando os recursos do sistema operacional. Operações associadas: 1.Criar (F) - criar uma fila F vazia 2.Inserir (x, F) - insere x no fim de F 3.Vazia (F) - testa se F está vazia 4.Primeiro (F) - acessa o elemento do início da fila 5.Elimina (F) - elimina o elemento do início da fila Implementação de filas como lista Sequencial ou Encadeada ? Dinâmica ou Estática ? Só tem sentido falarmos em fila sequencial ou encadeada dinâmica, uma vez que não existe movimentação de elementos. As filas encadeadas são usadas quando não há previsão do tamanho máximo da fila. 3.2 Implementação Sequencial de Fila Definição da Estrutura de Dados Conceito de Lista [SCE182] Página 39 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Type índice = 0..maxfila; fila = array[1..maxfila] of TipoElem; Var F: fila; Começo, {posição anterior ao primeiro elemento} Fim: índice; {posição do último elemento} Operações com Filas 1. Criar (F) - criar uma fila F vazia Procedure CriaFila (Var Começo, Fim: indice); Begin Começo := 0; Fim := 0; End; 2. Inserir (x, F) - insere x no fim de F Procedure Inserir (x: TipoElem; Var F: fila; Var Fim: indice); Begin If Fim < maxfila Then Begin Fim := Fim + 1; F[Fim] := x; End Else { OVERFLOW } End; 3. Vazia (F) - testa se F está vazia Function Vazia (Começo, Fim: indice): Boolean; Begin If Começo = Fim Then {FILA VAZIA} End; 4. Primeiro (F) - acessa o elemento do início da fila Function Primeiro (F: fila; Começo: indice): TipoElem; Begin x := F[Começo + 1]; End; 5. Elimina (F) - elimina o elemento do início da fila Procedure Eliminar (Var Começo: indice, Fim: indice); Begin If (Começo = Fim) Then {FILA VAZIA} Conceito de Lista [SCE182] Página 40 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Else Começo := Começo + 1; End; 3.3 Implementação Encadeada de Fila Definição da Estrutura de Dados Type tpont = ^reg_fila; fila = tpont; reg_fila = record info: TipoElem; lig: tpont; End; Var Começo, Fim: fila; Operações com Filas 1. Criar (F) - criar uma fila F vazia Procedure CriaFila (Var Começo, Fim: fila); Begin Começo := nil; Fim := nil; End; 2. Inserir (x, F) - insere x no fim de F {só se lista não vazia} Procedure Inserir (x: TipoElem; Var Fim: fila); Begin new(Fim^.lig); Fim := Fim^.lig; Fim^.info := x; Fim^.lig := nil; End; 3. Vazia (F) - testa se F está vazia Function Vazia (Começo, Fim: fila): boolean; Begin Vazia := ( Começo = nil) and (Fim = nil); End; Conceito de Lista [SCE182] Página 41 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 4. Primeiro (F) - acessa o elemento do início da fila Function Primeiro (Var Começo: fila): TipoElem; Begin Primeiro := Começo^.info; End; 5. Elimina (F) - elimina o elemento do início da fila Procedure Elimina (Var Começo, Fim: fila;); Var p: fila; Begin if (começo <> nil) then begin p := Começo; Começo := Começo^.lig; {válido se lista não vazia} If ( Começo = Fim) Then Begin Começo := nil; Fim := nil; End; dispose(p); end End; 3.4 Problema na Implementação com Fila O que acontece com a fila considerando a seguinte sequência de operações sobre um fila IEIEIEIEIE. (I - inserção e E - eliminação) A fila terá sempre 1 ou 0 elementos, no entanto num certo instante: Ou seja, apenas um elemento na fila, o qual ocupa a última posição do array! Na próxima inserção, teremos uma condição de overflow e a fila está vazia ! Alternativa: no algoritmo de eliminação após a atualização de Começo, verificar se a fila ficou vazia, i.e, Começo = Fim; se este for o caso, reinicializar Começo = Fim := 0; Conceito de Lista [SCE182] Página 42 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Portanto, ficaria: Procedure Eliminar (Var Começo, Fim: indice); Begin If ( Começo = Fim) Then {FILA VAZIA} Else Begin Começo := Começo + 1; If Começo = Fim Then Begin Começo := 0; Fim := 0; End; End; End; O que aconteceria se a sequência fosse IIEIEIEIEI... A lista estaria com no máximo dois elementos, mas ainda ocorreria overflow com a lista quase vazia. Alternativa:Forçar Fim a usar o espaço liberado por Começo (Fila Circular) 3.5 Fila Circular Fila Circular Implementada como Anel Conceito de Lista [SCE182] Página 43 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 1.Índices do array: 0 .. m-1 2.Ponteiros de controle: Começo e Fim 3.Inicialmente Começo = Fim = 0 4.Quando Fim = m-1, o próximo elemento será inserido na posição 0 (se essa posição estiver vazia!) 5.Condição de fila vazia Começo = Fim 6.Para inserir: Procedure Inserir (Var Fim: indice); Begin If Fim = m-1 Then Fim := 0 Else Fim := Fim + 1; {ou Fim := ( Fim + 1 ) mod m} End; 7.Para eliminar um elemento ProcedureEliminar (Var Começo: indice); Begin Começo := (Começo + 1) mod m; End; Problema: Nesta representação, como teremos fila cheia ??? Começo = Fim Para resolver esse problema, utilizaremos apenas m-1 posições do anel. Deste modo, existe sempre um elemento vazio e, portanto, Fim não coincide com Começo. O algoritmo para inserção em anel fica: Procedure Inserir (Var Fim: indice; Começo: indice; F: fila); Begin If (Fim + 1) mod m = Começo Then {FILA CHEIA} Else Begin Fim := (Fim + 1) mod m; {um registro fica vazio} F[Fim] := x; End; End; O algoritmo para eliminação em anel fica: Procedure Eliminar (Var Comeco: indice; Fim: indice); Begin If Começo = Fim Then {FILA VAZIA} Conceito de Lista [SCE182] Página 44 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Else Começo := (Começo + 1) mod m; End; 3.6 Exercício 1. Escreva a função esvazie( ) recebendo uma fila como entrada, esvazie a fila descartando todos os seus elementos. 4 Árvores 4.1 Conceito de Árvores Representação gráfica As três formas de representação gráfica são: Representação por parênteses aninhados ( A (B) ( C (D (G) (H)) (E) (F (I)) ) ) Diagrama de inclusão Representação hierárquica Conceito de Lista [SCE182] Página 45 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Motivação diversas aplicações necessitam de estruturas mais complexas que as listas estudadas até agora inúmeros problemas podem ser modelados através de árvores árvores admitem tratamento computacional eficiente quando comparadas às estruturas mais genéricas como os grafos (os quais, por sua vez são mais flexíveis e complexos) Definição Uma árvore enraizada T, ou simplesmente uma árvore, é um conjunto finito de elementos denominados nós ou vértices tais que: T = 0 é a árvore dita vazia ou existe um nó especial r, chamado raiz de T; os restantes constituem um único conjunto vazio ou são divididos em m (deve ser maior ou igual a 1) conjuntos distintos não vazios que são as subárvores de r, cada subárvore a qual é, por sua vez, uma árvore. Notação: Tv, se v é um nó de T então a notação Tv indica a subárvore de T com raiz em v. Subárvore Seja a árvore acima T = {A, B, ...} A árvore T possui duas subárvores: Tb e Tc onde Tb = { B } e Tc = {C, D, ...} A subárvore Tc possui 3 subárvores: Td, Tf e Te onde Td = {D, G, H} Tf = {F, I} Te = {E} As subárvores Tb, Te, Tg, Th, Ti possuem apenas o nó raiz e nenhuma subárvore. Exemplo: representação da expressão aritmética: (a + (b * (c / d) - e)) Conceito de Lista [SCE182] Página 46 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Nós filhos, pais, tios, irmãos e avô Seja v o nó raiz da subárvore Tv de T. Os nós raízes w1, w2, ... wj das subárvores de Tv são chamados filhos de v. v é chamado pai de w1, w2, ... wj. Os nós w1, w2, ...wj são irmãos. Se z é filho de w1 então w2 é tio de z e v é avô de z. Grau de saída, descendente e ancestral O número de filhos de um nó é chamado grau de saída desse nó. Se x pertence à subárvore Tv, então, x é descendente de v e v é ancestral, ou antecessor, de x. Se neste caso x é diferente de v então x é descendente próprio de v e v é ancestral próprio de x. Nó folha e nó interior Um nó que não possui descendentes próprios é chamado de nó folha, ou seja, um nó folha é aquele com grau de saída nulo. Um nó que não é folha (isto é, possui grau de saída diferente de zero) é chamado nó interior ou nó interno. Grau de uma árvore O grau de uma árvore é o máximo entre os graus de seus nós. Floresta Uma floresta é um conjunto de zero ou mais árvores. Conceito de Lista [SCE182] Página 47 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Caminho, comprimento do caminho Uma sequência de nós distintos v1, v2, ..., vk, tal que existe sempre entre nós consecutivos ( isto é, entre v1 e v2, entre v2 e v3, ... , v(k-1) e vk) a relação "é filho de"ou "é pai de" é denominada um caminho na árvore. Diz-se que v1 alcança vk e que vk é alcançado por v1. Um caminho de vk vértices é obtido pela sequência de k-1 pares. O valor k-1 é o comprimento do caminho. Nível (ou profundidade) e altura de um nó O nível ou profundidade, de um nó é o número de nós do caminho da raiz até o nó. O nível da raiz, é portanto, 1. A altura de um nó v é o número de nós no maior caminho de v até um de seus descendentes. As folhas têm altura 1. Nível da raiz (profundidade) e altura de uma árvore O nível da raiz é 1 (acima). A altura de uma árvore T é igual ao máximo nivel de seus nós. Representa-se a altura de T por h(T) e a altura da subárvore de raiz v por h(v). Árvore Ordenada Uma árvore ordenada é aquela na qual os filhos de cada nó estão ordenados. Assume-se ordenação da esquerda para a direita. Desse modo a árvore do primeiro exemplo é ordenada, mas, a árvore abaixo não. Árvore Cheia Árvore com número máximo de nós Uma árvore de grau d tem número máximo de nós se cada nó, com exceção das folhas tem grau d. Conceito de Lista [SCE182] Página 48 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Árvore cheia de grau 2: implementação sequencial. Array com 7 posições: Armazenamento por nível: posição do nó posição dos filhos do nó 1 2,3 2 4,5 3 6,7 i (2i,2i+1) Dentre as árvores, as binárias são, sem dúvida, as mais comumente utilizadas nas aplicações em computação. 4.2 Exercícios 1. Para a árvore abaixo: Conceito de Lista [SCE182] Página 49 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 � Quantas subárvores ela contém? � Quais os nós folhas? � Qual o grau de cada nó? � Qual o grau da árvore? � Liste os ancestrais dos nós B, G e I. � Identifique todas as relações de parentesco entre os nós � Justifique: podemos dizer que uma árvore é um dígrafo conexo onde existe um único nó sem predecessor e todos os demais têm um único predecessor. 2. Dada uma árvore cujo grau da raiz é d, como transformá-la em uma floresta com d árvores? 3. Dada uma floresta com d árvores como transformá-la em uma árvore cujo nó raiz tem grau d ? 4. Para a árvore do exercício 1: � Liste os vértices de quem C é ancestral próprio � Liste os vértices de quem D é descendente próprio � Dê o nível e altura do vértice F. � Dê o nível e a altura do vértice A. � Qual a altura da árvore ? 5. Explique porque a árvore do primeiro exercício e a árvore abaixo são isomórfas. 6. Para uma árvore de grau d com número máximo de nós, diga: � Qual o grau dos nós internos da árvore? � Qual o grau dos nós folhas? � Quantos nós tem a árvore se o grau d e a altura é h? � Qual a altura da árvore se o grau é d e o número de nós é n? 7. Para uma árvore cheia de grau d: � Se um nó estiver armazenado na posição i de um array, em que posições estarão seus d filhos? Considerando esta alocação sequencial, quais as consequências de inserções e eliminações na árvore? Conceito de Lista [SCE182] Página 50 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 4.3 Árvore Binária Uma Árvore Binária T é um conjunto finito de elementos denominados nós ou vértices, tal que: � T = 0 e a árvore é dita vazia ou � existe um nó especial r, chamado raiz de T, os restantes podem ser divididos em dois subconjuntos disjuntos, Tre e Trd, que são as subárvores esquerda e direitade r, respectivamente e as quais, por sua vez, também são árvores binárias. 4.3.1 Definição da Estrutura de Dados Type PNo = ^no; no = record info: Telem; esq, dir: PNo; End; Type tree: PNo; 4.3.2 Operações associadas ao TAD árvore binária padrão: � Definir uma árvore vazia � Criar um nó raiz � Verificar se árvore vazia ou não � Criar um filho à direita de um dado nó � Criar um filho à esquerda de um dado nó � Verificar qual o nível de um dado nó � Retornar o pai de um dado nó 1. Define uma árvore vazia Conceito de Lista [SCE182] Página 51 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Define uma árvore vazia e deve ser utilizado antes de qualquer outro. Procedure Define(var t: tree); Begin t:=nil; End; 2. Cria um nó raiz Retorna o nó raiz da árvore em t Procedure Cria_Raiz(var t: tree; item: Telem); Var no: tree; Begin new(nó); no^.esq:=nil; no^.dir:=nil; no^.info:=item; t:=no; End; 3. Verifica se árvore vazia ou não Retorna true se árvore vazia, false c.c. Function Vazia (t:tree):boolean; Begin vazia:=(t=nil); End; 4. Criar um filho à direita de um dado nó � Busca elemento contendo Item_Pai � Se Item_Pai ainda não possui filho à direita, então cria um filho à sua direita com o conteúdo de item. Procedure Adicionar_Dir(t: tree; item_pai, item: Telem) Var pai, no: tree; Begin pai:=Localiza(t,item_pai); If(pai<>nil) Then If (pai^.dir<>nil) Then erro("item já possui subárvore direita") Else Begin New(nó); no^.esq:=nil; no^.dir:=nil; no^.info:=item; pai^.dir:=nó; End; End; 5. Criar um filho à esquerda de um dado nó Procedure Adicionar_Esq(t: tree; item_pai, item: Telem) Var pai, no: tree; Begin pai:=Localiza(t,item_pai); If(pai<>nil) Then If (pai^.esq<>nil) Then erro("item já possui subárvore direita") Conceito de Lista [SCE182] Página 52 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Else Begin New(nó); no^.esq:=nil; no^.dir:=nil; no^.info:=item; pai^.esq:=nó; End; End; 6. Verificar qual o nível de um dado nó � Retorna o nível onde está um nó com item pesquisado � Se item não existe retorna zero Function Nível (t: tree; item: Telem): integer; Var p: integer; Procedure Travessia (ptr: tree; niv: integer); Begin If ptr<>nil Then Begin niv:=niv+1; If Igual(ptr^.info, item) Then p:=niv; Else Begin Travessia(ptr^.esq, niv); If p=0 Then Travessia(ptr^.dir, niv); End; End; End; Begin {nivel} p:=0; Travessia(t,p); nivel:=p; End; 7. Retornar o pai de um dado nó � Dado um item, procura se item existe na árvore � Caso positivo retorna o conteúdo do pai do nó Function Pai(t: tree; item:Telem):Telem; Var achou: boolean; it : Telem; Procedure Travessia(t: tree; var it: Telem); Begin If not Vazia(t) Then Begin If t^.esq<>nil Then If Igual(item, t^.esq^.info) Then Begin achou:=true; it:=t^.info; End; If not achou Then Conceito de Lista [SCE182] Página 53 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 If t^.dir<>nil Then If Igual(item, t^.dir^.info) Then Begin achou:=true; it:=t^.info; End; If not achou Then Travessia(t^.esq, it); If not achou Then Travessia(t^.dir, it); End; End; {Travessia} Begin {pai} If t<>nil Then Begin achou:=false; If Igual(item, t^.info) Then pai:=t^.info; {pai do raiz é ele próprio} Else Begin Travessia(t,it); pai:=it; End; End; End; 4.3.3 Percurso em Árvores Binárias Percorrer uma árvore visitando cada nó uma única vez gera uma sequência linear de nós, e então passa a ter sentido falar em sucessor e predecessor de um nó segundo um determinado percurso. Há três maneiras recursivas de se percorrer árvores binárias. Travessia em Pré-Ordem 1. se árvore vazia; fim 2. visitar o nó raiz 3. percorrer em pré-ordem a subárvore esquerda 4. percorrer em pré-ordem a subárvore direita Conceito de Lista [SCE182] Página 54 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Pré-ordem: � ABDCEGFHI � visita o nó quando passar a sua esquerda � notação pré-fix � Exercício: aplique para a expressão aritmética ilustrada anteriormente Travessia em In-Ordem 1. se árvore vazia, fim 2. percorrer em in-ordem a subárvore esquerda 3. visitar o nó raiz 4. percorrer em in-ordem a subárvore direita In-ordem: � DBAEGCHFI � visita o nó quando passar embaixo do nó � notação in-fix � Exercício: aplique para a expressão aritmética ilustrada anteriormente Travessia em Pós-Ordem 1. se árvore vazia, fim 2. percorrer em Pós-Ordem a subárvore esquerda 3. percorrer em Pós-Ordem a subárvore direita 4. visitar o nó raiz Pós-Ordem Conceito de Lista [SCE182] Página 55 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 � DBGEHIFCA � visita o nó quando passar a sua direita � notação pós-fix � Exercício: aplique para a expressão aritmética ilustrada anteriormente Algoritmos de Percurso - versão recursiva Procedure PreOrdem(N:Pno); Begin If N <> nil Then Begin Processa(N); {p. ex. imprime} PreOrdem(N^.esq); PreOrdem(N^.dir); End; End; Procedure InOrdem(N:Pno); Begin If N <> nil Then Begin InOrdem(N^.esq); Processa(N); {p. ex: imprime} InOrdem(N^.dir); End; End; Procedure PosOrdem(N:Pno) Begin If N <> nil Then Begin PosOrdem(N^.esq); PosOrdem(N^.dir); Processa(N); {p.ex.: imprime} End; End; Operações Facilitadas pelos Algoritmos de Percurso: Pesquisa se um elemento existe Conceito de Lista [SCE182] Página 56 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 � retorna true se elemento está na árvore � árvore binária comum (não é ABBusca) � sem sentinela Function Esta_Presente(t: tree; item: Telem): boolean; Begin Esta_Presente:= busca(t, item) <> nil; End; � Busca declarada apenas na implementation: Esta_Presente é disponível na interface � Retorna nil se elemento não encontrado � Usa Pós-Ordem para percorrer a árvore - visita equivale à verificar se elemento é encontrado Function Busca(t:tree; item: Telem): tree; Var achou: boolean; ptr: tree; Procedure Pós_Ordem(t:tree); Begin If (t<>nil) and (not achou) then Begin Pós_Ordem(t^.esq); Pós_Ordem(t^.dir); If Igual(item, t^.info) Then Begin achou:=true; ptr:=t; End; End; End; {Pós_Ordem} Begin {busca} achou:=false; ptr:=nil; Pós_Ordem(t); busca:=ptr; End; {busca} Outra versão para busca � Utiliza implicitamente percurso em Pré-Ordem Function Busca1(t: tree; item: Telem): tree; Var p: tree; Function local(t: tree; item: Telem):tree; Begin p:=nil; If t<> nil then If t^.info=item then p:=t; Else Begin local:=local(t^.esq, item); If (p=nil) Then local:=local(t^.dir, item); End; local:=p; End; {local} Begin {busca1} busca1:=local(t, item); End; Conceito de Lista [SCE182] Página 57 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Tornar uma árvore vazia � Desaloca todo o espaço de memória da árvore e retorna a árvore ao estado equivalente ao define, isto é, nula. � Utiliza o algoritmo de Pós-Ordem para percurso Procedure Tornar_Vazia(var t: tree); Begin If not Vazia(t) Then Begin Tornar_Vazia(t^.esq); Tornar_Vazia(t^.dir); Dispose(t); End; t:=nil; End; Treinar o rastreamento dos Algoritmos de Travessia em Árvore Binária. 4.3.4 Exercícios - Percursoem Árvore Binária 1) Escrever o algoritmo de visita em Pré-Ordem utilizando alocação dinâmica mas sem utilizar procedimentos recursivos. Utilizar pilha para saber o endereço da subárvore que resta à direita. processar raiz A guardar A para poder acessar C depois passa à B e processa essa subárvore idem para D retorna B (topo da pilha) para acessar D que é a subárvore esquerda 2) Escrever uma função recursiva que calcule a altura de uma árvore binária dada. A altura de uma árvore é igual ao máximo nível de seus nós. 4.3.5 Árvore Binária de Busca Uma Árvore Binária de Busca T (ABB) ou Árvore Binária de Pesquisa é tal que T = 0 e a árvore é dita vazia ou seu nó raiz contém uma chave e: 1. Todas as chaves da subárvore esquerda são menores que a chave da raiz. 2. Todas as chaves da subárvore direita são maiores que a chave raiz. 3. As subárvores direita e esquerda são também Árvores Binárias de Busca. Tabelas de Palavras reservadas de um compilador Pascal Conceito de Lista [SCE182] Página 58 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Em algoritmo de busca a medida de eficiência é dada pelo número de comparações necessárias para se localizar uma chave, ou descobrir que ela não existe. Numa lista linear com n chaves, temos que, no pior caso, faremos n comparações. O número de comparações cresce linearmente em função do número de chaves. Busca binária Pesquisa realizada se informação está armazenada de forma ordenada e em sequência (em um array). Qual a eficiência do Algoritmo de busca binária ? � Para n = 8 posições, o pior caso é quando precisando chegar na posição 1 ou 8. � A cada divisão, desprezamos toda a porção maior ou menor que a chave. Com busca binária obtemos a melhor eficiência possível em array, mas, ficamos limitados à representação sequencial (deslocamentos, previsão de memória, etc). Podemos utilizar a Árvore Binária de Busca e obteremos o mesmo desempenho anterior - desde que a altura seja mínima. É a característica de altura mínima que garante que estamos tomando a chave do meio da porção Conceito de Lista [SCE182] Página 59 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 pesquisada !! Com Árvore Binária de Busca Perfeitamente Balanceadas temos altura mínima. O número de comparações será igual a : Para garantir a performance ótima temos que garantir que a árvore seja balanceada durante a construção e que o balanceamento seja mantido em inserções e eliminações ! Definição da Estrutura de Dados e Algoritmos: Type Pno=^No; Tree = Pno; No=record chave: Tchave; esq, dir: Pno; End; Var raiz: Tree; Algoritmo de Busca Function Busca (x: Tchave; raiz: Tree;):Tree; Var achou: boolean; p: Tree; Begin p:=raiz; achou:=false; While (not achou) and (p<>nil) do If p^.chave = x Then achou:=true Else If p^.chave > x Then p:=p^.esq Else p:=p^.dir; Busca:=p; End; Algoritmo de Busca Recursivo Function Busca (x: Tchave; raiz: Tree;):Tree; Begin If raiz=nil Then Busca = nil Else If raiz^.chave = x Then busca:=raiz Else If raiz^.chave < x Then Busca:=Busca(x, raiz^.dir) Else Busca:=Busca(x,raiz^.esq); End; Algoritmo de Busca: versão com sentinela - para eliminar teste duplo durante a busca. Conceito de Lista [SCE182] Página 60 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Function Busca (x: Tchave; raiz: Tree):Tree; Var sentinela, p: Tree; Begin p:=raiz; sentinela^.chave:=x; While p^.chave <> x do If p^.chave x Then p:=p^.dir Else p:=p^.esq; Busca:=p; End; Como fica a configuração VAZIA da ABB com sentinela?? 4.3.6 Árvore Balanceada e Perfeitamente Balanceada Árvore Binária Balanceada Uma árvore binária é dita balanceada se, para cada nó, as alturas de suas subárvores diferem de, no máximo 1. Árvore Binária Perfeitamente Balanceada Uma árvore binária é dita perfeitamente balanceada se, para cada nó, o número de nós de suas subárvores diferem de no máximo, 1. Conceito de Lista [SCE182] Página 61 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 Altura de Árvore Perfeitamente Balanceada Árvore Perfeitamente Balanceada é a árvore com menor altura para o seu número de nós. Procedimento recursivo para geração balanceada de nós: 1. Use um nó para a raiz. 2. Gere a subárvore esquerda com nl = n div 2 nós, usando este mesmo procedimento. 3. Gere a subárvore direita com nr = n - nl - 1 nós, usando este mesmo procedimento. Program Buildtree(input, output); Type ref = ^ node; node = record key:integer; left, right: ref; End; Var n: integer; root: ref; Function tree(n: integer): ref; Var newnode: ref; x, nl, nr: integer; Begin If n=0 Then tree:=nil; Else Begin Conceito de Lista [SCE182] Página 62 de 62 http://www.unicruz.tche.br/prof/maspohn/apostila/edados.html 15/8/2001 nl:=n div 2; nr:=n - nl -1; read(x); { conteúdo do nó } new (newnode); with newnode^ do Begin key:=x; left:=tree(nl); right:=tree(nr); End; tree:=newnode; End; End; {tree} Procedure Printtree(t: ref; h: integer); Var i: integer; Begin {imprime tree t com indentação h} If t<>nil Then with t^ do Begin printtree(left, h+1); for i:=1 to h do write(' '); writeln(key); printtree(right, h+1); End; End; {printtree); Begin {buildtree: programa principal} read(n); { primeiro inteiro especIfica número de nós } root:=tree(n); printtree(root, 0); End; { buildtree}
Compartilhar