Você está na página 1de 176
Estrutura de Dados com Algoritmos e C Escrever um livro nao é uma tarefa facil, nunca foi. Toma tempo, exige pesquisa e dedicagao. Como as editoras nao desejam mais publicar este titulo, nada mais natural que tentar comercializa-lo na forma de arquivo (semelhante a um ebook). Mas, esta atividade também toma tempo e exige dedicac¢ao. Pensando assim, resolvi liberar este livro para consulta publica, se vocé acha que este livro te ajudou e quiser colaborar comigo, passe numa lotérica, e deposite o valor que achar que deve. Terminou de pagar a conta de luz, telefone ou agua e sobrou troco, vocé pode depositar na minha conta. Eu acredito que nés dois podemos sair ganhando, vocé porque teve acesso a um bom material que te ajudou e eu como incentivo a continuar escrevendo livros. Caso de certo, talvez os préximos livros nem sejam publicados por uma editora e estejam liberados diretamente para sua consulta. Qualquer valor depositado sera direcionado para a conta poupanca do meu filho, para quando ele estiver na maioridade ter recursos para comegar um negocio préprio, financiar seus estudos, etc. Dados para depésito: Marcos Aurelio Pchek Laureano. Banco: 104 - Caixa Econémica Federal Agéncia: 1628 Operagao: 001 Conta: 6012-2 Curitiba, 13 de margo de 2012. Estrutura de Dados com Algoritmos e C Marcos Laureano Copyright® 2008 por Brasport Livros e Multimidia Ltda Todos 0s direitos reservados. Nenhuma parte deste livro padera ser reproduzida, sob qualquer meio, especialmente em fotocopia (xerox), sem a permissda, por escrito, da Editora Editor: Sergio Martins de Oliveira Diretora Editorial: Rosa Maria Oliveira de Queiroz Assistente de Produgao: Marina dos Anjos Martins de Oliveira Revisdo de Texto: Maria Helena A. M. Oliveira Editoragao Eletrénica: Abreu's System LTDA. Capa: Use Design Técnica e muita atencéo foram empregadasna producdo deste livro. Porém,erros de digitaco e/ou impressdo podem ocorrer. Qualquer divida, inclusive de conceito, soliitamos enviar mensagem para brasport@brasport.com.br, para que nossa equipe, juntamente como autor, possa esclarecer. ABrasport e 0(s) autor(es) ndo assumem qualquer responsabilidade por eventuais danos ou perdas a pessoas ou bens, originados do uso deste livto. Dados Internacionais de Catalogagao na Publicagao (CIP) (Camara Brasileira do Livro, SP, Brasil) BRASPORT Livros e Multi Ltda. Rua Pardal Mallet, 23 — Tijuca 20270-280 Rio de Janeiro-RU Tels. Fax: (21) 2568.1415/2568.1507/2569.0212/2565.8257 emails: brasport@brasport.com.br vendas@brasport.com.br editorial@brasport.com.br site: www.brasport.com.br Filial Av, Paulista, 807 — conj. 915 01311-100 - Sao Paulo-SP Tel. Fax (11): 3287.1752 email: — filalsp@brasport.com.br Aos meus genitores Luiz Otdvio (in memoriam) e Natalia. Agradecimentos Agradeco & Brasport por mais esta oportunidade de publicar um livro sobre um tema em que varios autores jé trabalharam (é claro que este livro tem um diferencial em relagao aos demais). Aos meus colegas professores ¢ alunos que ajudaram a evolugio deste ma- terial nos iiltimos anos. E para uma linda flor, que, além de alegrar os meus dias, corrigiu os assas- sinatos a lingua portuguesa. Sobre o autor Marcos Laureano é tecnélogo em Processamento de Dados pela ESEEI, Pés-graduado em Administragio pela FAE Business School e Mestre em Infor- miatica Aplicada pela Pontificia Universidade Catélica do Parana. Doutorando em Informatica Aplicada pela Pontificia Universidade Catolica do Parana. ‘Trabalha com programagéo em C no ambiente Unix (AIX/HP-UX) desde 1997 ¢ Linux desde 2000, sendo especialista em seguranca de sistemas operacio- nais. E professor de graduagio e pés-graduagio, tendo lecionado em varias ins- tituigdes nos iiltimos anos. E autor de varios guias de utilizagio/configuragio de aplicativos para os ambientes Unix e Linux. Possui artigos e livros publicados sobre programacio, sistemas operacionais e seguranga de sistemas. Dentre eles, Programando em C para Linux, Unix e Windows, também publicado pela Brasport. Atualmente leciona disciplinas relacionadas & segurana, programacio, re- des de computadores e sistemas operacionais em cursos de graduagio ¢ pés-gra~ duagio do Centro Universitario Franciscano (UNIFAE) e atua como consultor na drea de projetos de desenvolvimento e seguranca de sistemas. O autor pode ser contactado pelo e-mail marcos@laureano.eti.br ou atra- vés de sua pagina wwwlaureano.eti.br, onde est disponivel vasto material so- bre programagio, seguranga de sistemas ¢ sistemas operacionais. Sobre o Livro O crescimento dos cursos tecnolégicos especificos e com curta duragao (2 a 3 anos) gerou uma demanda de livros que tratam diretamente o assunto de maneira clara ¢ eficiente. Este livro é indicado aos cursos tecnolégicos, para estudantes e profissionais que precisem dominar os conceitos ¢ os algoritmos de forma répida ¢ precisa. O material foi preparado com a experiéncia do autor em lecionar a discipli: na, somada a sua experiéncia profissional. Outros professores e coordenadores de cursos foram consultados; com isto, este material tem os assuntos pertinentes a drea e pode ser adotado tranqiiilamente em cursos de 40 ou 80 horas de Estru- tura de Dados ou Programagio de Computadores. Os algoritmos podem ser aplicados ¢ convertidos para qualquer linguagem de programacio, e os programas em C sio simples e objetivos, facilitando o entendimento dos estudantes e profissionais que nao dominam totalmente esta linguagem. Sumario 1. Estrutura de Dados . 1.1 Dados Homogéneos... 1.1.1 Vetor 1.1.2 Matriz. 1.1.3 Ponteiros 1.2 Dados Heterogéneos 1.3 Exercicios.. 2. Uso de Meméria 2.1 Alocagéo de meméria Estatica x Dina 2.2 Alocagéo dinamica de meméria 2.3 Fungées para alocacio de meméria 2.3.1 Fungio malloc. 2.3.3 Fungio realloc. 2.3.4 Fungio free 2.4 Utilizando as fungées para alocacio de meméria 2.5 Alocagao de meméria e estruturas em C., 2.6 Ponteiros para ponteiros — mistério ou nao 2.7 Mais alocagao de vetores e matrizes como ponteiros 1 Controle de agenda com ponteiros de estruturas € vetores. 3.1 Representagio das Operagdes com Pseudo-cédigo 3.2 Pilhas em C. 3.3 Exercicios... XIV Estrutura de Dados com Algoritmos e C 4. Fila... 4.1 Representagao de Filas com Pseudo-cédigos. 4.2 Filas em C. 4.3 Exercicios.. . Fungio para célculo de Fatoria Numero triangular 5.3 Nameros de Fibonacci 5.4 Algoritmo de Euclide: ‘Torres de Hanoi 5.6 Curiosidades com Recursividade. 5.7 Cuidados com Recursividade .. 5.8 Vantagens.. 5.9 Exercicios.. 6. Lista... 6.1 Vetores ou alocagao dinamic: 6.2 Listas em C 6.3 Exercicios... 7. Pesquisa 7.1 Pesquisa Seqiiencial 7.2 Pesquisa Bindria... 7.3 Exercicios.. 8. Ordenacio. 8.1 BubbleSort... 8.2 Ordenacio por Sele: 8.3 Ordenacio por Inserga 8.4 QuickSort .. 8.5 MergeSort. 8.6 Exercicios... 9. Arvores Binarias .... 9.1 Analogia entre drvore 9.2 Arvore binari 9.2.1 Relagoes . Sumério XV 128 129 130 _ 9.2.2 Arvore Bindria Completa .... 9.3 Arvores de Busca Biniria 9.4 Operagdes em Arvores Bindrias 9.4.1 Insergio.. 9.4.2 Pesquisa 9.4.3 Exclusio . 9.4.4 Maior elemento 9.4.5 Menor elemento . 9.4.6 Percorrendo uma arvore 9.5 Representagdes de drvores em C. 9.6 Implementagio em C 9.7 Exercicio ... Referéncias Bibliograficas ... Indice Remissivo . Lista de Programas 1.1: Declaragio de vetor em C.. 1.2: Exemplo de uso de vetores.. 1.3: Exemplo de uso de matrizes ‘xemplo de uso de matrizes com varias dimens6es. 2xemplo de uso de ponteiros 1.6: Ponteiros como referéncia em fungée: 1.7: Aritmética com ponteiros 1.8: Vetor como ponteiro ... 1.9: Exemplo de estrutura .. 1.10: Exemplo de uso de estruturas com vetores. 2.1: Declaragao de vetor como ponteiro 2.2: Declaragao de matriz, como ponteiro.. 2.3: Exemplo de uso do malloc ¢ realloc..... 2.4: Exemplo de uso do calloc... 2.5: Exemplo de uso de estruturas com ponteiros.. 2.6: Ponteiro para ponteiro 2.7; Exemplo de uso de alocagao de matrizes ... 2.8: Exemplo de uso de alocagéo de matrizes dentro de funcées XVII Estrutura de Dados com Algoritmos e C 2.9: 3.1: 3.2: 4.1: 4.2: 43: 44: 5.1: 5.2: 5.3: 5.6: 5.7: 5.8: 5.9: 5.10: Torre de Hanoi recursivo .. 5.11: Natori - Imprimindo as fases da lu: 5.12: Dhyanh - Saitou, aku, soku ¢ zan. 6.1: 6.2: 6.3: TA: 7.2: 8.1: 8.2: 8.3: ; Descobrindo 0 niimero triangular (recursivo).. Exemplo completo de uso de vetor (ponteiros) de estruturas .. Exemplo de manipulagio de pilha.... Exemplo de manipulagio de pilha com estrutura... Exemplo de manipulagio de fila em C Reajuste da fila... Declaragao de estrutura circular ... Manipulagao de fila circular em C... Fatorial (versio iterativa).... Fatorial (versio recursiva) Descobrindo o mimero triangular (iterativo) .. Calculo do n-ésimo termo de Fibonacci (versio iterativa)... Calculo do n-ésimo termo de Fibonacci (versio recursiva). Calculo do MDC iterativo..... Calculo do MDC recursivo... Calculo do MDC recursivo... Exemplo de manipulagio de lista simples em C. Exemplo de manipulagio de lista encadeada em C Fungdes para manipulagio de listas Fungio para pesquisa seqiiencial... Fungio para pesquisa binaria Fungo BubbleSort... Fungio BubbleSort melhorado..... Fungio Select... Lista de Programas XIX 8.4: Funcio Insert... 109 8.5: Ordenagio QuickSort . A113 8.6: Ordenagio MergeSor 117 8.7: Ordenagio MergeSort. 119 9.1; Representagio com vetor de filhos... 1138 9.2; Representagéo dinamic: 1138 9,3: Representago dinamica de uma drvore binéria.. 1139 9.4: Implementacao das operacdes 1139 Lista de Tabelas 2.1: Operacées com ponteiros 2.2: Operagées com ponteiros de ponteiros .. : Calculo de fatorial de 6 7.1; Entradas e freqiiéncias para calculo de comparacées média 8.1: BubbleSort - primeira varredura 8.2: BubbleSort - segunda varredura, 8.3: Selegio - 0 que ocorre em cada passo.... 8.4: Insergfo - 0 que ocorre em cada paso... 9.1: Comparagées para busca de um elemento... Lista de Algoritmos 1.1: Calculo da posigao de indices de um vetor na meméria 1.2: Calculo da posigao de indices de uma matriz na memoria 3.1: Verificagio se a pilha esta vazia (fungio EMPTY(S)) 2 3.2: Colocar um item na pilha (funcio PUSH(S,x)) B : Retirada de um item da pilha (fungio POP(S)) B 3.4: Pega o item do topo da pilha mas nao desempilha (fungao STACKPOP(S)) . 3.5: Tamanho da pilha (fungao SIZE(S)) 4.1: Inclusio de dados na fila (ENQUEUE(Q,x)).. 4.2; Retirada de dados na fila (DEQUEUE(Q)).. 4.3: Verificagio se a fila esta vazia (fungio EMPTY(Q)).. 4.4: Tamanho da fila (fungao SIZE(Q)) . réximo elemento da fila (fungo FRONT(Q). 5.1: Algoritmo de Euclides. 5.2: Passar n pecas de uma torre (A) para outra (C) 6.1: Insergao numa lista duplamente encadeada... 6.2: Remogio numa lista duplamente encadeada..... 7.1: Pesquisa seqiiencial.... 7.2; Pesquisa seqiiencial com ajuste de freqiiéncia .... XXII Estrutura de Dados com Algoritmos e C 7.3: Pesquisa binaria . 8.1: Ordenagio Bubble 8.2: Ordenagio por Selegio. Ordenagio por Insergio . : QuickSort . Particiona - Divisio do vetor ... 8 8.6: MergeSort.... 8.7; Intercala. 8.8: MergeSort.... 9.1: Inserir elemento na drvore - iterativo ... 9.2: Inserir elemento na drvore - recursivo.. 9.3: Pesquisar elemento na drvore - iterativo.... 9.4: Pesquisar elemento na drvore - recursivo .. 9. Exclusio na arvore 9.6: Sucessor . 9.7; Maior elemento da drvore . 9.8: Menor elemento da arvore 9.9: Operagao Percorre - Pré-ordem 9.10: Operagao Percorre - Pés-ordem.. 9.11: Operagio Percorre - Em-ordem.. Lista de Figuras Ll: 1.2 LA: 1.5: 1.6: 17: 1.8: 1.9: 2.1: 2.2: 2.3: 3.1: 3.2: 4.1: 4.2: 5.1: 5.2: 5.3 : Representagao de um vetor na mem6dria: : Matriz 2x2 - Calculo de posigao na meméria : Descobrindo 0 quinto elemento triangular de forma recursiva. Exemplo de Vetor.. Calculo de posigao na meméria .... Exemplo de Matriz.... Uma matriz de duas dimensdes vista como dois vetores... Chamada de fungao com ponteiros.. Vetor como Ponteiro em C Registro de funcionério.... Matriz como vetor de ponteiros. Exemplo de ponteiro na meméria.... Exemplo de ponteiro para ponteiro na meméria.... Exemplo de Pilha.... Operagées em uma pilha..... Operacées numa fila . Operagées numa fila circular. Niimeros triangulares... Descobrindo 0 quinto elemento triangular .. XXIV = Estrutura de Dados com Algoritmos ¢ C 5.4: O que ocorre a cada chamada ... 5.6: Torre de Hanoi 5.7: Movimentos conforme algoritmo ... 6.1: Exemplo de lista simplesmente encadeada ... 6.2: Exemplo de lista duplamente encadeada.. 6.3: Inclusao de novo elemento... 6.4: Exclusio de elemento 6.5: Problemiatica do crescimento do vetor ... 6.6: Crescimento de uma lista sem utilizar vetor.. 7.1: Processo de pesquisa seqiiencial.... 7.2: Busca binaria... 8.1: Exemplo de Ordenagio por Seleco com niimeros inteiros 8.2: Exemplo de Ordenagio por Insergio com ntimeros inteiro 8. : Seqiiéncia de ordenagio por inser¢io . 8.4: Algoritmo da ordenagio por inser¢a 8.5: Ordenagio QuickSort . 8.6: Ordenagio MergeSort. 8.7: Ordenagio MergeSort. 9.1; Analogia entre arvores. 9.2; Representagio de uma arvore. 9.3: Arvore Bindria completa de nivel 3. 9.4: Arvore de busca bindria - duas organizagoes diferentes ... 9.5: Exclusao de folha.... 9.6: Exclusao de um né com um filho.. 9.7; Exclusio de um n6 com dois filhos .. 9.8: Representagio com vetores... 9.9: Representagio dinamica ... Estrutura de Dados “Nao existe vitéria sem sacrificio!” Filme Transformers Um computador é uma maquina que manipula informagées. O estudo da ciéncia da computacio inclui 0 exame da organizacio, manipulagao ¢ utilizagio destas informagdes num computador. Conseqiientemente, é muito importante entender os conceitos de organizacao e manipulagao de informagées. A automatizagio de tarefas é um aspecto marcante da sociedade moderna, ena ciéncia da computacio houve um proceso de desenvolvimento simultineo ¢ interativo de maquinas (hardware) ¢ dos elementos que gerenciam a execugio automitica (software) de uma tarefa. Nesta grande evoluco do mundo computacional, um fator de relevante im- portincia é a forma de armazenar as informagées, jé que, informatica é a ciéncia da informagao. Entao de nada adiantaria 0 grande desenvolvimento do hardware e do software se a forma de armazenamento e tratamento da informagao nao acompanhasse esse desenvolvimento. Por isso a importancia das estruturas de da- dos, que nada mais sao do que formas otimizadas de armazenamento e tratamento das informagées eletronicamente. As estruturas de dados, na maioria dos casos, baseiam-se nos tipos de ar- mazenamento vistos dia a dia, ou seja, nada mais sio do que a transformagao de uma forma de armazenamento ja conhecida e utilizada no mundo real adaptada para o mundo computacional. Por isso, cada tipo de estrutura de dados possui vantagens e desvantagens ¢ cada uma delas tem sua drea de atuacio (massa de dados) otimizada. 2 Estrutura de Dados com Algoritmos ¢ C Os dados manipulados por um algoritmo podem possuir natureza distinta, isto é, podem ser ntimeros, letras, frases etc. Dependendo da natureza de um dado, algumas operagées podem ou nao fazer sentido quando aplicadas a eles. Por exemplo, nao faz sentido falar em somar duas letras - algumas linguagens de programacio permitem que ocorra a soma dos valores ASCII correspondentes de cada letra. Para poder distinguir dados de naturezas distintas ¢ saber quais operagdes podem ser realizadas com eles, os algoritmos lidam com 0 conceito de tipo de dados. O tipo de um dado define o conjunto de valores que uma varidvel pode assumir, bem como 0 conjunto de todas as operacées que podem atuar sobre qualquer valor daquela variével. Por exemplo, uma varidvel do tipo inteiro pode assumir o conjunto de todos os ntimeros ¢ de todas as operagdes que podem ser aplicadas a estes ntimeros. Os tipos de dados manipulados por um algoritmo podem ser classificados em dois grupos: atémicos ¢ complexos ou compostos. Os tipos atémicos sio aqueles cujos elementos do conjunto de valores sao indivisiveis, por exemplo: 0 tipo inteiro, real, caractere e légico. Por outro lado, os tipos complexos sio aqueles cujos elementos do conjunto de valores podem ser decompostos em par- tes mais simples. Se um tipo de dado pode ser decomposto, entio o tipo de dado é dito estruturado, e a organizacao de cada componente ¢ as relagdes entre eles constituem a disciplina de Estrutura de Dados. 1.1 Dados Homogéneos Uma estrutura de dados, que utiliza somente um tipo de dado, em sua defi- nigdo é conhecida como dados bomogéneos, Varidveis compostas homogéneas cor- respondem a posigdes de meméria, identificadas por um mesmo nome, individu- alizado por indices e cujo contetido é composto do mesmo tipo. Sendo os vetores (também conhecidos como estruturas de dados unidimensionais) ¢ as matrizes (estruturas de dados bidimensionais) os representantes dos dados homogéneos. 1.1.1 Vetor O vetor € uma estrutura de dados linear que necessita de somente um indice para que seus elementos sejam enderecados. E. utilizado para armazenar uma lista de valores do mesmo tipo, ou seja, 0 tipo vetor permite armazenar mais de um valor em uma mesma varidvel. Um dado vetor é definido como tendo um Estrutura de Dados 3 numero fixo de células idénticas (seu contetido é dividido em posigGes). Cada célula armazena um e somente um dos valores de dados do vetor. Cada uma das células de um vetor possui seu préprio enderego, ou indice, através do qual pode ser referenciada. Nessa estrutura todos os elementos sao do mesmo tipo, € cada um pode receber um valor diferente [3, 21, 4]. Algumas caracteristicas do tipo vetor({10]): * Alocagao estatica (deve-se conhecer as dimensdes da estrutura no momento da declaracao) * Estrutura homogénea * Alocagio seqiiencial (bytes contiguos) * Insergao/Exclusio — Realocagio dos elementos — Posig&o de meméria nao liberada Notas de um aluno Vetor nora) [as 7.4/8.1 |+6 45 1 2 3 4 5 +——— indices Figura 1.1: Exemplo de Vetor A partir do endereco do primeiro elemento é possivel determinar a loca- lizagio dos demais elementos do vetor. Isso é possivel porque os elementos do vetor estao dispostos na memoria um ao lado do outro e cada elemento tem o seu tamanho fixo (algoritmo 1.1 [10]). A partir do algoritmo 1.1 é possivel deduzir a formula genérica para célculo de posico na meméria de um elemento qualquer. Sendo n o elemento, a formu- la se dé por Pos, = endereco Inicial + ((n- 1) * tamanbo do tipo do elemento). 4 Estrutura de Dados com Algoritmos ¢ C Algoritmo 1. ‘Alculo da posigao de indices de um vetor na meméria 1 begin 2 | Enderego Elemento 1 — enderego Inicial | Endereco Elemento 2 — endereco Inicial + tamanho Elemento 4 | Endereco Elemento 3 — enderego Inicial + (2 * tamanho Elemento) 5 6 Endereco Elemento 4 — endereco Inicial + (3 * tamanho Elemento) end A figura 1.1 mostra um vetor de notas de alunos, a referéncia NOTA[4] indica o valor 4.6 que se encontra na coluna indicada pelo indice 4. A definigao de um vetor em C se da pela sintaxe: tipo_do_dado nome_do_vetor[ tamanho_do_vetor ] programa 1.1 contém um exemplo de declaragio de um vetor na lingua- gem C. A figura 1.2 apresenta a representagio deste vetor na meméria do compu- tador (lembrando que um vetor é guardado na meméria de forma seqiiencial). Programa 1.1: Declaragio de vetor em C 6 [0] if2] cf] if] ef2] “7 | [T° _ 21) 22/24) al|b|]cl}d Enderegos de meméria f 41 2 3 4 5 6 7 Figura 1.2: Representagio de um vetor na meméria Estrutura de Dados 5 Programa 1.2: Exemplo de uso de vetores 7° programa_vetor_OL.c*/ #include #define TAMANHO 5 int main (void) ( . int ilndice; 1o | int iValora; int iSoma; int aVetor [TAMANHO]; float Media; 15] for (ilndice = 0; iIndice < { printi("Entre com o valor $d:",ilndice + 1); scanf(" 8d", &iValorA); aVetorilndice] = iValorA; ‘“AMANHO; indice++) indice < TAMANHO; ilndice++) iSoma += aVetor[ilndice}; } fMedia = (float) iSoma/TAMANHO; printf("Media : 8£\n", fMedia); return 0; 30 } Lembrete: Caso seja colocada num programa a instrugio a[2]++ esté sen do dito que a posicao 2 do vetor a sera incrementada. 1.1.2 Matriz Uma matriz é um arranjo bidimensional ou multidimensional de alocagio estatica e seqiiencial. A matriz é uma estrutura de dados que necessita de um indice para referenciar a linha e outro para referenciar a coluna para que seus elementos sejam enderecados. Da mesma forma que um vetor, uma matriz é de- finida com um tamanho fixo, todos os elementos sio do mesmo tipo, cada célula contém somente um valor ¢ os tamanhos dos valores so os mesmos (em C, um char ocupa | dytee um int 4 bytes) [3, 21, 4]. 6 Estrutura de Dados com Algoritmos ¢ C Os elementos ocupam posigdes contiguas na meméria. A alocagio dos ele- mentos da matriz na meméria pode ser feita colocando os elementos linha-por- linha ou coluna-por-coluna. Para uma matriz de 2x2— (figura 1.3) 0 algoritmo para calcular as pos da meméria é listado em 1.2 [10]. Matriz M if o 4 0 1 Figura 1.3: Matriz 2x2 - Célculo de posigao na meméria No algoritmo 1.2, sendo Ca quantidade de colunas por linhas, i o néimero da linha ej a posigao do elemento dentro linha, é possivel definir a formula ge- nérica para acesso na meméria, onde Pos, = endereco inicial + ((i-1) * C * tamanbo do tipo do elemento) + ((j-1) * tamanko do tipo do elemento). A figura 1.4 demonstra a aplicagao da formula. Amatriz LETRAS (figura 1.5) é composta de 18 elementos (3 linhas e 6 colunas), a referéncia a MATRIZ[3] [3] (onde o primeiro 3 indica a linha e 0 segundo 3 indica a coluna) retorna o elemento ’N’; no caso de MATRIZ[2] [5] (segunda linha e terceira coluna) ird retornar o elemento ’E’. Como é uma ma- triz de strings (linguagem C), a chamada a MATRIZ [3] ird reproduzir o valor “DONALD”. Algoritmo 1.2: Cilculo da posigao de indices de uma matriz na meméria 1 begin 2 | M00 — endereco Inicial 3 MO1 -— enderego Inicial + 1 * tamanho Elemento 4 | M10 — enderoco Inicial + i * C * tamanho Elemento 5 M11 — enderego Inicial + i * C * tamanho Elemento + j * tamanho Elemento 6 end Estrutura de Dados 7 an biz Cis Representagao na memoria dat ez fig Elie tetet yelp - Gs ee isa SIEGE SEE ae hem Posigdo de hs: = 11 + ((3-1) *3* 1) + ((2-1)"1) = 18 C=3 e Tamanho = 1 (caracter) 2011 3012 40:3 Representacao na memoria 502; 602 70:3 20| 30 | 40 | 80 [60 | 70|s0|90|100] Enderogos de 8051 9032 10035 WTS TRE ee we | ‘moma Posigéo de 803 = 1 + ((3-1) *3* 4) + ((1-1)*4) = 25 C =3¢e Tamanho = 4 (inteiro) Figura 1.4: Cilculo de posigio na meméria Uma matriz consiste de dois ou mais vetores definidos por um conjunto de elementos. Cada dimensio de uma matriz é um vetor (figura 1.6). O primeiro conjunto (dimensio) € considerado o primeiro vetor, o segundo conjunto 0 se- gundo vetor e assim sucessivamente [4]. A definigao de uma matriz em C se da pela sintaxe: tipo_do_dado nome_da_matriz[ quantidade_linhas ] [ quantidade_colunas ] O programa 1.3 apresenta um exemplo de declaragio e utilizagao de matri- zes bidimensionais na linguagem C. Matri iz Letras 1.2 3 4 5 6-+——— Colunas 1 M/A;|R/]C|}O|S 2i|Nials|slelR 3 |/D}o|N]/AlLio Jt Figura 1.5: Exemplo de Matriz. 8 Estrutura de Dados com Algoritmos ¢ C letras [3] [3] ! | 0 0 0 1 0 2 1 0 1 1 1 2 2 0 2 1 2 2 Figura 1.6: Uma matriz de duas dimensdes vista como dois vetores A linguagem C permite ainda trabalhar com matrizes de varias dimensdes (matrizes n-dimensionais), embora 0 seu uso fique mais restrito em aplicagdes cientificas face 4 sua pouca praticidade de uso. A definigao de uma matriz de varias dimensdes em C se da pela sintaxe: tipo_do_dado nome_da_matriz[{tamanho_dimensdo_1] [tamanho_ dimensdo_2] [tamanho_dimensao_3] [tamanho_dimensao_n] 4 19 29 Estrutura de Dados 9 Programa 1.3: Exemplo de uso de matrizes 7° programa_matriz_O1.c 7 #include #define DIMENSAO 2 int main (void) int iLinha, iColuna; int iDeterminante; int iValorA; int aMatriz [DIMENSAO}[DIMENSAO /* Uma regra que se pode sempre levar em consideragao: para cada dimensio de uma matris, sempre baveré um laco (normalmente um for). Se bouver duas dimensoes, entao baverié dois lagos. */ for (iLinha=0; iLinha < DIMENSAQ; iLinha++) { for ((Coluna=0; iColuna < DIMENSAO; iColuna++) { printf ("Entre item $d &d:",iLinha +1, iColuna+ 1); scanf "8d", &iValorA); matriz {iLinha][iColuna] = iValorA; } } iDeterminante = aMatriz{0]{0] * aMatriz [1] [1] - aMatriz{0][1] * aMatriz.[1](0}; printf ("Determinante : $d\n", iDeterminante); return 0; programa 1.4 é um exemplo de defini dimensionais na linguagem C. 10 € utilizagdo de matri es multi- 10 Estrutura de Dados com Algoritmos e C Programa I xemplo de uso de matrizes com varias dimensdes 7° programa_matriz 02. */ #include 4| #define DIM_12 #define DIM_2 5 #define DIM_3 3 #define DIM_44 9 | int main (void) ( int ij,k,); int aMatriz [DIM_1][DIM_2][DIM_3][DIM_4}; 14 | 2 Gédigo para zerar uma matriz de quatro dimensoes */ for (i=0; i < DIM_1I; i++) { for (j=0; j < DIM_2; j+#) { 19 for (k-0; k < DIM_3; k++) { for (1-0; 1 < DIM_4; ++) { aMatriz [i]{j][k|[l] = isj+kel; 24 ) } } J 29] / Uma regra que se pode sempre levar em consideragio: para cada dlimensio de wma ma Se bouver quatro dimensoesentao haverd quatro lag */ For (i=0; i < DIM_1; is+) { sempre haverd um lago (normalmente um for). for (j=0; j < DIM_2; j++) { for (k-0; k < DIM_3; k++) { for (1-0; 1 < DIM_4; ++) 39 { print("\nValor para matriz em [$d] [8d] [8d] [8d] = 8a", i,jk, aMatriz{il (i) 011); } } “4 } J return 0; Estrutura de Dados 11 1.1.3 Ponteiros A linguagem C implementa o conceito de ponteiro. O ponteiro é um tipo de dado como int, char ou float. A diferenca do ponteiro em relagio aos outros tipos de dados é que uma varidvel que seja ponteiro guardaré um endereco de meméria [5, 7]. Por meio deste enderego pode-se acessar a informacio, dizendo que a vari- dvel ponteiro aponta para uma posicao de meméria. O maior problema em relagio ao ponteiro é entender quando se est trabalhando com o seu valor, ou seja, 0 enderego, e quando se est trabalhando com a informagio apontada por ele. Operador & e * O primeiro operador de ponteiro é &. Ele é um operador unério que devol- ve o endereco na meméria de seu operando. Por exemplo: m = count; poe © enderego na meméria da varidvel count em m, Esse enderego é a posicio interna da variével na meméria do computador e nao tem nenhuma relacdo com o valor de count. O operador & tem como significado o endereco de. O segundo operador é*, que é 0 complemento de _&. O* é um operador unario que devolve 0 valor da variavel localizada no endereco que o segue. Por exemplo, se m con- tém 0 enderego da variével count: q = *m; coloca o valor de count em q. O operador * tem como significado no endereco de. Lembrete: Cuide-se para nao confundir 0 operador de ponteiros (*) como multiplicagio na utilizagéo de ponteiros ¢ vice-versa. A declaragao de uma variavel ponteiro é dada pela colocagio de um aste- risco (*) na frente de uma varidvel de qualquer tipo. Na linguagem C, é possivel definir ponteiros para os tipos basicos ou estruturas. A definigao de um ponteiro nio reserva espaco de memoria para o seu valor e sim para o seu contetido. Antes de utilizar um ponteiro, o mesmo deve ser inicializado, ou seja, deve ser colocado um endereco de meméria vélido para ser acessado posteriormente. Um ponteiro pode ser utilizado de duas maneiras distintas. Uma maneira é trabalhar com 0 enderego armazenado no ponteiro ¢ outro modo é trabalhar com a érea de memoria apontada pelo ponteiro. Quando se quiser trabalhar com 0 en- dereco armazenado no ponteiro, utiliza-se 0 seu nome sem o asterisco na frente. Sendo assim qualquer operacao realizada ser feita no endereco do ponteiro. Como, na maioria dos casos, se deseja trabalhar com a meméria apontada pelo ponteiro, alterando ou acessando este valor, deve-se colocar um asterisco 12 Estrutura de Dados com Algoritmos e C antes do nome do ponteiro. Sendo assim, qualquer operagio realizada seré feita no endereco de meméria apontado pelo ponteiro. O programa 1.5 demostra a utilizacao de ponteiros para acesso i meméria. Programa 1.5: Exemplo de uso de ponteiros 1] /* programa_matris_02.c */ #include int main (void) 6| _ int piValor; /* ponteiro para inteiro */ int iVariavel = 27121975 piValor = &iVariavel; /* pegando 0 enderego de memsria da variével */ n printf ("Endereco: $d\n", piValor); printf("Valor : &d\n", *piValor); *piValor = 18011982; 16| printf ("Valor alterado: d\n", iVariavel); printf("Endereco : %d\n", piValor); return 0; Passando variaveis para fungdes por referéncia O ponteiro é utilizado para passar varidveis por referéncia, ou seja, varidveis que podem ter seu contetido alterado por fungdes e mantém este valor apés o término da funcio. Na declaragéo de uma funcio, deve-se utilizar 0 asterisco antes do nome do parametro, indicando que est sendo mudado o valor naquele endereco passado como parametro. No programa 1.6 é visto um exemplo de uma variével sendo alterada por uma fungao (figura 1.7). Estrutura de Dados 13 No main() Em soma() iValorA @ >| piValorA iValorB @ »| piValorB Figura 1.7: Chamada de fungio com ponteiros Programa 1.6: Ponteiros como referencia em fungdes /* programa_ponteiro_02.¢°/ #include void soma (int, int, int *); int main (void) f int iValorA; int iValorB; int iResultado; printf("Entre com os valores:"); 4 scanf("8d_ $d", &iValorA, &iValorB); printf("Endereco de iResultado = 8d\n", &iResultado); 19 soma (ValorA, iValorB, &iResultado);/* estd sendo passado o endereco de memria da varidvel, qualquer alteragio estard sendo realizada na meméria */ printf("Soma : $d\n", iResultado); 24 return 0; void soma (int piValorA, int piValorB, int * piResultado) 2] print("Endereco de piResultado = $d\n", piResultado); /*0-valor esté sendo colocado diretamente na meméria */ *piResultado = piValorA + piValorB; return; a4) 14 Estrutura de Dados com Algoritmos e C Aritmética com ponteiros Com uma variével do tipo ponteiro, é possivel realizar operagdes de soma e subtragio. Serd somada ou diminujida no ponteiro a quantidade de enderegos de meméria relativos ao tipo do ponteiro. Por exemplo, um ponteiro para int ocupa 4 bytes, uma operagio de soma neste ponteiro ird acrescentar 4 posigdes (unidades) na meméria. O programa 1.7 é visto como os enderecos de meméria sio manipulados através de aritmética de ponteiros, e a diferenca entre o tipo char - que ocupa | byte de memoria - e 0 tipo int - que ocupa 4 bytes. Programa 1.7: Aritmética com ponteiros 7* programa_ponteiro_03.c°/ #include int main (void) { int “piValor; int iValor; char *pcValor; 10] char cValor; piValor = &iValor, peValor = &cValor; 15] printf("Endereco de piValor = $d\n", piValor); printf("Endereco de peValor = 8d\n", pcValor); piValor++; /*somando uma unidade (4 bytes) na meméria */ peValors+; /* somando wma unidade (1 byte) na memdria */ printf("Endereco de piValor printf ("Endereco de peValor 4d\n", piValor); 4d\n", pcValor); return 0; Vetores e matrizes como ponteiros em C Vetores nada mais sio do que ponteiros com alocagio estatica de meméria, logo, todo vetor na linguagem C é um ponteiro, tal que 0 acesso aos indices do ve- tor podem ser realizados através de aritmética de ponteiros. Observe a figura 1.8: Estrutura de Dados 15 char t(5]; /* t(0] t€1] t(2] t{3) t(4] +7 strepy(t,"abede"); alolelaje “ety “2) (43) m(4) Figura 1.8: Vetor como Ponteiro em C Existem 2 formas para se obter 0 enderego (ponteiro) do inicio do vetor ou matriz: + st[0]; st Sendo a segunda opgio (t) a melhor, por ser mais simples. O endereco (pon- teiro) para o primeiro elemento do vetor ou matriz é dado pelo nome do vetor sem colchetes. O programa 1.8 mostra a utilizagio de um vetor com aritmética de ponteiros. Programa 1.8: Vetor como ponteiro 7° programa_vetor_02.c*/ #include 4 #include int main(void) ( char t(5]; strepy(t,”abede”); print("\n$1d &", 1, "Ds printf("\n$1d ec", tl, *(t+1)); 24 | printf("\n81d ec", 042, *(t42)); 16 Estrutura de Dados com Algoritmos e C printf("\n$1ld 8c", t43, *(t43)); printf("\n$1ld 8c", te4, *(t+4)); return 0; 1.2 Dados Heterogéneos Uma estrutura de dados é chamada de heterogénea quando envolve a utili- zacio de mais de um tipo basico de dado (inteiro ou caractere, por exemplo) para representar uma estrutura de dados. Normalmente, este tipo de dado é chamado de registro. Um registro é uma estrutura de dados que agrupa dados de tipos distintos ou, mais raramente, do mesmo tipo. Um registro de dados é composto por certo ntimero de campos de dados, que sao itens de dados individuais. Registros sio conjuntos de dados logicamente relacionados, mas de tipos diferentes (numéri- cos, légicos, caractere etc) (3, 21, 4]. O conceito de registro visa facilitar 0 agrupamento de varidveis que nao sao do mesmo tipo, mas que guardam estreita relagao légica. Registros corres- pondem a conjuntos de posigées de meméria conhecidos por um mesmo nome ¢ individualizados por identificadores associados a cada conjunto de posicées. O registro é um caso mais geral de varidvel composta na qual os elementos do conjunto nao precisam ser, necessariamente, homogéneos ou do mesmo tipo. O registro é constituido por componentes. Cada tipo de dado armazenado em um registro é chamado de campo. A figura 1.9 é um exemplo de registro de um funciondrio: composto de nome, departamento, data de nascimento e salirio. Na variével composta homogénea, a individualizacao de um elemento é fei- ta através de seus indices, ja no registro cada componente é individualizado pela explicitagao de seu identificador. A linguagem C utiliza as estruturas para representar um registro. Coma es trutura definida pode-se fazer atribuicao de varidveis do mesmo tipo de maneira simplificada. Veja 0 programa exemplo 1.9 (representagio da figura 1.9): Estrutura de Dados 17 Funcionario Marcos Laureano 27 | 12 | 1975 Informatica R$ 3.000,00 Figura 1.9: Registro de funcionério Programa 1.9: Exemplo de estrutura struct Funcionario at char nome [40]; struct { int dia; int mes; int ano; ) dataNasc; char departamento[10]; float salario; Para se fazer 0 acesso de um tinico campo deve-se utilizar o nome da estrutu- ra seguido de um ponto ¢ do nome do campo desejado da estrutura. A linguagem C também permite que seja criado um vetor de estruturas (programa 1.10). Programa 1.10: Exemplo de uso de estruturas com vetores /* programa_estrutura_O1.c*/ #include struct DADO ( char sNome(40); int ildade; 8) ii 18 Estrutura de Dados com Algoritmos e C int main(void) { struct DADO sDados{5]; 1B /* A estrutura é dividida em duas partes por wm ponto (). Tem-se 0 nome da estrutura & esquerda ¢ 0 nome do campo & direita, Neste exemplo, 18 como estd sendo manipulado um vetor de estruturas, também tem indice para cada linba do vetor. / for(iIndice=0;iIndice<5;iIndice++) { printf("\nEntre com o Nome ->" ); B scanf(" 8s", &sDados[ilndice].sNome }; printi("Entre com a Idade ->" ); scanf(" $d", &sDadosfilndice}.ildade ); ) 28| — for(iIndice-0;iIndice (>) =) Figura 2.1: Matriz como vetor de ponteiros Uso de Meméria 21 + void * malloc(int qty_bytes_alloc); * void * calloc(int qty, int size); + void * realloc(void * pointer, int new_size); * free( void * pointer); A fungio malloc permite que seja feita a alocagio de uma nova drea de meméria para uma estrutura. A fungio calloc tem a mesma funcionalidade de malloc, exceto que devem ser fornecidos 6 tamanho da drea e a quantidade de elementos. A funcio realloc permite que uma érea previamente alocada seja au- mentada ou diminufda e a fungao free libera uma drea alocada previamente com a fungio malloc, calloc ou realloc. 2.3.1 Fungao malloc Fa funcio malloc que realiza a alocagio de memoria. Deve-se informar para a funcio a quantidade de bytes para alocagio. A funcio iré retornar, se exis- tir meméria suficiente, um enderego que deve ser colocado em uma varidvel do tipo ponteiro. Como a fungio retorna um ponteiro para o tipo void, deve-se utilizar typecast, transformando este enderego para 0 tipo de ponteiro desejado. 2.3.2 Fungao calloc Em vez de se alocar uma quantidade de bytes através da funcio malloc, pode-se usar a funcao calloc e especificar a quantidade de bloco de um determi- nado tamanho, Funcionalmente a alocacio iré ocorrer de maneira idéntica. A tinica diferenga entre o malloc ¢ o calloc é que a ultima fungao, além de alocar o espago, também inicializa o mesmo com zeros. 2.3.3 Fungo realloc As vezes é necessario expandir uma rea alocada. Para isto deve-se usar a fungio realloc. Deve-se passar para cla 0 ponteiro retornado pelo malloc ¢ a indicagao do novo tamanho. A realocagio de meméria pode resultar na troca de blocos na meméria. 22. Estrutura de Dados com Algoritmos e C 2.3.4 Fungo free Quando nao se deseja mais uma drea alocada, deve-se liberd-la através da funcao free. Deve ser passado para a fungio o endereco, que se deseja liberar, que foi devolvido quando a alocacio da meméria ocorreu. 2.4 Utilizando as fung6es para alocagdo de memoria Um vetor nada mais é do que um ponteiro com alocagio estitica de memé- ria. Adeclaragao int aVetor[10]; é equivalente: Programa 2.1: Declaragio de vetor como ponteiro 1[ int *aVetor, aVetor = (int *) malloc(10 * sizeoffint *)); Quando se quer criar estruturas com dois indices (matrizes), trés indices (tijolos) etc. A declaragao da matriz seria algo como int aMatriz[2][3];, utilizando ponteiros para declarar uma matriz deve-se usar: Programa 2.2: Declaracio de matriz como ponteiro int **aMatriz; aMatriz = (int **) malloc( 2 * sizeof(int*)); * 2 linhas */ 3 | for( iO; 1<2; i++) f aMattiz{i] = (int *) malloc( 3 * sizeof(int)); /* 3 colunas */ A notagao aMatriz[i] [4] pode ser utilizada com matrizes alocadas di- namicamente equivalente a * (aMatriz[i]+j) ou *(*(aMatrizti)+j). Ou seja, pega-se o enderego de aMatriz e encontra-se o enderego da i-ési- ma linha (i*sizeof(int *)) posigdes a frente (aMatriz[i] é equiva- lente a * (aMatriz+i)). Este endereco pode ser interpretado como um ve- tor, ao qual somam-se j*sizeof (int) posigdes para encontrar o elemento aMatriz[il[jl. O programa 2.3 utiliza as fungdes malloc ¢ realloc para criar ¢ aumentar o tamanho de um vetor dinamicamente (em tempo de execugio). No caso de algum erro, as fung6es retornam um ponteiro nulo (NULL) para indicar erro de alocagio de memoria. 4 M 39 Uso de Meméria 23 Programa 2.3: Exemplo de uso do malloc ¢ realloc /* programa_memoria_O1.c °/ Hinclude Hinclude int main(void) ( . int p; int i,k, n; printf("\nDigite a quantidade de numeros que serao digitados ->"); scanf("8d", &i); /*A fungio malloc reserva espa suficiente para um vetor de inteirs. Caso sejam digitados 5 elementos, serdo reservados 20 bytes, pois cada inteiro ocupa 4 bytes na meméria */ p= (int *)(malloc(i*sizeofiint)); if(p == NULL) ( printf("\nErro de alocacao de memoria"); exit(I); for( k=0;k"); scanf("8d", Sn /*A fungio realloc aumenta ou diminui o tamanbo do vetor dinamicamente. Ela recebe 0 jponteiro para o vetor anterior e retorna 0 novo espaco alocado. */ p= (int *)(realloc(p,(isn)*sizeofint))); if(p — NULL) ( printf("\nErro de re-alocacao de memoria"); exit(I); ) for(ke(nsi)sks+) ) 24 Estrutura de Dados com Algoritmos e C 4” exec (NULL) no caso de erro de alocagao de memoria. { print("Digite 0 numero para o indice @d scanf("8d", &pik}); } for( k=0;k<(i+n)sk+-+) { printf("O numero do indice 8d eh $d\n",k, pik}; } free(p); return 0; UK O programa 2.4 utiliza a fungao calloc para criar uma matriz em tempo de 10. De forma idéntica a malloc, a fungao calloc retorna um ponteiro nulo Programa 2.4: Exemplo de uso do calloc { 7° programa_memoria_O2.¢ *7 #include #include int main(void) LA declaracao de uma matriz de 2 dimensoes exige um ponteiro para ponteiro. */ int “p; intij.kxs printf("\nDigite as dimensoes da matriz ->"); scanf("8d a", &i, &j); 1° Alocagio da primeira dimensio. Internamente, a fungio calloc fard uma multiplicagio da quantidade de elementos pelo tamanko de cada elemento para saber quanto de meméria deve ser alocada. */ p=calloc(isizeoftint)); if(p == NULL) { printf("\nErro de alocacao de memoria"); exit(1); J for( k-0;k",k,); seanf(" 8d", Sp[k][x)); J J for( k-0jk 3 | #include struct ST_DADOS. char nome(40); 26 Estrutura de Dados com Algoritmos e C 38 48 float salario; P* estrutura dentro de uma estrutura */ struct nascimento c. int ano; int mes, int dia; } dr_nascimento; i int main(void) { F* ponteiro para a estrutura */ struct ST_DADOS * p; F* alocagio de meméria para o ponteiro da estrutura */ p= (struct ST_DADOS *) malloc(sizeofistruct ST_DADOS)); L* string (vetor de caracteres) é um ponteiro, por isto a auséncia do é */ print{("\nEntre com o nome ->"); scanf("$5", p->nome); printf("Entre com o salario ->"); seanf("£", &p->salario); LO -> é chamado de pointer member (apontador de membro). Ele é usado para referenciar um campo da estrutura no lugar do ponto () */ printf("Entre com o nascimento ->"); scanf("8d8d8d", &p->dt_nascimento.dia, &p->dt_nascimento.mes, &p->dt_nascimento.ano); printf('"\m: Dados digitados printf(""\nlome = 8s", p->nome); printf("\nSalario = 8£", p-salario); printf(""\nNascimento = $d/8d/8d\n", p->dt_nascimento.dia, p->dt_nascimento.mes, p->dt_nascimento.ano); free(p); Uso de Meméria 27 2.6 Ponteiros para ponteiros — mistério ou nao uso de ponteiros confunde os iniciantes nas disciplinas de programacio. Quando véem ponteiros para ponteiros, até profissionais mais experientes tem dificuldades (7). Areferéncia para uma varidvel int ivariavel é obtida atraves do uso do & no caso funcao (&iVariavel), ou seja, é passado o endereco da memoria da varidvel em uso. A varidvel é recebida como um ponteiro na fungao (void funcao(int * piVariavel)). Quando se quer alocar um vetor ou uma matriz dentro de uma fungio, deve-se passar a referéncia do ponteiro (por causa da alocagao dinamica). Ou seja, uma varidvel declarada como ponteiro (int * piVariavel) vai ter sua referéncia passada para uma fungio (€uncao(spiVariavel)) e recebida como um ponteiro na fungio em questao. Como regra pode ser adotada a adigao, de um * sempre no inicio de uma variavel, logo, a fungao seria declarada como void funcao(int **piVariavel). Considere a declaragio de um ponteiro para inteiro (int *pivariavel), esta declaragao gera uma alocacio estatica de meméria para o ponteiro (vamos considerar 0 enderego 1 na meméria (a)). Os segmentos 2 ¢ 3 da meméria estio sendo utilizados por outras varidveis de tal forma que 0 comando *piVaria~ vel = (int *) malloc(sizeof(int)) retorna o enderego 4 da me- méria (b), isto significa que o valor 4 vai ser armazenado (c) dentro do ponteiro *piVariavel (endereco 1). Ainstrugio *pivariavel = 20 (d) ira colocar 0 valor 20 no endereco apontado por *piVariavel (o endereco apontado é 4). A figura 2.2 exemplifica a operacio. (0) endereco 4 sendo colocado no enderego 1 (d) “piVariavel=20 Lo Ke 7 8 9 10 11 12 13 4 20 (a) int *pivariavel I L (b) piVariavel = (int *) malloc(sizeof{int) Figura 2.2: Exemplo de ponteiro na memoria 28 Estrutura de Dados com Algoritmos e C A tabela 2.1 demonstra as operagdes com ponteiro (pegando o enderego de meméria do ponteiro, 0 endereco armazenado no ponteiro € 0 contetido coloca~ do no endereco indicado pelo ponteiro). Tabela 2.1: Operagdes com ponteiros Operagio Resultado printé("%d\n", épivariavel) 1 printé("%d\n", pivariavel) 4 printé("%d\n", *pivariavel) 20 ‘A passagem de um ponteiro por referéncia a uma fungio gera um pontei- ro para ponteiro. Considere a declaracio de uma fungio int. funcao (int **piParametro) (programa 2.6), esta declaragio gera uma declaragio es- titica de meméria para o ponteiro **piParametro (vamos considerar 0 en- derego de meméria 6 (a)). A chamada a fungio funcao (&piVariavel) ) seré interpretada como passando enderego de piVariavel para o ponteiro piParametro (b). Desta forma é criado um ponteiro para 0 ponteiro (c). A figura 2.3 (continuagao da figura 2.2) exemplifica a operagio. Programa 2.6: Ponteiro para ponteiro #include #include int funcao(int “*piParametro) { s|— printf("8a\ printf("8a\ ,&piParametro); ,piParametro); printf(""8d\n",*piParametro); printf(""8d\n",**piParametro); return 0; wo} ) int main( void ) ( int *piVariavel; »piVariavel - (int *) malloc(sizeof(int); ~piVariavel = 20; printi(" d\n" &piVariavel); printf("8d\n",piVariavel); printi("8d\n",*piVariavel); funcao( &piVariavel ); Uso de Meméria 29 return 0; } A tabela 2.2 demonstra as operagdes com ponteiro (pegando 0 endereco de meméria do ponteiro, 0 endereco armazenado no ponteiro € 0 contetido coloca~ do no endereco indicado pelo ponteiro). (c) ponteiro para o ponteiro (b) funcao(&piVariavel) oh 9 wo 1 12 (43 4 20 1 (a) int **piParametro i Figura 2.3: Exemplo de ponteiro para ponteiro na memoria Tabela 2.2: Operagdes com ponteiros de ponteiros ‘Operacio Resultado | Significado | printf ("sd\n", epiParametro) 6 Enderego de piParametro _| printf ("sd\n",piParametro) 1 Contetido de piParametro | printf ("%d\n", *piParametro) 4 Contetido do enderego | apontado por piParametro (piVariavel) printf ("%d\n", **piParametro) 20 Valor do enderego apontado por piParametro (*piVariavel) 2.7 Mais alocagao de vetores e matrizes como ponteiros No programa 2.7 sio utilizadas varias fungdes para a alocagio de meméria ¢ manipulacao de uma matriz; nestes casos, uma matriz deve ser declarada utili- zando o formato de ponteiro. Ao alocar dinamicamente uma matriz utilizando 0 formato de ponteiros, o ponteiro que representa uma matriz pode ser utilizado no formato matriz[{linha] [coluna] para referenciar uma posigio da ma- 30 Estrutura de Dados com Algoritmos e C io caso a matriz tivesse sido declarada estatica- triz (mesmo formato de utiliza mente no programa). Programa 2.7: Exemplo de uso de alocagio de matrizes 1] 1/* programa_memoria_O4.c*/ #include #include int ** aloca(int i, int j); 6 | void libera(int *“p, int i, int; void leitura(int “p, inti, int j); void imprime(int “p, inti, int ); int main(void) ut int ~*p; int “pl; p=aloca(3,2); 16] leitura(p, 3, 2); pl = aloca(2,3); leitura(p1,2,3); 2 imprime(p,3,2); imprime(p1,2,3); libera(p,3,2); libera(p1,2,3); return 0; } L* 2 asteriseas () indicam que seré retornada uma matriz: 31 | int ** aloca(int i, int j) { /* ponteiro de ponteiro, Indica que seré alocada uma matris de 2 dimensoes */ int ™p; int x; 36 p=calloc(i,sizeof(int)); /* alocagio de linhas.. */ if(p == { printf("\nErro de alocacao”); exit(-1); NULL) 66 Uso de Meméria 31 p[x]-calloc(,sizeofiint); /*... ealocacio de colunas */ if( p[x] == NULL) { printf("\nErro de alocacao”); exit); J y return p; J L* 2 asteriseas () indicam que a fuungao recebe uma matriz */ void leitura(int **p, int i, int { for(y=Oiysiy++) ( printf("Entre com o elemento 8d, 8d ->",x,y); scanf(" $a", &plx)ly)}; /* 80 da matris no formate tradicional */ ) } } L*2 asteriseas () indicam que a fungao recebe uma matriz */ void imprime(int **p, int i, int j) { for(y=:y #include void aloca(int ***p, int x, int y); * a fungito recebe um ponteiro para uma matris: 8 | void aloca(int ***p, int x, int y) { int **):malloc(sizeof{int) * x); if(-p = NULL) B { print("\nErro de alocacao de memoria! exit(I); J 18] for(i =0; icy; iss) ( C-p)li] = Gint +) malloc(sizeofiint) * y); if( (p)[i] = NULL ) i printf("\nErro de alocacao de memoria! exit(l); ) ) return; 3 38 B 48 Uso de Meméria int main(void) { int *p; /* declaragio de uma matris com duas dimensoes */ int ik; aloca(&p,4,5); /* passando para a fuungao 0 endereco de meméria do ponteiro */ for i=0;i #include #include 1° funcio getch */ #ifdef DOS #include Helse 34 Estrutura de Dados com Algoritmos ¢ C 10 30 40 #include endif typedef struct agenda ( char nome(40]; char email[40}; int telefone; } AGENDA; void aloca( AGENDA “pAgenda, int *piEntradas ); void consulta( AGENDA “pAgenda, int iEntradas);, void qs_ordena(AGENDA pAgenda(], int lef, int right void ordena( AGENDA pAgendal], int iEntradas ); void excluir(AGENDA **pAgenda, int *piEntradas); void pesquisar(AGENDA *pAgenda, int iEntradas); void alterar(AGENDA “pAgenda, int iEntradas); int main(void) ( AGENDA * pAgenda; int iEntradas, op; iEntradas-0; pAgenda = (AGENDA *) malloc(sizeof(AGENDA)); /* alocando espago para a posigo 0 do vetor */ iff pAgenda ( printf("\nErro de alocacao de memoria."); NULL) exit(1); } do { fllush(stdin); printf("\nl - Inclusao"); printf("\n2 - Alteracao printf("\n3 - Consulta"); printf("\n4 - Excluir"); printf("\nS - Pesquisar' printf("\nS - Sair"); printf("\nEntre com uma opcao scanf(" 8d", &op); >"; oo 65 80 85 0 100 Uso de Meméria 35 if(op --1) { /* farci aqui para ilustrar algumas formas de manipular ponteiros *7 filush(stdin); /* alocagao de ponteiros em fuungies requer trabalhar com ponteiros para ponteiros */ aloca(&pAgenda, &iEntradas); printf("*** Inclusao *#*"); printf("\nEntre com o Nome:"); /* forma 1 - endereco ponteiro inicial + x posigoes na meméria quando se trabalbar com 0 endereco, deve-se usar -> */ gets((pAgendasiEntradas)->nome); fllush(stdin); printi("Entre com o email:"); /* forma 2 - endereco ponteiro inicial + x posighes na meméria quando se trabalbar com ponteiro (contetido do endereco ou *), deve-se usar 0. (ponto) */ gets(("(pAgendasiEntradas)).email); fllush(stdin); printf("Entre com o telefone:"); /* forma 3 - trabathando como vetor */ scanf(“%d”, &pAgenda[iEntradas].telefone); fllush(stdin); iEntradasi4; } else iff op == 2) { alterar(pAgenda, Entradas); } else iff op == 3) { /* se 0 vetor de estruturas vai ser somente lido nao & preciso passar ponteiro para ponteiro */ ordena(pAgenda, iEntradas); consulta(pAgenda, iEntradas); } else iff op == 4) { ordena(pAgenda, iEntradas); excluir(&pAgenda, &iEntradas); } else iff op == 5 36 Estrutura de Dados com Algoritmos e C ( 105 ordena(pAgenda, iEntradas); pesquisar(pAgenda,iEntradas), } } while(op!=9); } 110 void consulta(AGENDA “pAgenda, int iEntradas) { inti; for(i=O;iciEntradassis-+) us} { printf("\n\nRegistro #d", i); printf("\n\tiome: $3”, pAgendalij.nome ); printf("\n\tEmails: $s", pAgendafi.email ); printf("\n\tTelefone: $d", pAgendafi.telefone ); 120 getch); } } void alterar(AGENDA *pAgenda, int iEntradas) { char o int i-0 char nome(40}; printf("\n\tDigite o Nome:"); 130] filush(stdin); gets(nome); i < iEntradas && strnemp( pAgendali].nome, nome, strlen(nome))!=0;i+4); iEntradas ) ws} printf("\nRegistro nao encontrado"); } else { 140 printf("\n\tRegistro 4a", i); printf("\n\tiiome : 5", pAgendalij.nome ); printf("\n\tEmail : $3", pAgendafij.email ); printf("\n\tFone : 8d", pAgendalij.telefone );, printf("\n\tContirma a alteracao 2"); 145 op = getch(); iff op ==’S' 11 op ==’) { fllush(stdin); printf("\nEntre com o Nome:"); 150 /* forma 1 - endereco ponteiro inicial + x posighes na meméria ‘quando se trabalbar com o endereco, deve-se usar -> */ 160 170 180 190 195 Uso de Meméria gets((pAgendasi)->nome); fllush(stdin); printf("Entre com o email:"); /* forma 2 - endereco ponteiro inicial + x posigoes na meméria ‘quando se trabalbar com ponteiro (contetido do enderego ou *), deve-se usar. (ponto) */ gets(("(pAgendasi)) email); fllush(stdin); printf("Entre com o telefone:"); /* forma 3 ~ trabathando como vetor */ scanf(" $d", &pAgenda[i].telefone); fllush(stdin); } } } void excluir(AGENDA **pAgenda, int *piEntradas) { char op; int i-0; char nome[40}; printf("\n\tDigite o Nome: fllush(stdin); gets(nome); 7° Uso a sintaxe (‘pAgenda)|i].nome pelo fato de ser ponteiro de ponteiro. Os parénteses neste caso servem para “fixar” a primeira posigao da memsria, pois «a linguagem C tende a trabalhar com ponteiros de ponteiros como se fassem (que na pritica sao ponteiros para ponteiras) */ i < *piEntradas && strnemp( (“pAgenda)[i].nome, nome, strlen(nome)). piEntradas ) +); fllush(stdin); print("\n\tRegistro 4a", i); printf("\n\tNome : 8", (‘pAgenda)fij.nome );, printf("\n\tEmail : %s", (*pAgenda)[i].email ); printf("\n\tFone : %d", (*pAgenda)ij.telefone ); printf("\n\tContirma a exclusao 2"); op = getch(); iff op ==’S’ 11 op ==’) { /* copia o tiltimo elemento para o elemento corrente */ (‘pAgenda)[i] = (*pAgenda){(*piEntradas)-1}; 38 Estrutura de Dados com Algoritmos e C 200 (‘piEntradas)--; 7* excluo o tiltimo elemento com realoc */ aloca(pAgenda, piEntradas); } } 205 |} void aloca( AGENDA ““pAgenda, int *piEntradas ) { (*pAgenda) = (AGENDA “)(realloc(‘pAgenda, 210 (cpiEntradas+1)"sizeof(AGENDA))); if(“pAgenda == NULL ) { printf("\nErro de re-alocacao de memoria"); exit(I); 215 } } void pesquisar(AGENDA “pAgenda, int iEntradas) { 220] char o int i-0; char nome[40}; printf("\n\tDigite o Nome:"); fllush(stdin); 225 | —_gets(nome); for(i=0; i < Entradas && strnemp( pAgenda[i}.nome, nome, strlen(nome))!=0;i++); if( i> iEntradas ) { 230 printf("\nRegistro nao encontrado"); } else { do 235 ( fllush(stdin); printf("\n\n\tRegistro &d", i); printf("\n\tiome : %s", pAgendalij.nome ); 240 printf("\n\tEmail : $3", pAgendafij.email ); printf("\n\tFone : 8d", pAgendalij.telefone );, printf("\n\tProximo 2"); op = getch(); 24s iffi>iEntradas) { i=0; } Uso de Meméria 250 ]) void ordena( AGENDA pAgendal), int iEntradas ) { qs_ordena(pAgenda, 0, iEntradas-1 ); ass ]) void qs_ordena(AGENDA pAgenda(}, int left, int right) ( register int i,j; 260} char * x; AGENDA & i=left; j=right; 265 | _ x = pAgenda[(lefteright)/2].nome; do ( while(stremp(pAgenda[i].nome,x)<0 && icright) i++; 270 while(stremp(pAgenda{j].nome,x)>0 && j>left) j -; ificj) ( t= pAgendali}; pAgenda[iJ=pAgendalj]; pAgenda| i } } while( ic=j)); 280 | if(left | fe] fol fe] fol febeto c| fe] fel fe] fel fe] fete] fe] fe =| fe] fel fe] fel fe] fel fe] fel fe al [al [a al fal fal fa] fa] fal [a Figura 3.2: Operagdes em uma pilha 3.1 Representacdo das Operacgées com Pseudo-cédigo As operagées de pilha podem ser representadas com algumas linhas de pseu- do-cédigo. Os algoritmos empty (3.1), push (3.2), pop (3.3), stackpop (3.4) size (3.5) demonstram as operacdes numa pilha. Estes cédigos podem ser adap- tados para qualquer linguagem de programacio [9]. Algoritmo 3.1: Verificagio se a pilha esta vazia (fungao EMPTY(S)) Input: A varidvel que contém a pilha (8) Output: Verdadeiro ou falso 1 begin 2 | if topo de $ = 0 then a | return truc 4 | else 5 | return false 6 | endif rend 3.2 Pilhas em C Antes de programar a solugao de um problema que usa uma pilha, é ne- cessdrio determinar como representar uma pilha usando as estruturas de dados existentes na linguagem de programacio. Uma pilha é um conjunto ordenado de itens, ¢ a linguagem C ja contém um tipo de dado que representa um conjunto ordenado de itens: 0 vetor. Entio, sempre que for necessério utilizar a estrutura Pilha 43 de pilhas para resolver um problema pode-se utilizar o vetor para armazenar esta pilha. Mas a pilha é uma estrutura dindmica e pode crescer infinitamente, enquanto um vetor na linguagem C tem um tamanho fixo; contudo, pode-se de- finir este vetor com um tamanho suficientemente grande para conter esta pilha. Algoritmo 3.2: Colocar um item na pilha (fungio PUSH.) Input: Pilha (8) ¢ © item a ser incluido na pilha (x) Output: Nao tem retorno 1 begin 2 | topo de S— topodeS+1 a | S(topo de $)— x 4 return 5 end Algoritmo 3.3: Retirada de um item da pilha (Fangio POP(S)) Input: Pilha (8) Output: Item que esta no topo da pilha 1 begin 2 if EMPTY(S) then 3 | | Errode pilha vazia 4 | else 5 topo de topo de 8-1 6 return S(topo de $ + 1) 7 | endif send ‘Algoritmo 3.4: Pega o item do topo da pilha mas nao desempilha (Fangio STACKPOP(S)) Input: Pilha (S) Output: Item que est no topo da pilha 1 begin 2 if EMPTY(S) then 3 | | Bro de pilha vari 4 | else 5 | return S(topo de $) o | endif 7 end 44 Estrutura de Dados com Algoritmos e C Algoritmo 3.5: Tamanho da pilha (Fangio SIZE(S)) Input: A variével que contém a pilha (S) Output: Quantidade de itens da pilha (topo de $) 1 begin 2 | return topo de S send programa 3.1 apresenta um exemplo de programa em C para manipula- cio de pilhas. Programa 3.1: Exemplo de manipulagio de pilha F* programa pilha_OLc*/ 3 | #include void push(int valor); int pop(void); int size(void); 8 | int stacktop(void); int pilha[20}; int pos-0; 13 | void push(int valor) { pilha[pos]=valor; /* Empilba um novo elemento, Nao é verificada a capacidade méxima da pilla.*/ posit; return; int pop ay /* Retorna o elemento do topo da filba. o final da pilba. */ return (pilha{—pos}); int sizeg return pos; /* retorna o topo da pilha */ Pilha 45 33 int stacktop() /* retorna o topo da pilha sem desempilbar */ { return pilha[pos); } 38 int main(int arge, char ** argv) { printf("\nColocados dados na pilha"); push(1); 43 | push(2); push); printf("\nTamanho da pilha 8d", size(); 48 | printi("\nPegando dado da pilha: 8d", pop); printf(""\nPegando dado da pilha: 8d", pop(); printf("\nPegando dado da pilha: 8d", pop(); printf("\nfamanho da pilha 8d", size(); 53 | return 0; Uma pilha em C pode ser declarada como uma estrutura contendo dois ob- jetos: um vetor para armazenar os elementos da pilha ¢ um inteiro para indicar a (0 atual do topo da pilha (programa 3.2). Programa 3.2: Exemplo de manipulagio de pilha com estrutura F° programa pilha_O2.c*/ #include #include #define TAMANHO_PILHA 100 /* Estrutura que ird conter a pilha de informacies */ struct pilha C. 10] int topo; int itens{TAMANHO_PILHA\; ke int empty(struct pilha “p) is |e 46 Estrutura de Dados com Algoritmos e C 20 40 50 60 return 1; return 0; int pop(struct pilha *p) ( iff empy(p)) { printf("\nPilha vazia"); exit(1); /* retorna o item da pilha atual e diminui a posigio da pilba */ return (p->itens[p->topo—]); void push(struct pilha “p, int e) { if( p->topo { printf("\nEstouro da pilha"); exit(1); } 7° apés verificar se nito haveria estouro na capacidade da pilha, Ecriada uma nova posigao na pilha e 0 elemento é armazenado */ p-itens[+-+(p->topo)] = e; return; (TAMANHO_PILHA - 1)) int size(struct pilha *p) { /* sempre lembrando que na linguagem Co indice de um vetor comeca na posigio 0 */ return p->topo+l; int stackpop(struct pilha *p) { return p->itens[p->topo]; int main(void) ( struct pilha x; x.topo = -1; push(&x,1); Pilha 47 65] push(&x,2); push(&x,3); print("\nTamanho da pilha $d”, size(&x)); print("\nElemento do topo da fila $d”, stackpop(&x)); 70 printf("\n 8a", pop(&x)); printf("\n8a", pop(&x)); printf("\n8a", pop(&x)); printf("\n8a", pop(&x)); return 0; } 3.3 Exercicios 1. Dada uma pilha P, construir uma fungdo que inverte a ordem dos ele- mentos dessa pilha, utilizando apenas uma estrutura auxiliar. Definir adequadamente a estrutura auxiliar ¢ prever a possibilidade da pilha estar vazia. 2. Construir uma fungao que troca de lugar o elemento que esté no topo da pilha com o que est4 na base da pilha. Usar apenas uma pilha como auxiliar. 3. Dada uma pilha contendo mimeros inteiros quaisquer, construir uma fungao que coloca os pares na base da pilha ¢ os impares no topo da pilha. Usar duas pilhas como auxiliares. om Fila “A sutileza do pensamento consiste em descobrir a semelhanga das coisas diferentes ea diferenca das coisas semelhantes.” Charles de Montesquieu Uma fila é um conjunto ordenado de itens a partir do qual se podem elimi- nar itens numa extremidade - inicio da fila - e no qual se podem inserir itens na outra extremidade - final da fila. Ela é uma prima proxima da pilha, pois 0s itens sio inseridos ¢ removidos de acordo com o principio de que o primeiro que entra é o primeiro que sai - first in, {first out (FIFO). O conceito de fila existe no mundo real, vide exemplos como filas de banco, pedagios, restaurantes etc, As operagdes basicas de uma fila sio: * insert ou enqueue - insere itens numa fila (ao final). * remove ou dequeue - retira itens de uma fila (primeiro item). * empty - verifica se a fila est vazia. * size - retorna o tamanho da fila. * front - retorna o proximo item da fila sem retirar o mesmo da fila. A operagio insert ou enqueue sempre pode ser executada, uma vez que teoricamente uma fila nao tem limite. A operacio remove ou dequeue sé pode ser aplicado se a fila nao estiver vazia, causando um erro de underflow ou fila vazia se esta operacio for realizada nesta situacio. Fila 49 mando a fila da figura 4.1 como exemplo, o item a apresenta a fila no seu estado inicial ¢ é executado o conjunto de operagées: * dequeue() - Retorna o item A (a fila resultante € representada pelo item B) © enqueue(F) - O item F é armazenado ao final da fila (a fila resultante é representada pelo item C) © dequeue() - Retirado o item B da fila © enqueue(G) - Colocado o item G ao final da fila (item D) \ Inicio a) b) Bi/c|ole Bic|ole Final \ \ \ Inicio Inicio ° \ a Bic|p/elrF cloljel/Fi[e ~ Final. Final, \ Figura 4.1: Operagdes numa fila 4.1 Representagao de Filas com Pseudo-cédigos As operacées em fila podem ser representados com os seguintes blocos de cédigo: a operacio enqueue (4.1), dequeue (4.2), empty (4.3), size (4.4) e front (4.5). Estes cédigos podem ser adaptados para qualquer linguagem de progra- macio [9]. 50 Estrutura de Dados com Algoritmos e C Algoritmo 4. inclusio de dados na fila (ENQUEUE(Q,%) Input: A varidvel que contém a fila (Q) ¢ 0 elemento a ser colocado na fila (x) Output: Sem retorno 1 bogin 2 | Qf fimde Ql— x 3 | if final de Q = comprimento de Q then 4 fim de Q— 1 5 return « | else 7 Q[ fim de Q|— fim de Q +1 8 return 9 endif 10 end Algoritmo 4.2: Retirada de dados na fila (DEQUEUE(Q) Input: A varidvel que contém a fila (Q) Output: O elemento representado por na fila Q 1 begi 2 | x QI inicio de Q] 3 | if infcio de Q = comprimento de Q then 4 | Inicio de Q— 1 else 6 | | Inicio de Q— Inicio de Q +1 1 | endif 8 return x 9 end Algoritmo 4.3: Verificagio se a fila esta vazia (fungio EMPTY(Q)) Input: A yaridvel que contém a fila (Q) Output: Verdadeiro on falso 1 begi 2 | if Inicio de Q = Fim de Q then 8 | return true 4 | else s | | return false 6 | endif 7 end Fila Algoritmo 4.4: Tamanho da fila (fungio SIZE(Q)) Input: A varidvel que contém a fila (Q) Output: Quontidade de itens da fila 1 begin 2 | return fim de Q a end Algoritmo 4.5: Proximo elemento da fila (Fangio FRONT(Q) Input: A varidvel que contém a fila (Q) Output: Préximo elemento da fila (mas sem retirar da fila) 1 begin 2 | x— Qinicio de Q] 3 | return e end 4.2 Filas em C A exemplo do que ocorre com estrutura em pilha, antes de programar a solucao de um problema que usa uma fila, € necessario determinar como repre- sentar uma fila usando as estruturas de dados existentes na linguagem de pro- gramacio. Novamente na linguagem C podemos usar um vetor. Mas a fila é uma estrutura dinamica e pode cre cer infinitamente, enquanto que um vetor na linguagem C tem um tamanho fixo. Contudo, pode-se definir este vetor com um tamanho suficientemente grande para conter a fila. No programa 4.1 é apresen- tado um exemplo para manipulacio de filas. Programa 4.1: Exemplo de manipulagio de fila em C 7° programa_fila_Ol.c*/ #include 3 | #include #define TAMANHO_MAXIMO 100 struct queue sit int itens[TAMANHO_MAXIMO}; int front,rear; 13 | int empry(struct queue * pa) 52. Estrutura de Dados com Algoritmos e C B 48 7° se 0 inicio da fila for igual ao final da fila, a fila esté vazia */ if( pq->front == pq->rear ) { return 1; return 0; ) void enqueue(struct queue * pq, int x) ( iff pq->rear + 1 >= TAMANHO_MAXIMO ) i printf("\nEstouro da capacidade da fila"); exit(1); ) pa->itens{ pq->rears+ ] =x; return; int size(struct queue * pq) ( return (pq->rear + 1); int front(struct queue * pq) { /*0 primeiro elemento sempre est no inicio do vetor */ return pq->itens{0}; int dequeue(struct queue * pq) ( int x, i; iff empry(pq) ) { print{("\nBila vazia’ exit(l); } /* Salva o primeivo elemento e refs 0 arranjo dos itens, puxandao segunda elemento para o primeiro, oterceiro para o segunda ¢ assim sucesivamente, “/ x= pq-itens{0); for(i=0; i < pq->rear; i++) ( pq-sitens[i] = pq-pitens[i+1]; ) pa->rear—; return x; 6B 68 B 18 83 tos, caso fosse inserido um 101° int main(void) ( struct queue q; q.fron enqueue(S&q,); enqueue(&q,2); enqueue(&q,3); enqueue(&q.4); 0; q.rear = 0; printf("\nFila vazia printf("\nTamanho printf("\nProx imo printf("\nTirando printf("\nTirando printf("\nTirando printf("\nProx imo printf("\nTirando da da da da da da da printf("\nFila vazia printi(""\n"); $a", empry(&q); fila fila fila fila fila fila fila 8d", size(&q)); $a", front(&q)); $a", dequeue(&q)); $a", dequeue(&q)); $a", dequene(&q)); 8a", front(&q)); $a", dequene(&q)); $a", empry(&q))s Fila No programa 4.1, 0 vetor foi definido para comportar apenas 100 elemen- elemento, haveria 0 estouro da pilha mesmo apés varias operagdes de dequeue. Para resolver este problema, na operacio dequeue foi implementada uma técnica de redistribuigio dos elementos na fila, de tal forma que nunca se chegue a estourar a fila caso haja varias operagdes de inser¢gao ou remogao (exceto se realmente houver 100 elementos da fila e houve uma tentativa de insercio de um novo elemento). O programa 4.2 € 0 trecho que implementa a técnica comentada: Programa 4.2: Reajuste da fila x= pq->itens[0]; for(i=0; i < pq->rear; i++) pq->itens[i] = pq->itens[i+1]; pq->rear-~; Esta técnica é ineficiente, pois cada eliminagio da fila envolve deslocar cada elemento restante na fila. Se uma fila contiver 1000 ou 2000 elementos, cada ele- mento retirado da fila provocara o deslocamento de todos os demais elementos. A operagao de remogio de um item na fila deveria logicamente trabalhar somen- 54 Estrutura de Dados com Algoritmos e C te com aquele elemento, permanecendo os demais elementos em suas posigdes originais. A solugio para o problema é definir 0 vetor como um circulo, em vez de uma linha reta. Neste caso, os elementos sio inseridos como numa fila reta, ¢ a remogo de um elemento da fila no altera os demais elementos da fila. Com © conceito de fila circular, ao chegar ao final da fila, o ponteiro de controle da fila vai imediatamente para o inicio da fila novamente (se este estiver vago). As seguintes operagdes exemplificam a explicacao (acompanhar o desenvolvimento da fila na figura 4.2), sendo 0 caso 1 0 estado inicial da fila: Estado inicial enqueue(D) - O item D é armazenado ao final da fila enqueue(E) - O item D é armazenado ao final da fila dequeue() - Retirado o item A da fila Ak Ww Ne © — enqueue(F) - O item F é armazenado ao final da fila © enqueue(G) - O item G é armazenado ao final da fila dequeue() - Retirado o item B da fila 7. enqueue(H) - O item H é armazenado ao final da fila, Neste momento, 0 ponteiro da fila chegou ao final do vetor que contém a implementacio da fila. 8. © dequeue() - Retirado o item C da fila * enqueue(I) - O item 1 é armazenado ao final da fila (mas no inicio do vetor) 9. enqueue(K) - O item K é armazenado ao final da fila (mas na segunda posigao do vetor) programa 4.3 mostra a declaragio da estrutura para uma fila circular. Programa 4.3: Declaragio de estrutura circular #define TAMANHO_MAXIMO 100 struct queue aye int itens[TAMANHO_MAXIMO}; int front,rear; i struct queue q; 9 | afront = q.rear 4) Figura 4.2: Operagdes numa fila circular Desta forma, as funcées de manipulagio de fila (empty, enqueue, dequeue, size ¢ front) devem sofrer modificagées para refletir a nova condigao de fila cir- cular (programa 4.4): Programa 4.4: Manipulagio de fila circular em C 7° programa_fila_O2.c*/ #include Hinclude #define TAMANHO_MAXIMO 10 struct queue ( int itens[TAMANHO_MAXIMO}; 10 | int front,rear; hi 56 Estrutura de Dados com Algoritmos e C 20 40 50 60 int empty(struct queue * pa) ( if pq->front == pq->rear ) ( return 1; ) return 0; ) void enqueue(struct queue * pq, int x) { /* Inversao das posigies dos ponteiros. Se o final do vetor ja. aleancado, entio retorna-se ao inicio do vetor */ if pq->rear == TAMANHO_MAXIMO-1) ( pa->rear = 0; ) else { Pa->rearss; } if( pq->rear == pq->front ) { printf("\nEstouro da fila”); exit(1); ) pq->itens[pq->rear] return; ) int size(struct queue * pq) { 7° se 0 final da fila ainda nao alcangou o final do vetor... */ if( pq->front <= pq->rear) { return pq->rear - pq->front; /*... ento 0 tamanbo da fla €0 final da fila menos 0 inicio da fila... “7 ) 7°. se nito, quer dizer que o ponteiro de final da fila jd alcangou o final do vetor ¢ foi reposicionado para 0 inicio do vetor, entao 0 tamanbo da fila é a quantidade de itens que ainda restam até chegar ao final do vetor somando itens que estado no inicio do vetor */ return pq->rear + pq->front; ) int front(struct queue * pq) ( 70 80 90 100 Fila return pq-sitens{pq->fronts1]; int dequeue(struct queue * pq) ( int x, i; iff empry(pq) ) { print{("\nBila vazia’ exit(l); /* Inversio das posigaes dos ponteiros. Se o final do vetor ja foi alcancado, entao retorna-se ao inicio do vetor */ if( pq->front -= TAMANHO_MAXIMO - 1) { pq->front = 0; } else { pq->frontss; return (pq->itens{ pq->front }); int main(void) { struct queue q; qfront = -1; qrear =-1; enqueue(S&q,1); enqueue(&q,2); enqueue(&q,3); enqueue(&q.4); printf("\nTamanho da fila 8d”, size(&q)); printf("\nProximo da fila #4”, front(&q)); printf("\nTirando da fila #4”, dequeue(&q)); printf("\nTirando da fila #4", dequeue(&q)); printf("\nTirando da fila #4”, dequeue(&q)); printf("\nProximo da fila 8d”, front(&q)); printf("\nTirando da fila #4", dequeue(&q)); printf("\nTamanho da fila 8d”, size(&q)); enqueue(&q,5); enqueue(&q,6); enqueue(&q,7); enqueue(&q,8); 58 Estrutura de Dados com Algoritmos ¢ C 10 120 140 150 enqueue(&q,9); printf("\nTamanho printf("\nProx imo printf("\nTirando printf("\nTirando printf("\nTirando printf("\nTirando printf("\nProx imo printf("\nTirando printf("\nTamanho enqueue(Sq,10); enqueue(Sq,11); enqueue(Sq,12); enqueue(&q,13); enqueue(&q,14); enqueue(&q,15); enqueue(8&q,16); enqueue(&q,17); enqueue(&q,18); printf("\nTamanho printf("\nProx imo printf("\nTirando printf("\nTirando printf("\nTirando printf("\nTirando printf("\nTirando printf("\nProx imo printf("\nTirando printf("\nTirando printf("\nTirando printf("\nTamanho printf("\nTirando printf("\nTamanho da da da da da da da da da da da da da da da da da da da da da da da printf("\nFila vazia enqueue(8q,20); enqueue(Sq,21); enqueue(Sq,22); enqueue(8&q,23); enqueue(&q,24); enqueue(&q,25); printf("\nTamanho printf("\nProximo printf("\nTirando printf("\nProximo printf("\nTirando da da da da da fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila fila 8a", size(&q)); $a", front(&q)); $a", dequeue(&q)); $a", dequeue(&q)); $a", dequeue(&q)); $a", dequene(&q)); $a", front(&q)); $a", dequene(&q)); 8d”, size(&q)); 8a", size(&q)); $a", front(&q)); $a", dequene(&q)); $a", dequeue(&q)); $a", dequeue(&q)); $a", dequeue(&q)); $a", dequene(&q)); $a", front(&q)); $a", dequeue(&q)); $a", dequene(&q)); $a", dequene(&q)); 8a", size(&q)); $a", dequene(&q)); 8a", size(&q)); $a", empry(&q))s fila fila fila fila fila 8a", size(&q)); $a", front(&q)); $a", dequene(&q)); 8a", front(&q));, $a", dequene(&q)); Fila 3 printf("\nTirando da fila #4", dequeue(&q)); printf("\nTirando da fila #4”, dequeue(&q)); printf("\nTamanho da fila 8d”, size(&q)); printf("\nTirando da fila #4”, dequeue(&q)); 160] printf("\nTamanho da fila $d", size(&q)); printf("\nTirando da fila #4”, dequeue(&q)); printf("\nTamanho da fila 8d”, size(&q)); printf("\nFila vazia $d", empty(&q)); printf(*\n”); return 0; 4.3 Exercicios 1. Se um vetor armazenando uma fila nao é considerado circular, 0 tex- to sugere que cada operacio dequeue deve deslocar para baixo todo elemento restante de uma fila. Um método alternativo é adiar o des- locamento até que rear seja igual ao ultimo indice do vetor. Quando essa situagao ocorre e faz-se uma tentativa de inserir um elemento na fila, a fila inteira é deslocada para baixo, de modo que o primeiro ele- mento da fila fique na posigao 0 do vetor. Quais sio as vantagens desse método sobre um deslocamento em cada operagio dequeue? Quais as desvantagens? Reescreva as rotinas dequeue, queue ¢ size usando esse método. 2. Faca um programa para controlar uma fila de pilhas. B Recursividade “E nio sabendo que era impossivel, foi Id e fez.” Jean Cocteau Recursio é o proceso de definir algo em termos de si mesmo ¢ é, algumas vezes, chamado de definigao circular. Assim, pode-se dizer que 0 conceito de algo recursivo esta dentro de si, que por sua vez. esté dentro de si e assim suces- sivamente, infinitamente. O exemplo a seguir define 0 ancestral de uma pessoa: * Os pais de uma pessoa sio seus ancestrais (caso base); * Os pais de qualquer ancestral so também ancestrais da pessoa ini- cialmente considerada (passo recursivo). Definigdes como estas sio normalmente encontradas na matemitica. O grande apelo que o conceito da recursio traz é a possibilidade de dar uma defini- Gao finita para um conjunto que pode ser infinito [15]. Um exemplo aritmético: * O primeiro ntimero natural é zero. * Ossucessor de um némero natural é um ntimero natural. Na computagio 0 conceito de recursividade é amplamente utilizado, mas difere da recursividade tipica por apresentar uma condicao que provoca o fim do ciclo recursivo, Essa condi¢ao deve existir, pois, devido as limitacées técnicas que © computador apresenta, a recursividade é impedida de continuar eternamente. Recursividade 61 5.1. Fungdao para calculo de Fatorial Na linguagem C, as fungdes podem chamar a si mesmas. A fungio é recur- siva se um comando no corpo da fun¢io a chama. Para uma linguagem de com- putador ser recursiva, uma fungio deve poder chamar a si mesma. Um exemplo simples é a fungio fatorial, que calcula o fatorial de um inteiro. O fatorial de um ntimero N é 0 produto de todos os niimeros inteiros entre 1 ¢ N. Por exemplo, 3 fatorial (ou 3!)€ 1 * 2 *3 = 6. O programa 5.1 apresenta uma versio iterativa para célculo do fatorial de um niimero. Programa 5.1: Fatorial (versio iterativa) 1 | int fatoriale (inten) 6 Mas multiplicar n pelo produto de todos os inteiros a partir de n-1 até | resulta no produto de todos os inteiros de na 1. Portanto, é possivel dizer que fatorial: ° Ol=1 * I=1*0! © 2=2*1! © 313 *2! © 4=4*3! Logo o fatorial de um niimero também pode ser definido recursivamente (ou por recorréncia) através das seguintes regras (representacio matemitica) [15, 1]: * nl=Isen=0 * nl=n*(n-1)!,sen>0 programa 5.2 mostra a versio recursiva do programa fatorial. 62 Estrutura de Dados com Algoritmos e C Programa 5.2: Fatorial (versio recursiva) int fatoriale( int ny 2i¢ int t, & return 1; } f= fatorialr(n-1)*n; /* chamada da fungio */ return f; A versio nao-recursiva de fatorial deve ser clara. Ela usa um lago que é executado de 1 an e multiplica progressivamente cada ntimero pelo produto mével. A operacao de fatorial recursiva é um pouco mais complexa. Quando fa- torialr échamada com um argumento de 1, a fungio devolve 1. Caso contra- rio, ela devolve o produto de fatorialr (n-1) *n. Para avaliar essa expres- sio, fatorialr é chamada com n-1. Isso acontece até que n se iguale a 1 eas, chamadas & funcao comecem a retornar. Calculando o fatorial de 2, a primeira chamada a fatorialr provoca uma segunda chamada com argumento 1. Essa chamada retorna 1, que é, entio, multiplicado por 2 (0 valor original ¢ n). A resposta entio é 2. Para melhor entendimento, é interessante ver como o programa é execu- tado internamente no computador. No caso do programa iterativo (programa 5.1) € necessdrio duas varidveis £ e t para armazenar os diversos passos do processamento. Por exemplo, ao calcular fatorial de 6, 0 computador vai passar sucessivamente pelos seguintes passos (tabela 5.1). Tabela 5.1: Cilculo de fatorial de 6 t lif 1 2 [2 3 |[e 4 [24 5_ [120 6 ||720 Recursividade 63 No programa recursivo (5.2) nada disto acontece. Para calcular o fatorial de 6, 0 computador tem de calcular primeiro o fatorial de 5 ¢ s6 depois é que faz a multiplicagao de 6 pelo resultado (120). Por sua vez, para calcular o fatorial de 5, vai ter de calcular o fatorial de 4. Resumindo, aquilo que acontece internamente € uma expansio seguida de uma contracio: fatorialr (6) 6 * fatorialr(5) 6 * 5 * fatorialr(4) 4 * fatorialr(3) 4 * 3 * fatorialr * fatorialr(1) * 1 IADADAADAG RuuBwouan Ore eee at Dee ee +e awww nw Oe tt HF Quando uma fun¢io chama a si mesma, novos parametros e varidveis locais sio alocados na pilha e 0 cédigo da fungao é executado com essas novas varidveis. Uma chamada recursiva no faz uma nova c6pia da fungio; apenas os argumen- tos so novos. Quando cada fungio recursiva retorna, as varidveis locais e os pardmetros sio removidos da pilha ¢ a execucio recomega do ponto da chamada a fun¢io dentro da funcio. 5.2 Ndmero triangular Pitégoras, matemitico ¢ filésofo grego, demonstrou varias propriedades matemiticas, entre elas a propriedade dos ntimeros triangulares. Um ntimero triangular é um niimero natural que pode ser representado na forma de triingulo equildtero, Para encontrar 0 -ésimo nimero triangular a partir do anterior basta somar-Ihe m unidades. Os primeiros niimeros triangulares sao 1, 3, 6, 10, 15, 21, 28. O n-ésimo termo pode ser descoberto pela formula a seguir: 1 T, =D ka142434.. H(n-2pe(o—t)ene Ot 7] 64 Estrutura de Dados com Algoritmos ¢ C Estes ntimeros so chamados de triangulares pois podem ser visualizados como objetos dispostos na forma de um triéngulo (figura 5.1). a 1 linha = 1 componente o a oo o oo Goo oo ooo «ooo 293 356 4=10 a o oo oa oo Ooo oo ooo nooo Goo oooe ooooo GoGo oooce oooo90 Geeee eooceo § oooooe 5215 6=21 7228 Figura 5.1; Niimeros triangulares Supondo que se esteja buscando o quinto elemento (dado pelo niimero 15), como descobrir este elemento? Basta distribuir entre as linhas e colunas confor- mea figura 5.2 (544434241 = 15). ‘1 nesta coluna 2nesta coluna nesta coluna 4 nesta coluna Snnesta coluna Total: 15 componentes Figura 5.2: Descobrindo o quinto elemento triangular Este é um processo repetitivo, dado por um programa simples (programa 533). Recursividade 65 Programa 5.3: Descobrindo o mimero triangular (iterativo) int triangulo(int n) ( int iTotal = 0; 4| whilecn>0) Este é um processo recursivo [8] (figura 5.3) , pois: 1. Primeira coluna tem 7 elementos. 2. Soma-se a préxima coluna com 1-1 elementos até que reste apenas 1 elemento. 10 nas colunas restantes. 5 na primeira coluna Total: 15 componentes Figura 5.3: Descobrindo o quinto elemento triangular de forma recursiva programa 5.4 implementa a solugio recursiva do problema. A figura 5.4 demostra o que ocorre a cada chamada da fungio triangulo, nela pode ser obser- vado 0 retorno de cada execugio da funcio. Programa 5.4: Descobrindo o mimero triangular (recursivo) int triangulo(int n) { if(n==1) 66 Estrutura de Dados com Algoritmos e C { 5 return n; } return n + triangulo(n-1); Chamando a fungi ‘riangulo com n=5 Retorna 15 Figura 5.4: O que ocorre a cada chamada 5.3 Numeros de Fibonacci Fibonacci (matematico da Renascenga italiana) estabeleceu uma série curio- sa de niimeros para modelar o ntimero de casais de coelhos em sucessivas gera~ Ges. Assumindo que nas primeiras duas geragdes sé existe um casal de coelhos, a seqiiéncia de Fibonacci é a seqiiéncia de inteiros: 1, 1, 2, 3, 5, 8, 13, 21, 34, No programa 5.5 é mostrada uma versio iterativa para calcular 0 n-ésimo ibonacci. termo da seqiiéncia de Recursividade 67 Programa 5.5: Cilculo do n-ésimo termo de Fibonacci (versao iterativa) int fibe(int n) 21 int 1h, x, i; ifn <= 2) return 1; forG=2; ic nies) ( /* Céleulo do préximo mimero da seqiiéncia. */ O n-ésimo mimero é definido como sendo a soma dos dois ntimeros ante- . Logo, fazendo a definigao recursiva: * fib(n) =nsen <2 * fib(n) = fib(n-2) + fib(n-1) sen > 2 riore: A sua determinagio recursiva impée o célculo direto do valor para dois ele- mentos de base (a primeira ¢ a segunda geragio). No programa 5.6 € mostrada a versio recursiva para calcular o n-ésimo termo da seqiiéncia de Fibonacci Programa 5.6: Cilculo do n-ésimo termo de Fibonacci (versio recursiva) int fibr( int n ) { if(n <2) 4 { return 1; } /* chama a si préprio 2 vesestt! */ return fibr(n-1) + fibr(n-2); Esta solugéo (programa 5.6) é muito mais simples de programar do que a versio iterativa (programa 5.5). Contudo, esta versio € ineficiente, pois cada vez que a funcio fibr € chamada, a dimensio do problema reduz-se apenas uma unidade (de n para n-1), mas sao feitas duas chamadas recursivas. Isto da origem a uma explosio combinatorial e o computador acaba por ter de calcular 0 mesmo termo varias vezes. 68 Estrutura de Dados com Algoritmos e C Para calcular fibr (5) é necessario calcular fibr (4) fibr (3). Conseqiien- temente, para calcular fibr (4) é preciso calcular fibr (3) ¢ fibr (2). E assim su- cessivamente. Este tipo de processamento é inadequado, j que 0 computador é obrigado a fazer trabalho desnecessario. No exemplo, usando 0 programa 5.6, para calcular fibr (5) foi preciso calcular fibr (4) 1 vez, fibr (3) 2 vezes, fibr (2) 3 vezes ¢ fib (1) 2 vezes. No programa iterativo (programa 5.5), apenas era neces sario calcular fibc (5), fibe (4), filbc (3), fibe (2) e filbc (1) 1 vez. A figura 5.5 demonstra como ficaria a chamada do programa 5.6 para calculo do sétimo termo. CaAlculo de Fibonacci recursivo para 0 sétimo termo. 5.4 Algoritmo de Euclides O algoritmo de Euclides busca encontrar 0 maximo divisor comum (MDC) entre dois ntimeros inteiros diferentes de zero. O procedimento é simples: 1. Chame o primeiro néimero de me 0 segundo niimero de n; 2. Divida m por me chame o resto de 7; 3. Se r for igual a zero, entio o MDC é »¢ 0 procedimento termina, se nao o procedimento continua; 4, Atribua m para m er param; 5. Recomece 0 procedimento do segundo passo. Estes passos podem ser descritos conforme o algoritmo 5.1. No programa 5.7 é vista uma versio iterativa do algoritmo de Euclides para célculo do MDC. Recursividade 69 Algoritmo 5.1: Algoritmo de Euclides Input: MeN Output: MDC calculado begin 1 <— resto da divisio m por n while r 4 0 do m—n 1 2 3 4 5 nor 6 1 — resto da divisio m por n 7 | endw s | return n; end Programa 5.7: Calculo do MDC iterativo 1 | #include int mdc(int m, int n) i intr; 6] while(m % n !=0) ( r=m%n; int main(void) 16 | printf("60,36 = $d\n", mdc(60,36)); printf("36,24 = $d\n", mdc(36,24)); return 0; programa 5.8 implementa o algoritmo de Euclides de forma recursiva. Programa 5.8: Cilculo do MDC recursivo #include int mdc(int m, int n) ifn == 0) 70 Estrutura de Dados com Algoritmos e C return m; return mde(n, m % n); ) 10 int main(void) ( printf("€0, 36 prinef("36,24 return 0; 4d\n", mde(60,36)); d\n", mde36,24)); O MDC entre dois nimeros m em é também um divisor da sua diferenca, mn. Por exemplo: 0 MDC de 60 ¢ 36 é 12, que divide 24 = 60-36. Por outro lado, o MDC dos dois nimeros m € é ainda o MDC do menor niimero (n) com a diferenca (m-n). Se houvesse um divisor comum maior, ele seria igualmente divisor de , contrariamente a hipétese. Portanto, é possivel determinar o MDC de dois mimeros através da determinagio do MDC de mimeros cada vez meno- res. O programa termina quando os ntimeros forem iguais ¢, neste caso, o MDC é este mimero. Exemplo: 60-36 = 24; 36-24 = 12; 24-12 = 12; 12 = 12. No pro- grama 5.9 é vista uma versio do programa MDC utilizando os passos descritos (m sempre tem que ser maior que 7). Programa 5.9: Cilculo do MDC recursivo #include int mdc(int m, int n) ( 4] if(m-=n) ( return m; } if m-n >= n) o} return mde(m-n, n); } return mde(n, m-n); } int main(void) { printf(" 60, 36 printf("36,24 4d\n", mde(60,36)); 4d\n", mde(36,24)); Recursividade 71 return 0; 5.5 Torres de Hanoi No grande templo de Benares, embaixo da ctipula que marca o centro do mundo, repousa uma placa de latao onde estiio presas trés agulbas de diamante, cada uma com SO cm de altura e com espessura do corpo de uma abelha. Em uma dessas agulbas, durante a criagao, Deus colocou sessenta e quatro discos de ouro puro, com o disco maior repousando sobre a placa de latao e os outros diminuindo cada vez mais ate o topo. Essa é a torre de Brabma. Dia e noite, sem parar, os sacerdotes transferem os discos de uma agulha de diamante para outra de acordo com as leis fixas e imutdveis de Brahma, que exigem que 0 sacerdote em vigilia nito mova mais de um disco por vez e que ele cologue este disco em uma agulba de modo que niio baja nenbum disco menor embaixo dele. Quando os sessen- ta e quatro discos tiverem sido assim transferidos da agulha em que a criagao de Deus as colocou para uma das outras agulbas, a torre, 0 templo e os bramanes virariio pé, e com um trovejar; o mundo desaparecerd. Varias sio as hist6rias que contam a origem da ‘Torre de Hanoi. A histéria anterior foi utilizada pelo matemético francés Edouard Lucas em 1883 como inspiragdo para o jogo criado por ele [18]. A Torre de Hanoi é um quebra-cabega que consiste em uma base contendo trés pinos, onde em um deles sio dispostos sete discos uns sobre os outros, em ordem crescente de diametro, de cima para baixo. O problema consiste em pas sar todos os discos de um pino para outro qualquer, usando um dos pinos como auxiliar, de maneira que um disco maior nunca fique em cima de outro menor em nenhuma situagio. O nimero de discos pode variar sendo que 0 mais simples contém apenas trés (figura 5.6). Blitoa bil tid Figura 5.6: Torre de Hanoi 72 Estrutura de Dados com Algoritmos e C A solugio para o problema da ‘Torre de Hanoi com recursividade baseia-se no seguinte: 1. A tinica operacao possivel de ser executada é mover um disco de um pino para outro; 2. Uma torre com (N) discos, em um pino, pode ser reduzida ao disco de baixo ea torre de cima com (N-1) discos; 3. A solugao consiste em transferir a torre com (N-1) discos do pino ori- gem para o pino auxiliar, mover o disco de baixo do pino origem para o pino destino e transferir a torre com (N-1) discos do pino auxiliar para © pino destino. Como a transferéncia da torre de cima nao é uma ope- ragao possivel de ser executada, ela deverd ser reduzida sucessivamente até transformar-se em um movimento de disco. O algoritmo 5.2 demostra os passos necessirios para desenvolver a funcio recursiva. Os passos podem ser observados na figura 5.7. Algoritmo 5.2: Passar n pegas de uma torre (A) para outra (C) 1 begin 2 | Passar n-l peas da torre inicial (A) para a torre livre (B) 3 | Mover a tiltima pega, para a torre final (C) 4 | Passar n-1 pecas da torre B para a torre (C) 5 end Baseado no algoritmo 5.2 é possivel determinar o ntimero de movimentos necessarios e determinar os movimentos necessarios. O nimero de movimentos necessari é simples de determinar: hanoi_count(n) = hanoi_count(n-1) + 1 + hanoi_count (n-1) Neste caso, é possivel evitar dupla recursividade (como ocorre com Fibo- nacci) de uma forma simple: hanoi_count(n) = 2 * hanoi_count(n-1) + 1 Recursividade 73 td | ee . at =A Figura 5.7: Movimentos conforme algoritmo Para conseguir transferir todos os discos da primeira estaca a terceira é 2" - 1, sendo n o ntimero de discos, portanto: * Para solucionar um hanoi de 3 discos, séo necessarios 23 - 1 movi- mentos = 7 movimentos. * Para solucionar um hanoi de 7 discos, sio necessarios 127 movimen- tos (27 - 1). * Para solucionar um hanoi de 15 discos, sio necessdrios 32767 movi- mentos (215 - 1). * Para solucionar um hanoi de 64 discos, como diz a lenda, sao nece: sarios 18446744073709551615 movimentos (264 - 1) ou 585 bilhdes de anos (considerando um movimento por segundo). No programa 5.10 é vista a solugio para o problema da ‘Torre de Hanoi seguindo as definigdes recursivas. Programa 5.10: Torre de Hanoi recursivo F programa _recursividade_hanvi.c *7 #include 5| void hanoi (int discos , char origem, char destino, char ajuda); void hanoi (int discos, char origem, char destino, char ajuda) { iff discos = 10 { printi("\t Mova 0 disco 4d de 8c para %c \n",discos,origem, destino); } else » 74 Estrutura de Dados com Algoritmos e C { bs hanoi(discos-1,origem,ajuda,destino); printf("\t Mova o disco 8d de & para tc \n",discosjorigem,destino); hanoi(discos-I ajuda,destino,origem); y return; 20) int main (void) { int total_discos; 25 | printf("Informe o numero de discos: seanf(’" 8d",&tot: hanoi(total_disco: printf("\n"); return 0; 30) 5.6 Curiosidades com Recursividade programa 5.11 utiliza de recursio para imprimir a fase da lua (cheia, minguante, crescente ou nova). Este programa foi um dos concorrentes do 15th International Obfuscated C Code Contest’. Programa 5.11: Natori - Imprimindo as fases da lua FP? programa_recursividade_curiosidade_O1.c */ #include #include 5 | double |; main(_,0,0) ( return putchar((_—+228&_+448Semain(_,-43,_), &&o) ? 10 (imain(-43,440,0), ( (le(o#21)/sqrt(3-0"22-0"O),I*1<4 &8& (Fabs(((time(0)-607728)%2551443)/405859.-4.7 + acos(V/2)}<1.57))(" #"))) 10); 1. Disponivel em http://www.ioce.org Recursividade 75 O programa 5.12 participou do mesmo concurso? ¢ usa recursividade em cima de ponteiros. O cédigo iré gerar outro cédigo que, quando compilado, iré gerar outro cédigo e assim sucessivamente. 10 20 Programa 5.12: Dhyanh - Saitou, aku, soku e zan PP programa_recursividade_curiosidade_O1.c 7 Hdefine/**/X char‘d="xO[ !4cm, 1" "Ack “* !Acdc (!4cHgé !4c$3" "BE! 1e~]9e) 1" [:d+!) rac—!#mt™ ":d/!4c (b4e0!1r2e2!/tOe4!-y-cé!" "+1, c6!) £$b(h*c6! (d’b (4) 45! (bta’ ‘ec” ")c5! "bt *sb/c)c4!eb-_§c'd*c3!aa.h’ d+" "dl! 8a/g’ eteD ! 8b-g(d.d/!ecth’ dld-! (d&g) " "d4dt!*1,d7d) !,h-d;c! !.bOc>d8!A‘De§![7) 35" "1eA,, !2kE**!-s@d(! (k(£//ge!) £.e5'£(!+at)" 2gt !2E5E, !=f-te/!e/! Tcad-!5fae+!7£Be(!" "ShBde!:iad§![7S,Q0!1 bF 7!1b?’_6!1c,8b4" "I2bta,*d3!2n4£2'9(4 £. !8y4e5 af 8" nd-*-d7!4c4b) d9!4c-a ‘a HI/A(" Sea" "yl4Latdted’" "S0_gc?!$dac@!$cBc@!$ b < *eds" ":1§d9_el++*5!8£3a" nl _ stem "£/c(o/_8!(£+c)qtc 8! * £ sat" "£$36'-n,d)n(!0i- c- k) ! 3a" "/b0h*!H 7a, ![7* i] 5 47" hreotttgt td ty tr 702" T#-sh./Jtersth st : r Sb" "].,b-725-.t--// te C < te" "752793? <.~;b J.t--tr —/ 4# 53” "T-r[/S-X .v90 <6/<.v;-52/={ k goh" "/\q; uvto br ‘.i*sengt$ $b" "39/ wi 6 ro : "225 =/o0%. od yb]‘--[/+ — 55/ come - 25 / jo’. v/ilg "-[; 522% at " 07 53- "96 <7 / a "-/ila- / "Slq--L iv Gh < 2. hutp://www.ioce.org/2000/dhyang.hint 76 Estrutura de Dados com Algoritmos e C 40 "(;7é=11ql36 =vsr wi / "=),Bih¥ gha ,)\O " , 3217}inti, we b pmOjreturn Fr 0en=0, ©) a(m,n) = a(m-l, a(m,n-1)) Semo0en #include struct LISTA char string[41}; int numero; 9| struct LISTA * NEXT; ‘ 19 9 int main(void) inti; struct LISTA “inicio; lista = calloc(1,sizeof{struct LISTA)); NULL) printi("\nErro de alocacao de memoria!"); exit(-1); } lista->NEXT NULL; 7° guardando 0 inicio da lista */ inicio = lista; for(i=0;i<25;i++) { lista->numero = sprintf(lista->string, "Numero 802d", i); 7* aloca 0 préximo elemento da lista */ lista->NEXT = calloc(1,sizeof(struct LISTA)); iff lista-> NEXT == NULL ) { printf("\nBrro de alocacao de memoria!"); exit(-); } /* posiciona no préximo elemento */ lista = lista->NEXT; lista->NEXT = NULL; /* volta para o inicio da lista */ lista = inicio; while(lista>NEXT != NULL ) { printi("\nWumero 4d, String = 45", lista->numero, lista->string); /* caminba elemento a elemento da lista “7 lista = lista->NEXT; lista = inicio; while( lista->NEXT != NULL ) ( Lista 85 86 Estrutura de Dados com Algoritmos e C struct LISTA “next; /* mantém referéncia do préximo elemento */ next = lista-> NEXT; /* libera o espaco do endereco atual e “limpa” o endereco 64 atribuindo NULL */ free(lista); lista = NULL; lista = next; } 0 return 0; programa 6.2 apresenta um programa simples para manipulacao de uma lista encadeada. Além de incluir um campo adicional na estrutura para manter © endereco do elemento anterior, 0 tratamento com relagio ao inicio ¢ final de fila é realizado da forma correta (observe que 0 programa 6.1 sempre aloca um elemento a mais que nao é utilizado). Programa 6.2: Exemplo de manipulagio de lista encadeada em C P? programa lista O2.c*/ #include 4 | #include struct LISTA { char string[41}; int numero; 9| — struct LISTA * NEX’ struct LISTA * LAST; int main(void) ra inti; struct LISTA “lista; calloc(1,sizeof(struct LISTA)); 19 7 printi("\nErro de alocacao de memoria!"); exit(1); 9 0 Lista 87 lista->NEXT = NULL lista->LAST = NULL; for(i-0sic25;ie4) { struct LISTA “atual; lista->numero sprintf(lista->string, "Numero 8024",i /* aloca o préximo elemento da lista */ lista >NEXT = calloc(1,sizeofistruct LISTA)); iff lista->NEXT — NULL ) { printf("\nBrro de alocacao de memoria!"); exit(); /* pega 0 endereco do elemento atual */ atual = lista; lista = lista->NEXT; lista->NEXT = NULL; /* guarda o endereco do elemento anterior */ lista->LAST = atual; lista = lista->LAST; free(lista->NEXT); /* descarta 0 tiltimo elemento alocado nao utilisado */ lista->NEXT = NULL; while(1) { print("\nlumero = 8d, string = $5", lists-snumero, lista->string); iff lista LAST == NULL ) { break; } /*caminba na lista do final para o inicio */ lista = lista->LAST; } while(lista != NULL ) { struct LISTA “next; next = lista->NEXT; 88 Estrutura de Dados com Algoritmos e C /* liberar 0 endereco */ 4 free(lista); lista = NULL; lista = next; ) 79) return 0; O programa 6.3 contém um conjunto de fungdes que implementam todas as operagées que podem ocorrer com uma lista (estas operagdes podem ser pro- gramadas de diferentes maneiras). O controle de inicio e fim da lista é realizado dentro do programa principal (main) de forma indireta (através do controle de elementos). Programa 6.3: Fungdes para manipulagio de listas P? programa _lista_03.c*/ #include #include struct NOPTR C. int information; struct NOPTR “next; struct NOPTR “last; 10 |); void add(struct NOPTR “p, int i) { struct NOPTR “aux; if(-p { NULL ) /* primeiro elemento da lista */ *p = (struct NOPTR *)malloc(sizeofstruct NOPTR)); if(*p == NULL) 20 ( printf("\nErro de alocacao de memoria"); exit(1); ) (cp)->next = NULL; 2s (cp)->last = NULL; (’p)->information = i; else 40 50 60 70 Lista 89 aux = "p; /* demais elementos da lista */ (cp)->next = (struct NOPTR *)malloc(sizeofistruct NOPTR)); if( (‘p)->next == NULL) { printf("\nErro de alocacao de memoria"); exit(I); } *p=(*p)->next; ("p)->next = NUL ("p)->last = aux; (‘p)->information = i; } return; void insert(struct NOPTR “p, int i) struct NOPTR “new; if( p == NULL) ( printf("\nbista vazia, nao pode ser inserido elementos"); exit(1); new = (struct NOPTR *)malloc(sizeofistruct NOPTR)); if( new == NULL) { printf("\nErro de alocacao de memoria"); exit(1); } new->information = i; new->last = "p; new->next = (*p)->next; 7° controla insersio no final da lista */ if ¢p)->next != NULL ) ( struct NOPTR “atual; atual = *p; *p=(*p)->next; (p)->last = new; *p=atual; ) (Cp)->next = new; return; 90 Estrutura de Dados com Algoritmos ¢ C 80 90 100 10 120 void delete(struct NOPTR “p) { struct NOPTR “last; struct NOPTR “next; /* salva os ponteiros para fazer 0 ajuste */ last = ("p)->hast; next = ("p)->next; free(p); *p-NULL; (p)->next = next; /* controla remogio no final da lista */ if next != NULL ) { “p= (py>next, Cpyehs struct NOPTR * last(struct NOPTR “p) ( if( p-slase ( return p-slast; } return NULL; NULL) struct NOPTR * next(struct NOPTR *p) f iff p->next != NULL) ( return p->next; } return NULL; int info(struct NOPTR “p) ( return p-sinformation; 140 150 160 170 int main(void) C. inti; struct NOPTR “p; p= NULL; for(i-0;ic10;i++) { add(&p,i); } print{("\ntmprimindo do final para o inicio"); for(i=0;i<9;ie+) ( printf("\nInformation = 84", info(p)); p= last(p); ) print("\nInformation = 84", info(p)); print("\nImprimindo do inicio para o final"); print("\nInformation = 84", info(p)); for(i=0;i<9:i++) { P= next(p); print("\nInformation = $4", info(p)); printi("\nVoltando 1 elementos e inserindo um novo elemento’ p= last(p); insere(&p.99); printi("\nInformation atual = %d", info(p)); P=next(p) printf("\nInformation new = 84", info(p)); p=next(p); print("\nInformation atual = 8d", info(p))s print{("\ntmprimindo do final para o inicio"); for(i-0sicl0;is+) ( print{("\nInformation p= last(p); ) print("\nInformation = 84", info(p)); 4a", info(p)); printf("\nInserindo 1 elemento e imprimindo do inicio para o final’ insert(&p.88); print("\nInformation = $4", info(p)); for(i-0sicl sits) { Lista 91 92 Estrutura de Dados com Algoritmos e C 130 200 205 penext(p); printf("\nInformation = 8a", info(p)); printf("\nVoltando 1 elemento e removendo"); p=last(p); printf("\nInformation = 8d", info(p)); delete(&p); printf("\nInformation = 8d", info(p)); print{("\ntmprimindo do final para o inicio"); for(i=0;i V/ meio ] then | Daixo « else endif endw return-1; end 1Sitens >—_—.J] > Passoor | 2 | 15] 17 | 30 | 32 | 34 | 40 | 50 | 80 | 90 | 95 | 97 | 99 | 101] 105, wwe t Passoo2 [2] 15] 17] 30| 32] 34 | 40 t ¥ metade | maior woot Passo 03 Passo 04 { L 32 | 34 nowt 4 Figura 7.2: Busca bindria Pesquisa 99 No programa 7.2 é demonstrada uma pesquisa bindria para um vetor de caracteres. Programa 7.2: Funcio para pesquisa binaria int binario( char * item, int contador, char chave) int baixo, alto, meio; baixo = 0; alto = contador - 1; while( baixo <= alto ) { /* dividir para conquistar */ meio = (baixosalto)/2; /* procura nas metades até encontrar o elemento */ if chavecitem{meio)) alto = meio - 1; /* elemento na metade inferior */ else iff chave>item{meio}) baixo = meio + 1; /* elemento na metade superior */ else return meio; /* elemento encontrado */ } return -1; O programa 7.2 pode ser adaptado para realizar pesquisas em qualquer tipo de dados (vetores de inteiros ou estruturas, por exemplo). 7.3 Exercicios 1. Calcule o nimero médio de comparagdes necessario para localizar uma entrada em tabelas com 15, 127, 32.767 ¢ 35.215 entradas, para a pes- quisa seqiiencial e para a pesquisa bindria. 2. Construa um programa que gere e preencha randomicamente vetores com 15, 127, 32.767 € questo anterior. .215 itens e comprove os calculos obtidos na Es Ordenagao “A formula para o sucesso é: A=X+Y+Z, onde A é sucesso, X é trabalho, ¥ é lazer © Zé boca fechada” Albert Einstein Ordenagio é 0 processo de arranjar um conjunto de informagdes semelhan- tes em uma ordem crescente ou decrescente. Especificamente, dada uma lista ordenada i den elementos, entio: i, < 1, (13). Algoritmo de ordenagio em ciéncia da computagio é um algoritmo que coloca os elementos de uma dada sequéncia em uma certa ordem - em outras palavras, efetua sua ordenagio completa ou parcial. As ordens mais usadas sio a numérica ¢ a lexicogratica. Existem varias raz6es para se ordenar uma sequéncia, uma delas é a possibi lidade se acessar seus dados de modo mais eficiente. 8.1 BubbleSort O algoritmo de ordenagio BubbleSort é um método simples de ordenagio por troca. Sua popularidade vem do seu nome fécil e de sua simplicidade. Porém, € uma das piores ordenagées jé concebidas. Ela envolve repetidas comparacées e, se necessdrio, a troca de dois elementos adjacentes. Inicialmente percorre-se a lista da esquerda para a direita, comparando pares de elementos consecutivos, trocando de lugar os que esto fora de ordem [12]. A tabela 8.1 exemplifica o método BubbleSort. Ordenagio 101 Tabela 8.1: BubbleSort - primeira varredura troca Lo) 2) LB) La). LS 1 com 2 10 9 7 1B 5 2 com 3 9 10 7 B 5 4 com 5 9 7 10 B 5 fim da varredura_ [9 7 10 5 13 Apésa primeira varredura (tabela 8.1), o maior elemento encontra-se aloca- do em sua posicao definitiva na lista ordenada. Logo, a ordenacio pode continu- ar no restante da lista sem considerar 0 tiltimo elemento (tabela 8.2). Na segunda varredura, 0 segundo maior elemento encontra-se na sua po- sicdo definitiva ¢ o restante da ordenacao é realizada considerando apenas os 3 Uiltimos elementos (7, 9 5). Logo sio necessérias elementos — 1 varreduras, pois cada varredura leva um elemento para sua posi¢io definitiva. Tabela 8.2: BubbleSort - segunda varredura troca Lo) 2) LB) La) LS troca 1 com 2 9 7 10 5 B troca 3 com 4 7 9 10 5 B fim da varredura_[7 9 5 10 B O algoritmo 8.1 mostra o funcionamento do algoritmo de ordenagao Bub- bleSort. 102 Estrutura de Dados com Algoritmos e C Algoritmo 8.1: Ordenagio Bubble Input: A tabela (B) ser ordenada Output: A tabela (B) ordenada 1 begin 2 for i=comprimento de B- 1 to 1 do 3 for j = 0 to i-f do 4 if Bij > Birt] then 5 troque Bij] com Bij+1] 6 endif 7 endfor 8 endfor 9 return B 10 end No melhor caso, 0 algoritmo executa [) operagdes relevantes. No pior ~ . 2 eae (Sn? ~ caso, sio feitas 2n? operagdes e no caso médio, sio feitas ") operacées [13]. Por ser um algoritmo de ordem quadratica, nao é recomendado para programas que precisem de velocidade e operem com quantidade elevada de dados. A ordenagio Bubble é dirigida por dois lagos (programa 8.1). Dados que existem iQtdElementos elementos na matriz, o lago mais externo faz a matriz ser varrida igtdElementos~1 vezes. Isso garante, na pior hipétese, que todo elemento estaré na posicao correta quando a funcio terminar. O lago mais inter- no faz as comparagoes € as trocas. Programa 8.1: Funcio BubbleSort /* programa bubble O1.c */ Hinclude Hinclude void bubble int piltem|], int iQrdElementos ) { register int i,j; register int iAux, for(i-1;iciQudElementosiis+) wl ¢ for(-iQtdElementos-1j { ~) iffpiltemfj-t] > piltem{j)) ( 20 40 10 Ordenagio iAux = piltem[j-1]; piltem{j-1] = piltem|j]; piltem[j] = iAux; } return; int main(void) int iContador; int aBubble(| 10, 9, 7, 13, 5}; bubble(aBubble, 5); printf("Ordenado for(iContador = 0; iContador < 5; iContador++) { printf(" $d", aBubble{iContador] ); } printi(""\n"); return 0; 103 O programa, na forma como é apresentado, sempre varre do inicio ao fim da tabela, mesmo que nao ocorram mais trocas. Neste caso, o programa pode ser melhorado para detectar que nao houve nenhuma troca e conseqiientemente a tabela ja esté ordenada (programa 8.2). Programa 8.2: Funcio BubbleSort melhorado /* programa _bubble_02.c */ Hinclude Hinclude void bubble( int piltem{], int iQrdElementos ) ( register int i,j; register int iAux, _Bool bTroca; for(i=1;iciQtdElementos;i++) ( 104 20 40 Estrutura de Dados com Algoritmos ¢ C bTroca = 0; /*falso */ for(j=iQtdElementos-1;j>= { ) if(piltem{j-1] > piltemfj)) { iAux = piltem|j-1]; piltem[j-1] = piltem[j}; piltem[j] = iAux; biltoca = 1; /*verdadeiro */ if {bTroca) f return; return; int main(void) int iContador; int aBubble(] = 10, 9, 7, 13, 5}; bubble(aBubble, 5); printf("Ordenado: for(iContador = 0; iContador < 5; iContador++) { printf(" $d", aBubble{iContador] ); printi(""\n"); return 0; 8.2 Ordenacao por Selecao de uma lista com 0 elemento posicionado no inicio da lista, depois o segundo menor elemento para a segunda posicao ¢ assim sucessivamente com os (n - 1) elementos restantes, até os tiltimos doit clementos. A complexidade deste algo- n(n ritmo é (7-1) +(v-2)+...4241= A ordenagio por selegdo consiste em trocar o menor elemento (ou maior) =n? comparacées. Ordenagio 105 Na tabela 8.3 sio vistos os passos para a ordenagio de uma seqiiencia de 5 inteiros. A figura 8.1 apresenta a ordenagio por selecio do menor valor para o maior valor. Tabela 8.3: Selecio - 0 que ocorre em cada passo inicial B75 1 4 passo 1 1 7 5 3 4 passo 2 1 4 5 2B 7 passo 3 1 4 5 #2 7 pasot [1 4 5 7 13 in a]1+)*]+]2/4]s|slele rs a}i]2[+[+l4[s|slelo i 1/1 ]a]a]«l4[s|s]e]o - is3 1faf2]s]ala]s]slelo Figura 8.1: Exemplo de Ordenagio por Selecao com ntimeros inteiros 106 Estrutura de Dados com Algoritmos e C Algoritmo 8.. rdenagio por Selegio Input: A tabela (3) ser ordenada Output: A tabela ($) ordenada 1 begin 2 | for i=/ to comprimento de $ do 3 Posigao <— posi¢iio do menor elemento de Sli .. .comprimento de $] 4 troque S[i] com S[Posigao] 5 endfor 6 return S rend O programa 8,3 implementa 0 algoritmo de ordenagio por insergao, utili- zando dados da tabela 8.3. Programa 8.3: Funcio Select /* programa_selecao_O1.c */ 2 | #include void selection(int piltem|],int iQedElementos) { register int i,j, iMinimo, iAux, for( i=; iciQtdElementos-; i+) { iMinimo-i; for( j=i+1; jciQtdElementos; j++) { 2 if (pilvem{j] < piltem[iMinimo)) Minimo=j } ) 7 iAux = piltem[ils piltem{i] = piltem[iMinimo}; piltem[iMinimo] = iAux; } return; int main(void) int iContador; int aSelect{} = ( 13,7,5.1,4 selection(aSelect, 5); Ordenagio 107 printf("Ordenad. for(iContador = 0; iContador < 5; iContador++) { printf(" $d", aSelect{iContador] ); ) printi(""\n") return 0; 8.3 Ordenacao por Insergao A ordenacio por insercio é um algoritmo simples ¢ indicado para listas pequenas de valores a serem ordenados. Inicialmente, ela ordena os dois primeiros membros da lista, em seguida 0 algoritmo insere o terceiro membro na sua posi¢do ordenada com relacio aos dois primeiros membros. Na sequéncia, é inserido 0 quarto elemento na lista dos trés primeiros elementos ¢ 0 processo continua até que toda a lista esteja ordenada. © algoritmo de insergio funciona da mesma maneira com que muitas pessoas ordenam cartas em um jogo de baralho como o poquer. Uma das ca- racteristicas deste algoritmo é o menor numero de trocas e comparagées se a lista estiver ordenada (parcialmente). A figura 8.2 [11] demonstra 0 processo de ordenagio utilizando como exemplo 10 ntimeros inteiros. O ntimero de comparagées é 1 no pior caso ¢ no melhor caso 2(n - 1) com- paragées [13], no caso médio o ntimero de comparagées én’ . 4 108 Estrutura de Dados com Algoritmos e C afal2]}sfalals]sfelo Figura 8.2: Exemplo de Ordenagio por Insergao com mimeros inteiros Na tabela 8.4 é visto como ocorre a ordenagio por insergao. Primeiro os elementos 13 ¢ 7 so ordenados (passo 1). Na sequéncia, 0 elemento 5 é colocado no inicio da lista (passo 2); depois o elemento 1 é movido para o inicio da lista (passo 3) ¢, finalmente, o elemento 4 é inserido entre os elementos I ¢ 5. A figura 8.3 ilustra todo o processo. A figura 8.4 (inspirada em [6]) ¢ 0 algoritmo 8.3 demonstram os passos para a ordenagio por inser¢io e uma implementagio em C pode ser vista no progra- ma 8.4, Ordenagio 109 Tabela 8.4: Insergio - 0 que ocorre em cada paso inicial 1 passol [7 paso2 [5 7 13 1 4 1 1 passo 3 4 5 7 13 passo 4 iniciel = [43]7]5]4a]4 Passo | 7 |13] 5 | 4 | 4 | Trocaacomb \_F abe af passo2 [5 | 7 | 13] 1 | 4 | Insere enoinicio da lista Ly. (ae b so "empurrados” para o final) a bed ft Passo3 [4 | 5 | 7/43] 4 | Insere dno inicio da lista ed abcd ft Passo4 1 4 | 4/5 | 7 | 43 | Insere fentreae b. Figura 8.3: Seqiiéncia de ordenacio por insergio, Programa 8.4: Fungio Insert /* programa_insercao_O1.c*/ #include 4 | void insert(int pittem[], int iQrdElementos) ( register int i,j, iAux; for( i=l; iciQtdElementos; i++) 9 iAux = piltem{i; 110 Estrutura de Dados com Algoritmos e C for { piltem[j+1]=piltem{j); je=0 && iAux < piltem[j); j-) } 4 piltemfjs: } return; } Aux; 19 | int main(void ( int iContador; int alnsert{] = { 13,7,5,1,4}; 24) insert(alnsert, 5); printi("ordenado:"); for(iContador = 0; iContador < 5; iContador++) { 29) printf" $d", alnsert{iContador] ); ) printi("\n"); 34) return 0; PQtdElementos RueR Figura 8.4: Algoritmo da ordenagio por insergio Ordenagio 111 Algoritmo 8.3: Ordenagio por Insergio Input: A tabela (B) ser ordenada Output: A tabela (B) ordenada 1 begin for i = 2 to comprimento de B do valor <— Bii] insira B[i] na sequéncia ordenada de B[l ...i- 1] jai-l vhile j > 0 F Bij] > valor do troque Bij + 1) com Bij] jmij-l 2 3 4 5 6 7 8 9 endw 1 BUj+1] — valor; un | endfor 12 return B as end 8.4 QuickSort © algoritmo QuickSort é do tipo divisio ¢ conquista. Um algoritmo deste tipo resolve varios problemas quebrando um determinado problema em mais (¢ menores) subproblemas {11}. O algoritmo, publicado pelo professor C.A.R. Hoare em 1962, baseia-se na idéia simples de partir um vetor (ou lista a ser ordenada) em dois subvetores, de tal maneira que todos os elementos do primeiro vetor sejam menores ou iguais a todos os elementos do segundo vetor. Estabelecida a divisio, o proble- ma estar resolvido, pois aplicando recursivamente a mesma técnica a cada um dos subvetores, 0 vetor estar ordenado ao se obter um subvetor de apenas 1 elemento. Os passos para ordenar uma sequéncia S an}é dado por (1): 453 3 ay 1. Seleciona um elemento do conjunto S. O elemento selecionado (p) é chamado de pivé. 2. Retire p de Se particione os elementos restantes de S em 2 seqiiéncias distintas, Le G. 112 Estrutura de Dados com Algoritmos e C 3. A particao L deverd ter os elementos menores ou iguais ao elemento pivé p, enquanto que a particio G conteré os elementos maiores ou iguais a p. 4. Aplique novamente o algoritmo nas partigdes Le G. Para organizar os itens, tais que os menores fiquem na primeira parti¢ao e os maiores na segunda parti¢ao, basta percorrer 0 vetor do inicio para o fim e do fim para o inicio simultaneamente, trocando os elementos. Ao encontrar-se no. meio da lista, tem-se a certeza de que os menores estado na primeira partigio e os maiores na segunda particio. A figura 8.5 ilustra 0 que se passa quando se faz a particao de um vetor com a sequéncia de elementos S = { 7, 1, 3, 9, 8 4 2, 7, 4 2, 3, 5}. Neste caso 0 pivd —— . oe . 1+12 é 4, pois é 0 valor do elemento que esta na sexta posi¢io (e 6 é igual a —— ). 2 A escolha do elemento pivé é arbitréria, pegar o elemento médio é apenas uma das possiveis implementagdes no algoritmo [15]. Outro método para a escolha do pivé consiste em escolher trés (ou mais) elementos randomicamente da lista, ordenar esta sublista e pegar 0 elemento médio [13]. 4 | pivo Pimeita trea Posigges 10-11 Segunda rec Posigies 4¢ 10 Torcelra toca Posies Se yi sitfafefafs fet 7 [sol 7] 5 | oe vece Posgaes 67 3/1}3l2;a]2}a|7\elol7]s5 ‘Os pareurses cruzaram-se; agora repete-2e 0 procodimorte, ocusivamonte, para os subvotoros fire as posicdes 1 © 0 @erite as posigdes 7 © 12 Figura 8.5: Ordenacio QuickSort Ordenagio 113 O QuickSort pode ser implementando pelos algoritmos 8.4 ¢ 8.5 [9]. O tem- po de execugao do algoritmo depende do fato de o particionamento ser balan- ceado ou nio, e isso por sua vez depende de quais elementos sio usados para particionar. Se o valor do pivd, para cada particao, for o maior valor, entio o algoritmo se tornaré numa ordenagio lenta com um tempo de processamento 7’. A complexidade é dada por log, 7 no melhor caso ¢ caso médio. O pior caso dado n? comparacées [15, 9]. Algoritmo 8.4: QuickSort Input: Vetor V, indice inicial p, indice final r Output: Vetor V, ordenado 1 begin 2 | if p void qs( char ‘item, int let, int right); void qs( char “item, int left, int right) { inti; char x,y; left; right; item [ (leftsright)/2 J; /* elemento pivo */ 10 7° particao das listas */ do | 114 Estrutura de Dados com Algoritmos e C /* procura elementos maiores que o pivé na primeira parte’/ while(item[illeft) 25 ( na segunda parte */ i } iffic-j) 30 ( /* processo de troca (ordenagao) */ y = itemfil; itemfi] = itemf]; item] i ) whilegi Hinclude void mergesort(int v{),int inicio,int fim) ; void intercala(int v[}, int inicio, int meio, int fim); 118 9 19 9 Estrutura de Dados com Algoritmos ¢ C void mergesort(int v],int inicio,int fim) ( int meio; if (inicio < fim) ( meio = (iniciosfim)/2; mergesort(\,inicio,meio); mergesort(v,meio+ I, fim); intercala(y, inicio, meio, fim); ) return ; void intercala(int v{}, int inicio, int meio, int fim) ( /* intercalagao no vetor tempordrio auxiliar “/ intij,k, *auxiliar; auxiliar = (int *) calloc(sizeof(int) , fim-inicio+1); while( ic=meio && j<=fim ) if vil vil) { auxiliar{k] = vii); else auailiar{ = vi; ines ) kas ) while( i <= meio ) auxiliar(k] = v{i}; kes while( <- fim) { auxiliar{k] = v{j}; ins Kea 0 4 9 84 realizada junto com o processo de ordenagio) do MergeSort, no programa 8.7 Ordenagio /* copia vetor intercalado para o vetor original */ for( i = 0; i< (fim - inicio)41; i++) { v[inicio + i] = auxiliar{ij; free(ausiliar); return; int main(void) int iContador; int aMerge(] = { 91,11,13,17,19,21,1,3,89,24 mergesort(aMerge, 0, 10); printf("Ordenado for(iContador = 0; iContador < 11; iContador++) { printf(" $d", aMerge[iContador] ); } printf(“\n"); return 0; 119 No algoritmo 8.8 é vista uma versio mais simplificada (a intercalagao é vista a implementagio do algoritmo. Programa 8.7: Ordenagio MergeSort 7* programa_merge 2.c*/ Hinclude Hinclude void mergesort(int v[],int inicio,int fim) int i,jk,meio,*auxiliar; iffinicio 120 20 30 40 50 Estrutura de Dados com Algoritmos ¢ C return; } /* ordenagio recursiva das duas metades */ meio = (inicio+fim)/2; mergesort(,;inicio,meio); mergesort(v;meio+,fim); /* intercalacao no vetor temporério auxiliar */ auxiliar = (int *) malloc(sizeof(int) * (fim-inicio+1)); while(icmeio+1 | j Nivel 2.) ——p» Nivel 3.) ———» Figura 9.3: Arvore Bindria completa de nivel 3 9.3 Arvores de Busca Binaria Uma arvore de busca bindria (Binary search tree) é uma drvore binaria onde a informagao que o né esquerdo possui é menor ou igual & informagao da chave. De forma andloga, a informagio que 0 né direito possui é maior ou igual & in- formacio da chave. O objetivo de organizar dados em arvores de busca bindria é facilitar a tarefa de procura de um determinado valor. A partir da raiz e de posse da informacao a ser encontrada, é possivel saber qual o caminho (galho) a ser percorrido até encontrar 0 né desejado. Para tanto, basta verificar se 0 valor procurado é maior, menor ou igual ao né que se esté posicionando. Deve-se observar que nao existe uma tinica forma de organizar um conjunto de informagées em uma Arvore de busca binéria, afinal, dependendo da escolha do né raiz, obtém-se arvores diferentes. Na figura 9.4 os valores ({ 2, 3, 5, 5, 7, 8}) sio organizados em drvores de busca de duas maneiras diferentes. A () Figura 9.4: Arvore de busca binéria - duas organizagbes diferentes 130 Estrutura de Dados com Algoritmos e C As duas érvores contém exatamente os mesmos valores, porém possuem es- truturas diferentes. Enquanto a arvore A esta enraizada em um dos nés de valor 5, a arvore B est enraizada no né de valor 2. Supondo que se esté buscando 0 valor 8 nas arvores, as comparacées seriam como se segue na tabela 9.1. Tabela 9.1: Comparacées para busca de um elemento Arvore A’ Arvore B | 8>5 8>2 | 8>7 8>3 | Encontrad 8>7 | Encontrado! _| Na arvore A, so realizadas menos comparagées em relagio & utilizada na arvore B, O melhor caso iré ocorrer quando a arvore estiver cheia, neste caso a procura de um item teré tempo proporcional a log 7, enquanto o pior caso ocor- rerd quando todos os nés da arvores apontarem somente para um dos lados, caso em que o tempo de processamento da procura ser proporcional a7. 9.4 Operagées em Arvores Binarias 9.4.1 Insergao A inser: Jo comeca com uma busca procurando pelo valor na arvore. Se 0 elemento nao existir na avore, é alcangada a folha, ¢ entio inserido o valor nesta posicao. Ou seja, é examinada a raiz ¢ introduzido um novo né na subarvore da esquerda, se o valor novo é menor do que a raiz, ou na subarvore da direita, se o valor novo for maior do que a rai Os algoritmos 9.1 (versio iterativa) ¢ 9.2 (versio recursiva) demonstram 0 processo de inclusio de um elemento na arvore. Arvores Bindrias 131 Algoritmo 9.1: Inserir elemento na arvore - iterativo Input: Arvore tree, elemento « 1 begin no <— NULO atual — raiz de inee while atual # NULO do 2 s 4 5 no — atual 6 if x < atual then r 8 9 | atual — esquerda de tree else | atual — direita de tree 10 endif n | endw 12 | Arvore vazia, primeiro elemento € a raiz 13 | if no= NULO then 4 | raiz de tree — x as | else 16 if x < no (campo chave) then 7 | esquerda de no — x 18 else 19 | direita de no — x 20 ondif a | endif 22 | return 23 end Algoritmo 9.2: Inserir elemento na arvore - recursivo Input: Arvore tre, elemento « 1 begin 2 | Arvore vazia, primeiro elemento é a raiz 3 | if tree = NULO then tree — x 4 | else if x < tree (campo chave) then 5 tree — esquerda de iree 6 Inserir(tree,x) 1 | else if x> tree (campo chave) then 8 tree — direita de tree ° Inserix(tree.x) ao | endif u | return a2 end 132 Estrutura de Dados com Algoritmos e C Os algoritmos deixam claro 0 processo de inser¢ao, 0 novo valor é primeiro comparado com o valor da raiz. Se seu valor for menor que a raiz, é comparado entio com o valor do filho da esquerda da raiz. Se seu valor for maior, entio compara-se com 0 filho da direita da raiz. Este processo continua até que se chegue a um né folha, ¢ entao adiciona-se 0 filho a direita ou 4 esquerda, depen- dendo de seu valor ser maior ou menor que o valor da folha. 9.4.2 Pesquisa Para a busca em uma drvore bindria por um valor especifico deve-se exa- minar a raiz. Se o valor for igual & raiz, o valor existe na arvore. Se o valor for menor do que a raiz, entio deve-se buscar na subérvore da esquerda, ¢ assim recursivamente em todos os nés da subarvore. Similarmente, se 0 valor for maior que a raiz, entdio deve-se buscar na sub- arvore da direita. Até alcangar 0 né-folha da arvore, encontrando-se ou nao o valor requerido. Esta operacio efetua /og n operagdes no caso médio ¢ 7 no pior caso quan- do a arvore esté desequilibrada; neste caso, a arvore é considerada uma drvore degenerada. Os algoritmos 9.3 (versio iterativa) ¢ 9.4 (versio recursiva) demonstram 0 processo de pesquisa de um elemento na érvore. Algoritmo 9.3: Pesquisar elemento na arvore - iterativo Input: Arvore free, elemento « Output: Verdadeiro ou falso 1 begin 2 | while tree 4 NULO E tree (campo chave) 3 if x < tree (eampo chave) then. tree — esquerda de tree 4 else if x > tree (campo chave) then 5 | tree — direita de tree 6 else , | return verdadeiro 8 endif 9 | endw 10 | return falso a1 end Arvores Bindrias 133 Algoritmo 9.4: Pesquisar elemento na arvore - recursivo Input: Arvore free, elemento « Output: Verdadeiro on falso 1 begin 2 | if tree 3 | else if 1 < tree (campo chave) then 4 tree —— esquerda de iree 5 return Pesquisar(tree,x) 6 | else if 1> tree (campo chave) then , 8 9 ‘LO then return Falso tree — direita de tree return Pesquisar(trce,x) endif 10 return Verdadeiro a end 9.4.3 Exclusao O proceso de exclusio de um né é mais complexo que as operacées ante- riores, distintos para realizar a exclusio [9]. Exclusao na folha Para excluir um né de uma 4rvore bindria, deve-se considerar trés casos A exclusio de um né que se encontra no fim da arvore, isto é, que seja uma fo- Iha, é 0 caso mais simples de exclusio. Basta remover 0 né da drvore (figura 9.5). Figura 9.5: Exclusio de folha Exclusao de né com um filho Caso o né que serd excluido tenha um tinico filho, o pai do né (avé do filho) herda o filho. Isto é, o filho assume a posigio do pai na drvore (figura 9.6). 134 Estrutura de Dados com Algoritmos e C 95, Figura 9.6: Exclusio de um né com um filho Exclusao de né com dois filhos Se onda ser excluido tiver dois filhos, 0 processo de exclusio poderé operar de duas maneiras diferentes: * Substituir o valor do né a ser retirado pelo valor sucessor (0 né mais a esquerda da subdrvore direita). * Substituir o valor do n6 a ser retirado pelo valor antecessor (0 né mais a direita da subarvore esquerda). Realizada a escolha, remove-se 0 né sucessor (ou antecessor). A figura 9.7 exemplifica a operagio. O né com valor 50 sera excluido e possui como sucessor o valor 55 e como antecessor imediato do 55 0 né com valor 53. Desta forma, o filho (53) do né com valor 55 seré promovido no lugar do né a ser excluido (50), © né 55 continuard em sua posi¢ao ¢ o filho do n6 53 (no caso 0 né com valor 54) sera passado para o né de valor 55. Estas operagdes podem ser vistas nos algoritmos 9.5 ¢ 9.6. 6 Figura 9.7: Exclusao de um né com dois filhos Arvores Bindrias 135 Algoritmo 9.5: Exclusio na arvore Input: Arvore free, elemento x Output: Verdadeiro on falso 1 begin 2 | if tree = NULO then return Falso 3 | else if 1 < tree (campo chave) then 4 tree <— esquerda de tree 5 return Excluir(tree,x) 6 | else if 1> tree (cumpo chave) then 7 tree —— direita de tree 8 return Excluir(tree,x) 9 | endif 10 | else u auxiliar — tree 2 if direita de auriliar = NULO then tree — esquerda de auriliar 18 else if esquerda de auriliar = NULO then u | tree — direita de auciliar 15 else 16 | Sueessor(auxiliar, esquerda de auriliar) a endif as | endif 19 | return Verdadeiro 20 end Algoritmo 9.6: Sucessor Input: Arvore g, Arvore r 1 begin if direita de r # NULO then | Sucessor(q, direita de r) else qr endif return 2 3 4 5 6 1 — esquerda de r 1 8 end 136 Estrutura de Dados com Algoritmos e C 9.4.4 Maior elemento O maior elemento da arvore, né com o maior valor, seré encontrado sempre na folha mais 4 direita da arvore [9]. Para encontrar 0 maior valor, basta procurar a partir da raiz sempre na subarvore da direita (algorimo 9.7). Algoritmo 9.7: Maior elemento da drvore Input: Arvore tree Output: Maior elemento 1 begin 2 | while ree # NULO do 3 maior — tree (campo informagao) 4 tree —— direita de tree 5 | endw 6 return maior vend 9.4.5 Menor elemento O menor elemento da drvore, né com o menor valor, seré encontrado sempre na folha mais & esquerda da drvore [9]. Para encontrar 0 menor valor, basta procurar a partir da raiz sempre na subarvore da esquerda (algorimo 9.8), 9.4.6 Percorrendo uma arvore Uma operagio comum é percorrer uma arvore binaria, 0 que consiste em visitar todos os nés desta arvore segundo algum critério. Esse percurso, também chamado de travessia da arvore, pode ser feito de trés formas [15, 16): * pré-ordem ou profundidade - 0s filhos de um né sao processados apés o nd. * pés-ordem - os filhos sio processados antes do né. * em-ordem ou simétrica - em que se processa o filho a esquerda, 0 né, ¢ finalmente o filho & direita. Arvores Bindrias 13 Algoritmo 9.8: Menor elemento da arvore Input: Arvore tree Output: Menor elemento 1 begin 2 | while ince # NULO do 3 menor «— tree (campo informagio) 4 tree — esquerda de tree 5 | endw 6 | return menor rend A operagio percorrer pode ser descrita nos algoritmos 9.9, 9.10 ¢ 9.11. Algoritmo 9.9: Operacao Percorre - Pré-ordem 1 begin 2 | Visita a raiz 3 | Percorre a subérvore esquerda em pré-ordem 4 | Percorre a subérvore direita em pré-ordem 5 end Algoritmo 9.10: Operagio Percorre - Pés-ordem 1 begin Percorre a subérvore esquerda em pés-ordem Percorre a subdrvore direita em pds-ordem Visita a raiz 5 end Algoritmo 9.11; Operagio Percorre - Em-ordem 1 begin 2 | Percorre a subdrvore esquerda em pés-ordem 3 | Percorre a subérvore direita em pés-ordem 4 | Visita a raiz 5 end 138 Estrutura de Dados com Algoritmos e C 9.5 Representacées de arvores em C Arvores bindrias podem ser representadas como um vetor de filhos (progra- ma 9.1 ou de forma dindmica (programa 9.2). Programa 9.1: Representagio com vetor de filhos #define FILHOS 4 typedef struct NO { 4| int info; struct NO “pai; struct NO “filhos[FILHOS}; }NO; Programa 9.2: Representagio dinamica typedef struct NO ( int info; 3 | — struct NO “pai; struct NO “filho; /* 12filbo */ struct NO *irmao; /* privimo irmao"/ NO; A representacio no formato de vetores da figura 9.2 pode ser verificada na figura 9.8. Na figura fica clara a representagao de arvores, com vetores, ter limi- tages para a quantidade de filhos. svenuto ee ton fatwfeoly [uly 8 e|_titos uk cJelele [eis & ie 08 £ fivos {r[e@lelely[»}—_ n] fe sys [s [_ | ¢ & fe i € Athos a toe cl@)*||sn (fel [ede ]f [ele [@ [eye Figura 9.8: Representagio com vetores Arvores Binirias 139 Se no programa 9.2 0 campo filbo for um ponteiro para subarvore esquerda € 0 campo irmao como o ponteiro para a raiz da subarvore direita, tem-se uma drvore binéria como a representada na figura 9.9. ve es & ——_ ar of fn 7 ' + oe Jun) ete] |, fm] fe] e]s uk i a a | & [run [rue HE Se Jw? ete YF Yuore Pave Figura 9.9: Representagio dindmica Com 0 programa 9,3 tem-se a representagio de uma drvore bindria confor- mea figura 9.3 vista anteriormente. Programa 9.3: Representacio dindmica de uma érvore bindria typedef struct NO { int info; struct NO *esquerda; 4 | struct NO “direita; struct NO “pai; NO; 9.6 Implementacao em C No programa 9.4 podem ser vistas todas as operagées ¢ algoritmos concei- tuados anteriormente. Programa 9.4: Implementagio das operagdes 7° programa_arvore O1.c*/ #include 140 19 34 9 Estrutura de Dados com Algoritmos ¢ C typedef struct NO ( int info; struct NO “esquerda, “direita; ) node, “arvore; arvore root = NULL; arvore pesquisar(arvore, int); int proxmaior(int); void inserir(arvore *, int); void imprimir(arvore, int); void range(arvore, int, int); int excluir_menor(void) void excluir(arvore *, int); void del(arvore *, arvore *); void percorre_preordem(node *); void percorre_posordem(node *); void percorre_emordem(node *); int maior(node *); int menor(node * ); int main(void) i int x, y, opeao; do { printi("\nEntre com a opcao’ printf("\n inserir"); printf("\n pesquisar"); printf("\n excluir o menor"); printf(‘"\n rexcluir"); printf("\n procurar o maior qui printf("\n imprimir a arvore"); printf("\n mostrar nos do intervalo"); printf("\n percorrer"); printf("\n maior e menor”); printf("\n ---10:sair do programa\n"); printf("\n->");, fflush(stdin); scanf(" $d", &opcao); switch(opcao) { case 1: { Arvores Bindrias 141 printf("\n Informe o valor ~: 4 scanf(" 8a", &x); inserir(&root, x); imprimir(root, 0); break; } 39 case 2: { printf("\n Informe o valor ->"); scanf(""8d", Sx); if(pesquisar(root, x) != NULL) “ ( printf" Encontrado\n"); ) else ( “ print{(” Nao encontrado! \n"); ) break; ) case 3: 14 F printf" Excluido o menor = 8d\n", excluir_menor(); imprimir(root, 0); break; } 9 case 4: { printf("\n Informe o valor ~: scanf(" 8d", &x); excluir(&root, x); 84 imprimir(root, 0); break; } case 5: { 89 printf("\n Informe o valor ~: scanf(""8d", Sx); imprimir(root, 0); printf("\n --- proximo maior que = $d", proxmaior(x)); break; o } case 6: ( imprimir(root, 0); break; 9 } case 7: { 142 Estrutura de Dados com Algoritmos e C printf("\n Informe [min, max]" scanf("8d_%d",8x, &y); los range(root, x, y); break; } case 8: { 109 printf("\nPercorrendo em ordem ->"); percorre_emordem(root); printf("\nPercorrendo em pre ordem ->"); percorre_preordem(root); printf("\nPercorrendo em pos ordem ->"); 4 percorre_posordem(root); break; } case 9: { 19 printf(""\nMaior = $d”, maior(root)); printf("\nMenor = $d", menor(root)); break; } } 124] while(opcao!=10); arvore pesquisar(arvore ¥, int chave) ( 129) if(v-- NULL) ( return NULL; ) if(v->info == chave) 134 { return v; } else ifiv->info < chave) 139, { return pesquisar(v->direita, chave); } else { return pesquisar(v->esquerda, chave); ws] y } /* maior no préximo elemento informado */ int proxmaior(int chave) 199 | arvore p=-NULL, ¥; Is4 159 169 14 179 Ist 189 199 Arvores Bindrias v= root; while( v != NULL && v->info != chave) { if(v->info < chave) v=v-odireita; else ( =v v= v->esquerda; ) ) ify — NULL) ( printf("\n Elemento nao encontrado"); return -1; ) if v->direita != NULL) ( v = v-odireita; while(v->esquerda != NULL) ( v=v-sesquerda; ) return v->info; ) if(p |= NULL) ( return p->info; ) else ( return -1; ) ) void inserir(arvore *p, int chave) { if { *p = (arvore) malloc(sizeof(node)); (’p)->info = chave; (cp)->esquerda = NULL; ("p->direita = NULL; } else if((*p)->info < chave) NULL) 143 144 209 29 24 29 BO Estrutura de Dados com Algoritmos ¢ C inserir(&(("p)->direita), chave); else { inserir(S((*p)->esquerda), chave); return; void imprimir(arvore y, int nivel) inti; if(v != NULL) ( imprimir(v->esquerda, nivel+1); for(i-0; icnivel; i++) printf" "); } printf("#d\n", y->info); imprimir(v->direita, nivel+1); } return; /* mostra os nés de umm intervalo informado */ void range(arvore v, int x, int y) ( if(v == NULL ) ( return; ) if(w-sinfo >- ») ( range(v-esquerda, x,y); ) iff(e <= v->info && v->info direita, xy); return; 29 259 269 279 284 289 /* excluir 0 menor */ int excluir_menor(void) int menor; arvore P, Vj iffroot->esquerd: menor = root root = root->direita; else ( v= root; do ( p= v= v-sesquerda; ) while(v->esquerda != NULL}; menor = v->info; p-esquerda = v->direita; return menor; /* exclusao de elemento da drvore */ void excluir(arvore *p, int chave) ( arvore q; ifep { print("\n Elemento nao existe!"); nae iff chave < (*p)->info ) ‘ excluir(§&((*p)->esquerda), chave); tse if( chave > (*p)->info ) excluir(S&((*p)->direita), chave); else NULL) Arvores Bindrias 145 146 Estrutura de Dados com Algoritmos e C else if( q->esquerda == NULL) fl “p= q->direita; 299 } else { del(S&q, &(q->esquerda)); } 304 free(q); return; 309 | /* procuera sucessor para depois exchuir */ void del(arvore “q, arvore *r) { if((*r)->direita != NULL) { sid del(q, &((*r)->direita)); } else { (a)->info = (*r)->info; 319 Ca="5 = (*r)->esquerda; return; } 304 /* percorrer uma drvore utilizando o algoritmo de pré-ordem */ void percorre_preordem(node * arvore) ( iff arvore == NULL) 329] return; } printf(" $d", arvore->info); 334] percorre_preordem(arvore->esquerda); percorre_preordem(arvore->direita); return; } 339 /* percorrer uma drvore utilisando o algoritmo de pés-ordem */ void percorre_posordem(node * arvore) Arvores Bindrias 147 iff arvore aaa] NULL) return; percorre_posordem(arvore->esquerda); 349 | percorre_posordem(arvore->direita); printf(" $d", arvore->info); return; 354 | /* percorrer uma drvore utilisando no modo em-ordem */ void percorre_emordem(node * arvore) iff arvore ( 359 return; NULL) percorre_emordem(arvore->esquerda); printf(" $d", arvore->info); 364 | — percorre_emordem(arvore->direita); return; } 369 | /* pesquisa do maior elemento na drvore */ int maior(node * arvore ) { int maior; maior = arvore->info; 374 while arvore != NULL ) ( irvore->info; arvore->direita; a] return maior; /* pesquisa do menor elemento na drvore */ 384 | int menor(node * arvore) int menor; menor = arvore->info; 389 | — while( arvore = NULL) 148 Estrutura de Dados com Algoritmos e C menor = arvore-info; arvore = arvore->esquerda; 304 return menor; 9.7 Exercicio 1. Implemente os algoritmos iterativos, para manipulagio de arvores, emC. Referéncias Bibliograficas (1) [2] B] [4] [5] [6] (7) [8] 19] [10] D. Baldwin and G. W. Scragg. Algorithms and Data Structures: The Science of. Computing. Charles River Media, first edition, 2004. P. E. Black. Dictionary of Algorithms and Data Structures. U.S. National Insti- tute of Standards and ‘Technology, 2006. Ackermann’s function in www.nist. gov/dads/HTML/ackermann.hunl. P. Deshpande and O. Kakde. C and Data Structures. Charles River Media, first edition, 2004. J. Keogh and K. Davidson. Data Structures Demystified. McGraw-Hill/Os- borne, first edition, 2004. B. W. Kernighan and D. Ritchie. The C Programming Language. Prentice Hall, second edition, 1988. D.E. Knuth. The Art of Computer Programming: Fundamental Algorithms, volume 3. Addison-Wesley, third edition, 1997. A. Koening. C Traps and Pitfalls, Addison-Wesley, first edition, 1989. R. Lafore. Teach Yourself Data Structures and Algorithms in 24 hours. Samns Publishing, first edition, 1999. C.E. Leiserson, C, Stein, R. L. Rivest, and ‘T. H. Cormen. Introduction to Algorithms. MIT Press and McGraw-Hill, second edition, 2001. F. Lorenzi, PN. de Mattos, and T. P. de Carvalho. Estrutura de dados. Thomson, first edition, 2007. 150 Estrutura de Dados com Algoritmos e C [11] B.R. Preiss. Data Structures and Algorithms with Object-Oriented Design Patterns in C++. John Wiley and Sons, first edition, 1998. {12] D. D. Salvetti and L. M. Barbosa. A/goritmos. Pearson Makron Books, first edition, 1998. [13] H. Schildt. C The Complete Reference. McGraw-Hill/Osborne, fourth edi- tion, 2000. 14] E.A. Schmitz and A. A. de Souza Teles. Pascal ¢ técnicas de programagio. LIC Editora, third edition, 1988. [15] A.M. Tenenbaum, Y. Langsam, and M. J. Augenstein. Estruturas de Dados Usando C. Pearson, first edition, 1995. [16] P. Veloso, C. dos Santos, P, Azeredo, and A. Furtado. Estrutura de Dados. Editora Campus, first edition, 1983. 16. Tiragem. [17] Wikipedia. The free encyclopedia. http://en.wikipedia.org, 2007. ‘Trian- gular Number. [18] Wikipedia. The free encyclopedia. http://en.wikipedia.org, 2007. ‘Tower of Hanoi. [19] Wikipedia. The free encyclopedia. http://en.wikipedia.org, 2007. Acker- mann function. [20] Wikipedia. The free encyclopedia. http://en.wikipedia.org, 2007. Merge Sort. [21] N. Wirth. Algorithms and Data Structures. Prentice Hall, first edition, 1985. indice Remissivo A E algoritmo de Euclides 68 empty 41, 48 alocagio dinamica 19 estrutura de dados 125 alocagio estitica 19 estruturas de dados 1 alocagio estética de meméria 14,22 __estruturas estéticas 19 arquivo 94 estruturas hierdrquicas 125 drvore bindria 126 drvore de busca bindria 129 F drvore estritamente bindria 128 Fibonacci 66 drvores 125 FIFO 48 fila 48, 51,79 B free 22 Bancos de dados 94 front 48 bidimensional ou multidimensional 5 fungio fatorial 61 BubbleSort 100 fungdes recursivas 76, 77 Cc H calloc 21, 25 heterogénea 16 conjunto ordenado de itens 40 I D informagées 1 dados 2 insert ou enqueue 48 dados homogéneos 2 ividir conquistar 97 L ivisio e conquista 111, 115 LIFO - last in first out 40 152 Estrutura de Dados com Algoritmos e C lista 79 lista duplamente encadeada 79 lista € nao ordenada 79 lista encadeada 86 listas 82 Listas encadeadas 79, 86 lista simplesmente encadeada 79 logn 132 M malloc 21, 25 matriz 5,94 matrizes n-dimensionais 8 maximo divisor comum (MDC) 68 MergeSort 115 N ntimero triangular 63 Oo operador 11 operador de ponteiro 11 Ordenagao 100 ordenacio por insergio. 107, 108 ordenacio por selegao. 104, 105 P passar variéveis por referéncia 12 pesquisa binéria 94, 97 pesquisa seqiiencial 94 pilha 40, 79 ponteiro 11, 14 ponteiros para ponteiros 27 pop 41 push 41 Q QuickSort 111 R realloc 21 Recursio é 60 recursividade 60 registro 16, 94 remove ou dequeue 48 Ss seqiiéncia de Fibonacci 66 size 41, 48 stackpop 41 struct 25 T tabela 94 tipo de dados 2 ‘Torre de Hanoi 71 travessia da drvore 136 Vv vetor 2, 51, 82, 94 vetor de ponteiros 20 Vetores 82

Você também pode gostar