Você está na página 1de 176

Estrutura de Dados

com Algoritmos e C
Escrever um vro no uma tarefa fc, nunca fo. Toma tempo,
exge pesqusa e dedcao. Como as edtoras no dese|am mas
pubcar este ttuo, nada mas natura que tentar comercaza-o
na forma de arquvo (semehante a um ebook). Mas, esta atvdade
tambm toma tempo e exge dedcao.
Pensando assm, resov berar este vro para consuta pbca,
se voc acha que este vro te a|udou e quser coaborar comgo,
passe numa otrca, e deposte o vaor que achar que deve.
Termnou de pagar a conta de uz, teefone ou gua e sobrou
troco, voc pode depostar na mnha conta. Eu acredto que ns
dos podemos sar ganhando, voc porque teve acesso a um
bom matera que te a|udou e eu como ncentvo a contnuar
escrevendo vros. Caso de certo, tavez os prxmos vros nem
se|am pubcados por uma edtora e este|am berados dretamente
para sua consuta.
Ouaquer vaor depostado ser dreconado para a conta poupana
do meu ho, para quando ee estver na maordade ter recursos
para comear um negco prpro, nancar seus estudos, etc.
Dados para depsto:
Marcos Aureo Pchek Laureano.
Banco: 104 - Caxa Econmca Federa
Agnca: 1628
Operao: 001
Conta: 6012-2
Curtba, 13 de maro de 2012.
Estrutura de Dados
com Algoritmos e C
Marcos Laureano
BRASPORT Livros e Multimdia Ltda.
Rua Pardal Mallet, 23 Tijuca
20270-280 Rio de Janeiro-RJ
Tels. Fax: (21) 2568.1415/2568.1507/2569.0212/2565.8257
e-mails: 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 So Paulo-SP
Tel. Fax (11): 3287.1752
e-mail: ilialsp@brasport.com.br
Copyright 2008 por Brasport Livros e Multimdia Ltda.
Todos os direitos reservados. Nenhuma parte deste livro poder ser reproduzida, sob qualquer
meio, especialmente em fotocpia (xerox), sem a permisso, por escrito, da Editora.
Editor: Sergio Martins de Oliveira
Diretora Editorial: Rosa Maria Oliveira de Queiroz
Assistente de Produo: Marina dos Anjos Martins de Oliveira
Reviso de Texto: Maria Helena A. M. Oliveira
Editorao Eletrnica: Abreus System LTDA.
Capa: Use Design
Tcnica e muita ateno foram empregadas na produo deste livro. Porm, erros de digitao e/ou impresso podem
ocorrer. Qualquer dvida, inclusive de conceito, solicitamos enviar mensagem para brasport@brasport.com.br,
para que nossa equipe, juntamente com o autor, possa esclarecer. A Brasport e o(s) autor(es) no assumem qualquer
responsabilidade por eventuais danos ou perdas a pessoas ou bens, originados do uso deste livro.
Dados Internacionais de Catalogao na Publicao (CIP)
(Cmara Brasileira do Livro, SP, Brasil)
Aos meus genitores Luiz Otvio (in memoriam) e Natlia.



Agradecimentos
Agradeo Brasport por mais esta oportunidade de publicar um livro sobre
um tema em que vrios autores j trabalharam ( claro que este livro tem um
diferencial em relao aos demais).
Aos meus colegas professores e alunos que ajudaram a evoluo deste ma-
terial nos ltimos anos.
E para uma linda or, que, alm de alegrar os meus dias, corrigiu os assas-
sinatos lngua portuguesa.



Sobre o autor
Marcos Laureano tecnlogo em Processamento de Dados pela ESEEI,
Ps-graduado em Administrao pela FAE Business School e Mestre em Infor-
mtica Aplicada pela Pontifcia Universidade Catlica do Paran. Doutorando
em Informtica Aplicada pela Pontifcia Universidade Catlica do Paran.
Trabalha com programao em C no ambiente Unix (AIX/HP-UX) desde
1997 e Linux desde 2000, sendo especialista em segurana de sistemas operacio-
nais.
professor de graduao e ps-graduao, tendo lecionado em vrias ins-
tituies nos ltimos anos.
autor de vrios guias de utilizao/congurao de aplicativos para os
ambientes Unix e Linux. Possui artigos e livros publicados sobre programao,
sistemas operacionais e segurana de sistemas. Dentre eles, Programando em
C para Linux, Unix e Windows, tambm publicado pela Brasport.
Atualmente leciona disciplinas relacionadas segurana, programao, re-
des de computadores e sistemas operacionais em cursos de graduao e ps-gra-
duao do Centro Universitrio Franciscano (UNIFAE) e atua como consultor
na rea de projetos de desenvolvimento e segurana de sistemas.
O autor pode ser contactado pelo e-mail marcos@laureano.eti.br ou atra-
vs de sua pgina www.laureano.eti.br, onde est disponvel vasto material so-
bre programao, segurana de sistemas e sistemas operacionais.



Sobre o Livro
O crescimento dos cursos tecnolgicos especcos e com curta durao (2
a 3 anos) gerou uma demanda de livros que tratam diretamente o assunto de
maneira clara e eciente.
Este livro indicado aos cursos tecnolgicos, para estudantes e prossionais
que precisem dominar os conceitos e os algoritmos de forma rpida e precisa.
O material foi preparado com a experincia do autor em lecionar a discipli-
na, somada sua experincia prossional. Outros professores e coordenadores
de cursos foram consultados; com isto, este material tem os assuntos pertinentes
rea e pode ser adotado tranqilamente em cursos de 40 ou 80 horas de Estru-
tura de Dados ou Programao de Computadores.
Os algoritmos podem ser aplicados e convertidos para qualquer linguagem
de programao, e os programas em C so simples e objetivos, facilitando o
entendimento dos estudantes e prossionais que no dominam totalmente esta
linguagem.



Sumrio
1. Estrutura de Dados ................................................................................ 1
1.1 Dados Homogneos ................................................................................ 2
1.1.1 Vetor ........................................................................................... 2
1.1.2 Matriz .......................................................................................... 5
1.1.3 Ponteiros ................................................................................... 11
1.2 Dados Heterogneos ............................................................................. 16
1.3 Exerccios ............................................................................................... 18
2. Uso de Memria ................................................................................... 19
2.1 Alocao de memria Esttica x Dinmica ........................................... 19
2.2 Alocao dinmica de memria ............................................................. 20
2.3 Funes para alocao de memria ....................................................... 20
2.3.1 Funo malloc ........................................................................... 21
2.3.2 Funo calloc ............................................................................ 21
2.3.3 Funo realloc ........................................................................... 21
2.3.4 Funo free ............................................................................... 22
2.4 Utilizando as funes para alocao de memria ................................. 22
2.5 Alocao de memria e estruturas em C ............................................... 25
2.6 Ponteiros para ponteiros mistrio ou no .......................................... 27
2.7 Mais alocao de vetores e matrizes como ponteiros ........................... 29
2.7.1 Controle de agenda com ponteiros de estruturas e vetores ..... 33
3. Pilha ...................................................................................................... 40
3.1 Representao das Operaes com Pseudo-cdigo .............................. 42
3.2 Pilhas em C ............................................................................................ 42
3.3 Exerccios ............................................................................................... 47
XIV Estrutura de Dados com Algoritmos e C
4. Fila ........................................................................................................ 48
4.1 Representao de Filas com Pseudo-cdigos........................................ 49
4.2 Filas em C .............................................................................................. 51
4.3 Exerccios ............................................................................................... 59
5. Recursividade ....................................................................................... 60
5.1. Funo para clculo de Fatorial .......................................................... 61
5.2 Nmero triangular ................................................................................ 63
5.3 Nmeros de Fibonacci .......................................................................... 66
5.4 Algoritmo de Euclides ........................................................................... 68
5.5 Torres de Hanoi ..................................................................................... 71
5.6 Curiosidades com Recursividade........................................................... 74
5.7 Cuidados com Recursividade ................................................................ 76
5.8 Vantagens ............................................................................................... 77
5.9 Exerccios ............................................................................................... 77
6. Lista ...................................................................................................... 79
6.1 Vetores ou alocao dinmica? .............................................................. 82
6.2 Listas em C ............................................................................................ 84
6.3 Exerccios ............................................................................................... 93
7. Pesquisa ................................................................................................ 94
7.1 Pesquisa Seqencial ............................................................................... 94
7.2 Pesquisa Binria ..................................................................................... 97
7.3 Exerccios ............................................................................................... 99
8. Ordenao ........................................................................................... 100
8.1 BubbleSort ........................................................................................... 100
8.2 Ordenao por Seleo ........................................................................ 104
8.3 Ordenao por Insero ...................................................................... 107
8.4 QuickSort ............................................................................................ 111
8.5 MergeSort ............................................................................................ 115
8.6 Exerccios ............................................................................................. 124
9. rvores Binrias ................................................................................. 125
9.1 Analogia entre rvores ......................................................................... 125
9.2 rvore binria ...................................................................................... 126
9.2.1 Relaes .................................................................................. 127
Sumrio XV
9.2.2 rvore Binria Completa ....................................................... 128
9.3 rvores de Busca Binria ..................................................................... 129
9.4 Operaes em rvores Binrias .......................................................... 130
9.4.1 Insero ................................................................................... 130
9.4.2 Pesquisa ................................................................................... 132
9.4.3 Excluso .................................................................................. 133
9.4.4 Maior elemento ...................................................................... 136
9.4.5 Menor elemento ..................................................................... 136
9.4.6 Percorrendo uma rvore ......................................................... 136
9.5 Representaes de rvores em C ......................................................... 138
9.6 Implementao em C .......................................................................... 139
9.7 Exerccio .............................................................................................. 148
Referncias Bibliogrcas ....................................................................... 149
ndice Remissivo ..................................................................................... 151



Lista de Programas
1.1: Declarao de vetor em C ............................................................................. 4
1.2: Exemplo de uso de vetores ............................................................................ 5
1.3: Exemplo de uso de matrizes .......................................................................... 9
1.4: Exemplo de uso de matrizes com vrias dimenses .................................... 10
1.5: Exemplo de uso de ponteiros ...................................................................... 12
1.6: Ponteiros como referncia em funes ....................................................... 13
1.7: Aritmtica com ponteiros ............................................................................ 14
1.8: Vetor como ponteiro ................................................................................... 15
1.9: Exemplo de estrutura .................................................................................. 17
1.10: Exemplo de uso de estruturas com vetores ............................................... 17
2.1: Declarao de vetor como ponteiro ............................................................ 22
2.2: Declarao de matriz como ponteiro .......................................................... 22
2.3: Exemplo de uso do malloc e realloc ............................................................ 23
2.4: Exemplo de uso do calloc ............................................................................ 24
2.5: Exemplo de uso de estruturas com ponteiros ............................................. 25
2.6: Ponteiro para ponteiro ................................................................................ 28
2.7: Exemplo de uso de alocao de matrizes .................................................... 30
2.8: Exemplo de uso de alocao de matrizes dentro de funes ...................... 32
XVIII Estrutura de Dados com Algoritmos e C
2.9: Exemplo completo de uso de vetor (ponteiros) de estruturas .................... 33
3.1: Exemplo de manipulao de pilha ............................................................... 44
3.2: Exemplo de manipulao de pilha com estrutura ....................................... 45
4.1: Exemplo de manipulao de la em C ........................................................ 51
4.2: Reajuste da la ............................................................................................. 53
4.3: Declarao de estrutura circular ................................................................. 54
4.4: Manipulao de la circular em C .............................................................. 55
5.1: Fatorial (verso iterativa) ............................................................................. 61
5.2: Fatorial (verso recursiva) ........................................................................... 62
5.3: Descobrindo o nmero triangular (iterativo) ............................................. 65
5.4: Descobrindo o nmero triangular (recursivo) ............................................ 65
5.5: Clculo do n-simo termo de Fibonacci (verso iterativa) ......................... 67
5.6: Clculo do n-simo termo de Fibonacci (verso recursiva)........................ 67
5.7: Clculo do MDC iterativo .......................................................................... 69
5.8: Clculo do MDC recursivo ......................................................................... 69
5.9: Clculo do MDC recursivo ......................................................................... 70
5.10: Torre de Hanoi recursivo .......................................................................... 73
5.11: Natori - Imprimindo as fases da lua .......................................................... 74
5.12: Dhyanh - Saitou, aku, soku e zan .............................................................. 75
6.1: Exemplo de manipulao de lista simples em C ......................................... 84
6.2: Exemplo de manipulao de lista encadeada em C .................................... 86
6.3: Funes para manipulao de listas ............................................................ 88
7.1: Funo para pesquisa seqencial ................................................................. 96
7.2: Funo para pesquisa binria ...................................................................... 99
8.1: Funo BubbleSort .................................................................................... 102
8.2: Funo BubbleSort melhorado ................................................................. 103
8.3: Funo Select ............................................................................................. 106
Lista de Programas XIX
8.4: Funo Insert ............................................................................................. 109
8.5: Ordenao QuickSort ............................................................................... 113
8.6: Ordenao MergeSort ............................................................................... 117
8.7: Ordenao MergeSort ............................................................................... 119
9.1: Representao com vetor de lhos ............................................................ 138
9.2: Representao dinmica ............................................................................ 138
9.3: Representao dinmica de uma rvore binria ........................................ 139
9.4: Implementao das operaes ................................................................... 139



Lista de Tabelas
2.1: Operaes com ponteiros ............................................................................ 28
2.2: Operaes com ponteiros de ponteiros ...................................................... 29
5.1: Clculo de fatorial de 6 ............................................................................... 62
7.1: Entradas e freqncias para clculo de comparaes mdias ..................... 96
8.1: BubbleSort - primeira varredura ............................................................... 101
8.2: BubbleSort - segunda varredura ................................................................ 101
8.3: Seleo - o que ocorre em cada passo ....................................................... 105
8.4: Insero - o que ocorre em cada passo ...................................................... 109
9.1: Comparaes para busca de um elemento ................................................ 130



Lista de Algoritmos
1.1: Clculo da posio de ndices de um vetor na memria ............................... 4
1.2: Clculo da posio de ndices de uma matriz na memria ........................... 6
3.1: Vericao se a pilha est vazia (funo EMPTY(S)) ................................. 42
3.2: Colocar um item na pilha (funo PUSH(S,x)) .......................................... 43
3.3: Retirada de um item da pilha (funo POP(S)) .......................................... 43
3.4: Pega o item do topo da pilha mas no desempilha (funo
STACKPOP(S)) ....................................................................................... 43
3.5: Tamanho da pilha (funo SIZE(S)) ........................................................... 44
4.1: Incluso de dados na la (ENQUEUE(Q,x)) ............................................. 50
4.2: Retirada de dados na la (DEQUEUE(Q)) ................................................ 50
4.3: Vericao se a la est vazia (funo EMPTY(Q)) ................................... 50
4.4: Tamanho da la (funo SIZE(Q)) ............................................................. 51
4.5: Prximo elemento da la (funo FRONT(Q)) ......................................... 51
5.1: Algoritmo de Euclides ................................................................................. 69
5.2: Passar n peas de uma torre (A) para outra (C) .......................................... 72
6.1: Insero numa lista duplamente encadeada ................................................ 82
6.2: Remoo numa lista duplamente encadeada ............................................... 83
7.1: Pesquisa seqencial ...................................................................................... 95
7.2: Pesquisa seqencial com ajuste de freqncia ............................................ 97
XXII Estrutura de Dados com Algoritmos e C
7.3: Pesquisa binria ........................................................................................... 98
8.1: Ordenao Bubble ..................................................................................... 102
8.2: Ordenao por Seleo .............................................................................. 106
8.3: Ordenao por Insero ............................................................................ 111
8.4: QuickSort .................................................................................................. 113
8.5: Particiona - Diviso do vetor .................................................................... 115
8.6: MergeSort .................................................................................................. 121
8.7: Intercala ..................................................................................................... 122
8.8: MergeSort .................................................................................................. 123
9.1: Inserir elemento na rvore - iterativo ....................................................... 131
9.2: Inserir elemento na rvore - recursivo ...................................................... 131
9.3: Pesquisar elemento na rvore - iterativo ................................................... 132
9.4: Pesquisar elemento na rvore - recursivo ................................................. 133
9.5: Excluso na rvore ..................................................................................... 135
9.6: Sucessor ..................................................................................................... 135
9.7: Maior elemento da rvore ......................................................................... 136
9.8: Menor elemento da rvore ........................................................................ 137
9.9: Operao Percorre - Pr-ordem ............................................................... 137
9.10: Operao Percorre - Ps-ordem ............................................................. 137
9.11: Operao Percorre - Em-ordem ............................................................. 137



Lista de Figuras
1.1: Exemplo de Vetor .......................................................................................... 3
1.2: Representao de um vetor na memria ....................................................... 4
1.3: Matriz 2x2 - Clculo de posio na memria ............................................... 6
1.4: Clculo de posio na memria .................................................................... 7
1.5: Exemplo de Matriz ........................................................................................ 7
1.6: Uma matriz de duas dimenses vista como dois vetores .............................. 8
1.7: Chamada de funo com ponteiros ............................................................. 13
1.8: Vetor como Ponteiro em C ......................................................................... 15
1.9: Registro de funcionrio ............................................................................... 17
2.1: Matriz como vetor de ponteiros .................................................................. 20
2.2: Exemplo de ponteiro na memria ............................................................... 27
2.3: Exemplo de ponteiro para ponteiro na memria ........................................ 29
3.1: Exemplo de Pilha ......................................................................................... 40
3.2: Operaes em uma pilha ............................................................................. 42
4.1: Operaes numa la .................................................................................... 49
4.2: Operaes numa la circular ....................................................................... 55
5.1: Nmeros triangulares .................................................................................. 64
5.2: Descobrindo o quinto elemento triangular ................................................ 64
5.3: Descobrindo o quinto elemento triangular de forma recursiva ................. 65
XXIV Estrutura de Dados com Algoritmos e C
5.4: O que ocorre a cada chamada ..................................................................... 66
5.6: Torre de Hanoi ............................................................................................ 71
5.7: Movimentos conforme algoritmo ............................................................... 73
6.1: Exemplo de lista simplesmente encadeada ................................................. 80
6.2: Exemplo de lista duplamente encadeada ..................................................... 81
6.3: Incluso de novo elemento .......................................................................... 81
6.4: Excluso de elemento .................................................................................. 82
6.5: Problemtica do crescimento do vetor ....................................................... 83
6.6: Crescimento de uma lista sem utilizar vetor ............................................... 84
7.1: Processo de pesquisa seqencial .................................................................. 95
7.2: Busca binria ................................................................................................ 98
8.1: Exemplo de Ordenao por Seleo com nmeros inteiros ..................... 105
8.2: Exemplo de Ordenao por Insero com nmeros inteiros ................... 108
8.3: Seqncia de ordenao por insero ....................................................... 109
8.4: Algoritmo da ordenao por insero ....................................................... 110
8.5: Ordenao QuickSort ............................................................................... 112
8.6: Ordenao MergeSort ............................................................................... 116
8.7: Ordenao MergeSort ............................................................................... 117
9.1: Analogia entre rvores ............................................................................... 126
9.2: Representao de uma rvore .................................................................... 127
9.3: rvore Binria completa de nvel 3 ........................................................... 129
9.4: rvore de busca binria - duas organizaes diferentes ........................... 129
9.5: Excluso de folha ....................................................................................... 133
9.6: Excluso de um n com um lho .............................................................. 134
9.7: Excluso de um n com dois lhos ........................................................... 134
9.8: Representao com vetores ....................................................................... 138
9.9: Representao dinmica ............................................................................ 139
1. Estrutura de Dados

No existe vitria sem sacrifcio!
Filme Transformers
Um computador uma mquina que manipula informaes. O estudo da
cincia da computao inclui o exame da organizao, manipulao e utilizao
destas informaes num computador. Conseqentemente, muito importante
entender os conceitos de organizao e manipulao de informaes.
A automatizao de tarefas um aspecto marcante da sociedade moderna,
e na cincia da computao houve um processo de desenvolvimento simultneo
e interativo de mquinas (hardware) e dos elementos que gerenciam a execuo
automtica (software) de uma tarefa.
Nesta grande evoluo do mundo computacional, um fator de relevante im-
portncia a forma de armazenar as informaes, j que, informtica a cincia
da informao. Ento de nada adiantaria o grande desenvolvimento do hardware
e do software se a forma de armazenamento e tratamento da informao no
acompanhasse esse desenvolvimento. Por isso a importncia das estruturas de da-
dos, que nada mais so do que formas otimizadas de armazenamento e tratamento
das informaes eletronicamente.
As estruturas de dados, na maioria dos casos, baseiam-se nos tipos de ar-
mazenamento vistos dia a dia, ou seja, nada mais so do que a transformao de
uma forma de armazenamento j conhecida e utilizada no mundo real adaptada
para o mundo computacional. Por isso, cada tipo de estrutura de dados possui
vantagens e desvantagens e cada uma delas tem sua rea de atuao (massa de
dados) otimizada.
2 Estrutura de Dados com Algoritmos e C
Os dados manipulados por um algoritmo podem possuir natureza distinta,
isto , podem ser nmeros, letras, frases etc. Dependendo da natureza de um
dado, algumas operaes podem ou no fazer sentido quando aplicadas a eles.
Por exemplo, no faz sentido falar em somar duas letras - algumas linguagens de
programao permitem que ocorra a soma dos valores ASCII correspondentes
de cada letra.
Para poder distinguir dados de naturezas distintas e saber quais operaes
podem ser realizadas com eles, os algoritmos lidam com o conceito de tipo de
dados. O tipo de um dado dene o conjunto de valores que uma varivel pode
assumir, bem como o conjunto de todas as operaes que podem atuar sobre
qualquer valor daquela varivel. Por exemplo, uma varivel do tipo inteiro pode
assumir o conjunto de todos os nmeros e de todas as operaes que podem ser
aplicadas a estes nmeros.
Os tipos de dados manipulados por um algoritmo podem ser classicados
em dois grupos: atmicos e complexos ou compostos. Os tipos atmicos so
aqueles cujos elementos do conjunto de valores so indivisveis, por exemplo:
o tipo inteiro, real, caractere e lgico. Por outro lado, os tipos complexos so
aqueles cujos elementos do conjunto de valores podem ser decompostos em par-
tes mais simples. Se um tipo de dado pode ser decomposto, ento o tipo de dado
dito estruturado, e a organizao de cada componente e as relaes entre eles
constituem a disciplina de Estrutura de Dados.
1.1 Dados Homogneos
Uma estrutura de dados, que utiliza somente um tipo de dado, em sua de-
nio conhecida como dados homogneos. Variveis compostas homogneas cor-
respondem a posies de memria, identicadas por um mesmo nome, individu-
alizado por ndices e cujo contedo composto do mesmo tipo. Sendo os vetores
(tambm conhecidos como estruturas de dados unidimensionais) e as matrizes
(estruturas de dados bidimensionais) os representantes dos dados homogneos.
1.1.1 Vetor
O vetor uma estrutura de dados linear que necessita de somente um ndice
para que seus elementos sejam endereados. utilizado para armazenar uma
lista de valores do mesmo tipo, ou seja, o tipo vetor permite armazenar mais de
um valor em uma mesma varivel. Um dado vetor denido como tendo um
Estrutura de Dados 3
nmero xo de clulas idnticas (seu contedo dividido em posies). Cada
clula armazena um e somente um dos valores de dados do vetor. Cada uma das
clulas de um vetor possui seu prprio endereo, ou ndice, atravs do qual pode
ser referenciada. Nessa estrutura todos os elementos so do mesmo tipo, e cada
um pode receber um valor diferente [3, 21, 4].
Algumas caractersticas do tipo vetor([10]):
Alocao esttica (deve-se conhecer as dimenses da estrutura no
momento da declarao)
Estrutura homognea
Alocao seqencial (bytes contguos)
Insero/Excluso
Realocao dos elementos
Posio de memria no liberada
Figura 1.1: Exemplo de Vetor
A partir do endereo do primeiro elemento possvel determinar a loca-
lizao dos demais elementos do vetor. Isso possvel porque os elementos do
vetor esto dispostos na memria um ao lado do outro e cada elemento tem o seu
tamanho xo (algoritmo 1.1 [10]).
A partir do algoritmo 1.1 possvel deduzir a frmula genrica para clculo
de posio na memria de um elemento qualquer. Sendo n o elemento, a frmu-
la se d por Pos
n
= endereo Inicial + ( (n - 1) * tamanho do tipo do elemento).

4 Estrutura de Dados com Algoritmos e C


Algoritmo 1.1: Clculo da posio de ndices de um vetor na memria
A gura 1.1 mostra um vetor de notas de alunos, a referncia NOTA[4]
indica o valor 4.6 que se encontra na coluna indicada pelo ndice 4.
A denio de um vetor em C se d pela sintaxe:
tipo_do_dado nome_do_vetor[ tamanho_do_vetor ]
O programa 1.1 contm um exemplo de declarao de um vetor na lingua-
gem C. A gura 1.2 apresenta a representao deste vetor na memria do compu-
tador (lembrando que um vetor guardado na memria de forma seqencial).
Programa 1.1: Declarao de vetor em C
1 int i[3];
i[0]=21;
i[1]=22;
i[2]=24;
6 char c[4];
c[0]=a;
c[1]=b;
c[2]=c;
c[3]=d;
Figura 1.2: Representao de um vetor na memria
Estrutura de Dados 5
Programa 1.2: Exemplo de uso de vetores
/* programa_vetor_01.c */
#include <stdio.h>
5 #dene TAMANHO 5
10
int main (void)
{
int iIndice;
int iValorA;
int iSoma;
int aVetor [TAMANHO];
oat fMedia;
15
20
for (iIndice = 0; iIndice < TAMANHO; iIndice++)
{
printf("Entre com o valor %d:", iIndice + 1);
scanf("%d", &iValorA);
aVetor[iIndice] = iValorA;
}
30
iSoma = 0;
for (iIndice=0; iIndice < TAMANHO; iIndice++)
{
iSoma += aVetor[iIndice];
}
fMedia = (oat) iSoma/TAMANHO;
printf ("Media : %f\n", fMedia);
return 0;
}
Lembrete: Caso seja colocada num programa a instruo a[2]++ est sen-
do dito que a posio 2 do vetor a ser incrementada.
1.1.2 Matriz
Uma matriz um arranjo bidimensional ou multidimensional de alocao
esttica e seqencial. A matriz uma estrutura de dados que necessita de um
ndice para referenciar a linha e outro para referenciar a coluna para que seus
elementos sejam endereados. Da mesma forma que um vetor, uma matriz de-
nida com um tamanho xo, todos os elementos so do mesmo tipo, cada clula
contm somente um valor e os tamanhos dos valores so os mesmos (em C, um
char ocupa 1 byte e um int 4 bytes) [3, 21, 4].
6 Estrutura de Dados com Algoritmos e C
Os elementos ocupam posies contguas na memria. A alocao dos ele-
mentos da matriz na memria pode ser feita colocando os elementos linha-por-
linha ou coluna-por-coluna.
Para uma matriz de 2x2 (gura 1.3) o algoritmo para calcular as posies
da memria listado em 1.2 [10].
Figura 1.3: Matriz 2x2 - Clculo de posio na memria
No algoritmo 1.2, sendo C a quantidade de colunas por linhas, i o nmero
da linha e j a posio do elemento dentro linha, possvel denir a frmula ge-
nrica para acesso na memria, onde Pos
ij
= endereo inicial + ((i-1) * C * tamanho
do tipo do elemento) + ((j-1) * tamanho do tipo do elemento). A gura 1.4 demonstra
a aplicao da frmula.
A matriz LETRAS (gura 1.5) composta de 18 elementos (3 linhas e 6
colunas), a referncia a MATRIZ[3][3] (onde o primeiro 3 indica a linha e o
segundo 3 indica a coluna) retorna o elemento N; no caso de MATRIZ[2][5]
(segunda linha e terceira coluna) ir retornar o elemento E. Como uma ma-
triz de strings (linguagem C), a chamada a MATRIZ[3] ir reproduzir o valor
DONALD.
Algoritmo 1.2: Clculo da posio de ndices de uma matriz na memria
Estrutura de Dados 7
Figura 1.4: Clculo de posio na memria
Uma matriz consiste de dois ou mais vetores denidos por um conjunto de
elementos. Cada dimenso de uma matriz um vetor (gura 1.6). O primeiro
conjunto (dimenso) considerado o primeiro vetor, o segundo conjunto o se-
gundo vetor e assim sucessivamente [4].
A denio de uma matriz em C se d pela sintaxe:
tipo_do_dado nome_da_matriz[ quantidade_linhas ] [ quantidade_colunas ]
O programa 1.3 apresenta um exemplo de declarao e utilizao de matri-
zes bidimensionais na linguagem C.
Figura 1.5: Exemplo de Matriz
8 Estrutura de Dados com Algoritmos e C
Figura 1.6: Uma matriz de duas dimenses vista como dois vetores
A linguagem C permite ainda trabalhar com matrizes de vrias dimenses
(matrizes n-dimensionais), embora o seu uso que mais restrito em aplicaes
cientcas face sua pouca praticidade de uso.
A denio de uma matriz de vrias dimenses em C se d pela sintaxe:
tipo_do_dado nome_da_matriz[tamanho_dimenso_1] [tamanho_
dimenso_2]
[tamanho_dimenso_3] ... [tamanho_dimenso_n]
Estrutura de Dados 9
Programa 1.3: Exemplo de uso de matrizes
/* programa_matriz_01.c */
#include <stdio.h>
4 #dene DIMENSAO 2
9
int main (void)
{
int iLinha, iColuna;
int iDeterminante;
int iValorA;
int aMatriz [DIMENSAO][DIMENSAO
14
/* Uma regra que se pode sempre levar em considerao:
para cada dimenso de uma matriz, sempre haver um lao
(normalmente um for). Se houver duas dimenses, ento haver dois laos. */
19
24
for (iLinha=0; iLinha < DIMENSAO; iLinha++)
{
for (iColuna=0; iColuna < DIMENSAO; iColuna++)
{
printf ("Entre item %d %d:", iLinha + 1, iColuna + 1);
scanf ("%d", &iValorA);
matriz [iLinha][iColuna] = iValorA;
}
}
iDeterminante = aMatriz[0][0] * aMatriz [1][1] -
aMatriz[0][1] * aMatriz [1][0];
printf ("Determinante : %d\n", iDeterminante);
29
return 0;
}
O programa 1.4 um exemplo de denio e utilizao de matrizes multi-
dimensionais na linguagem C.
10 Estrutura de Dados com Algoritmos e C
Programa 1.4: Exemplo de uso de matrizes com vrias dimenses
4
/* programa_matriz_02.c */
#include <stdio.h>
#dene DIM_1 2
#dene DIM_2 5
#dene DIM_3 3
#dene DIM_4 4
9
int main (void)
{
int i,j,k,l;
int aMatriz [DIM_1][DIM_2][DIM_3][DIM_4];
14 /* Cdigo para zerar uma matriz de quatro dimenses */
for (i=0; i < DIM_1; i++)
19
24
{
for (j=0; j < DIM_2; j++)
{
for (k=0; k < DIM_3; k++)
{
for (l=0; l < DIM_4; l++)
{
aMatriz [i][j][k][l] = i+j+k+l;
}
}
}
}
29
34
39
44
/* Uma regra que se pode sempre levar em considerao: para cada
dimenso de uma matriz, sempre haver um lao (normalmente um for).
Se houver quatro dimensoes ento haver quatro laos */
For (i=0; i < DIM_1; i++)
{
for (j=0; j < DIM_2; j++)
{
for (k=0; k < DIM_3; k++)
{
for (l=0; l < DIM_4; l++)
{
printf("\nValor para matriz em [%d] [%d] [%d] [%d] = %d",
i,j,k,l, aMatriz[i][j][k][l]);
}
}
}
}

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 loat. A diferena do ponteiro em relao aos outros
tipos de dados que uma varivel que seja ponteiro guardar um endereo de
memria [5, 7].
Por meio deste endereo pode-se acessar a informao, dizendo que a vari-
vel ponteiro aponta para uma posio de memria. O maior problema em relao
ao ponteiro entender quando se est trabalhando com o seu valor, ou seja, o
endereo, e quando se est trabalhando com a informao apontada por ele.
Operador & e *
O primeiro operador de ponteiro &. Ele um operador unrio que devol-
ve o endereo na memria de seu operando. Por exemplo: m = &count; pe
o endereo na memria da varivel count em m. Esse endereo a posio
interna da varivel na memria do computador e no tem nenhuma relao com
o valor de count. O operador & tem como signicado o endereo de. O segundo
operador *, que o complemento de &. O * um operador unrio que devolve
o valor da varivel localizada no endereo que o segue. Por exemplo, se m con-
tm o endereo da varivel count: q = *m; coloca o valor de count em q. O
operador * tem como signicado no endereo de.
Lembrete: Cuide-se para no confundir o operador de ponteiros (*) como
multiplicao na utilizao de ponteiros e vice-versa.
A declarao de uma varivel ponteiro dada pela colocao de um aste-
risco (*) na frente de uma varivel de qualquer tipo. Na linguagem C, possvel
denir ponteiros para os tipos bsicos ou estruturas. A denio de um ponteiro
no reserva espao de memria para o seu valor e sim para o seu contedo. Antes
de utilizar um ponteiro, o mesmo deve ser inicializado, ou seja, deve ser colocado
um endereo de memria vlido para ser acessado posteriormente.
Um ponteiro pode ser utilizado de duas maneiras distintas. Uma maneira
trabalhar com o endereo armazenado no ponteiro e outro modo trabalhar com
a rea de memria apontada pelo ponteiro. Quando se quiser trabalhar com o en-
dereo armazenado no ponteiro, utiliza-se o seu nome sem o asterisco na frente.
Sendo assim qualquer operao realizada ser feita no endereo do ponteiro.
Como, na maioria dos casos, se deseja trabalhar com a memria 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 operao realizada ser feita
no endereo de memria apontado pelo ponteiro. O programa 1.5 demostra a
utilizao de ponteiros para acesso memria.
Programa 1.5: Exemplo de uso de ponteiros
1 /* programa_matriz_02.c */
#include <stdio.h>
6
int main (void)
{
int *piValor; /* ponteiro para inteiro */
int iVariavel = 27121975
piValor = &iVariavel; /* pegando o endereo de memria da varivel */
11
printf ("Endereco: %d\n", piValor);
printf ("Valor : %d\n", *piValor);
16
*piValor = 18011982;
printf ("Valor alterado: %d\n", iVariavel);
printf ("Endereco : %d\n", piValor);
return 0;
}
Passando variveis para funes por referncia
O ponteiro utilizado para passar variveis por referncia, ou seja, variveis
que podem ter seu contedo alterado por funes e mantm este valor aps o
trmino da funo.
Na declarao de uma funo, deve-se utilizar o asterisco antes do nome do
parmetro, indicando que est sendo mudado o valor naquele endereo passado
como parmetro. No programa 1.6 visto um exemplo de uma varivel sendo
alterada por uma funo (gura 1.7).
Estrutura de Dados 13
Figura 1.7: Chamada de funo com ponteiros
Programa 1.6: Ponteiros como referncia em funes
/* programa_ponteiro_02.c */
#include <stdio.h>
4
void soma (int, int, int *);
int main (void)
{
int iValorA;
int iValorB;
int iResultado;
printf ("Entre com os valores:");
14
scanf ("%d %d", &iValorA, &iValorB);
printf("Endereco de iResultado = %d\n", &iResultado);
19
soma (iValorA, iValorB, &iResultado);/* est sendo passado o endereo de memria
da varivel, qualquer alterao estar
sendo realizada na memria */
printf ("Soma : %d\n", iResultado);
24
return 0;
}
29
34
void soma (int piValorA, int piValorB, int * piResultado)
{
printf("Endereco de piResultado = %d\n", piResultado);
/* o valor est sendo colocado diretamente na memria */
*piResultado = piValorA + piValorB;
return;
}
14 Estrutura de Dados com Algoritmos e C
Aritmtica com ponteiros
Com uma varivel do tipo ponteiro, possvel realizar operaes de soma
e subtrao. Ser somada ou diminuda no ponteiro a quantidade de endereos
de memria relativos ao tipo do ponteiro. Por exemplo, um ponteiro para int
ocupa 4 bytes, uma operao de soma neste ponteiro ir acrescentar 4 posies
(unidades) na memria. O programa 1.7 visto como os endereos de memria
so manipulados atravs de aritmtica de ponteiros, e a diferena entre o tipo
char - que ocupa 1 byte de memria - e o tipo int - que ocupa 4 bytes.
Programa 1.7: Aritmtica com ponteiros
/* programa_ponteiro_03.c */
#include <stdio.h>
5
10
int main (void)
{
int *piValor;
int iValor;
char *pcValor;
char cValor;
piValor = &iValor;
pcValor = &cValor;
15 printf ("Endereco de piValor = %d\n", piValor);
printf ("Endereco de pcValor = %d\n", pcValor);
piValor++; /* somando uma unidade (4 bytes) na memria */
pcValor++; /* somando uma unidade (1 byte) na memria */
20
printf ("Endereco de piValor = %d\n", piValor);
printf ("Endereco de pcValor = %d\n", pcValor);
25
return 0;
}
Vetores e matrizes como ponteiros em C
Vetores nada mais so do que ponteiros com alocao esttica de memria,
logo, todo vetor na linguagem C um ponteiro, tal que o acesso aos ndices do ve-
tor podem ser realizados atravs de aritmtica de ponteiros. Observe a gura 1.8:
Estrutura de Dados 15
Figura 1.8: Vetor como Ponteiro em C
Existem 2 formas para se obter o endereo (ponteiro) do incio do vetor ou
matriz:
&t[0];
t
Sendo a segunda opo (t) a melhor, por ser mais simples. O endereo (pon-
teiro) para o primeiro elemento do vetor ou matriz dado pelo nome do vetor
sem colchetes.
O programa 1.8 mostra a utilizao de um vetor com aritmtica de ponteiros.
Programa 1.8: Vetor como ponteiro
/* programa_vetor_02.c */
4
#include <stdio.h>
#include <string.h>
int main(void)
{
char t[5];
9
strcpy(t,abcde);
24
printf("\n%ld %c", t, *t);
printf("\n%ld %c", t+1, *(t+1));
printf("\n%ld %c", t+2, *(t+2));

16 Estrutura de Dados com Algoritmos e C


printf("\n%ld %c", t+3, *(t+3));
printf("\n%ld %c", t+4, *(t+4));
return 0;
}
1.2 Dados Heterogneos
Uma estrutura de dados chamada de heterognea quando envolve a utili-
zao de mais de um tipo bsico 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
nmero de campos de dados, que so itens de dados individuais. Registros so
conjuntos de dados logicamente relacionados, mas de tipos diferentes (numri-
cos, lgicos, caractere etc) [3, 21, 4].
O conceito de registro visa facilitar o agrupamento de variveis que no
so do mesmo tipo, mas que guardam estreita relao lgica. Registros corres-
pondem a conjuntos de posies de memria conhecidos por um mesmo nome
e individualizados por identicadores associados a cada conjunto de posies.
O registro um caso mais geral de varivel composta na qual os elementos do
conjunto no precisam ser, necessariamente, homogneos ou do mesmo tipo. O
registro constitudo por componentes. Cada tipo de dado armazenado em um
registro chamado de campo.
A gura 1.9 um exemplo de registro de um funcionrio: composto de
nome, departamento, data de nascimento e salrio.
Na varivel composta homognea, a individualizao de um elemento fei-
ta atravs de seus ndices, j no registro cada componente individualizado pela
explicitao de seu identicador.
A linguagem C utiliza as estruturas para representar um registro. Com a es-
trutura denida pode-se fazer atribuio de variveis do mesmo tipo de maneira
simplicada. Veja o programa exemplo 1.9 (representao da gura 1.9):
Estrutura de Dados 17
Figura 1.9: Registro de funcionrio
Programa 1.9: Exemplo de estrutura
2
struct Funcionario
{
char nome [40];
7
12
struct
{
int dia;
int mes;
int ano;
} dataNasc;
char departamento[10];
oat salario;
};
Para se fazer o acesso de um nico campo deve-se utilizar o nome da estrutu-
ra seguido de um ponto e do nome do campo desejado da estrutura. A linguagem
C tambm permite que seja criado um vetor de estruturas (programa 1.10).
Programa 1.10: Exemplo de uso de estruturas com vetores
/* programa_estrutura_01.c */
#include <stdio.h>
3
8
struct DADO
{
char sNome[40];
int iIdade;
};
18 Estrutura de Dados com Algoritmos e C
int main(void)
{
struct DADO sDados[5];
13
18
23
/* A estrutura dividida em duas partes por um ponto (.). Tem-se o nome da
estrutura esquerda e o nome do campo direita. Neste exemplo,
como est sendo manipulado um vetor de estruturas, tambm tem
ndice para cada linha do vetor. */
for(iIndice=0;iIndice<5;iIndice++)
{
printf("\nEntre com o Nome ->" );
scanf("%s", &sDados[iIndice].sNome );
printf("Entre com a Idade ->" );
scanf("%d", &sDados[iIndice].iIdade );
}
28 for(iIndice=0;iIndice<5;iIndice++)
{
printf("\n%s tem %d anos", sDados[iIndice].sNome, sDados[iIndice].iIdade);
}
return;
}
Lembrete: Estruturas so utilizadas para referenciar mltiplos tipos de dados.
1.3 Exerccios
Fazer um programa em C que implemente o algoritmo 1.1 para acessar
elementos de vetores via ponteiros. Crie uma funo:
imprime_array_elemento(int *aArray, int iElemento);
Fazer um programa em C que implemente o algoritmo 1.2 para acessar
elementos de uma matriz via ponteiros. Considerando uma matriz de
2x2:
Crie uma funo:
imprime_matriz_elemento_estatica(int paMatriz[][2],
int piLinha,
int piColuna);
Aps entender o captulo 2, crie uma funo:
imprime_matriz_elemento_dinamica(int ** paMatriz,
int piLinha,
int piColuna);
1.
2.

2. Uso de Memria
Nada traz de volta os bons e velhos
tempos quanto uma memria fraca.
Franklin P. Adams
2.1 Alocao de memria Esttica x Dinmica
A alocao esttica ocorre em tempo de compilao, ou seja, no momento
em que se dene uma varivel ou estrutura necessrio que se denam seu tipo
e tamanho. Nesse tipo de alocao, ao se colocar o programa em execuo, a
memria necessria para utilizar as variveis e estruturas estticas precisa ser
reservada e deve car disponvel at o trmino do programa (rotina ou funo).
A alocao dinmica ocorre em tempo de execuo, ou seja, as variveis e
estruturas so declaradas sem a necessidade de se denir seu tamanho, pois ne-
nhuma memria ser reservada ao colocar o programa em execuo. Durante a
execuo do programa, no momento em que uma varivel ou parte de uma estru-
tura precise ser utilizada, sua memria ser reservada e, no momento em que no
for mais necessria, deve ser liberada. Isso feito com o auxlio de comandos ou
funes que permitem, por meio do programa, reservar e/ou liberar memria.
Pode-se dizer que os vetores e matrizes so estruturas estticas e, por esse
motivo, devemos denir seu nmero de posies. Algumas linguagens permitem
criar vetores dinmicos por meio do uso de ponteiros e sua memria reservada
durante a execuo do programa.
Mesmo vetores dinmicos devem ter o seu tamanho conhecido no momen-
to da alocao da memria, pois um vetor deve ter toda sua memria alocada
20 Estrutura de Dados com Algoritmos e C
antes da sua utilizao. Estruturas encadeadas dinmicas possuem tamanho vari-
vel, pois diferentemente de vetores dinmicos, sua memria reservada por ele-
mento e no para toda a estrutura.
2.2 Alocao dinmica de memria
Vrias linguagens de programao possibilitam manipular dinamicamente a
memria das suas estruturas de dados. Algumas linguagens como o Java possibi-
litam que uma estrutura de dados cresa ou diminua quase que sem interferncia
do programador. Outras linguagens como o C exigem que o trabalho de aloca-
o de memria seja feito antecipadamente pelo programador.
Na linguagem C, uma matriz na prtica um vetor de ponteiros, onde cada
coluna um ponteiro para uma linha. Na gura 2.1 pode ser visualizada a repre-
sentao de uma matriz como um vetor com ponteiros.
2.3 Funes para alocao de memria
Na linguagem C, a alocao dinmica de memria pode ser realizada com
apenas quatro chamadas a funes:
Figura 2.1: Matriz como vetor de ponteiros
Uso de Memria 21
void * malloc(int qty_bytes_alloc);
void * calloc(int qty, int size);
void * realloc(void * pointer, int new_size);
free( void * pointer);
A funo malloc permite que seja feita a alocao de uma nova rea de
memria para uma estrutura. A funo calloc tem a mesma funcionalidade de
malloc, exceto que devem ser fornecidos o tamanho da rea e a quantidade de
elementos. A funo realloc permite que uma rea previamente alocada seja au-
mentada ou diminuda e a funo free libera uma rea alocada previamente com
a funo malloc, calloc ou realloc.
2.3.1 Funo malloc
a funo malloc que realiza a alocao de memria. Deve-se informar
para a funo a quantidade de bytes para alocao. A funo ir retornar, se exis-
tir memria suciente, um endereo que deve ser colocado em uma varivel do
tipo ponteiro.
Como a funo retorna um ponteiro para o tipo void, deve-se utilizar o
typecast, transformando este endereo para o tipo de ponteiro desejado.
2.3.2 Funo calloc
Em vez de se alocar uma quantidade de bytes atravs da funo malloc,
pode-se usar a funo calloc e especicar a quantidade de bloco de um determi-
nado tamanho. Funcionalmente a alocao ir ocorrer de maneira idntica.
A nica diferena entre o malloc e o calloc que a ltima funo, alm de
alocar o espao, tambm inicializa o mesmo com zeros.
2.3.3 Funo realloc
s vezes necessrio expandir uma rea alocada. Para isto deve-se usar a
funo realloc. Deve-se passar para ela o ponteiro retornado pelo malloc e a
indicao do novo tamanho. A realocao de memria pode resultar na troca de
blocos na memria.

22 Estrutura de Dados com Algoritmos e C


2.3.4 Funo free
Quando no se deseja mais uma rea alocada, deve-se liber-la atravs da
funo free. Deve ser passado para a funo o endereo, que se deseja liberar,
que foi devolvido quando a alocao da memria ocorreu.
2.4 Utilizando as funes para alocao de memria
Um vetor nada mais do que um ponteiro com alocao esttica de mem-
ria. A declarao int aVetor[10]; equivalente:
Programa 2.1: Declarao de vetor como ponteiro
1 int *aVetor;
aVetor = (int *) malloc(10 * sizeof(int *));
Quando se quer criar estruturas com dois ndices (matrizes), trs ndices
(tijolos) etc. A declarao da matriz seria algo como int aMatriz[2][3];,
utilizando ponteiros para declarar uma matriz deve-se usar:
Programa 2.2: Declarao de matriz como ponteiro
3
int **aMatriz;
aMatriz = (int **) malloc( 2 * sizeof(int *)); /* 2 linhas */
for( i=0; i<2; i++ )
{
aMatriz[i] = (int *) malloc( 3 * sizeof(int)); /* 3 colunas */
}
A notao aMatriz[i][j] pode ser utilizada com matrizes alocadas di-
namicamente equivalente a *(aMatriz[i]+j) ou *(*(aMatriz+i)+j).
Ou seja, pega-se o endereo de aMatriz e encontra-se o endereo da i-si-
ma linha (i*sizeof(int *)) posies frente (aMatriz[i] equiva-
lente a *(aMatriz+i)). Este endereo pode ser interpretado como um ve-
tor, ao qual somam-se j*sizeof(int) posies para encontrar o elemento
aMatriz[i][j].
O programa 2.3 utiliza as funes malloc e realloc para criar e aumentar
o tamanho de um vetor dinamicamente (em tempo de execuo). No caso de
algum erro, as funes retornam um ponteiro nulo (NULL) para indicar erro de
alocao de memria.
Uso de Memria 23
Programa 2.3: Exemplo de uso do malloc e realloc
/* programa_memoria_01.c */
#include <stdio.h>
#include <stdlib.h>
4
9
int main(void)
{
int *p;
int i,k, n;
printf("\nDigite a quantidade de numeros que serao digitados ->");
scanf("%d", &i);
14
/* A funo malloc reserva espao suciente para um vetor de inteiros.
Caso sejam digitados 5 elementos, sero reservados 20 bytes, pois cada
inteiro ocupa 4 bytes na memria */
p = (int *)(malloc(i*sizeof(int)));
if( p == NULL )
{
printf("\nErro de alocacao de memoria");
exit(1);
}
24
for( k=0;k<i;k++)
{
printf("Digite o numero para o indice %d ->", k);
scanf("%d", &p[k]);
}
29
for( k=0;k<i;k++)
{
printf("O numero do indice %d eh %d\n", k, p[k]);
}
34
printf("\nDigite quantos elementos quer adicionar ao vetor ->");
scanf("%d", &n
39
44
/* A funo realloc aumenta ou diminui o tamanho do vetor dinamicamente. Ela recebe o
ponteiro para o vetor anterior e retorna o novo espao alocado. */
p = (int *)(realloc(p,(i+n)*sizeof(int)));
if( p == NULL )
{
printf("\nErro de re-alocacao de memoria");
exit(1);
}
for(;k<(n+i);k++)
}
24 Estrutura de Dados com Algoritmos e C
49
{
printf("Digite o numero para o indice %d ->", k);
scanf("%d", &p[k]);
}
for( k=0;k<(i+n);k++)
{
printf("O numero do indice %d eh %d\n", k, p[k]);
}
free(p);
54
return 0;
}
O programa 2.4 utiliza a funo calloc para criar uma matriz em tempo de
execuo. De forma idntica a malloc, a funo calloc retorna um ponteiro nulo
(NULL) no caso de erro de alocao de memria.
Programa 2.4: Exemplo de uso do calloc
3
/* programa_memoria_02.c */
#include <stdio.h>
#include <stdlib.h>
8
int main(void)
{
/* A declarao de uma matriz de 2 dimenses exige um ponteiro para ponteiro. */
int **p;
int i,j,k,x;
printf("\nDigite as dimensoes da matriz ->");
scanf("%d %d", &i, &j);
13
18
23
/* Alocao da primeira dimenso. Internamente, a funo calloc far uma multiplicao da
quantidade de elementos pelo tamanho de cada elemento para saber quanto de memria
deve ser alocada. */
p = calloc(i,sizeof(int));
if( p == NULL )
{
printf("\nErro de alocacao de memoria");
exit(1);
}
for( k=0;k<i;k++)
{
/* Alocao das linhas de cada coluna (segunda dimenso) */
p[k] = calloc(j,sizeof(int));
if( p[k] == NULL )
{
Uso de Memria 25
28
printf("\nErro de alocacao de memoria");
exit(1);
}
}
33
38
for( k=0;k<i;k++)
{
for(x=0;x<j;x++)
{
printf("Digite o numero para o indice %d,%d ->", k,x);
scanf("%d", &p[k][x]);
}
}
43
48
for( k=0;k<i;k++)
{
for(x=0;x<j;x++)
{
printf("O numero do indice %d,%d eh %d\n", k,x, p[k][x]);
}
}
printf("\nLiberando memoria alocada");
for( k=0;k<i;k++)
{
free(p[k]); /* Primeiro deve ser liberada a memria para linha da matriz... */
}
free(p); /* ... para depois liberar a memria do vetor que continha as linhas. */
}
2.5 Alocao de memria e estruturas em C
A estrutura de dados struct tambm pode ser alocada dinamicamente com
as funes malloc ou calloc. O programa 2.5 trabalha com uma estrutura de da-
dos struct alocada dinamicamente.
Programa 2.5: Exemplo de uso de estruturas com ponteiros
3
/* programa_memoria_03.c */
#include <stdio.h>
#include <stdlib.h>
struct ST_DADOS
{
char nome[40];
26 Estrutura de Dados com Algoritmos e C
8
oat salario;
13
/* estrutura dentro de uma estrutura */
struct nascimento
{
int ano;
int mes;
int dia;
} dt_nascimento;
};
18
int main(void)
{
23
/* ponteiro para a estrutura */
struct ST_DADOS * p;
/* alocao de memria para o ponteiro da estrutura */
p = (struct ST_DADOS *) malloc(sizeof(struct ST_DADOS));
28
/* string (vetor de caracteres) um ponteiro, por isto a ausncia do & */
printf("\nEntre com o nome ->");
scanf("%s", p->nome);
33
printf("Entre com o salario ->");
scanf("%f", &p->salario);
38
/* O -> 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("%d%d%d", &p->dt_nascimento.dia,
&p->dt_nascimento.mes,
&p->dt_nascimento.ano);
48
printf("\n===== Dados digitados ====");
printf("\nNome = %s", p->nome);
printf("\nSalario = %f", p->salario);
printf("\nNascimento = %d/%d/%d\n", p->dt_nascimento.dia,
p->dt_nascimento.mes,
p->dt_nascimento.ano);
free(p);
}
Uso de Memria 27
2.6 Ponteiros para ponteiros mistrio ou no
O uso de ponteiros confunde os iniciantes nas disciplinas de programao.
Quando vem ponteiros para ponteiros, at prossionais mais experientes tm
diculdades [7].
A referncia para uma varivel int iVariavel obtida atrves do uso do
& no caso funcao(&iVariavel), ou seja, passado o endereo da memria
da varivel em uso. A varivel recebida como um ponteiro na funo (void
funcao(int * piVariavel)).
Quando se quer alocar um vetor ou uma matriz dentro de uma funo,
deve-se passar a referncia do ponteiro (por causa da alocao dinmica). Ou
seja, uma varivel declarada como ponteiro (int * piVariavel) vai ter sua
referncia passada para uma funo (funcao(&piVariavel)) e recebida
como um ponteiro na funo em questo. Como regra pode ser adotada a adio
de um * sempre no incio de uma varivel, logo, a funo seria declarada como
void funcao(int **piVariavel) .
Considere a declarao de um ponteiro para inteiro (int *piVariavel),
esta declarao gera uma alocao esttica de memria para o ponteiro (vamos
considerar o endereo 1 na memria (a)). Os segmentos 2 e 3 da memria esto
sendo utilizados por outras variveis de tal forma que o comando *piVaria-
vel = (int *) malloc(sizeof(int)) retorna o endereo 4 da me-
mria (b), isto signica que o valor 4 vai ser armazenado (c) dentro do ponteiro
*piVariavel (endereo 1). A instruo *piVariavel = 20 (d) ir colocar
o valor 20 no endereo apontado por *piVariavel (o endereo apontado
4). A gura 2.2 exemplica a operao.
Figura 2.2: Exemplo de ponteiro na memria
28 Estrutura de Dados com Algoritmos e C
A tabela 2.1 demonstra as operaes com ponteiro (pegando o endereo de
memria do ponteiro, o endereo armazenado no ponteiro e o contedo coloca-
do no endereo indicado pelo ponteiro).
Tabela 2.1: Operaes com ponteiros
Operao Resultado
printf("%d\n",&piVariavel)
1
printf("%d\n",piVariavel)
4
printf("%d\n",*piVariavel)
20
A passagem de um ponteiro por referncia a uma funo gera um pontei-
ro para ponteiro. Considere a declarao de uma funo int funcao(int
**piParametro) (programa 2.6), esta declarao gera uma declarao es-
ttica de memria para o ponteiro **piParametro (vamos considerar o en-
dereo de memria 6 (a)). A chamada a funo funcao(&piVariavel))
ser interpretada como passando endereo de piVariavel para o ponteiro
piParametro (b). Desta forma criado um ponteiro para o ponteiro (c). A
gura 2.3 (continuao da gura 2.2) exemplica a operao.
Programa 2.6: Ponteiro para ponteiro
5
10
#include <stdio.h>
#include <stdlib.h>
int funcao(int **piParametro)
{
printf("%d\n",&piParametro);
printf("%d\n",piParametro);
printf("%d\n",*piParametro);
printf("%d\n",**piParametro);
return 0;
}
int main( void )
{
int *piVariavel;
*piVariavel = (int *) malloc(sizeof(int);
*piVariavel = 20;
printf("%d\n",&piVariavel);
printf("%d\n",piVariavel);
printf("%d\n",*piVariavel);
20
funcao( &piVariavel );
Uso de Memria 29
return 0;
}
A tabela 2.2 demonstra as operaes com ponteiro (pegando o endereo de
memria do ponteiro, o endereo armazenado no ponteiro e o contedo coloca-
do no endereo indicado pelo ponteiro).
Figura 2.3: Exemplo de ponteiro para ponteiro na memria
Tabela 2.2: Operaes com ponteiros de ponteiros
Operao Resultado Signicado
printf("%d\n",&piParametro)
6 Endereo de piParametro
printf("%d\n",piParametro)
1 Contedo de piParametro
printf("%d\n",*piParametro)
4 Contedo do endereo
apontado por piParametro
(piVariavel)
printf("%d\n",**piParametro)
20 Valor do endereo apontado
por piParametro (*piVariavel)
2.7 Mais alocao de vetores e matrizes como ponteiros
No programa 2.7 so utilizadas vrias funes para a alocao de memria
e manipulao de uma matriz; nestes casos, uma matriz deve ser declarada utili-
zando o formato de ponteiro. Ao alocar dinamicamente uma matriz utilizando o
formato de ponteiros, o ponteiro que representa uma matriz pode ser utilizado
no formato matriz[linha][coluna] para referenciar uma posio da ma-
30 Estrutura de Dados com Algoritmos e C
triz (mesmo formato de utilizao caso a matriz tivesse sido declarada estatica-
mente no programa).
Programa 2.7: Exemplo de uso de alocao de matrizes
1
1/* programa_memoria_04.c */
#include <stdio.h>
#include <stdlib.h>
6
int ** aloca(int i, int j);
void libera(int **p, int i, int j);
void leitura(int **p, int i, int j);
void imprime(int **p, int i, int j);
11
int main(void)
{
int **p;
int **p1;
16
p = aloca(3,2);
leitura(p, 3, 2);
p1 = aloca(2,3);
leitura(p1,2,3);
21
imprime(p,3,2);
imprime(p1,2,3);
libera(p,3,2);
libera(p1,2,3);
26
return 0;
}
31
36
/* 2 asteriscos (*) indicam que ser retornada uma matriz */
int ** aloca(int i, int j)
{
/* ponteiro de ponteiro. Indica que ser alocada uma matriz de 2 dimenses */
int **p;
int x;
p = calloc(i,sizeof(int)); /* alocao de linhas... */
if( p == NULL )
{
printf("\nErro de alocacao");
exit(-1);
Uso de Memria 31
41
46
51
}
for(x=0;x<i;x++)
{
p[x]=calloc(j,sizeof(int)); /* ... e alocao de colunas */
if( p[x] == NULL )
{
printf("\nErro de alocacao");
exit(-1);
}
}
return p;
}
61
66
/* 2 asteriscos (*) indicam que a funo recebe uma matriz */
void leitura(int **p, int i, int j)
{
int x,y;
for(x=0;x<i;x++)
{
for(y=0;y<j;y++)
{
printf("Entre com o elemento %d,%d ->", x, y);
scanf("%d", &p[x][y]); /* uso da matriz no formato tradicional */
}
}
}
71
76
/* 2 asteriscos (*) indicam que a funo recebe uma matriz */
void imprime(int **p, int i, int j)
{
int x,y;
for(x=0;x<i;x++)
{
for(y=0;y<j;y++)
{
printf("\nElemento %d,%d = %d", x, y, p[x][y]);
}
}
}
81
86
/* 2 asteriscos (*) indicam que a funo recebe uma matriz */
void libera(int **p, int i, int j)
{
int x;
for(x=0;x<i;x++)
{
free(p[x]); /* libera coluna a coluna */
32 Estrutura de Dados com Algoritmos e C
91
}
free(p); /* libera as linhas */
}
Finalmente, o programa 2.8 realiza a alocao de matrizes de uma forma
diferente. No programa 2.7 a funo aloca retorna o ponteiro para uma matriz
alocada, no programa 2.8 a funo aloca recebe um ponteiro j denido e deve
alocar a memria solicitada pelo programador. Sempre que uma funo precisa
alocar memria para um ponteiro recebido, esta funo deve receber o ponteiro
do ponteiro. A notao de uso de ponteiro para ponteiro normalmente confunde
os prossionais menos experientes. Pode-se tomar como regra a utilizao do
ponteiro precedido de um * e entre parnteses para cada dimenso que se deseja
manipular. A partir da segunda dimenso possvel utilizar a notao de coluna
para manipulao da matriz.
Programa 2.8: Exemplo de uso de alocao de matrizes dentro de funes
3
1/* programa_memoria_05.c */
#include <stdio.h>
#include <stdlib.h>
void aloca(int ***p, int x, int y);
8
13
/* a funo recebe um ponteiro para uma matriz */
void aloca(int ***p, int x, int y)
{
int i;
*p = (int **)malloc(sizeof(int) * x);
if( *p == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(1);
}
18
28
for( i = 0; i<y; i++)
{
(*p)[i] = (int *) malloc(sizeof(int) * y);
if( (*p)[i] == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(1);
}
}
return;
}
Uso de Memria 33
33
int main(void)
{
int **p; /* declarao de uma matriz com duas dimenses */
int i,k;
aloca(&p,4,5); /* passando para a funo o endereo de memria do ponteiro */
38
43
for( i=0;i<4;i++)
{
for( k=0;k<5;k++)
{
p[i][k] = i + k;
}
}
48
53
for( i=0;i<4;i++)
{
for( k=0;k<5;k++)
{
printf("%d ", p[i][k]); /* referncia aos elementos atravs de linha e coluna */
}
printf("\n");
}
return 0;
}
2.7.1 Controle de agenda com ponteiros de estruturas e vetores
O programa 2.9 contm vrias formas de manipulao e alocao de pontei-
ros com vetores. O objetivo do programa criar um cadastro simples de agenda
(em memria) com incluso de novas entradas e alterao, consulta, excluso e
pesquisa (ordenada) de entradas j existentes.
Programa 2.9: Exemplo completo de uso de vetor (ponteiros) de estruturas
/* agenda.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
5
/* funo getch */
#ifdef DOS
#include <conio.h>
#else
34 Estrutura de Dados com Algoritmos e C
10
#include <curses.h>
#endif
15
typedef struct agenda
{
char nome[40];
char email[40];
int telefone;
} AGENDA;
20
void aloca( AGENDA **pAgenda, int *piEntradas );
void consulta( AGENDA *pAgenda, int iEntradas);
void qs_ordena(AGENDA pAgenda[], int left, int right );
void ordena( AGENDA pAgenda[], int iEntradas );
25
void excluir(AGENDA **pAgenda, int *piEntradas);
void pesquisar(AGENDA *pAgenda, int iEntradas);
void alterar(AGENDA *pAgenda, int iEntradas);
30
int main(void)
{
AGENDA * pAgenda;
35
int iEntradas, op;
iEntradas=0;
40
pAgenda = (AGENDA *) malloc(sizeof(AGENDA)); /* alocando espao para a posio 0
do vetor */
if( pAgenda == NULL )
{
printf("\nErro de alocacao de memoria.");
exit(1);
}
45
50
55
do
{
fush(stdin);
printf("\n1 - Inclusao");
printf("\n2 - Alteracao");
printf("\n3 - Consulta");
printf("\n4 - Excluir");
printf("\n5 - Pesquisar");
printf("\n9 - Sair");
printf("\nEntre com uma opcao -> ");
scanf("%d", &op);
Uso de Memria 35
60
if( op == 1 )
{
/* farei aqui para ilustrar algumas formas de manipular ponteiros */
fush(stdin);
/* alocao de ponteiros em funes requer trabalhar com ponteiros
para ponteiros */
aloca(&pAgenda, &iEntradas);
65
70
printf("*** Inclusao ***");
printf("\nEntre com o Nome:");
/* forma 1 - endereo ponteiro inicial + x posies na memria
quando se trabalhar com o endereo, deve-se usar -> */
gets((pAgenda+iEntradas)->nome);
fush(stdin);
75
printf("Entre com o email:");
/* forma 2 - endereo ponteiro inicial + x posies na memria
quando se trabalhar com ponteiro (contedo do endereo ou *),
deve-se usar o . (ponto) */
gets((*(pAgenda+iEntradas)).email);
fush(stdin);
80
printf("Entre com o telefone:");
/* forma 3 - trabalhando como vetor */
scanf(%d, &pAgenda[iEntradas].telefone);
fush(stdin);
85
90
95
100
iEntradas++;
}
else if( op == 2)
{
alterar(pAgenda, iEntradas);
}
else if( op == 3 )
{
/* se o vetor de estruturas vai ser somente lido
no preciso passar ponteiro para ponteiro */
ordena(pAgenda, iEntradas);
consulta(pAgenda, iEntradas);
}
else if( op == 4)
{
ordena(pAgenda, iEntradas);
excluir(&pAgenda, &iEntradas);
}
else if( op == 5
36 Estrutura de Dados com Algoritmos e C
105
{
ordena(pAgenda, iEntradas);
pesquisar(pAgenda,iEntradas);
}
} while(op!=9);
}
110
115
120
void consulta(AGENDA *pAgenda, int iEntradas)
{
int i;
for(i=0;i<iEntradas;i++)
{
printf("\n\nRegistro %d", i);
printf("\n\tNome: %s", pAgenda[i].nome );
printf("\n\tEmails: %s", pAgenda[i].email );
printf("\n\tTelefone: %d", pAgenda[i].telefone );
getch();
}
}
125
130
void alterar(AGENDA *pAgenda, int iEntradas)
{
char op;
int i=0;
char nome[40];
printf("\n\tDigite o Nome:");
fush(stdin);
gets(nome);
135
140
145
150
for(i=0; i < iEntradas && strncmp( pAgenda[i].nome, nome, strlen(nome))!=0;i++);
if( i>= iEntradas )
{
printf("\nRegistro nao encontrado");
}
else
{
printf("\n\tRegistro %d", i);
printf("\n\tNome : %s", pAgenda[i].nome );
printf("\n\tEmail : %s", pAgenda[i].email );
printf("\n\tFone : %d", pAgenda[i].telefone );
printf("\n\tConirma a alteracao ?");
op = getch();
if( op == S || op == s )
{
fush(stdin);
printf("\nEntre com o Nome:");
/* forma 1 - endereo ponteiro inicial + x posies na memria
quando se trabalhar com o endereo, deve-se usar -> */
Uso de Memria 37
gets((pAgenda+i)->nome);
fush(stdin);
155
160
printf("Entre com o email:");
/* forma 2 - endereo ponteiro inicial + x posies na memria
quando se trabalhar com ponteiro (contedo do endereo ou *),
deve-se usar o . (ponto) */
gets((*(pAgenda+i)).email);
fush(stdin);
165
printf("Entre com o telefone:");
/* forma 3 - trabalhando como vetor */
scanf("%d", &pAgenda[i].telefone);
fush(stdin);
}
}
}
170
175
180
185
190
195
void excluir(AGENDA **pAgenda, int *piEntradas)
{
char op;
int i=0;
char nome[40];
printf("\n\tDigite o Nome:");
fush(stdin);
gets(nome);
/* Uso a sintaxe (*pAgenda)[i].nome pelo fato de ser ponteiro de ponteiro.
Os parnteses neste caso servem para xar a primeira posio da memria, pois
a linguagem C tende a trabalhar com ponteiros de ponteiros como se fossem
matrizes (que na prtica so ponteiros para ponteiros) */
for(i=0; i < *piEntradas && strncmp( (*pAgenda)[i].nome, nome, strlen(nome))!=0;i++);
if( i>= *piEntradas )
{
printf(\nRegistro no encontrado);
}
else
{
fush(stdin);
printf("\n\tRegistro %d", i);
printf("\n\tNome : %s", (*pAgenda)[i].nome );
printf("\n\tEmail : %s", (*pAgenda)[i].email );
printf("\n\tFone : %d", (*pAgenda)[i].telefone );
printf("\n\tConirma a exclusao ?");
op = getch();
if( op == S || op == s )
{
/* copio o ltimo elemento para o elemento corrente */
(*pAgenda)[i] = (*pAgenda)[(*piEntradas)-1];
38 Estrutura de Dados com Algoritmos e C
200
205
(*piEntradas)--;
/* excluo o ltimo elemento com realoc */
aloca(pAgenda, piEntradas);
}
}
}
210
215
void aloca( AGENDA **pAgenda, int *piEntradas )
{
(*pAgenda) = (AGENDA *)(realloc(*pAgenda,
(*piEntradas+1)*sizeof(AGENDA)));
if( *pAgenda == NULL )
{
printf("\nErro de re-alocacao de memoria");
exit(1);
}
}
220
225
void pesquisar(AGENDA *pAgenda, int iEntradas)
{
char op;
int i=0;
char nome[40];
printf("\n\tDigite o Nome:");
fush(stdin);
gets(nome);
230
235
240
245
for(i=0; i < iEntradas && strncmp( pAgenda[i].nome, nome, strlen(nome))!=0;i++);
if( i>= iEntradas )
{
printf("\nRegistro nao encontrado");
}
else
{
do
{
fush(stdin);
printf("\n\n\tRegistro %d", i);
printf("\n\tNome : %s", pAgenda[i].nome );
printf("\n\tEmail : %s", pAgenda[i].email );
printf("\n\tFone : %d", pAgenda[i].telefone );
printf("\n\tProximo ?" );
op = getch();
i++;
if(i>=iEntradas)
{
i = 0;
}
Uso de Memria 39
250
} while( op == S || op == s);
}
}
255
void ordena( AGENDA pAgenda[], int iEntradas )
{
qs_ordena(pAgenda, 0, iEntradas-1 );
}
260
void qs_ordena(AGENDA pAgenda[], int left, int right )
{
register int i, j;
char * x;
AGENDA t;
265
i = left;
j = right;
x = pAgenda[(left+right)/2].nome;
270
275
280
do
{
while(strcmp(pAgenda[i].nome,x)<0 && i<right) i++;
while(strcmp(pAgenda[j].nome,x)>0 && j>left) j --;
if( i<=j )
{
t = pAgenda[i];
pAgenda[i]=pAgenda[j];
pAgenda[j]=t;
i++;
j--;
}
} while( i<=j );
if( left < j ) qs_ordena(pAgenda, left, i);
if( i<right) qs_ordena(pAgenda, i, right );
}
3. Pilha
A tecnologia dominada por aqueles que
gerenciam o que no entendem.
Arthur Bloch
Uma pilha um conjunto ordenado de itens, no qual novos itens podem ser
inseridos e a partir do qual podem ser eliminados itens de uma extremidade, cha-
mada topo da pilha. Tambm chamada de lista linear, onde todas as inseres e
eliminaes so feitas em apenas uma das extremidades, chamada topo. A gura
3.1 mostra a representao de uma pilha.
Figura 3.1: Exemplo de Pilha
A estrutura de dados do tipo pilha tem como caracterstica que a ltima
informao a entrar a primeira a sair (LIFO - last in rst out). A estrutura em
pilha tem os seguintes mtodos ou funes:
Pilha 41
push - coloca uma informao na pilha (empilha).
pop - retira uma informao da pilha (desempilha).
size - retorna o tamanho da pilha.
stackpop - retorna o elemento superior da pilha sem remov-lo (equi-
valente s operaes de pop e um push).
empty - verica se a pilha est vazia.
A aplicao da estrutura de pilhas mais freqente em compiladores e sis-
temas operacionais, que a utilizam para controle de dados, alocao de variveis
na memria etc.
O problema no uso de pilhas controlar o nal da pilha. Isto pode ser feito
de vrias formas, sendo a mais indicada criar um mtodo para vericar se existem
mais dados na pilha para serem retirados.
Tomando a pilha da gura 3.2 como exemplo, a operao push(H) ir
acrescentar um novo elemento ao topo da pilha sendo, em seguida, executado
um conjunto de operaes sobre a pilha:
2 - push(I) - Coloca o elemento I no topo da Pilha
3 - pop() - Retorna o elemento I
4 - pop() - Retorna o elemento H
5 - pop() - Retorna o elemento F
6 - pop() - Retorna o elemento E
7 - pop() - Retorna o elemento D
8 - push(D) - Coloca o elemento D no topo da Pilha
9 - push(E) - Coloca o elemento E no topo da Pilha

42 Estrutura de Dados com Algoritmos e C


Figura 3.2: Operaes em uma pilha
3.1 Representao das Operaes com Pseudo-cdigo
As operaes de pilha podem ser representadas com algumas linhas de pseu-
do-cdigo. Os algoritmos empty (3.1), push (3.2), pop (3.3), stackpop (3.4) e
size (3.5) demonstram as operaes numa pilha. Estes cdigos podem ser adap-
tados para qualquer linguagem de programao [9].
Algoritmo 3.1: Vericao se a pilha est vazia (funo EMPTY(S))
3.2 Pilhas em C
Antes de programar a soluo de um problema que usa uma pilha, ne-
cessrio determinar como representar uma pilha usando as estruturas de dados
existentes na linguagem de programao. Uma pilha um conjunto ordenado de
itens, e a linguagem C j contm um tipo de dado que representa um conjunto
ordenado de itens: o vetor. Ento, sempre que for necessrio 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 dinmica e pode crescer innitamente,
enquanto um vetor na linguagem C tem um tamanho xo; contudo, pode-se de-
nir este vetor com um tamanho sucientemente grande para conter esta pilha.
Algoritmo 3.2: Colocar um item na pilha (funo PUSH(S,x))
Algoritmo 3.3: Retirada de um item da pilha (funo POP(S))
Algoritmo 3.4: Pega o item do topo da pilha mas no desempilha (funo
STACKPOP(S))
44 Estrutura de Dados com Algoritmos e C
Algoritmo 3.5: Tamanho da pilha (funo SIZE(S))
O programa 3.1 apresenta um exemplo de programa em C para manipula-
o de pilhas.
Programa 3.1: Exemplo de manipulao de pilha
/* programa_pilha_01.c */
3
#include <stdio.h>
8
void push(int valor);
int pop(void);
int size(void);
int stacktop(void);
int pilha[20];
int pos=0;
13
void push(int valor)
{
pilha[pos]=valor;
/* Empilha um novo elemento. No vericada a capacidade
mxima da pilha.*/
pos++;
return;
}
23
int pop()
{
/* Retorna o elemento do topo da lha. No vericado
o nal da pilha. */
return (pilha[--pos]);
}
28
int size()
{
return pos; /* retorna o topo da pilha */
}
Pilha 45
33
int stacktop() /* retorna o topo da pilha sem desempilhar */
{
return pilha[pos];
}
38
43
int main(int argc, char ** argv )
{
printf("\nColocados dados na pilha");
push(1);
push(2);
push(3);
printf("\nTamanho da pilha %d", size());
48
printf("\nPegando dado da pilha: %d", pop());
printf("\nPegando dado da pilha: %d", pop());
printf("\nPegando dado da pilha: %d", pop());
53
printf("\nTamanho da pilha %d", size());
return 0;
}
Uma pilha em C pode ser declarada como uma estrutura contendo dois ob-
jetos: um vetor para armazenar os elementos da pilha e um inteiro para indicar a
posio atual do topo da pilha (programa 3.2).
Programa 3.2: Exemplo de manipulao de pilha com estrutura
/* programa_pilha_02.c */
5
#include <stdio.h>
#include <stdlib.h>
#dene TAMANHO_PILHA 100
10
/* Estrutura que ir conter a pilha de informaes */
struct pilha
{
int topo;
int itens[TAMANHO_PILHA];
};
15
int empty(struct pilha *p)
{
if( p->topo == -1
46 Estrutura de Dados com Algoritmos e C
20
{
return 1;
}
return 0;
}
25
30
int pop(struct pilha *p)
{
if( empty(p) )
{
printf("\nPilha vazia");
exit(1);
}
/* retorna o item da pilha atual e diminui a posio da pilha */
return (p->itens[p->topo--]);
}
35
40
45
void push(struct pilha *p, int e)
{
if( p->topo == (TAMANHO_PILHA - 1))
{
printf("\nEstouro da pilha");
exit(1);
}
/* aps vericar se no haveria estouro na capacidade da pilha,
criada uma nova posio na pilha e o elemento armazenado */
p->itens[++(p->topo)] = e;
return;
}
50
int size(struct pilha *p)
{
/* sempre lembrando que na linguagem C o ndice de um
vetor comea na posio 0 */
return p->topo+1;
}
55
int stackpop(struct pilha *p)
{
return p->itens[p->topo];
}
60
int main(void)
{
struct pilha x;
x.topo = -1;
push(&x,1);
Pilha 47
65
push(&x,2);
push(&x,3);
printf("\nTamanho da pilha %d", size(&x));
printf("\nElemento do topo da ila %d", stackpop(&x));
70
75
printf("\n%d", pop(&x));
printf("\n%d", pop(&x));
printf("\n%d", pop(&x));
printf("\n%d", pop(&x));
return 0;
}
3.3 Exerccios
Dada uma pilha P, construir uma funo que inverte a ordem dos ele-
mentos dessa pilha, utilizando apenas uma estrutura auxiliar. Denir
adequadamente a estrutura auxiliar e prever a possibilidade da pilha
estar vazia.
Construir uma funo que troca de lugar o elemento que est no topo
da pilha com o que est na base da pilha. Usar apenas uma pilha como
auxiliar.
Dada uma pilha contendo nmeros inteiros quaisquer, construir uma
funo que coloca os pares na base da pilha e os mpares no topo da
pilha. Usar duas pilhas como auxiliares.
1.
2.
3.
4. Fila
A sutileza do pensamento consiste em
descobrir a semelhana das coisas diferentes
e a diferena das coisas semelhantes.
Charles de Montesquieu
Uma la um conjunto ordenado de itens a partir do qual se podem elimi-
nar itens numa extremidade - incio da la - e no qual se podem inserir itens na
outra extremidade - nal da la.
Ela uma prima prxima da pilha, pois os itens so inseridos e removidos
de acordo com o princpio de que o primeiro que entra o primeiro que sai - rst in,
rst out (FIFO).
O conceito de la existe no mundo real, vide exemplos como las de banco,
pedgios, restaurantes etc. As operaes bsicas de uma la so:
insert ou enqueue - insere itens numa la (ao nal).
remove ou dequeue - retira itens de uma la (primeiro item).
empty - verica se a la est vazia.
size - retorna o tamanho da la.
front - retorna o prximo item da la sem retirar o mesmo da la.
A operao insert ou enqueue sempre pode ser executada, uma vez que
teoricamente uma la no tem limite. A operao remove ou dequeue s pode
ser aplicado se a la no estiver vazia, causando um erro de underow ou la vazia
se esta operao for realizada nesta situao.

Fila 49
Tomando a la da gura 4.1 como exemplo, o item a apresenta a la no seu
estado inicial e executado o conjunto de operaes:
dequeue() - Retorna o item A (a la resultante representada pelo
item B)
enqueue(F) - O item F armazenado ao nal da la (a la resultante
representada pelo item C)
dequeue() - Retirado o item B da la
enqueue(G) - Colocado o item G ao nal da la (item D)
Figura 4.1: Operaes numa la
4.1 Representao de Filas com Pseudo-cdigos
As operaes em la podem ser representados com os seguintes blocos de
cdigo: a operao enqueue (4.1), dequeue (4.2), empty (4.3), size (4.4) e front
(4.5). Estes cdigos podem ser adaptados para qualquer linguagem de progra-
mao [9].

50 Estrutura de Dados com Algoritmos e C


Algoritmo 4.1: Incluso de dados na la (ENQUEUE(Q,x))
Algoritmo 4.2: Retirada de dados na la (DEQUEUE(Q))
Algoritmo 4.3: Vericao se a la est vazia (funo EMPTY(Q))
Fila 51
Algoritmo 4.4: Tamanho da la (funo SIZE(Q))
Algoritmo 4.5: Prximo elemento da la (funo FRONT(Q))
4.2 Filas em C
A exemplo do que ocorre com estrutura em pilha, antes de programar a
soluo de um problema que usa uma la, necessrio determinar como repre-
sentar uma la usando as estruturas de dados existentes na linguagem de pro-
gramao. Novamente na linguagem C podemos usar um vetor. Mas a la
uma estrutura dinmica e pode crescer innitamente, enquanto que um vetor na
linguagem C tem um tamanho xo. Contudo, pode-se denir este vetor com um
tamanho sucientemente grande para conter a la. No programa 4.1 apresen-
tado um exemplo para manipulao de las.
Programa 4.1: Exemplo de manipulao de la em C
3
/* programa_la_01.c */
#include <stdio.h>
#include <stdlib.h>
#dene TAMANHO_MAXIMO 100
8
struct queue
{
int itens[TAMANHO_MAXIMO];
int front,rear;
};
13
int empty(struct queue * pq)
52 Estrutura de Dados com Algoritmos e C
18
23
28
{
/* se o incio da la for igual ao nal da la, a la est vazia */
if( pq->front == pq->rear )
{
return 1;
}
return 0;
}
void enqueue(struct queue * pq, int x)
{
if( pq->rear + 1 >= TAMANHO_MAXIMO )
{
printf("\nEstouro da capacidade da ila");
exit(1);
}
pq->itens[ pq->rear++ ] = x;
return;
}
33
int size(struct queue * pq)
{
return (pq->rear + 1);
}
38
int front(struct queue * pq)
{
/* o primeiro elemento sempre est no incio do vetor */
return pq->itens[0];
}
43
48
53
58
int dequeue(struct queue * pq)
{
int x, i;
if( empty(pq) )
{
printf("\nFila vazia");
exit(1);
}
/* Salva o primeiro elemento e refaz o arranjo dos itens, puxando o segundo elemento
para o primeiro, o terceiro para o segundo e assim sucessivamente. */
x = pq->itens[0];
for( i=0; i < pq->rear; i++)
{
pq->itens[i] = pq->itens[i+1];
}
pq->rear--;
return x;
}
Fila 53
63
68
int main(void)
{
struct queue q;
q.front = 0; q.rear = 0;
enqueue(&q,1);
enqueue(&q,2);
enqueue(&q,3);
enqueue(&q,4);
73
78
printf("\nFila vazia %d", empty(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nFila vazia %d", empty(&q));
83 printf("\n");
}
No programa 4.1, o vetor foi denido para comportar apenas 100 elemen-
tos, caso fosse inserido um 101
0
elemento, haveria o estouro da pilha mesmo
aps vrias operaes de dequeue. Para resolver este problema, na operao
dequeue foi implementada uma tcnica de redistribuio dos elementos na la,
de tal forma que nunca se chegue a estourar a la caso haja vrias operaes de
insero ou remoo (exceto se realmente houver 100 elementos da la e houve
uma tentativa de insero de um novo elemento). O programa 4.2 o trecho que
implementa a tcnica comentada:
Programa 4.2: Reajuste da la
5
x = pq->itens[0];
for( i=0; i < pq->rear; i++)
{
pq->itens[i] = pq->itens[i+1];
}
pq->rear--;
Esta tcnica ineciente, pois cada eliminao da la envolve deslocar cada
elemento restante na la. Se uma la contiver 1000 ou 2000 elementos, cada ele-
mento retirado da la provocar o deslocamento de todos os demais elementos.
A operao de remoo de um item na la deveria logicamente trabalhar somen-
54 Estrutura de Dados com Algoritmos e C
te com aquele elemento, permanecendo os demais elementos em suas posies
originais.
A soluo para o problema denir o vetor como um crculo, em vez de
uma linha reta. Neste caso, os elementos so inseridos como numa la reta, e
a remoo de um elemento da la no altera os demais elementos da la. Com
o conceito de la circular, ao chegar ao nal da la, o ponteiro de controle da
la vai imediatamente para o incio da la novamente (se este estiver vago). As
seguintes operaes exemplicam a explicao (acompanhar o desenvolvimento
da la na gura 4.2), sendo o caso 1 o estado inicial da la:
Estado inicial
enqueue(D) - O item D armazenado ao nal da la
enqueue(E) - O item D armazenado ao nal da la
dequeue() - Retirado o item A da la
enqueue(F) - O item F armazenado ao nal da la
enqueue(G) - O item G armazenado ao nal da la
dequeue() - Retirado o item B da la
enqueue(H) - O item H armazenado ao nal da la. Neste momento, o
ponteiro da la chegou ao nal do vetor que contm a implementao
da la.
dequeue() - Retirado o item C da la
enqueue(I) - O item I armazenado ao nal da la (mas no incio
do vetor)
enqueue(K) - O item K armazenado ao nal da la (mas na segunda
posio do vetor)
O programa 4.3 mostra a declarao da estrutura para uma la circular.
Programa 4.3: Declarao de estrutura circular
#dene TAMANHO_MAXIMO 100
4
9
struct queue
{
int itens[TAMANHO_MAXIMO];
int front,rear;
};
struct queue q;
q.front = q.rear = -1
1.
2.
3.
4.
5.
6.
7.
8.
9.
Fila 55
Figura 4.2: Operaes numa la circular
Desta forma, as funes de manipulao de la (empty, enqueue, dequeue,
size e front) devem sofrer modicaes para reetir a nova condio de la cir-
cular (programa 4.4):
Programa 4.4: Manipulao de la circular em C
/* programa_la_02.c */
#include <stdio.h>
#include <stdlib.h>
5
#dene TAMANHO_MAXIMO 10
10
struct queue
{
int itens[TAMANHO_MAXIMO];
int front,rear;
};
56 Estrutura de Dados com Algoritmos e C
15
20
int empty(struct queue * pq)
{
if( pq->front == pq->rear )
{
return 1;
}
return 0;
}
25
30
35
40
void enqueue(struct queue * pq, int x)
{
/* Inverso das posies dos ponteiros. Se o nal do vetor j foi
alcanado, ento retorna-se ao incio do vetor */
if( pq->rear == TAMANHO_MAXIMO-1)
{
pq->rear = 0;
}
else
{
pq->rear++;
}
if( pq->rear == pq->front )
{
printf("\nEstouro da ila");
exit(1);
}
pq->itens[pq->rear] = x;
return;
}
45
50
int size(struct queue * pq)
{
/* se o nal da la ainda no alcanou o nal do vetor... */
if( pq->front <= pq->rear)
{
return pq->rear - pq->front; /* ... ento o tamanho da la o nal
da la menos o incio da la... */
}
55
/* ... se no, quer dizer que o ponteiro de nal da la j alcanou o nal do vetor
e foi reposicionado para o incio do vetor, ento o tamanho da la a quantidade
de itens que ainda restam at chegar ao nal do vetor somando itens que esto
no incio do vetor */
return pq->rear + pq->front;
}
60
int front(struct queue * pq)
{
Fila 57
return pq->itens[pq->front+1];
}
65
70
int dequeue(struct queue * pq)
{
int x, i;
if( empty(pq) )
{
printf("\nFila vazia");
exit(1);
}
75
80
/* Inverso das posies dos ponteiros. Se o nal do vetor j foi alcanado,
ento retorna-se ao incio do vetor */
if( pq->front == TAMANHO_MAXIMO - 1)
{
pq->front = 0;
}
else
{
pq->front++;
}
return (pq->itens[ pq->front ]);
}
85
90
int main(void)
{
struct queue q;
q.front = -1;
q.rear = - 1;
enqueue(&q,1);
enqueue(&q,2);
enqueue(&q,3);
enqueue(&q,4);
95
100
105
printf("\nTamanho da ila %d", size(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
enqueue(&q,5);
enqueue(&q,6);
enqueue(&q,7);
enqueue(&q,8);
58 Estrutura de Dados com Algoritmos e C
110
115
enqueue(&q,9);
printf("\nTamanho da ila %d", size(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
120
125
130
135
140
enqueue(&q,10);
enqueue(&q,11);
enqueue(&q,12);
enqueue(&q,13);
enqueue(&q,14);
enqueue(&q,15);
enqueue(&q,16);
enqueue(&q,17);
enqueue(&q,18);
printf("\nTamanho da ila %d", size(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nFila vazia %d", empty(&q));
150
155
enqueue(&q,20);
enqueue(&q,21);
enqueue(&q,22);
enqueue(&q,23);
enqueue(&q,24);
enqueue(&q,25);
printf("\nTamanho da ila %d", size(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nProximo da ila %d", front(&q));
printf("\nTirando da ila %d", dequeue(&q));
Fila 59
160
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nTirando da ila %d", dequeue(&q));
printf("\nTamanho da ila %d", size(&q));
printf("\nFila vazia %d", empty(&q));
165
printf(\n);
return 0;
}
4.3 Exerccios
Se um vetor armazenando uma la no considerado circular, o tex-
to sugere que cada operao dequeue deve deslocar para baixo todo
elemento restante de uma la. Um mtodo alternativo adiar o des-
locamento at que rear seja igual ao ltimo ndice do vetor. Quando
essa situao ocorre e faz-se uma tentativa de inserir um elemento na
la, a la inteira deslocada para baixo, de modo que o primeiro ele-
mento da la que na posio 0 do vetor. Quais so as vantagens desse
mtodo sobre um deslocamento em cada operao dequeue? Quais as
desvantagens? Reescreva as rotinas dequeue, queue e size usando esse
mtodo.
Faa um programa para controlar uma la de pilhas.
1.
2.
5. Recursividade

E no sabendo que era impossvel, foi l e fez.
Jean Cocteau
Recurso o processo de denir algo em termos de si mesmo e , algumas
vezes, chamado de denio circular. Assim, pode-se dizer que o conceito de
algo recursivo est dentro de si, que por sua vez est dentro de si e assim suces-
sivamente, innitamente.
O exemplo a seguir dene o ancestral de uma pessoa:
Os pais de uma pessoa so seus ancestrais (caso base);
Os pais de qualquer ancestral so tambm ancestrais da pessoa ini-
cialmente considerada (passo recursivo).
Denies como estas so normalmente encontradas na matemtica. O
grande apelo que o conceito da recurso traz a possibilidade de dar uma deni-
o nita para um conjunto que pode ser innito [15]. Um exemplo aritmtico:
O primeiro nmero natural zero.
O sucessor de um nmero natural um nmero natural.
Na computao o conceito de recursividade amplamente utilizado, mas
difere da recursividade tpica por apresentar uma condio que provoca o m do
ciclo recursivo. Essa condio deve existir, pois, devido s limitaes tcnicas que
o computador apresenta, a recursividade impedida de continuar eternamente.

Recursividade 61
5.1. Funo para clculo de Fatorial
Na linguagem C, as funes podem chamar a si mesmas. A funo recur-
siva se um comando no corpo da funo a chama. Para uma linguagem de com-
putador ser recursiva, uma funo deve poder chamar a si mesma. Um exemplo
simples a funo fatorial, que calcula o fatorial de um inteiro. O fatorial de um
nmero N o produto de todos os nmeros inteiros entre 1 e N. Por exemplo,
3 fatorial (ou 3!) 1 * 2 *3 = 6. O programa 5.1 apresenta uma verso
iterativa para clculo do fatorial de um nmero.
Programa 5.1: Fatorial (verso iterativa)
1
6
int fatorialc ( int n )
{
int t, f;
f = 1;
for ( t = 1; t<=n; t++ )
f = f * t
return f;
}
Mas multiplicar n pelo produto de todos os inteiros a partir de n-1 at 1
resulta no produto de todos os inteiros de n a 1. Portanto, possvel dizer que
fatorial:
0! = 1
1! = 1 * 0!
2! = 2 * 1!
3! = 3 * 2!
4! = 4 * 3!
Logo o fatorial de um nmero tambm pode ser denido recursivamente (ou
por recorrncia) atravs das seguintes regras (representao matemtica) [15, 1]:
n! = 1, se n = 0
n! = n * (n-1)! , se n > 0
O programa 5.2 mostra a verso recursiva do programa fatorial.

62 Estrutura de Dados com Algoritmos e C


Programa 5.2: Fatorial (verso recursiva)
2

7
int fatorialr( int n)
{
int t, f;
/* condio de parada */
if( n == 1 || n == 0)
{
return 1;
}
f = fatorialr(n-1)*n; /* chamada da funo */
return f;
}
A verso no-recursiva de fatorial deve ser clara. Ela usa um lao que
executado de 1 a n e multiplica progressivamente cada nmero pelo produto
mvel.
A operao de fatorial recursiva um pouco mais complexa. Quando fa-
torialr chamada com um argumento de 1, a funo devolve 1. Caso contr-
rio, ela devolve o produto de fatorialr(n-1)*n. Para avaliar essa expres-
so, fatorialr chamada com n-1. Isso acontece at que n se iguale a 1 e as
chamadas funo comecem a retornar.
Calculando o fatorial de 2, a primeira chamada a fatorialr provoca uma
segunda chamada com o argumento 1. Essa chamada retorna 1, que , ento,
multiplicado por 2 (o valor original e n). A resposta ento 2.
Para melhor entendimento, interessante ver como o programa execu-
tado internamente no computador. No caso do programa iterativo (programa
5.1) necessrio duas variveis f e t para armazenar os diversos passos do
processamento. Por exemplo, ao calcular fatorial de 6, o computador vai passar
sucessivamente pelos seguintes passos (tabela 5.1).
Tabela 5.1: Clculo de fatorial de 6
t f
1 1
2 2
3 6
4 24
5 120
6 720
Recursividade 63
No programa recursivo (5.2) nada disto acontece. Para calcular o fatorial de
6, o computador tem de calcular primeiro o fatorial de 5 e s depois que faz a
multiplicao 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 expanso seguida de uma contrao:
fatorialr(6)
6 * fatorialr(5)
6 * 5 * fatorialr(4)
6 * 5 * 4 * fatorialr(3)
6 * 5 * 4 * 3 * fatorialr(2)
6 * 5 * 4 * 3 * 2 * fatorialr(1)
6 * 5 * 4 * 3 * 2 * 1
6 * 5 * 4 * 3 * 2
6 * 5 * 4 * 6
6 * 5 * 24
6 * 120
720
Quando uma funo chama a si mesma, novos parmetros e variveis locais
so alocados na pilha e o cdigo da funo executado com essas novas variveis.
Uma chamada recursiva no faz uma nova cpia da funo; apenas os argumen-
tos so novos. Quando cada funo recursiva retorna, as variveis locais e os
parmetros so removidos da pilha e a execuo recomea do ponto da chamada
funo dentro da funo.
5.2 Nmero triangular
Pitgoras, matemtico e lsofo grego, demonstrou vrias propriedades
matemticas, entre elas a propriedade dos nmeros triangulares. Um nmero
triangular um nmero natural que pode ser representado na forma de tringulo
equiltero. Para encontrar o n-simo nmero triangular a partir do anterior basta
somar-lhe n unidades. Os primeiros nmeros triangulares so 1, 3, 6, 10, 15, 21,
28. O n-simo termo pode ser descoberto pela frmula a seguir:
T
n
n
= = + + + + ( ) + ( ) + =
+ ( )
[ ]
=

k n n n
n n
k
1 2 3 2 1
1
2
17
1
...
64 Estrutura de Dados com Algoritmos e C
Estes nmeros so chamados de triangulares pois podem ser visualizados
como objetos dispostos na forma de um tringulo (gura 5.1).
Figura 5.1: Nmeros triangulares
Supondo que se esteja buscando o quinto elemento (dado pelo nmero 15),
como descobrir este elemento? Basta distribuir entre as linhas e colunas confor-
me a gura 5.2 (5+4+3+2+1 = 15).
Figura 5.2: Descobrindo o quinto elemento triangular
Este um processo repetitivo, dado por um programa simples (programa
5.3).
Recursividade 65
Programa 5.3: Descobrindo o nmero triangular (iterativo)
4
9
int triangulo(int n)
{
int iTotal = 0;
while( n > 0 )
{
iTotal += n;
n--;
}
return iTotal;
}
Este um processo recursivo [8] (gura 5.3) , pois:
Primeira coluna tem n elementos.
Soma-se a prxima coluna com n-1 elementos at que reste apenas 1
elemento.
Figura 5.3: Descobrindo o quinto elemento triangular de forma recursiva
O programa 5.4 implementa a soluo recursiva do problema. A gura 5.4
demostra o que ocorre a cada chamada da funo triangulo, nela pode ser obser-
vado o retorno de cada execuo da funo.
Programa 5.4: Descobrindo o nmero triangular (recursivo)
int triangulo(int n)
{
if( n == 1 )
1.
2.
66 Estrutura de Dados com Algoritmos e C
5
{
return n;
}
return n + triangulo(n-1);
}
Figura 5.4: O que ocorre a cada chamada
5.3 Nmeros de Fibonacci
Fibonacci (matemtico da Renascena italiana) estabeleceu uma srie curio-
sa de nmeros para modelar o nmero de casais de coelhos em sucessivas gera-
es. Assumindo que nas primeiras duas geraes s existe um casal de coelhos, a
seqncia de Fibonacci a seqncia de inteiros: 1, 1, 2, 3, 5, 8, 13, 21, 34, ....
No programa 5.5 mostrada uma verso iterativa para calcular o n-simo
termo da seqncia de Fibonacci.
Recursividade 67
Programa 5.5: Clculo do n-simo termo de Fibonacci (verso iterativa)
2
7
12
int bc(int n)
{
int l,h, x, i;
if( n <= 2)
return 1;
l = 0;
h = 1;
for(i=2; i<= n; i++)
{
/* Clculo do prximo nmero da seqncia. */
x = l;
l = h;
h = x + l;
}
return h;
}
O n-simo nmero denido como sendo a soma dos dois nmeros ante-
riores. Logo, fazendo a denio recursiva:
b(n) = n se n <= 2
b(n) = b(n-2) + b(n-1) se n > 2
A sua determinao recursiva impe o clculo direto do valor para dois ele-
mentos de base (a primeira e a segunda gerao). No programa 5.6 mostrada a
verso recursiva para calcular o n-simo termo da seqncia de Fibonacci.
Programa 5.6: Clculo do n-simo termo de Fibonacci (verso recursiva)
4
9
int br( int n )
{
if( n <= 2)
{
return 1;
}
/* chama a si prprio 2 vezes!!! */
return br(n-1) + br(n-2);
}
Esta soluo (programa 5.6) muito mais simples de programar do que
a verso iterativa (programa 5.5). Contudo, esta verso ineciente, pois cada
vez que a funo ibr chamada, a dimenso do problema reduz-se apenas uma
unidade (de n para n-1), mas so feitas duas chamadas recursivas. Isto d origem
a uma exploso combinatorial e o computador acaba por ter de calcular o mesmo
termo vrias vezes.

68 Estrutura de Dados com Algoritmos e C


Para calcular ibr(5) necessrio calcular ibr(4) e ibr(3). Conseqen-
temente, para calcular ibr(4) preciso calcular ibr(3) e ibr(2). E assim su-
cessivamente. Este tipo de processamento inadequado, j que o computador
obrigado a fazer trabalho desnecessrio. No exemplo, usando o programa 5.6, para
calcular ibr(5) foi preciso calcular ibr(4) 1 vez, ibr(3) 2 vezes, ibr(2) 3
vezes e ibr(1) 2 vezes. No programa iterativo (programa 5.5), apenas era neces-
srio calcular ibc(5), ibc(4), ibc(3), ibc(2) e ibc(1)1 vez. A gura 5.5
demonstra como caria a chamada do programa 5.6 para clculo do stimo termo.
Figura 5.5: Clculo de Fibonacci recursivo para o stimo termo
5.4 Algoritmo de Euclides
O algoritmo de Euclides busca encontrar o mximo divisor comum (MDC)
entre dois nmeros inteiros diferentes de zero. O procedimento simples:
Chame o primeiro nmero de m e o segundo nmero de n;
Divida m por n e chame o resto de r;
Se r for igual a zero, ento o MDC n e o procedimento termina, se
no o procedimento continua;
Atribua n para m e r para n;
Recomece o procedimento do segundo passo.
Estes passos podem ser descritos conforme o algoritmo 5.1.
No programa 5.7 vista uma verso iterativa do algoritmo de Euclides para
clculo do MDC.
1.
2.
3.
4.
5.
Recursividade 69
Algoritmo 5.1: Algoritmo de Euclides
Programa 5.7: Clculo do MDC iterativo
1 #include <stdio.h>
6
11
int mdc(int m, int n)
{
int r;
while( m % n != 0)
{
r = m % n;
m = n;
n = r;
}
return n;
}
16
int main(void)
{
printf("60,36 = %d\n", mdc(60,36));
printf("36,24 = %d\n", mdc(36,24));
return 0;
}
O programa 5.8 implementa o algoritmo de Euclides de forma recursiva.
Programa 5.8: Clculo do MDC recursivo
5
#include <stdio.h>
int mdc(int m, int n)
{
if( n == 0)
70 Estrutura de Dados com Algoritmos e C
{
return m;
}
return mdc(n, m % n);
}
10
int main(void)
{
printf("60,36 = %d\n", mdc(60,36));
printf("36,24 = %d\n", mdc(36,24));
return 0;
}
O MDC entre dois nmeros m e n tambm um divisor da sua diferena,
m-n. Por exemplo: o MDC de 60 e 36 12, que divide 24 = 60-36. Por outro
lado, o MDC dos dois nmeros m e n ainda o MDC do menor nmero (n) com
a diferena (m-n). Se houvesse um divisor comum maior, ele seria igualmente
divisor de n, contrariamente hiptese. Portanto, possvel determinar o MDC
de dois nmeros atravs da determinao do MDC de nmeros cada vez meno-
res. O programa termina quando os nmeros forem iguais e, neste caso, o MDC
este nmero. Exemplo: 60-36 = 24; 36-24 = 12; 24-12 = 12; 12 = 12. No pro-
grama 5.9 vista uma verso do programa MDC utilizando os passos descritos
(m sempre tem que ser maior que n).
Programa 5.9: Clculo do MDC recursivo
4
9
#include <stdio.h>
int mdc(int m, int n)
{
if( m == n )
{
return m;
}
if( m-n >= n)
{
return mdc(m-n, n);
}
return mdc(n, m-n);
}
14
int main(void)
{
printf("60,36 = %d\n", mdc(60,36));
printf("36,24 = %d\n", mdc(36,24));
Recursividade 71
return 0;
}
5.5 Torres de Hanoi
No grande templo de Benares, embaixo da cpula que marca o centro do mundo,
repousa uma placa de lato onde esto presas trs agulhas de diamante, cada uma com 50
cm de altura e com espessura do corpo de uma abelha. Em uma dessas agulhas, durante a
criao, Deus colocou sessenta e quatro discos de ouro puro, com o disco maior repousando
sobre a placa de lato e os outros diminuindo cada vez mais ate o topo. Essa a torre de
Brahma. Dia e noite, sem parar, os sacerdotes transferem os discos de uma agulha de
diamante para outra de acordo com as leis xas e imutveis de Brahma, que exigem que
o sacerdote em viglia no mova mais de um disco por vez e que ele coloque este disco em
uma agulha de modo que no haja nenhum disco menor embaixo dele. Quando os sessen-
ta e quatro discos tiverem sido assim transferidos da agulha em que a criao de Deus as
colocou para uma das outras agulhas, a torre, o templo e os brmanes viraro p, e com
um trovejar, o mundo desaparecer.
Vrias so as histrias que contam a origem da Torre de Hanoi. A histria
anterior foi utilizada pelo matemtico francs douard Lucas em 1883 como
inspirao para o jogo criado por ele [18].
A Torre de Hanoi um quebra-cabea que consiste em uma base contendo
trs pinos, onde em um deles so dispostos sete discos uns sobre os outros, em
ordem crescente de dimetro, 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 que em cima de outro menor
em nenhuma situao. O nmero de discos pode variar sendo que o mais simples
contm apenas trs (gura 5.6).
Figura 5.6: Torre de Hanoi
72 Estrutura de Dados com Algoritmos e C
A soluo para o problema da Torre de Hanoi com recursividade baseia-se
no seguinte:
A nica operao possvel de ser executada mover um disco de um
pino para outro;
Uma torre com (N) discos, em um pino, pode ser reduzida ao disco de
baixo e a torre de cima com (N-1) discos;
A soluo 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
o pino destino. Como a transferncia da torre de cima no uma ope-
rao possvel de ser executada, ela dever ser reduzida sucessivamente
at transformar-se em um movimento de disco.
O algoritmo 5.2 demostra os passos necessrios para desenvolver a funo
recursiva. Os passos podem ser observados na gura 5.7.
Algoritmo 5.2: Passar n peas de uma torre (A) para outra (C)
Baseado no algoritmo 5.2 possvel determinar o nmero de movimentos
necessrios e determinar os movimentos necessrios.
O nmero de movimentos necessrio simples de determinar:
hanoi_count(n) = hanoi_count(n-1) + 1 + hanoi_count(n-1)
Neste caso, possvel evitar dupla recursividade (como ocorre com Fibo-
nacci) de uma forma simples:
hanoi_count(n) = 2 * hanoi_count(n-1) + 1
1.
2.
3.
Recursividade 73
Figura 5.7: Movimentos conforme algoritmo
Para conseguir transferir todos os discos da primeira estaca terceira 2
n
- 1,
sendo n o nmero de discos, portanto:
Para solucionar um hanoi de 3 discos, so necessrios 23 - 1 movi-
mentos = 7 movimentos.
Para solucionar um hanoi de 7 discos, so necessrios 127 movimen-
tos (27 - 1).
Para solucionar um hanoi de 15 discos, so necessrios 32767 movi-
mentos (215 - 1).
Para solucionar um hanoi de 64 discos, como diz a lenda, so neces-
srios 18446744073709551615 movimentos (264 - 1) ou 585 bilhes
de anos (considerando um movimento por segundo).
No programa 5.10 vista a soluo para o problema da Torre de Hanoi
seguindo as denies recursivas.
Programa 5.10: Torre de Hanoi recursivo
/* programa_recursividade_hanoi.c */
#include <stdio.h>
5 void hanoi (int discos , char origem, char destino, char ajuda);
void hanoi (int discos, char origem, char destino, char ajuda)
{
10
if( discos == 1)
{
printf("\t Mova o disco %d de %c para %c \n",discos,origem, destino);
}
else

74 Estrutura de Dados com Algoritmos e C


15
20
{
hanoi(discos-1,origem,ajuda,destino);
printf("\t Mova o disco %d de %c para %c \n",discos,origem,destino);
hanoi(discos-1,ajuda,destino,origem);
}
return;
}
25
30
int main (void)
{
int total_discos;
printf("Informe o numero de discos:");
scanf("%d",&total_discos);
hanoi(total_discos,A,B,C);
printf("\n");
return 0;
}
5.6 Curiosidades com Recursividade
O programa 5.11 utiliza de recurso para imprimir a fase da lua (cheia,
minguante, crescente ou nova). Este programa foi um dos concorrentes do 15th
International Obfuscated C Code Contest
1
.
Programa 5.11: Natori - Imprimindo as fases da lua
/* programa_recursividade_curiosidade_01.c */
5
#include <stdio.h>
#include <math.h>
double l;
main(_,o,O)
{
return putchar((_--+22&&_+44&&main(_,-43,_),_&&o) ?
10
15
( main(-43,++o,O),
( (l=(o+21)/sqrt(3-O*22-O*O),l*l<4 &&
(fabs(((time(0)-607728)%2551443)/405859.-4.7 +
acos(l/2))<1.57))[" #"]))
:10 );
}
1. Disponvel em http://www.iocc.org/years.html
Recursividade 75
O programa 5.12 participou do mesmo concurso
2
e usa recursividade em cima
de ponteiros. O cdigo ir gerar outro cdigo que, quando compilado, ir gerar
outro cdigo e assim sucessivamente.
Programa 5.12: Dhyanh - Saitou, aku, soku e zan
/* programa_recursividade_curiosidade_01.c */
5
10
15
20
25
#dene/**/X
char*d="X0[!4cM,!"
"4cK*!4cJc(!4cHg&!4c$j"
"8f!&~]9e)!|:d+!)rAc-!*m*"
":d/!4c(b4e0!1r2e2!/t0e4!-y-c6!"
"+|,c6!)f$b(h*c6!(db(i)d5!(b*a&c"
")c5!b+&bc)c4!&b-_$cd*c3!&a.hd+"
"d1!%a/ge+e0!%b-g(d.d/!&c*hd1d-!(d%g)"
"d4d+!*l,d7d)!,h-d;c!.b0c>d%!ADc$![7)35E"
"!1cA,,!2kE*!-s@d(!(k(f//g&!)f.e5f(!+a+)"
"f%2g*!?f5f,!=f-*e/!<d6e1!9e0f3!6f)-g5!4d*b"
"+e6!0f%k)d7!+~^c7!)z/d-+!n%a0(d5!%c1a+/d4"
"!2)c9e2!9b;e1!8b>e/! 7cAd-!5fAe+!7fBe(!"
"8hBd&!:iAd$![7S,Q0!1 bF 7!1b?_6!1c,8b4"
"!2b*a,*d3!2n4f2!${4 f. !%y4e5!&f%"
"d-^-d7!4c+b)d9!4c-a d :!/i(&d"
";!+la+d<!)l*b(d=! m- a &d>!&d"
"0_&c?!$dAc@!$cBc@!$ b < ^&d$"
":!$d9_&l++^$!%f3a n1 _ $ !&"
"f/c(o/_%!(f+c)q*c %! * f &d+"
"f$s&!-n,d)n(!0i- c- k) ! 3d"
"/b0h*!H7a,![7* i] 5 4 71"
"[=ohr&o*t*q**d *v *r ; 02"
"7*~=h./}tcrsth &t : r 9b"
"].,b-725-.t--// #r [ < t8-"
"752793? <.~;b ].t--+r / # 53"
30
35
"7-r[/9~X .v90 <6/<.v;-52/={ k goh"
"./}q; u vto hr .i*$engt$ $ ,b"
";$/ =t ;v; 6 =it.;7= : ,b-"
"725 = / o. .d ;b]--[/+ 55/ }o"
".d : - ?5 / }o. v/i]q - "
"-[; 5 2 = it . o;53- . "
"v96 <7 / =o : d =o"
"--/i ]q-- [; h. / = "
"i]q--[ ;v 9h ./ < - "
2. http://www.iocc.org/2000/dhyang.hint
76 Estrutura de Dados com Algoritmos e C
40
45
50
55
60
65
"52={cj u c& i t . o ; "
"?4=o:d= o-- / i ]q - "
"-[;54={ cj uc& i]q - -"
"[;76=i]q[;6 =vsr u.i / ={"
"=),BihY_gha ,)\0 " , o [
3217];int i, r,w,f , b ,x ,
p;n(){return r <X X X X X
768?d[X(143+ X r++ + *d ) %
768]:r>2659 ? 59: ( x = d
[(r++-768)% X 947 + 768] ) ?
x^(p?6:0):(p = 34 X X X )
;}s(){for(x= n (); ( x^ ( p
?6:0))==32;x= n () ) ;return x ; }
void/**/main X () { r = p
=0;w=sprintf (X X X X X X o
,char*d=); for ( f=1;f < * d
+143;)if(33-( b=d [ f++ X ] )
){if(b<93){if X(! p ) o
[w++]=34;for X(i = 35 +
(p?0:1);i<b; i++ ) o
[w++]=s();o[ w++ ]
=p?s():34;} else X
{for(i=92; i<b; i
++)o[w++]= 32;} }
else o [w++ ]
=10;o [
w]=0 ;
puts(o);}
5.7 Cuidados com Recursividade
Ao escrever funes recursivas, deve-se ter um comando if em algum lugar
para forar a funo a retornar sem que a chamada recursiva seja executada. Se
no existir, a funo nunca retornar quando chamada (equivalente a um loop
innito). Omitir o comando if um erro comum quando se escrevem funes
recursivas.
Isto garante que o programa recursivo no gere uma seqncia innita de
chamadas a si mesmo. Portanto, todo programa deve ter uma condio de parada
no recursiva. Nos exemplos vistos, as condies de paradas no recursivas eram:
Fatorial: 0! = 1
Nmeros Triangulares: n = 1
Seqncia de Fibonacci: b(1) = 1 e b(2) = 1

Recursividade 77
Euclides: n = 0
Hanoi: discos = 1
Sem essa sada no recursiva, nenhuma funo recursiva poder ser computada.
Ou seja, todo programa recursivo deve ter uma condio de parada no recursiva.
5.8 Vantagens
A maioria das funes recursivas no minimiza signicativamente o tama-
nho do cdigo ou melhora a utilizao da memria. Alm disso, as verses re-
cursivas da maioria das rotinas podem ser executadas um pouco mais lentamente
que suas equivalentes iterativas devido s repetidas chamadas funo. De fato,
muitas chamadas recursivas a uma funo podem provocar um estouro da pilha.
Como o armazenamento para os parmetros da funo e variveis locais est na
pilha e cada nova chamada cria uma nova cpia dessas variveis, a pilha pode pro-
vavelmente escrever sobre outra memria de dados ou de programa. Contudo,
no necessrio se preocupar com isso, a menos que uma funo recursiva seja
executada de forma desenfreada. A principal vantagem das funes recursivas a
possibilidade de utiliz-las para criar verses mais claras e simples de vrios algo-
ritmos, embora uma soluo no recursiva envolvendo outras estruturas (pilhas,
las etc.) seja mais difcil de desenvolver e mais propensa a erros. Dessa forma,
ocorre um conito entre a ecincia da mquina e a do programador. O custo
da programao est aumentando e o custo da computao est diminuindo.
Isto leva a um cenrio que no vale a pena para um programador demandar
muito tempo para elaborar programas iterativos quando solues recursivas
podem ser escritas mais rapidamente. Somente deve-se evitar o uso de solues
recursivas que utilizam recurso mltipla (Fibonacci, por exemplo).
5.9 Exerccios
Determine o que a seguinte funo recursiva em C calcula. Escreva
uma funo iterativa para atingir o mesmo objetivo:
5
int func(int n)
{
if( n == 0)
return 0;
return (n+ func(n-1));
}

1.
78 Estrutura de Dados com Algoritmos e C
Imagine vet como um vetor de inteiros. Apresente programas iterativos
e recursivos para calcular:
o elemento mximo do vetor;
o elemento mnimo do vetor;
a soma dos elementos do vetor;
o produto dos elementos do vetor;
a mdia dos elementos do vetor.
Uma cadeia s de caracteres palndrome se a leitura de s igual da
esquerda para a direita e da direita para a esquerda. Por exemplo, as
palavras seres, arara e ama so palndromes, assim como a sentena A
torre da derrota. Faa um programa que que lendo palavras do usu-
rio e imprima uma mensagem dizendo se as palavras so palndromes
ou no. O seu programa deve ter uma funo recursiva com o seguinte
prottipo: int palindrome( char * palavra, int irst,
int last);. Esta funo recebe como parmetros a palavra que est
sendo testada se palndrome ou no e os ndices que apontam para o
primeiro e ltimo caracteres da palavra. Talvez seja mais fcil fazer uma
funo com o seguinte prottipo: int checaPalindrome(char *
palavra, int last);. Esta funo recebe a palavra a ser verica-
da e o tamanho da palavra.
A funo de Ackermann [2, 19] denida recursivamente nos nmeros
no negativos como segue:
a(m,n) = n + 1 Se m = 0,
a(m,n) = a(m-1,1) Se m <> 0 e n = 0,
a(m,n) = a(m-1, a(m,n-1)) Se m <> 0 e n <> 0
Faa um procedimento recursivo para computar a funo de Ackermann.
Observao: Esta funo cresce muito rpido, assim ela deve ser impressa para
valores pequenos de m e n.
2.
a)
b)
c)
d)
e)
3.
4.
a)
b)
c)
6. Lista
Existem trs tipos de pessoas: as que
deixam acontecer, as que fazem acontecer
e as que perguntam o que aconteceu.
Provrbio escocs
Uma lista um estrutura de dados similar a um pilha ou la. Uma pilha ou
la tem um tamanho xo na memria, e o acesso a um elemento da estrutura
destri o item selecionado (operao dequeue e pop). Na lista, cada elemento
contm um elo (endereo) para o prximo elemento da lista. Desta forma a lista
pode ser acessada de maneira randmica, podendo ser retirados, acrescidos ou
inseridos elementos no meio da lista dinamicamente.
Uma lista pode ter uma entre vrias formas. Ela pode ser simplesmente
ligada ou duplamente ligada, pode ser ordenada ou no, e pode ser circular ou
no. Se uma lista simplesmente ligada, omitimos o ponteiro anterior em cada
elemento. Se uma lista ordenada, a ordem linear da lista corresponde or-
dem linear de chaves armazenadas em elementos da lista; o elemento minmo
o incio da lista e o elemento mximo o m. Se a lista no ordenada, os
elementos podem aparecer em qualquer ordem. Em uma lista circular, o pon-
teiro anterior do incio da lista aponta para o m, e o ponteiro prximo do m
da lista aponta para o incio. Desse modo, a lista pode ser vista como um anel
de elementos [9].
Listas encadeadas podem ser singularmente (ou simples) ou duplamente
encadeadas (ligadas). Uma lista simplesmente encadeada contm um elo (ende-
reo) com o prximo item da lista. Uma lista duplamente encadeada contm elos
(endereos) com o elemento anterior e com o elemento posterior a ele. O uso de
cada tipo de lista depende da sua aplicao.
80 Estrutura de Dados com Algoritmos e C
Normalmente a representao de uma lista simplesmente encadeada dada
por um endereo (n) que contm o prximo elemento (gura 6.1).
Figura 6.1: Exemplo de lista simplesmente encadeada
O ltimo elemento da lista pode ter o endereo para um endereo nulo
(caracterizando o nal da lista) ou o endereo para o primeiro item lista (carac-
terizando uma lista circular).
Uma lista sem endereos (tambm conhecida como ns) chamada de
lista vazia ou lista nula. Uma observao importante: a lista simplesmente en-
cadeada por ser utilizada para representar uma pilha ou la de dados dinami-
camente.
Listas duplamente encadeadas consistem em dados e endereos (ns) para
o prximo item e para o item precedente (gura 6.2). O fato de haver dois ns
tem duas vantagens principais: a lista pode ser lida em ambas as direes, o que
simplica o gerenciamento da lista. E, no caso de alguma falha de equipamento,
onde uma das extremidades seja perdida, a lista pode ser reconstruda a partir da
outra extremidade.
Lista 81
Figura 6.2: Exemplo de lista duplamente encadeada
As operaes para manipulao de uma lista so:
insert - insere num determinado ponto uma informao na lista.
add - adiciona ao nal da lista uma informao.
delete - deleta um n da lista.
info - retorna uma informao da lista atual.
next - desloca o ponteiro atual da lista caminhando para o nal.
last - desloca o ponteiro atual da lista caminhando para o incio da
lista.
A gura 6.3 ilustra a insero de um novo elemento entre os elementos 1 e
2. A gura 6.4 ilustra a remoo de um elemento.
Figura 6.3: Incluso de novo elemento

82 Estrutura de Dados com Algoritmos e C


Figura 6.4: Excluso de elemento
Nos algoritmos 6.1 e 6.2 so implementadas as operaes insert e delete con-
forme visto nas guras 6.3 e 6.4.
Algoritmo 6.1: Insero numa lista duplamente encadeada
6.1 Vetores ou alocao dinmica?
Vetores podem ser utilizados para implementao de listas, exceto por um
problema. Um vetor considerado pelas linguagens de programao como uma
nica varivel, mesmo um vetor que cresce dinamicamente. Por ser considerada,
uma nica varivel, a ocupao em memria contgua (lado a lado), portanto,
no possvel alocar novos elementos nos buracos da memria, o que pode limi-
tar o crescimento da lista.
Lista 83
A gura 6.5 apresenta a problemtica do crescimento de um vetor. Con-
siderando 90 posies de memria, um vetor ocupa os espaos compreendidos
entre as posies 01 e 12. Este vetor poder crescer at o tamanho mximo de 15
posies, que o maior espao livre e contguo disponvel.
Figura 6.5: Problemtica do crescimento do vetor
Se em vez de um vetor, a lista for implementada com estruturas de dados
(structs em C), possvel crescer at o tamanho mximo disponvel na memria.
Conforme a gura 6.6, aps a lista reservar os segmentos 13,14 e 15 ela pode
continuar crescendo a partir da ocupao do segmento 17,18 e 19 que eram os
prximos segmentos de memria disponveis.
Algoritmo 6.2: Remoo numa lista duplamente encadeada
84 Estrutura de Dados com Algoritmos e C
Figura 6.6: Crescimento de uma lista sem utilizar vetor
6.2 Listas em C
Na linguagem C, os vetores podem ser utilizados para representar uma lis-
ta, mas a lista tambm pode ser implementada atravs de estruturas com aloca-
o dinmica de memria.
Na implementao de listas em C normalmente so utilizadas estruturas
que, alm de conter as informaes necessrias - cdigos, nomes etc. -, tero
campos adicionais para controle da prpria lista. Os campos adicionais de con-
trole de lista so ponteiros para a prpria lista (isto possvel atravs da capacida-
de da linguagem de denio recursiva). No programa 6.1 demonstrado como
manipular uma lista simples. Na manipulao de lista simples exigida a criao
de um ponteiro para manter o incio da lista.
Programa 6.1: Exemplo de manipulao de lista simples em C
/* programa_lista_01.c */
#include <stdio.h>
#include <stdlib.h>
4
9
struct LISTA
{
char string[41];
int numero;
struct LISTA * NEXT;
};
Lista 85
14
int main(void)
{
int i;
struct LISTA *lista;
struct LISTA *inicio;
19
24
lista = calloc(1,sizeof(struct LISTA));
if( lista == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(-1);
}
lista->NEXT = NULL;
29
/* guardando o incio da lista */
inicio = lista;
for(i=0;i<25;i++)
{
lista->numero = i;
sprintf(lista->string, "Numero %02d", i);
34
39
/* aloca o prximo elemento da lista */
lista->NEXT = calloc(1,sizeof(struct LISTA));
if( lista->NEXT == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(-1);
}
44
/* posiciona no prximo elemento */
lista = lista->NEXT;
lista->NEXT = NULL;
}
49
/* volta para o incio da lista */
lista = inicio;
while(lista->NEXT != NULL )
{
printf("\nNumero = %d, String = %s", lista->numero, lista->string);
54
/* caminha elemento a elemento da lista */
lista = lista->NEXT;
}
59
lista = inicio;
while( lista->NEXT != NULL )
{
86 Estrutura de Dados com Algoritmos e C
struct LISTA *next; /* mantm referncia do prximo elemento */
next = lista->NEXT;
64
/* libera o espao do endereo atual e limpa o endereo
atribuindo NULL */
free(lista);
lista = NULL;
lista = next;
}
69
return 0;
}
O programa 6.2 apresenta um programa simples para manipulao de uma
lista encadeada. Alm de incluir um campo adicional na estrutura para manter
o endereo do elemento anterior, o tratamento com relao ao incio e nal de
la realizado da forma correta (observe que o programa 6.1 sempre aloca um
elemento a mais que no utilizado).
Programa 6.2: Exemplo de manipulao de lista encadeada em C
/* programa_lista_02.c */
#include <stdio.h>
4
9
#include <stdlib.h>
struct LISTA
{
char string[41];
int numero;
struct LISTA * NEXT;
struct LISTA * LAST;
};
14
int main(void)
{
int i;
struct LISTA *lista;
19
lista = calloc(1,sizeof(struct LISTA));
if( lista == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(1);
}
24
Lista 87
lista->NEXT = NULL;
lista->LAST = NULL;
29
for(i=0;i<25;i++)
{
struct LISTA *atual;
lista->numero = i;
sprintf(lista->string, "Numero %02d", i
34
39
/* aloca o prximo elemento da lista */
lista->NEXT = calloc(1,sizeof(struct LISTA));
if( lista->NEXT == NULL )
{
printf("\nErro de alocacao de memoria!");
exit(1);
}
44
/* pega o endereo do elemento atual */
atual = lista;
lista = lista->NEXT;
lista->NEXT = NULL;
49
/* guarda o endereo do elemento anterior */
lista->LAST = atual;
}
54
lista = lista->LAST;
free(lista->NEXT); /* descarta o ltimo elemento alocado no utilizado */
lista->NEXT = NULL;
59
while(1)
{
printf("\nNumero = %d, string = %s", lista->numero, lista->string);
if( lista->LAST == NULL )
{
break;
}
64
/* caminha na lista do nal para o incio */
lista = lista->LAST;
}
69
while(lista != NULL )
{
struct LISTA *next;
next = lista->NEXT;
88 Estrutura de Dados com Algoritmos e C
74
/* liberar o endereo */
free(lista);
lista = NULL;
lista = next;
}
79
return 0;
}
O programa 6.3 contm um conjunto de funes que implementam todas
as operaes que podem ocorrer com uma lista (estas operaes podem ser pro-
gramadas de diferentes maneiras). O controle de incio e m da lista realizado
dentro do programa principal (main) de forma indireta (atravs do controle de
elementos).
Programa 6.3: Funes para manipulao de listas
/* programa_lista_03.c */
#include <stdio.h>
#include <stdlib.h>
5
10
struct NOPTR
{
int information;
struct NOPTR *next;
struct NOPTR *last;
};
void add(struct NOPTR **p, int i)
{
struct NOPTR *aux;
15
20
25
if( *p == NULL ) /* primeiro elemento da lista */
{
*p = (struct NOPTR *)malloc(sizeof(struct NOPTR));
if( *p == NULL)
{
printf("\nErro de alocacao de memoria");
exit(1);
}
(*p)->next = NULL;
(*p)->last = NULL;
(*p)->information = i;
}
else
Lista 89
30
35
40
{
aux = *p; /* demais elementos da lista */
(*p)->next = (struct NOPTR *)malloc(sizeof(struct NOPTR));
if( (*p)->next == NULL)
{
printf("\nErro de alocacao de memoria");
exit(1);
}
*p = (*p)->next;
(*p)->next = NULL;
(*p)->last = aux;
(*p)->information = i;
}
return;
}
45
50
void insert(struct NOPTR **p, int i)
{
struct NOPTR *new;
if( p == NULL)
{
printf("\nLista vazia, no pode ser inserido elementos");
exit(1);
}
55
60
new = (struct NOPTR *)malloc(sizeof(struct NOPTR));
if( new == NULL)
{
printf("\nErro de alocacao de memoria");
exit(1);
}
new->information = i;
new->last = *p;
new->next = (*p)->next;
65
70
/* controla insero no nal da lista */
if( (*p)->next != NULL )
{
struct NOPTR *atual;
atual = *p;
*p = (*p)->next;
(*p)->last = new;
*p = atual;
}
(*p)->next = new;
75
return;
}
90 Estrutura de Dados com Algoritmos e C
80
void delete(struct NOPTR **p)
{
struct NOPTR *last;
struct NOPTR *next;
85
/* salva os ponteiros para fazer o ajuste */
last = (*p)->last;
next = (*p)->next;
free(p);
*p = NULL;
*p = last;
90
(p)->next = next;
95
/* controla remoo no nal da lista */
if( next != NULL )
{
*p = (*p)->next;
(*p)->last = last;
}
return;
}
100
105
struct NOPTR * last(struct NOPTR *p)
{
if( p->last != NULL )
{
return p->last;
}
return NULL;
}
110
115
struct NOPTR * next(struct NOPTR *p)
{
if( p->next != NULL )
{
return p->next;
}
return NULL;
}
120
int info(struct NOPTR *p)
{
return p->information;
}
Lista 91
125
130
int main(void)
{
int i;
struct NOPTR *p;
p = NULL;
for(i=0;i<10;i++)
{
add(&p,i);
}
135
140
printf("\nImprimindo do inal para o inicio");
for(i=0;i<9;i++)
{
printf("\nInformation = %d", info(p));
p = last(p);
}
printf("\nInformation = %d", info(p));
145
printf("\nImprimindo do inicio para o inal");
printf("\nInformation = %d", info(p));
for(i=0;i<9;i++)
{
p = next(p);
printf("\nInformation = %d", info(p));
}
150
printf("\nVoltando 1 elementos e inserindo um novo elemento");
p = last(p);
insert(&p,99);
155
160
165
printf("\nInformation atual = %d", info(p));
p = next(p);
printf("\nInformation new = %d", info(p));
p = next(p);
printf("\nInformation atual = %d", info(p));
printf("\nImprimindo do inal para o inicio");
for(i=0;i<10;i++)
{
printf("\nInformation = %d", info(p));
p = last(p);
}
printf("\nInformation = %d", info(p));
170
printf("\nInserindo 1 elemento e imprimindo do inicio para o inal");
insert(&p,88);
printf("\nInformation = %d", info(p));
for(i=0;i<11;i++)
{
92 Estrutura de Dados com Algoritmos e C
p = next(p);
printf("\nInformation = %d", info(p));
}
175
180
printf("\nVoltando 1 elemento e removendo");
p = last(p);
printf("\nInformation = %d", info(p));
delete(&p);
printf("\nInformation = %d", info(p));
185
printf("\nImprimindo do inal para o inicio");
for(i=0;i<10;i++)
{
printf("\nInformation = %d", info(p));
p = last(p);
}
printf("\nInformation = %d", info(p));
195
printf("\nImprimindo do inicio para o inal");
printf("\nInformation = %d", info(p));
for(i=0;i<10;i++)
{
p = next(p);
printf("\nInformation = %d", info(p));
}
200
205
delete(&p);
printf("\nImprimindo do inal para o inicio");
for(i=0;i<9;i++)
{
printf("\nInformation = %d", info(p));
p = last(p);
}
printf("\nInformation = %d", info(p));
return;
}
Lista 93
6.3 Exerccios
Conforme comentado, a lista pode ser utilizada para implementar uma
la ou uma pilha com crescimento dinmico, ento:
Crie um programa para manipulao de pilhas utilizando listas
Crie um programa para manipulao de las utilizando listas.
Observao: Lembrar que a pilha cresce e diminui ao nal, e a la cres-
ce ao nal e diminui frente.
Criar uma lista circular com 20 elementos, fazer um programa para
tratar esta lista.
Alterar o programa 6.3 para que as operaes next e last controlem
corretamente o incio e o m da la.
Alterar o programa 6.3 para a que a funo insert utilize a funo
add caso a insero seja no nal da lista.
1.
a)
b)
2.
3.
4.
7. Pesquisa
A arte de programar consiste na arte de
organizar e dominar a complexidade.
Edsger Dijkstra
Bancos de dados existem para que, de tempos em tempos, um usurio possa
localizar o dado de um registro simplesmente digitando sua chave. H apenas
um mtodo para se encontrar informaes em um arquivo (matriz) desordenado
e um outro para um arquivo (matriz) ordenado.
Encontrar informaes em uma matriz desordenada requer uma pesquisa
seqencial comeando no primeiro elemento e parando quando o elemento pro-
curado, ou o nal da matriz, encontrado. Esse mtodo deve ser usado em dados
desordenados, podendo ser aplicado tambm a dados ordenados. Se os dados
foram ordenados, pode-se utilizar uma pesquisa binria, o que ajuda a localizar
o dado mais rapidamente.
7.1 Pesquisa Seqencial
Este o mtodo mais simples de pesquisa, e consiste em uma varredura
serial da tabela (vetor, matriz ou arquivo), durante a qual o argumento de pes-
quisa comparado com a chave de cada entrada at ser encontrada uma igual,
ou ser atingido o nal da tabela, caso a chave procurada no exista. A pesquisa
seqencial fcil de ser codicada. O algoritmo 7.1 apresenta a implementao
em pseudo-cdigo em uma pesquisa seqencial genrica. A funo de pesquisa
pode ser implementada por duas formas:
Pesquisa 95
Retornar o prprio elemento encontrado;
Retornar o ndice do elemento (no caso de um vetor)
A gura 7.1 demostra um processo de pesquisa seqencial em um arquivo
qualquer. No caso, este arquivo deveria ter um campo chave para ser utilizado
na pesquisa ([6]).
Algoritmo 7.1: Pesquisa seqencial
O desempenho deste algoritmo bastante modesto, j que o nmero m-
dio de comparaes para a localizao de uma entrada arbitrria dado por
N
n
c
=
+1
2
Figura 7.1: Processo de pesquisa seqencial
A funo mostrada no programa 7.1 faz uma pesquisa em um vetor de ca-
racteres de comprimento conhecido at que seja encontrado, a partir de uma
chave especca, o elemento procurado:

96 Estrutura de Dados com Algoritmos e C


Programa 7.1: Funo para pesquisa seqencial
3
8
13
int pesquisa_sequencial( char * item, int contador, char chave )
{
register int t;
/* procura do 1
0
elemento do vetor at o ltimo */
for( t = 0; t<contador; t++)
{
if( chave == item[t] )
{
return t; /* se encontrar, retorna o ndice */
}
}
return -1; /* no encontrou a chave */
}
Esta funo devolve o ndice da entrada encontrada se existir alguma; caso
contrrio, ir devolver -1.
comum o fato de algumas entradas serem mais solicitadas do que outras.
Desta forma, se a tabela contiver nas suas primeiras posies as entradas mais
solicitadas, o nmero mdio de comparaes ser menor que se a lista estivesse
distribuida aleatoriamente. Observe a tabela 7.1, onde h cinco entradas e suas
freqncias de pesquisa.
Tabela 7.1: Entradas e freqncias para clculo de comparaes mdias
Entrada Freqncia
A 0,5
B 0,3
C 0,15
D 0,05
E 0,03
O nmero mdio de comparaes para a localizao de uma entrada, con-
siderando que elas aparecem na seqncia dada, ser dado por: N
c
= 1 * 0,5 + 2 *
0,3 + 3 * 0,15 + 4 * 0,05 + 5 * 0,03 = 1,9 comparaes.
Caso as entradas estivessem distribudas aleatoriamente, o nmero mdio
de comparaes seria dado por
N
n
c
=
+
=
+
=
1
2
5 1
2
3

comparaes.
Pesquisa 97
Como no possvel conhecer antecipadamente a distribuio das entradas
e suas freqncias de acesso, durante o processo de pesquisa possvel mover
as entradas mais solicitadas para o incio da tabela. Uma estratgia consiste em
mover a entrada para o incio da tabela cada vez que ela for solicitada [16, 14]. O
pseudo-cdigo 7.2 apresenta esta possibilidade.
Algoritmo 7.2: Pesquisa seqencial com ajuste de freqncia
7.2 Pesquisa Binria
Se o dado a ser encontrado se apresentar de forma ordenada, pode ser uti-
lizado um mtodo muito superior para encontrar o elemento procurado. Esse
mtodo a pesquisa binria que utiliza a abordagem dividir e conquistar. Ele
primeiro verica o elemento central, se esse elemento maior que a chave, ele
testa o elemento central da primeira metade; caso contrrio, ele testa o elemento
central da segunda metade. Esse procedimento repetido at que o elemento
seja encontrado ou que no haja mais elementos a testar (veja o algoritmo 7.3).
Por exemplo, para encontrar o nmero 4 na matriz 1 2 3 4 5 6 7 8 9, uma
pesquisa binria primeiro testa o elemento mdio, nesse caso 5. Visto que
maior que 4, a pesquisa continua com a primeira metade ou 1 2 3 4 5. O ele-
mento central agora 3, que menor que 4, ento a primeira metade descar-
tada. A pesquisa continua com 4 5. Nesse momento o elemento encontrado.
O desempenho deste algoritmo dado pela expresso log
2
n sendo o n o nmero
de elementos da tabela. A gura 7.2 exemplica a busca binria em uma tabela
ordenada; neste caso, est sendo pesquisado o elemento 34.
98 Estrutura de Dados com Algoritmos e C
Algoritmo 7.3: Pesquisa binria
Figura 7.2: Busca binria
Pesquisa 99
No programa 7.2 demonstrada uma pesquisa binria para um vetor de
caracteres.
Programa 7.2: Funo para pesquisa binria
2
7
int binrio( char * item, int contador, char chave)
{
int baixo, alto, meio;
baixo = 0;
alto = contador - 1;
while( baixo <= alto )
{
/* dividir para conquistar */
meio = (baixo+alto)/2;
12
17
/* procura nas metades at encontrar o elemento */
if( chave<item[meio])
alto = meio - 1; /* elemento na metade inferior */
else if( 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 Exerccios
Calcule o nmero mdio de comparaes necessrio para localizar uma
entrada em tabelas com 15, 127, 32.767 e 35.215 entradas, para a pes-
quisa seqencial e para a pesquisa binria.
Construa um programa que gere e preencha randomicamente vetores
com 15, 127, 32.767 e 35.215 itens e comprove os clculos obtidos na
questo anterior.
1.
2.
8. Ordenao
A frmula para o sucesso : A=X+Y+Z,
onde A sucesso, X trabalho, Y lazer
e Z boca fechada.
Albert Einstein
Ordenao o processo de arranjar um conjunto de informaes semelhan-
tes em uma ordem crescente ou decrescente. Especicamente, dada uma lista
ordenada i de n elementos, ento: i
1
<= i
2
<= ... <= I
n
[13].
Algoritmo de ordenao em cincia da computao um algoritmo que
coloca os elementos de uma dada sequncia em uma certa ordem - em outras
palavras, efetua sua ordenao completa ou parcial. As ordens mais usadas so a
numrica e a lexicogrca.
Existem vrias razes para se ordenar uma sequncia, uma delas a possibi-
lidade se acessar seus dados de modo mais eciente.
8.1 BubbleSort
O algoritmo de ordenao BubbleSort um mtodo simples de ordenao
por troca. Sua popularidade vem do seu nome fcil e de sua simplicidade. Porm,
uma das piores ordenaes j concebidas. Ela envolve repetidas comparaes e,
se necessrio, 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 exemplica o mtodo BubbleSort.
Ordenao 101
Tabela 8.1: BubbleSort - primeira varredura
troca L[1] L[2] L[3] L[4] L[5]
1 com 2 10 9 7 13 5
2 com 3 9 10 7 13 5
4 com 5 9 7 10 13 5
m da varredura 9 7 10 5 13
Aps a primeira varredura (tabela 8.1), o maior elemento encontra-se aloca-
do em sua posio denitiva na lista ordenada. Logo, a ordenao pode continu-
ar no restante da lista sem considerar o ltimo elemento (tabela 8.2).
Na segunda varredura, o segundo maior elemento encontra-se na sua po-
sio denitiva e o restante da ordenao realizada considerando apenas os 3
ltimos elementos (7, 9 e 5). Logo so necessrias elementos - 1 varreduras,
pois cada varredura leva um elemento para sua posio denitiva.
Tabela 8.2: BubbleSort - segunda varredura
troca L[1] L[2] L[3] L[4] L[5]
troca 1 com 2 9 7 10 5 13
troca 3 com 4 7 9 10 5 13
m da varredura 7 9 5 10 13
O algoritmo 8.1 mostra o funcionamento do algoritmo de ordenao Bub-
bleSort.
102 Estrutura de Dados com Algoritmos e C
Algoritmo 8.1: Ordenao Bubble
No melhor caso, o algoritmo executa
n
2
2
( )
operaes relevantes. No pior
caso, so feitas 2n
2
operaes e no caso mdio, so feitas
5
2
2
n
( )
operaes [13].
Por ser um algoritmo de ordem quadrtica, no recomendado para programas
que precisem de velocidade e operem com quantidade elevada de dados.
A ordenao Bubble dirigida por dois laos (programa 8.1). Dados que
existem iQtdElementos elementos na matriz, o lao mais externo faz a matriz
ser varrida iQtdElementos-1 vezes. Isso garante, na pior hiptese, que todo
elemento estar na posio correta quando a funo terminar. O lao mais inter-
no faz as comparaes e as trocas.
Programa 8.1: Funo BubbleSort
/* programa_bubble_01.c */
#include <stdio.h>
#include <stdlib.h>
5
10
void bubble( int piItem[], int iQtdElementos )
{
register int i,j;
register int iAux;
for(i=1;i<iQtdElementos;i++)
{
for(j=iQtdElementos-1;j>=i;j--)
{
if(piItem[j-1] > piItem[j])
{
Ordenao 103
15
20
iAux = piItem[j-1];
piItem[j-1] = piItem[j];
piItem[j] = iAux;
}
}
}
return;
}
25
int main(void)
{
int iContador;
int aBubble[] = { 10, 9, 7, 13, 5};
bubble(aBubble, 5);
30
35
printf("Ordenado:");
for(iContador = 0; iContador < 5; iContador++)
{
printf(" %d", aBubble[iContador] );
}
printf("\n");
40
return 0;
}
O programa, na forma como apresentado, sempre varre do incio ao m
da tabela, mesmo que no ocorram mais trocas. Neste caso, o programa pode ser
melhorado para detectar que no houve nenhuma troca e conseqentemente a
tabela j est ordenada (programa 8.2).
Programa 8.2: Funo BubbleSort melhorado
/* programa_bubble_02.c */
#include <stdio.h>
#include <stdlib.h>
5
10
void bubble( int piItem[], int iQtdElementos )
{
register int i,j;
register int iAux;
_Bool bTroca;
for(i=1;i<iQtdElementos;i++)
{
104 Estrutura de Dados com Algoritmos e C
15
20
bTroca = 0; /* falso */
for(j=iQtdElementos-1;j>=i;j--)
{
if(piItem[j-1] > piItem[j])
{
iAux = piItem[j-1];
piItem[j-1] = piItem[j];
piItem[j] = iAux;
bTroca = 1; /* verdadeiro */
}
}
25
30
if( !bTroca )
{
return;
}
}
return;
}
35
int main(void)
{
int iContador;
int aBubble[] = { 10, 9, 7, 13, 5};
bubble(aBubble, 5);
40
printf("Ordenado:");
for(iContador = 0; iContador < 5; iContador++)
{
printf(" %d ", aBubble[iContador] );
}
45
printf("\n");
return 0;
}
8.2 Ordenao por Seleo
A ordenao por seleo consiste em trocar o menor elemento (ou maior)
de uma lista com o elemento posicionado no incio da lista, depois o segundo
menor elemento para a segunda posio e assim sucessivamente com os (n - 1)
elementos restantes, at os ltimos dois elementos. A complexidade deste algo-
ritmo (n - 1) + (n - 2) + + 2 + 1 =
n n ( ) 1
2

= n
2
comparaes.
Ordenao 105
Na tabela 8.3 so vistos os passos para a ordenao de uma seqencia de 5
inteiros. A gura 8.1 apresenta a ordenao por seleo do menor valor para o
maior valor.
Tabela 8.3: Seleo - o que ocorre em cada passo
inicial 13 7 5 1 4
passo 1 1 7 5 13 4
passo 2 1 4 5 13 7
passo 3 1 4 5 13 7
passo 4 1 4 5 7 13
Figura 8.1: Exemplo de Ordenao por Seleo com nmeros inteiros
106 Estrutura de Dados com Algoritmos e C
Algoritmo 8.2: Ordenao por Seleo
O programa 8.3 implementa o algoritmo de ordenao por insero, utili-
zando dados da tabela 8.3.
Programa 8.3: Funo Select
2
/* programa_selecao_01.c */
#include <stdio.h>
7
12
void selection(int piItem[],int iQtdElementos)
{
register int i,j, iMinimo, iAux;
for( i=0; i<iQtdElementos-1; i++)
{
iMinimo=i;
for( j=i+1; j<iQtdElementos; j++)
{
if (piItem[j] < piItem[iMinimo])
17
22
iMinimo=j;
}
}
iAux = piItem[i];
piItem[i] = piItem[iMinimo];
piItem[iMinimo] = iAux;
}
return;
}
27
int main(void)
{
int iContador;
int aSelect[] = { 13,7,5,1,4 };
selection(aSelect, 5);
Ordenao 107
32
printf("Ordenado:");
for(iContador = 0; iContador < 5; iContador++)
{
printf(" %d ", aSelect[iContador] );
}
37
printf("\n");
return 0;
}
8.3 Ordenao por Insero
A ordenao por insero um algoritmo simples e indicado para listas
pequenas de valores a serem ordenados.
Inicialmente, ela ordena os dois primeiros membros da lista, em seguida o
algoritmo insere o terceiro membro na sua posio ordenada com relao aos
dois primeiros membros. Na sequncia, inserido o quarto elemento na lista
dos trs primeiros elementos e o processo continua at que toda a lista esteja
ordenada.
O algoritmo de insero funciona da mesma maneira com que muitas
pessoas ordenam cartas em um jogo de baralho como o pquer. Uma das ca-
ractersticas deste algoritmo o menor nmero de trocas e comparaes se a
lista estiver ordenada (parcialmente).
A gura 8.2 [11] demonstra o processo de ordenao utilizando como
exemplo 10 nmeros inteiros.
O nmero de comparaes n
2
no pior caso e no melhor caso 2(n - 1) com-
paraes [13], no caso mdio o nmero de comparaes n
4
2
.
108 Estrutura de Dados com Algoritmos e C
Figura 8.2: Exemplo de Ordenao por Insero com nmeros inteiros
Na tabela 8.4 visto como ocorre a ordenao por insero. Primeiro os
elementos 13 e 7 so ordenados (passo 1). Na sequncia, o elemento 5 colocado
no incio da lista (passo 2); depois o elemento 1 movido para o incio da lista
(passo 3) e, nalmente, o elemento 4 inserido entre os elementos 1 e 5. A gura
8.3 ilustra todo o processo.
A gura 8.4 (inspirada em [6]) e o algoritmo 8.3 demonstram os passos para
a ordenao por insero e uma implementao em C pode ser vista no progra-
ma 8.4.
Ordenao 109
Tabela 8.4: Insero - o que ocorre em cada passo
inicial 13 7 5 1 4
passo 1 7 13 5 1 4
passo 2 5 7 13 1 4
passo 3 1 5 7 13 4
passo 4 1 4 5 7 13
Figura 8.3: Seqncia de ordenao por insero
Programa 8.4: Funo Insert
/* programa_insercao_01.c */
#include <stdio.h>
4
9
void insert(int piItem[], int iQtdElementos)
{
register int i,j, iAux;
for( i=1; i<iQtdElementos; i++)
{
iAux = piItem[i];
110 Estrutura de Dados com Algoritmos e C
14
for( j=i-1; j>=0 && iAux < piItem[j]; j--)
{
piItem[j+1]=piItem[j];
}
piItem[j+1]=iAux;
}
return;
}
19
int main(void)
{
int iContador;
int aInsert[] = { 13,7,5,1,4 };
24
insert(aInsert, 5);
29
printf("Ordenado:");
for(iContador = 0; iContador < 5; iContador++)
{
printf(" %d ", aInsert[iContador] );
}
printf("\n");
34
return 0;
}
Figura 8.4: Algoritmo da ordenao por insero
Ordenao 111
Algoritmo 8.3: Ordenao por Insero
8.4 QuickSort
O algoritmo QuickSort do tipo diviso e conquista. Um algoritmo deste
tipo resolve vrios problemas quebrando um determinado problema em mais (e
menores) subproblemas [11].
O algoritmo, publicado pelo professor C.A.R. Hoare em 1962, baseia-se
na idia 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 diviso, o proble-
ma estar resolvido, pois aplicando recursivamente a mesma tcnica a cada um
dos subvetores, o vetor estar ordenado ao se obter um subvetor de apenas 1
elemento.
Os passos para ordenar uma sequncia S = {a
1
; a
2
; a
3
; ; an} dado por
[11]:
Seleciona um elemento do conjunto S. O elemento selecionado (p)
chamado de piv.
Retire p de S e particione os elementos restantes de S em 2 seqncias
distintas, L e G.
1.
2.
112 Estrutura de Dados com Algoritmos e C
A partio L dever ter os elementos menores ou iguais ao elemento
piv p, enquanto que a partio G conter os elementos maiores ou
iguais a p.
Aplique novamente o algoritmo nas parties L e G.
Para organizar os itens, tais que os menores quem na primeira partio e
os maiores na segunda partio, basta percorrer o vetor do incio para o m e do
m para o incio simultaneamente, trocando os elementos. Ao encontrar-se no
meio da lista, tem-se a certeza de que os menores esto na primeira partio e os
maiores na segunda partio.
A gura 8.5 ilustra o que se passa quando se faz a partio de um vetor com
a sequncia de elementos S = { 7, 1, 3, 9, 8, 4, 2, 7, 4, 2, 3, 5 }. Neste caso o piv
4, pois o valor do elemento que est na sexta posio (e 6 igual a

1 12
2
+
).
A escolha do elemento piv arbitrria, pegar o elemento mdio apenas uma
das possveis implementaes no algoritmo [15]. Outro mtodo para a escolha
do piv consiste em escolher trs (ou mais) elementos randomicamente da lista,
ordenar esta sublista e pegar o elemento mdio [13].
Figura 8.5: Ordenao QuickSort
3.
4.
Ordenao 113
O QuickSort pode ser implementando pelos algoritmos 8.4 e 8.5 [9]. O tem-
po de execuo do algoritmo depende do fato de o particionamento ser balan-
ceado ou no, e isso por sua vez depende de quais elementos so usados para
particionar. Se o valor do piv, para cada partio, for o maior valor, ento o
algoritmo se tornar numa ordenao lenta com um tempo de processamento n
2
.
A complexidade dada por n log
2
n no melhor caso e caso mdio. O pior caso
dado n
2
comparaes [15, 9].
Algoritmo 8.4: QuickSort
O programa 8.5 uma das vrias implementaes possveis do algoritmo
QuickSort. Esta verso uma adaptao em C [13] do programa apresentado em
[21]. Este programa poder ser adaptado para qualquer conjunto de dados ou
estruturas.
Programa 8.5: Ordenao QuickSort
#include <stdio.h>
void qs( char *item, int left, int right);
5
10
void qs( char *item, int left, int right)
{
int i,j;
char x,y;
i = left;
j = right;
x = item [ (left+right)/2 ]; /* elemento pivo */
15
/* partio das listas */
do
{
114 Estrutura de Dados com Algoritmos e C
20
/* procura elementos maiores que o piv na primeira parte*/
while(item[i]<x && i<right)
{
i++;
}
25
/* procura elementos menores que o piv na segunda parte */
while(x<item[j] && j>left)
{
j--;
}
30
if(i<=j)
{
/* processo de troca (ordenao) */
y = item[i];
35
item[i] = item[j];
item[j] = y;
i++;
j--;
}
} while(i<=j);
40
45
50
/* chamada recursiva */
if( left<j )
{
qs(item, left, j);
}
if( i<right )
{
qs(item, i, right);
}
return ;
}
55
int main(void)
{
char aVetor[]="3490bn09685lnv 3-49580bgojfog39458=9ugkj n098=526yh";
printf("\nAntes = [%s]", aVetor);
60
/* na primeira chamada, os parmetros iniciais so os extremos da matriz */
qs(aVetor,0,strlen(aVetor)-1);
printf("\nDepois = [%s]", aVetor);
return 0;
65
}
Ordenao 115
Algoritmo 8.5: Particiona - Diviso do vetor
8.5 MergeSort
Como o algoritmo QuickSort, o MergeSort outro exemplo de algoritmo do
tipo diviso e conquista, sendo um algoritmo de ordenao por intercalao ou
segmentao. A idia bsica a facilidade de criar uma seqncia ordenada a par-
tir de duas outras tambm ordenadas. Para isso, o algoritmo divide a seqncia
original em pares de dados, ordena-as; depois as agrupa em sequncias de quatro
elementos, e assim por diante, at ter toda a seqncia dividida em apenas duas
partes.
Ento, os passos para o algoritmo so [11]:
Dividir uma seqncia em duas novas seqncias.
Ordenar, recursivamente, cada uma das seqncias (dividindo nova-
mente, quando possvel).
Combinar (merge) as subseqncias para obter o resultado nal.
1.
2.
3.
116 Estrutura de Dados com Algoritmos e C
Nas guras 8.6 [11] e 8.7 [20] podem ser vistos exemplos de ordenao uti-
lizando os passos do algoritmo.
Figura 8.6: Ordenao MergeSort
Ordenao 117
A complexidade do algoritmo dada por n log
2
n em todos os casos. A des-
vantagem deste algoritmo precisar de uma lista (vetor) auxiliar para realizar a
ordenao, ocasionando em gasto extra de memria, j que a lista auxiliar deve
ter o mesmo tamanho da lista original.
Figura 8.7: Ordenao MergeSort
O algoritmo MergeSort (algoritmo 8.6), a exemplo do QuickSort, pode fazer
uso de um algoritmo auxiliar para realizar a intercalao dos itens (algoritmo
8.7). O programa 8.6 implementa estes algoritmos.
Programa 8.6: Ordenao MergeSort
/* programa_merge_01.c */
#include <stdio.h>
#include <stdlib.h>
4
void mergesort(int v[],int inicio,int m) ;
void intercala(int v[], int inicio, int meio, int m);
118 Estrutura de Dados com Algoritmos e C
9
14
19
void mergesort(int v[],int inicio,int m)
{
int meio;
if (inicio < m)
{
meio = (inicio+m)/2;
mergesort(v,inicio,meio);
mergesort(v,meio+1,m);
intercala(v, inicio, meio, m);
}
return ;
}
24
29
34
39
void intercala(int v[], int inicio, int meio, int m)
{
/* intercalao no vetor temporrio auxiliar */
int i,j,k, *auxiliar;
auxiliar = (int *) calloc(sizeof(int) , m-inicio+1);
i = inicio;
j = meio+1;
k = 0;
while( i<=meio && j<=m )
{
if( v[i] <= v[j] )
{
auxiliar[k] = v[i];
i++;
}
else
{
auxiliar[k] = v[j];
j++;
}
k++;
}
44
49
while( i <= meio )
{
auxiliar[k] = v[i];
i++;
k++;
}
54
while( j <= m )
{
auxiliar[k] = v[j];
j++;
k++;
Ordenao 119
}
59
/* copia vetor intercalado para o vetor original */
for( i = 0; i< (m - inicio)+1; i++)
{
v[inicio + i] = auxiliar[i];
}
64
free(auxiliar);
return;
}
69
int main(void)
{
int iContador;
int aMerge[] = { 9,1,11,13,17,19,21,1,3,89,24
74
mergesort(aMerge, 0, 10);
79
printf("Ordenado:");
for(iContador = 0; iContador < 11; iContador++)
{
printf(" %d ", aMerge[iContador] );
}
printf(\n);
84
return 0;
}
No algoritmo 8.8 vista uma verso mais simplicada (a intercalao
realizada junto com o processo de ordenao) do MergeSort, no programa 8.7
vista a implementao do algoritmo.
Programa 8.7: Ordenao MergeSort
/* programa_merge_02.c */
#include <stdio.h>
#include <stdlib.h>
5
void mergesort(int v[],int inicio,int m)
{
int i,j,k,meio,*auxiliar;
if(inicio == m)
{
120 Estrutura de Dados com Algoritmos e C
10
return;
}
15
/* ordenao recursiva das duas metades */
meio = (inicio+m)/2;
mergesort(v,inicio,meio);
mergesort(v,meio+1,m);
20
25
30
35
40
/* intercalao no vetor temporrio auxiliar */
i = inicio;
j = meio+1;
k = 0;
auxiliar = (int *) malloc(sizeof(int) * (m-inicio+1));
while(i<meio+1 || j<m+1)
{
if( i == meio+1) /* i passou do nal da primeira metade, pegar v[j] */
{
auxiliar[k] = v[j];
j++; k++;
}
else if ( j == m+1) /* j passou do nal da segunda metade, pegar v[i] */
{
auxiliar[k] = v[i];
i++; k++;
}
else if (v[i] < v[j]) /* v[i]<v[j], pegar v[i] */
{
auxiliar[k] = v[i];
i++; k++;
}
else /* v[j]<=v[i], pegar v[j] */
{
45
50
auxiliar[k] = v[j];
j++; k++;
}
}
/* copia vetor intercalado para o vetor original */
for( i=inicio; i<=m; i++)
{
v[i] = auxiliar[i-inicio];
}
free(auxiliar);
return ;
}
55
int main(void)
{
int iContador;
int aMerge[] = { 9,8,7,6,5,4,3,2,1 };
Ordenao 121
60
mergesort(aMerge, 0, 8);
65
printf("Ordenado:");
for(iContador = 0; iContador < 9; iContador++)
{
printf(" %d ", aMerge[iContador] );
}
printf("\n");
70
return 0;
}
Algoritmo 8.6: MergeSort
122 Estrutura de Dados com Algoritmos e C
Algoritmo 8.7: Intercala
Ordenao 123
Algoritmo 8.8: MergeSort
124 Estrutura de Dados com Algoritmos e C
8.6 Exerccios
BubbleSort - O programa 8.1 no reete exatamente o algoritmo 8.1.
Modique o programa para que o mesmo reita o algoritmo apresen-
tado.
BubbleSort - Utilizando o programa do exerccio anterior e tomando
como base o programa 8.1, implemente a ordenao bolha oscilante
[13] (tambm conhecido como ordenao bolha bidirecional).
QuickSort - Implemente em C os algoritmos QuickSort (8.4) e Particio-
na (8.5).
Fazer um programa que, utilizando ponteiros para um vetor de intei-
ros com 15 mil itens (gerados randomicamente), implemente todos os
algoritmos de ordenao vistos. O programa dever informar o tempo
necessrio para ordenar a lista nos dois sentidos (do maior para o me-
nor e vice-versa).
Considere que a ordenao para n nmeros leve t segundos. Calcule o
tempo de ordenao para 10, 100, 1.000, 10.000 e 100.000 elementos
para todos os algoritmos vistos.
1.
2.
3.
4.
5.
9. rvores Binrias
H trs maneiras de fazer as coisas: a
maneira errada, a maneira certa e uma
maneira melhor.
Annimo
Esquemas em rvores so utilizados para representar estruturas hierrqui-
cas (rvores genealgicas, campeonatos de futebol ou organizaes). Na cin-
cia da computao, as rvores podem ser utilizadas para representar decises,
denies formais de linguagem ou mesmo para representar hierarquia entre
elementos [1].
No contexto da programao e cincia da computao, uma estrutura de
dados que herda as caractersticas das topologias em rvore onde os dados esto
dispostos de forma hierrquica (um conjunto de dados hierarquicamente su-
bordinado a outro [16]).
9.1 Analogia entre rvores
Uma rvore composta por um elemento principal chamado raiz, que pos-
sui ligaes para outros elementos, que so denominados galhos ou lhos. Estes
galhos levam a outros elementos que tambm possuem outros galhos. O ele-
mento que no possui galhos conhecido como folha ou n terminal. Observe
a gura 9.1 [1].
126 Estrutura de Dados com Algoritmos e C
Figura 9.1: Analogia entre rvores
9.2 rvore binria
Uma rvore binria um conjunto nito de elementos que est vazio ou
particionado em trs subconjuntos [15]:
raiz da rvore - elemento inicial (nico);
subrvore da esquerda - se vista isoladamente compe uma outra
rvore;
subrvore da direita - se vista isoladamente compe uma outra r-
vore.
A rvore pode no ter nenhum elemento (rvore vazia). A denio recur-
siva e, devido a isso, muitas operaes sobre rvores binrias utilizam recurso.
As rvores onde cada n que no seja folha numa rvore binria tem sub-
rvores esquerda e direita no vazias so conhecidas como rvores estritamente
binrias. Uma rvore estritamente binria com n folhas tem 2n - 1 ns.
A gura 9.2 apresenta um mtodo convencional de representao de uma
rvore. Nesta rvore, o elemento A a raiz da rvore, a subrvore da esquerda
o elemento B e a da direita representada pelo elemento C. Um n sem lhos

rvores Binrias 127


chamado de folha. Sendo A a raiz de uma rvore binria e B sua subrvore,
dito que A pai de B e que B lho de A.
Figura 9.2: Representao de uma rvore
9.2.1 Relaes
Outras relaes (e conceitos) podem ser observados na gura 9.2:
B e C so lhos de A.
B e C so irmos.
D e E so irmos.
H e I so irmos.
T
A
a subrvore enraizada em A, portanto toda a rvore.
T
F
a subrvore enraizada em F, que contm os ns F, H e I.
Ns sem lhos so chamados de folhas, portanto os ns D, G, H e
I so folhas.
Grau de sada de um n
O nmero de lhos de um n chamado de grau de sada de um n. Por
exemplo, o n B tem grau de sada 2 e o n C grau 1.

128 Estrutura de Dados com Algoritmos e C


Caminho
Um caminho da rvore composto por uma seqncia de ns consecutivos
(n
1
,n
2
,n
3
,,n
k-1
,n
k
) tal que existe sempre a relao: n
j
pai de n
j+1
. Os k ns for-
mam um caminho de comprimento k - 1. O comprimento entre o n A e o n
H 3.
Nvel do n
O nvel de um n pode ser denido como o n raiz de nvel 0. Os outros
ns tm um nvel que uma unidade a mais do que o nvel do seu pai. Na rvore
da gura 9.2 tem-se:
Nvel 0: A
Nvel 1: B e C
Nvel 2: D, E e F
Nvel 3: G, H e I
Altura de um n
A altura de um n o comprimento do maior caminho do n at alguns de
seus descendentes. Descendentes do n so todos os ns que podem ser alcana-
dos caminhando-se para baixo a partir do n. A altura de cada uma das folhas 1.
Desta maneira a altura de A 4, a altura de C 3 enquanto que de E e F 2.
9.2.2 rvore Binria Completa
Na gura 9.3 pode ser vista uma rvore completa de nvel 3. Uma rvore
completa uma rvore estritamente binria na qual todas as folhas esto no mes-
mo nvel k. Sendo k a profundidade da rvore, o nmero total de ns 2
k+1
- 1 e
o nmero total de folhas 2
k
.
Embora uma rvore binria completa possua muitos ns (o mximo para
cada profundidade), a distncia da raiz a uma folha qualquer relativamente
pequena.
A rvore da gura 9.3 tem profundidade 3 com 15 ns (2
4
- 1) e 8 folhas
(2
3
).

rvores Binrias 129


Figura 9.3: rvore Binria completa de nvel 3
9.3 rvores de Busca Binria
Uma rvore de busca binria (Binary search tree) uma rvore binria onde
a informao que o n esquerdo possui menor ou igual informao da chave.
De forma anloga, a informao que o n direito possui maior ou igual in-
formao da chave. O objetivo de organizar dados em rvores de busca binria
facilitar a tarefa de procura de um determinado valor. A partir da raiz e de
posse da informao a ser encontrada, possvel saber qual o caminho (galho) a
ser percorrido at encontrar o n desejado. Para tanto, basta vericar se o valor
procurado maior, menor ou igual ao n que se est posicionando.
Deve-se observar que no existe uma nica forma de organizar um conjunto
de informaes em uma rvore de busca binria, anal, dependendo da escolha
do n raiz, obtm-se rvores diferentes. Na gura 9.4 os valores ({ 2, 3, 5, 5, 7, 8})
so organizados em rvores de busca de duas maneiras diferentes.
Figura 9.4: rvore de busca binria - duas organizaes diferentes
130 Estrutura de Dados com Algoritmos e C
As duas rvores contm exatamente os mesmos valores, porm possuem es-
truturas diferentes. Enquanto a rvore A est enraizada em um dos ns de valor
5, a rvore B est enraizada no n de valor 2. Supondo que se est buscando o
valor 8 nas rvores, as comparaes seriam como se segue na tabela 9.1.
Tabela 9.1: Comparaes para busca de um elemento
rvore A rvore B
8 > 5 8 > 2
8 > 7 8 > 3
Encontrado! 8 > 7
Encontrado!
Na rvore A, so realizadas menos comparaes em relao utilizada na
rvore B. O melhor caso ir ocorrer quando a rvore estiver cheia, neste caso a
procura de um item ter tempo proporcional a log n, enquanto o pior caso ocor-
rer quando todos os ns da rvores apontarem somente para um dos lados, caso
em que o tempo de processamento da procura ser proporcional a n.
9.4 Operaes em rvores Binrias
9.4.1 Insero
A insero comea com uma busca procurando pelo valor na rvore. Se o
elemento no existir na vore, alcanada a folha, e ento inserido o valor nesta
posio. Ou seja, examinada a raiz e introduzido um novo n na subrvore da
esquerda, se o valor novo menor do que a raiz, ou na subrvore da direita, se o
valor novo for maior do que a raiz.
Os algoritmos 9.1 (verso iterativa) e 9.2 (verso recursiva) demonstram o
processo de incluso de um elemento na rvore.
rvores Binrias 131
Algoritmo 9.1: Inserir elemento na rvore - iterativo
Algoritmo 9.2: Inserir elemento na rvore - recursivo
132 Estrutura de Dados com Algoritmos e C
Os algoritmos deixam claro o processo de insero, o novo valor primeiro
comparado com o valor da raiz. Se seu valor for menor que a raiz, comparado
ento com o valor do lho da esquerda da raiz. Se seu valor for maior, ento
compara-se com o lho da direita da raiz. Este processo continua at que se
chegue a um n folha, e ento adiciona-se o lho direita ou esquerda, depen-
dendo de seu valor ser maior ou menor que o valor da folha.
9.4.2 Pesquisa
Para a busca em uma rvore binria por um valor especco deve-se exa-
minar a raiz. Se o valor for igual raiz, o valor existe na rvore. Se o valor for
menor do que a raiz, ento deve-se buscar na subrvore da esquerda, e assim
recursivamente em todos os ns da subrvore.
Similarmente, se o valor for maior que a raiz, ento deve-se buscar na sub-
rvore da direita. At alcanar o n-folha da rvore, encontrando-se ou no o
valor requerido.
Esta operao efetua log n operaes no caso mdio e n no pior caso quan-
do a rvore est desequilibrada; neste caso, a rvore considerada uma rvore
degenerada.
Os algoritmos 9.3 (verso iterativa) e 9.4 (verso recursiva) demonstram o
processo de pesquisa de um elemento na rvore.
Algoritmo 9.3: Pesquisar elemento na rvore - iterativo
rvores Binrias 133
Algoritmo 9.4: Pesquisar elemento na rvore - recursivo
9.4.3 Excluso
O processo de excluso de um n mais complexo que as operaes ante-
riores. Para excluir um n de uma rvore binria, deve-se considerar trs casos
distintos para realizar a excluso [9].
Excluso na folha
A excluso de um n que se encontra no m da rvore, isto , que seja uma fo-
lha, o caso mais simples de excluso. Basta remover o n da rvore (gura 9.5).
Figura 9.5: Excluso de folha
Excluso de n com um lho
Caso o n que ser excludo tenha um nico lho, o pai do n (av do lho)
herda o lho. Isto , o lho assume a posio do pai na rvore (gura 9.6).
134 Estrutura de Dados com Algoritmos e C
Figura 9.6: Excluso de um n com um lho
Excluso de n com dois lhos
Se o n a ser excludo tiver dois lhos, o processo de excluso poder operar
de duas maneiras diferentes:
Substituir o valor do n a ser retirado pelo valor sucessor (o n mais
esquerda da subrvore direita).
Substituir o valor do n a ser retirado pelo valor antecessor (o n
mais direita da subrvore esquerda).
Realizada a escolha, remove-se o n sucessor (ou antecessor). A gura 9.7
exemplica a operao. O n com valor 50 ser excludo e possui como sucessor
o valor 55 e como antecessor imediato do 55 o n com valor 53. Desta forma, o
lho (53) do n com valor 55 ser promovido no lugar do n a ser excludo (50),
o n 55 continuar em sua posio e o lho do n 53 (no caso o n com valor
54) ser passado para o n de valor 55. Estas operaes podem ser vistas nos
algoritmos 9.5 e 9.6.
Figura 9.7: Excluso de um n com dois lhos

rvores Binrias 135


Algoritmo 9.5: Excluso na rvore
Algoritmo 9.6: Sucessor
136 Estrutura de Dados com Algoritmos e C
9.4.4 Maior elemento
O maior elemento da rvore, n com o maior valor, ser encontrado sempre
na folha mais direita da rvore [9]. Para encontrar o maior valor, basta procurar
a partir da raiz sempre na subrvore da direita (algorimo 9.7).
Algoritmo 9.7: Maior elemento da rvore
9.4.5 Menor elemento
O menor elemento da rvore, n com o menor valor, ser encontrado
sempre na folha mais esquerda da rvore [9]. Para encontrar o menor valor,
basta procurar a partir da raiz sempre na subrvore da esquerda (algorimo
9.8).
9.4.6 Percorrendo uma rvore
Uma operao comum percorrer uma rvore binria, o que consiste
em visitar todos os ns desta rvore segundo algum critrio. Esse percurso,
tambm chamado de travessia da rvore, pode ser feito de trs formas [15,
16]:
pr-ordem ou profundidade - os lhos de um n so processados
aps o n.
ps-ordem - os lhos so processados antes do n.
em-ordem ou simtrica - em que se processa o lho esquerda, o n,
e nalmente o lho direita.

rvores Binrias 137


Algoritmo 9.8: Menor elemento da rvore
A operao percorrer pode ser descrita nos algoritmos 9.9, 9.10 e 9.11.
Algoritmo 9.9: Operao Percorre - Pr-ordem
Algoritmo 9.10: Operao Percorre - Ps-ordem
Algoritmo 9.11: Operao Percorre - Em-ordem
138 Estrutura de Dados com Algoritmos e C
9.5 Representaes de rvores em C
rvores binrias podem ser representadas como um vetor de lhos (progra-
ma 9.1 ou de forma dinmica (programa 9.2).
Programa 9.1: Representao com vetor de lhos
#dene FILHOS 4
4
typedef struct NO {
int info;
struct NO *pai;
struct NO *lhos[FILHOS];
} NO;
Programa 9.2: Representao dinmica
3
typedef struct NO {
int info;
struct NO *pai;
struct NO *lho; /* 1
0
lho */
struct NO *irmao; /* prximo irmo*/
} NO;
A representao no formato de vetores da gura 9.2 pode ser vericada na
gura 9.8. Na gura ca clara a representao de rvores, com vetores, ter limi-
taes para a quantidade de lhos.
Figura 9.8: Representao com vetores
rvores Binrias 139
Se no programa 9.2 o campo lho for um ponteiro para subrvore esquerda
e o campo irmao como o ponteiro para a raiz da subrvore direita, tem-se uma
rvore binria como a representada na gura 9.9.
Figura 9.9: Representao dinmica
Com o programa 9.3 tem-se a representao de uma rvore binria confor-
me a gura 9.3 vista anteriormente.
Programa 9.3: Representao dinmica de uma rvore binria
typedef struct NO {
int info;
struct NO *esquerda;
4
struct NO *direita;
struct NO *pai;
} NO;
9.6 Implementao em C
No programa 9.4 podem ser vistas todas as operaes e algoritmos concei-
tuados anteriormente.
Programa 9.4: Implementao das operaes
/* programa_arvore_01.c */
4
#include <stdio.h>
#include <stdlib.h>
140 Estrutura de Dados com Algoritmos e C
9
typedef struct NO
{
int info;
struct NO *esquerda, *direita;
} node, *arvore;
arvore root = NULL;
14
19
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 *);
24
void percorre_preordem(node *);
void percorre_posordem(node *);
void percorre_emordem(node *);
int maior(node * );
int menor(node * );
29
int main(void)
{
int x, y, opcao;
34
39
44
49
do
{
printf("\nEntre com a opcao");
printf("\n ---1:inserir");
printf("\n ---2:pesquisar");
printf("\n ---3:excluir o menor");
printf("\n ---4:excluir");
printf("\n ---5:procurar o maior que");
printf("\n ---6:imprimir a arvore");
printf("\n ---7:mostrar nos do intervalo");
printf("\n ---8:percorrer");
printf("\n ---9:maior e menor");
printf("\n ---10:sair do programa\n");
printf("\n->");
fush(stdin);
scanf("%d", &opcao);
switch(opcao)
{
case 1:
{
rvores Binrias 141
54
59
64
69
74
79
84
89
94
99
printf("\n Informe o valor ->");
scanf("%d", &x);
inserir(&root, x);
imprimir(root, 0);
break;
}
case 2:
{
printf("\n Informe o valor ->");
scanf("%d", &x);
if(pesquisar(root, x) != NULL)
{
printf(" Encontrado\n");
}
else
{
printf(" Nao encontrado!\n");
}
break;
}
case 3:
{
printf(" Excluido o menor = %d\n", excluir_menor());
imprimir(root, 0);
break;
}
case 4:
{
printf("\n Informe o valor ->");
scanf("%d", &x);
excluir(&root, x);
imprimir(root, 0);
break;
}
case 5:
{
printf("\n Informe o valor ->");
scanf("%d", &x);
imprimir(root, 0);
printf("\n --- proximo maior que = %d", proxmaior(x));
break;
}
case 6:
{
imprimir(root, 0);
break;
}
case 7:
{
142 Estrutura de Dados com Algoritmos e C
104
109
114
119
124
printf("\n Informe [min, max]");
scanf("%d %d",&x, &y);
range(root, x, y);
break;
}
case 8:
{
printf("\nPercorrendo em ordem ->");
percorre_emordem(root);
printf("\nPercorrendo em pre ordem ->");
percorre_preordem(root);
printf("\nPercorrendo em pos ordem ->");
percorre_posordem(root);
break;
}
case 9:
{
printf("\nMaior = %d", maior(root));
printf("\nMenor = %d", menor(root));
break;
}
}
} while(opcao!=10);
}
129
134
139
144
arvore pesquisar(arvore v, int chave)
{
if( v == NULL)
{
return NULL;
}
if(v->info == chave)
{
return v;
}
else if(v->info < chave)
{
return pesquisar(v->direita, chave);
}
else
{
return pesquisar(v->esquerda, chave);
}
}
/* maior no prximo elemento informado */
int proxmaior(int chave)
149
{
arvore p=NULL, v;
rvores Binrias 143
154
159
164
169
174
179
184
v = root;
while( v != NULL && v->info != chave)
{
if(v->info < chave)
{
v = v->direita;
}
else
{
p = v;
v = v->esquerda;
}
}
if(v == NULL)
{
printf("\n Elemento nao encontrado");
return -1;
}
if( v->direita != NULL )
{
v = v->direita;
while(v->esquerda != NULL)
{
v = v->esquerda;
}
return v->info;
}
if(p != NULL)
{
return p->info;
}
else
{
return -1;
}
}
189
194
void inserir(arvore *p, int chave)
{
if( *p == NULL )
{
*p = (arvore) malloc(sizeof(node));
(*p)->info = chave;
(*p)->esquerda = NULL;
(*p)->direita = NULL;
}
199
else if((*p)->info < chave)
{
144 Estrutura de Dados com Algoritmos e C
204
inserir(&((*p)->direita), chave);
}
else
{
inserir(&((*p)->esquerda), chave);
}
return;
}
209
void imprimir(arvore v, int nivel)
{
int i;
214
219
224
if( v != NULL )
{
imprimir(v->esquerda, nivel+1);
for(i=0; i<nivel; i++)
{
printf(" ");
}
printf("%d\n", v->info);
imprimir(v->direita, nivel+1);
}
return;
}
229
234
239
244
/* mostra os ns de um intervalo informado */
void range(arvore v, int x, int y)
{
if( v == NULL )
{
return;
}
if(v->info >= x)
{
range(v->esquerda, x,y);
}
if(x <= v->info && v->info <= y)
{
printf(" %d", v->info);
}
if(v->info <= y)
{
range(v->direita, x,y);
}
return;
}
rvores Binrias 145
249
254
259
264
269
/* excluir o menor */
int excluir_menor(void)
{
int menor;
arvore p, v;
if(root->esquerda == NULL)
{
menor = root->info;
root = root->direita;
}
else
{
v = root;
do
{
p = v;
v = v->esquerda;
} while(v->esquerda != NULL);
menor = v->info;
p->esquerda = v->direita;
}
return menor;
}
274
/* excluso de elemento da rvore */
void excluir(arvore *p, int chave)
{
arvore q;
279
284
289
if(*p == NULL)
{
printf("\n Elemento nao existe!");
}
else if( chave < (*p)->info )
{
excluir(&((*p)->esquerda), chave);
}
else if( chave > (*p)->info )
{
excluir(&((*p)->direita), chave);
}
else
{
q = *p;
if(q->direita == NULL)
{
294
*p = q->esquerda;
}
146 Estrutura de Dados com Algoritmos e C
299
304
else if( q->esquerda == NULL)
{
*p = q->direita;
}
else
{
del(&q, &(q->esquerda));
}
free(q);
}
return;
}
309
314
319
/* procura sucessor para depois excluir */
void del(arvore *q, arvore *r)
{
if((*r)->direita != NULL)
{
del(q, &((*r)->direita));
}
else
{
(*q)->info = (*r)->info;
(*q) = *r;
*r = (*r)->esquerda;
}
return;
}
324
329
/* percorrer uma rvore utilizando o algoritmo de pr-ordem */
void percorre_preordem(node * arvore)
{
if( arvore == NULL )
{
return;
}
334
printf(" %d", arvore->info);
percorre_preordem(arvore->esquerda);
percorre_preordem(arvore->direita);
return;
}
339
/* percorrer uma rvore utilizando o algoritmo de ps-ordem */
void percorre_posordem(node * arvore)
{
rvores Binrias 147
344
if( arvore == NULL )
{
return;
}
349
percorre_posordem(arvore->esquerda);
percorre_posordem(arvore->direita);
printf(" %d", arvore->info);
return;
}
354
359
/* percorrer uma rvore utilizando no modo em-ordem */
void percorre_emordem(node * arvore)
{
if( arvore == NULL )
{
return;
}
364
percorre_emordem(arvore->esquerda);
printf(" %d", arvore->info);
percorre_emordem(arvore->direita);
return;
}
369
/* pesquisa do maior elemento na rvore */
int maior(node * arvore )
{
int maior;
maior = arvore->info;
374
379
while( arvore != NULL )
{
maior = arvore->info;
arvore = arvore->direita;
}
return maior;
}
384
/* pesquisa do menor elemento na rvore */
int menor(node * arvore)
{
int menor;
menor = arvore->info;
389 while( arvore != NULL )
148 Estrutura de Dados com Algoritmos e C
394
{
menor = arvore->info;
arvore = arvore->esquerda;
}
return menor;
}
9.7 Exerccio
Implemente os algoritmos iterativos, para manipulao de rvores,
em C.
1.



Referncias Bibliogrficas
[1] D. Baldwin and G. W. Scragg. Algorithms and Data Structures: The Science of
Computing. Charles River Media, rst edition, 2004.
[2] P. E. Black. Dictionary of Algorithms and Data Structures. U.S. National Insti-
tute of Standards and Technology, 2006. Ackermanns function in www.nist.
gov/dads/HTML/ackermann.html.
[3] P. Deshpande and O. Kakde. C and Data Structures. Charles River Media,
rst edition, 2004.
[4] J. Keogh and K. Davidson. Data Structures Demystied. McGraw-Hill/Os-
borne, rst edition, 2004.
[5] B. W. Kernighan and D. Ritchie. The C Programming Language. Prentice
Hall, second edition, 1988.
[6] D. E. Knuth. The Art of Computer Programming: Fundamental Algorithms,
volume 3. Addison-Wesley, third edition, 1997.
[7] A. Koening. C Traps and Pitfalls. Addison-Wesley, rst edition, 1989.
[8] R. Lafore. Teach Yourself Data Structures and Algorithms in 24 hours. Samns
Publishing, rst edition, 1999.
[9] C. E. Leiserson, C. Stein, R. L. Rivest, and T. H. Cormen. Introduction to
Algorithms. MIT Press and McGraw-Hill, second edition, 2001.
[10] F. Lorenzi, P. N. de Mattos, and T. P. de Carvalho. Estrutura de dados.
Thomson, rst 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, rst edition, 1998.
[12] D. D. Salvetti and L. M. Barbosa. Algoritmos. Pearson Makron Books, rst
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 e tcnicas de programao.
LTC Editora, third edition, 1988.
[15] A. M. Tenenbaum, Y. Langsam, and M. J. Augenstein. Estruturas de Dados
Usando C. Pearson, rst edition, 1995.
[16] P. Veloso, C. dos Santos, P. Azeredo, and A. Furtado. Estrutura de Dados.
Editora Campus, rst 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, rst edition,
1985.
A
algoritmo de Euclides 68
alocao dinmica 19
alocao esttica 19
alocao esttica de memria 14, 22
arquivo 94
rvore binria 126
rvore de busca binria 129
rvore estritamente binria 128
rvores 125
B
Bancos de dados 94
bidimensional ou multidimensional 5
BubbleSort 100
C
calloc 21, 25
conjunto ordenado de itens 40
D
dados 2
dados homogneos 2
dividir e conquistar 97
diviso e conquista 111, 115
E
empty 41, 48
estrutura de dados 125
estruturas de dados 1
estruturas estticas 19
estruturas hierrquicas 125
F
Fibonacci 66
FIFO 48
la 48, 51, 79
free 22
front 48
funo fatorial 61
funes recursivas 76, 77
H
heterognea 16
I
informaes 1
insert ou enqueue 48
L
LIFO - last in rst out 40



ndice Remissivo
152 Estrutura de Dados com Algoritmos e C
lista 79
lista duplamente encadeada 79
lista no ordenada 79
lista encadeada 86
listas 82
Listas encadeadas 79, 86
lista simplesmente encadeada 79
log n 132
M
malloc 21, 25
matriz 5, 94
matrizes n-dimensionais 8
mximo divisor comum (MDC) 68
MergeSort 115
N
nmero triangular 63
O
operador 11
operador de ponteiro 11
Ordenao 100
ordenao por insero 107, 108
ordenao por seleo 104, 105
P
passar variveis por referncia 12
pesquisa binria 94, 97
pesquisa seqencial 94
pilha 40, 79
ponteiro 11, 14
ponteiros para ponteiros 27
pop 41
push 41
Q
QuickSort 111
R
realloc 21
Recurso 60
recursividade 60
registro 16, 94
remove ou dequeue 48
S
seqncia de Fibonacci 66
size 41, 48
stackpop 41
struct 25
T
tabela 94
tipo de dados 2
Torre de Hanoi 71
travessia da rvore 136
V
vetor 2, 51, 82, 94
vetor de ponteiros 20
Vetores 82