Escolar Documentos
Profissional Documentos
Cultura Documentos
www.esab.edu.br
Estrutura de Dados
Diretor Geral
Nildo Ferreira
Diretora Acadêmica
Beatriz Christo Gobbi
Coordenadora do Núcleo de Educação a Distância
Beatriz Christo Gobbi
Coordenadora do Curso de Administração EAD
Rosemary Riguetti
Coordenador do Curso de Pedagogia EAD
Claudio David Cari
Coordenador do Curso de Sistemas de Informação EAD
David Gomes Barboza
Copyright © Todos os direitos desta obra são da Escola Superior Aberta do Brasil.
www.esab.edu.br
Av. Santa Leopoldina, nº 840
Coqueiral de Itaparica - Vila Velha, ES
CEP 29102-040
Apresentação
Caro estudante,
E você, como nosso aluno, deve estar interessado nessa área, em saber como planejar
e desenvolver rotinas computacionais que aperfeiçoem os resultados dos algoritmos,
evitando o desperdício de memória principal criando rotinas de alocação, pesquisa e
classificação dos dados.
Nesta disciplina, tendo como base os autores Forbellone et al.(2005), Cerqueira, Celes
e Rangel Netto (2004), Ascencio e Araújo (2010) e Guimarães (1994), você conhecerá
os conceitos e as técnicas de como implementar os tipos de alocação de memória, os
algoritmos de classificação e pesquisa de dados, as estruturas de dados avançadas,
como: Filas, Pilhas, Listas e Árvores.
Assim, esta disciplina possibilitará que você entenda, projete, desenvolva e valide
o uso de estruturas de dados mais eficientes e que possibilitem o aproveitamento
máximo dos recursos do computador.
Bom estudo!
Objetivo
Analisar, avaliar e implementar as principais técnicas de representação e
manipulação de dados, possibilitando aos alunos a capacidade de escolher as
estruturas de dados mais adequadas aos problemas que envolvam a ordenação e
recuperação de informações por meio de algoritmos de organização e busca.
Habilidades e competências
• Analisar e implementar estruturas de dados para solução de problemas
computacionais.
• Projetar estruturas de dados para resolução de problemas complexos.
• Avaliar as melhores estruturas de dados para cada tipo de problema
computacional.
• Codificar estruturas de dados.
• Analisar as diferentes estruturas de dados.
• Aplicar adequadamente estruturas de dados.
Ementa
Conceitos básicos e apontadores. Representação de estruturas simples e
encadeadas (listas lineares, pilhas, filas, árvores). Procedimentos e funções de
acesso, remoção e inserção de nós nas estruturas. Métodos de classificação e
ordenação de dados. Pesquisa de dados.
Sumário
1. Introduzir o aluno aos conceitos de Estruturas de Dados..................................................7
2. Funções – parte I...........................................................................................................15
3. Funções – parte II..........................................................................................................22
4. Variáveis compostas homogêneas: vetores....................................................................33
5. Alocação dinâmica de memória.....................................................................................40
6. Alocação dinâmica em C................................................................................................49
7. Tipos de passagens de parâmetros.................................................................................58
8. Tipos estruturados – parte I...........................................................................................68
9. Tipos estruturados – parte II..........................................................................................80
10. Matrizes – conceituação................................................................................................92
11. Matrizes – codificação.................................................................................................101
12. Matrizes – exercícios....................................................................................................109
13. Tipos abstratos de dados – parte I...............................................................................120
14. Tipos abstratos de dados – parte II..............................................................................126
15. Listas encadeadas........................................................................................................133
16. Listas encadeadas simples – conceituação..................................................................141
17. Listas encadeadas simples – codificação.....................................................................154
18. Lista encadeada simples – exercícios...........................................................................166
19. Listas circulares – conceituação...................................................................................175
20. Listas circulares – codificação......................................................................................182
21. Listas circulares – exercícios.........................................................................................190
22. Listas duplamente encadeadas – conceituação...........................................................197
23. Listas duplamente encadeadas – codificação..............................................................206
24. Listas circulares duplamente encadeadas – conceituação............................................214
25. Listas circulares duplamente encadeadas − codificação..............................................226
26. Pilhas – conceituação..................................................................................................237
27. Pilhas – codificação – parte I.......................................................................................244
28. Pilhas – codificação – parte II......................................................................................253
29. Filas – conceituação.....................................................................................................258
30. Filas – codificação........................................................................................................264
31. Filas – exercícios..........................................................................................................271
32. Fila dupla.....................................................................................................................277
33. Árvores – parte I..........................................................................................................284
34. Árvores – parte II.........................................................................................................292
35. Árvores – parte III........................................................................................................300
36. Árvores – parte IV........................................................................................................308
37. Árvores – parte V.........................................................................................................316
38. Métodos de pesquisa e ordenação: conceituação.........................................................324
39. Métodos de ordenação: bubble sort – parte 1.............................................................331
40. Métodos de ordenação: bubble sort – parte 2.............................................................338
41. Métodos de ordenação: quick sort – parte 1................................................................345
42. Métodos de ordenação: quick sort – parte 2................................................................352
43. Métodos de ordenação: merge sort..............................................................................361
44. Métodos de ordenação: merge sort..............................................................................368
45. Métodos de pesquisa: busca em vetor..........................................................................375
46. Métodos de pesquisa: árvore binária de pesquisa........................................................382
47. Métodos de pesquisa: árvore balanceada.....................................................................388
48. Exercícios de fixação.....................................................................................................395
Glossário.............................................................................................................................405
Referências.........................................................................................................................414
Introduzir o aluno aos conceitos de
1 Estruturas de Dados
Objetivo
Introduzir o aluno aos conceitos de estruturas de dados.
www.esab.edu.br 7
será identificado pela matrícula, nome, idade, sexo, data de nascimento,
nome do pai e da mãe. Nos dois casos os requisitos avaliados foram os
dados cadastrais de um aluno, porém no processo de abstração do que é
um “aluno”, os dois sistemas a serem desenvolvidos possuem definições
diferentes, mesmo se tratando de um aluno, e cabe ao analista fazer o
levantamento de acordo com o cenário que lhe é apresentado. Assim
como os dados cadastrais, as funcionalidades de cada sistema, as regras de
negócio e uso do sistema também dependerão da abstração de cada um
dos contextos apresentados.
A entrada refere-se aos dados que são colhidos no mundo real, ou seja,
externo ao computador, e a saída à sua transformação na informação
desejada. O processamento representa uma sequência finita de operações
a serem realizadas a partir dos dados de entrada, a fim de transformá-
los em informações. A abstração de dados se constitui pela análise dos
valores manipulados, os métodos e os procedimentos que devem ser
aplicados aos dados de entrada com a finalidade de resolver determinados
problemas, representados pela saída desejada.
Por exemplo, um sistema que deve converter uma medida em metros para
centímetros requer uma sequência ordenada de procedimentos e operações
que possibilitam ao computador realizar essa tarefa. Para tanto, é necessário
avaliar o problema e abstrair as regras importantes a serem transferidas
para o algoritmo, como a definição dos valores de entrada e saída como
números reais, já que as medidas manipuladas pelo sistema (metros e
centímetros) são números que podem conter casas decimais. Para conversão
da medida, será necessária a operação de divisão da medida em metros por
100, já que cada um metro corresponde a 100 centímetros. Por fim, será
necessária a utilização de procedimentos para interagir com o usuário para
que ele informe os dados que serão processados (entrada de dados) e para a
apresentação do resultado final da operação de conversão (saída de dados).
www.esab.edu.br 8
Para sua reflexão
A definição de quais métodos e procedimentos
serão utilizados para resolução de um problema
na forma de um sistema computacional não é
imediata, é construída a partir de testes e validação
dos resultados, por meio de uma técnica de
análise e refinamento dos processos. Requerem
também técnicas de representação do problema,
como fluxogramas, diagramas, algoritmos, todos
como uma documentação formal e roteiros de
validação da solução, aliados à expertise de cada
executor. Por isso, deve-se ter em mente que há
diversas estratégias para se chegar à solução de um
determinado problema, apresentando vantagens
e desvantagens em relação a cada uma delas, mas
todas devem garantir a resolução do problema.
www.esab.edu.br 9
finita de processamentos. Estes, por sua vez, são fundamentados nas
definições de como o sistema deverá funcionar, requisitos, testes e
validações especificadas pelos especialistas em tecnologia da informação.
www.esab.edu.br 10
acesso e processamento dos dados devem ocorrer de forma a atender às
necessidades da solução em tempo hábil e com garantia que os resultados
processados estão corretos.
Para Guimarães (1994, p. 2), as estruturas de dados “[...] são usadas nos
algoritmos para representar as informações do problema a ser resolvido”.
www.esab.edu.br 11
Observe que a definição de estrutura de dados está associada
ao armazenamento dos dados, mas não apenas isso. A forma de
armazenamento dos dados, como serão manipulados e sua organização
estão relacionados com as atividades e problemas do mundo real.
Portanto, levá-la com suas regras de negócios e formas de manipulação
para o computador requer do especialista a habilidade de avaliar,
compreender e modelar todo esse universo por meio da estrutura de
dados mais adequada.
www.esab.edu.br 12
1.3 Utilização e benefícios
Como visto nas seções anteriores, a conceituação de estrutura de dados
está diretamente relacionada ao desenvolvimento de algoritmos e sua
posterior implementação. Para criar e utilizar um algoritmo, é necessário
avaliar seu desempenho e a utilização dos recursos disponibilizados,
como alocação de espaços na memória principal ou secundária, e tempo
gasto para execução de determinada rotina.
Vamos refletir sobre, por exemplo, um sistema que precisa gerar um relatório
de produtos comercializados num determinado período. Os dados da
pesquisa precisam ser armazenados em uma estrutura de dados para depois
serem apresentados ao usuário. Porém, não se sabe previamente a quantidade
de elementos que serão armazenados, o tamanho da estrutura para esse
armazenamento, e que se pode variar de período para período. Uma solução
www.esab.edu.br 13
seria criar uma estrutura de dados com um tamanho absurdamente grande
que garantiria o armazenamento dos dados, mas haveria um desperdício de
memória do computador caso a quantidade de elementos do relatório fosse
muito baixa. Dessa forma, a melhor solução é a criação de uma tabela com a
quantidade exata de elementos que a pesquisa retornou, mas isto só poderá
ocorrer em tempo de execução do programa, pois somente no momento em
que a rotina que realiza a pesquisa dos produtos é executada é que se terá a
quantidade de elementos da tabela.
www.esab.edu.br 15
[...] a decomposição de um problema é fator determinante para a redução da
complexidade. [...] quando decompomos um problema em subproblemas,
estamos invariavelmente dividindo também a complexidade e, por consequência,
simplificando a resolução.
2.1 Definição
As funções são subprogramas que decompõem as grandes tarefas em
subtarefas. Um programa de computador geralmente consiste de várias
funções. A criação de funções visa à redução do código e à consequente
simplificação dos processos de validação e testes, uma vez que rotinas que
se repetem no código devem ser transformadas em funções que, então,
serão chamadas diversas vezes.
www.esab.edu.br 16
2.2 Pilha de execução
Como estudado na seção anterior, a modularização transforma um problema
em um conjunto de soluções, então, o próximo passo é compreender como
esses módulos são interligados, manipulados e executados.
www.esab.edu.br 17
“Função B”, novamente haverá um desvio, executando todas as instruções
da linha 1 a 5 da função B, e retornando para o algoritmo na linha 7,
dando sequência na execução até o seu encerramento.
www.esab.edu.br 18
Observe que no passo 1 a pilha estava vazia, não havia função em
execução. No passo 2 o algoritmo entrou em execução, sendo inserido na
pilha de execução. Assim que a “função A” foi chamada no algoritmo, ela
foi inserida no topo da pilha (passo 3) de execuções, mantendo-se ali até
que a função se encerre. No passo 4 a “Função A” foi encerrada, sendo
retirada da pilha e devolvendo a execução para o algoritmo (passo 5).
No momento em que a “Função B” é chamada e entra em execução, esta
é inserida no topo da pilha (passo 6), mantendo-se até que sua execução
se encerre. No encerramento da “Função B”, ela é retirada da pilha
(passo 7), devolvendo a execução para o algoritmo (passo 8) até que ele
termine sua execução e seja retirado da pilha (passo 9).
Nesse contexto, quando são declaradas variáveis dentro dessas funções, chamadas de
variáveis locais (FORBELLONE et al., 2005), elas passam a existir somente no momento
em que a função é executada.
www.esab.edu.br 19
Por isso essas variáveis não podem ser acessadas por outras funções, uma
vez que a execução das outras funções só terá sequência quando a função
no topo da pilha se encerrar. No momento em que isso ocorre, as variáveis
locais também saem da pilha, assim como a função, e deixam de existir.
www.esab.edu.br 20
Figura 4 – Declaração de variáveis em C.
Fonte: Elaborada pelo autor (2013).
A variável sexo foi declarada fora das funções teste() e main(). Sendo assim,
se trata de uma variável global, acessível às duas funções. Já a variável idade
foi declarada dentro da função main(), dessa forma sendo visível apenas a
essa função. Na função teste(), na linha 9, foi indicado um erro (quadrado
em vermelho), indicando que a variável idade não existe. Todas as
instruções, da linha 1 a linha 18 correspondem ao algoritmo em C, assim
temos nesse algoritmo as bibliotecas stdio e stdlib, a variável global sexo, as
funções teste() e main(), que possui a variável local idade.
[...] em alguns casos, uma determinada variável é utilizada apenas por um módulo
específico, o que não justifica uma definição global, pois somente se fazem necessários o
conhecimento e a utilização dessa variável dentro dos limites deste bloco lógico.
www.esab.edu.br 21
memória principal para criação e armazenamento de variáveis. Por outro
lado, caso os dados devam ser acessados pelas funções desenvolvidas
pelo programador e pelo método main(), que é o método principal e
obrigatório em todo programa desenvolvido em C, as variáveis devem ser
declaradas fora das funções, tornando-se assim globais e visíveis a todas as
estruturas de dados do algoritmo. Porém, mesmo que não sejam utilizadas,
ocupam espaço de memória enquanto o programa estiver em execução.
Estudo complementar
Para saber mais sobre os conceitos e exemplos de
funções em C, acesse aqui.
www.esab.edu.br 22
3 Funções – parte II
Objetivo
Apresentar a definição de ponteiros e conhecer a técnica de
recursividade e como utilizar variáveis estáticas dentro das funções.
www.esab.edu.br 23
dados que podem ser: números (inteiros e reais), caracteres (símbolo ou
conjunto de símbolos) e lógicos (verdadeiro ou falso). A criação de uma
variável se dá pela instrução de declaração da variável, que na linguagem
de programa C/C++ possui a semântica apresentada na Figura 5, observe:
• int idade;
• float peso, altura, media;
• char nome[30], cidade[30];
A declaração tem como finalidade criar as variáveis que serão utilizadas
no programa de computador, mas na prática o resultado é a alocação de
espaço na memória principal, na área chamada de Heap. Cada variável
declarada será alocada em um bloco de memória com endereço único.
Mesmo que a variável não seja utilizada, esse espaço não será liberado até
que o programa se encerre. O código a seguir apresenta a declaração de
variáveis em C/C++, observe:
www.esab.edu.br 24
No algoritmo declaram-se três variáveis: idade como inteiro, na linha
1, media como float, na linha 2, e sexo como char na linha 3. Antes da
execução do programa, essas instruções solicitam ao sistema operacional
um espaço de memória para cada variável. Essa ação é chamada de
alocação de memória e resulta na criação física da variável e da associação
a um endereço. A Figura 6 representa a alocação das variáveis do
algoritmo 1 na memória principal, veja:
Memória Principal
Memória
Heap
idade
Emq1
media identificador
Emq2 endereço valor
sexo
Emq3
www.esab.edu.br 25
Memória Principal
Memória
Heap
idade
Emq1 43
idade = 43;
media
Emq2 Emq1 43
sexo
Emq3
Observe que de acordo com o que foi estudado até aqui, toda variável
possui um identificador, um valor, um endereço de memória e um tipo
de dado. Mas há um tipo diferenciado de variável que é chamado de
ponteiro cuja finalidade é armazenar o endereço de uma variável na
memória do computador. A Figura 8 representa uma variável do tipo
ponteiro, observe:
idade nome
endXYZ
Endereço:endXYZ
Figura 8 – Variável do tipo ponteiro.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 26
A variável nome possui um endereço hipotético endXYZ, que por sua
vez está sendo atribuído à variável ponteiro que o armazenará. Caso
seja executada a instrução para imprimir o valor da variável ponteiro,
o resultado será a impressão do endereço armazenado, porém, caso seja
solicitada a impressão do valor nesse endereço, o resultado impresso será
o mesmo valor que está armazenado na variável nome.
3.2 Recursividade
A recursividade consiste em uma função fazer a chamada dela mesma
em seu código, e a sua finalidade é tornar algoritmos mais complexos em
rotinas mais simples e com menos linhas de codificação. Para Guimarães
(1994, p. 141): “[...] existem casos em que um procedimento ou função
chama a si próprio. Diz-se então que o procedimento ou função é
recursivo”. O código do Algoritmo 2 apresenta uma função recursiva de
pesquisa de um determinado valor inteiro em um vetor.
www.esab.edu.br 27
As regras de funcionamento do processo recursivo são: se a posição
visitada do vetor for igual ao tamanho do vetor, instrução da linha 2,
então todas as posições já foram visitadas e o valor não existe no vetor,
não há mais posições a serem pesquisadas e o método retornará um 0
(falso). Na regra da linha 4, compara-se o vetor na posição de pesquisa
com a chave, que é o valor procurado, e se for igual a função retornará
o valor 1 (verdadeiro), indicando que o valor existe. Caso o vetor não
tenha todas as posições visitadas e o valor não tenha sido achado, na
linha 7 a função é chamada recursivamente recebendo como parâmetro
o valor a ser pesquisado (chave), a nova posição de pesquisa, pois esta foi
incrementada na linha 6, e o tamanho do vetor.
www.esab.edu.br 28
O algoritmo solicita ao usuário um valor a ser pesquisado. Como o vetor
possui 12 elementos, na linha 18 é chamada a função pesquisar com os
parâmetros valor (valor a ser localizado no vetor), 0 (primeira posição de
pesquisa) e 12 (tamanho do vetor). A cada interação da função, caso o
valor não seja localizado e o vetor não tenha sido todo visitado, a função
pesquisar é reexecutada com o mesmo valor de pesquisa, o mesmo
tamanho de vetor, porém com a próxima posição de pesquisa.
Número de Valor do
Linhas Executadas
Chamadas Valor Posição Tamanho vetor Dados
do Método
do Método [posição]
Primeira - 1 70 0 12 1 6, 7, 8, 9, 10, 11,12
2 70 1 12 3 6, 7, 8, 9, 10, 11,12
3 70 2 12 15 6, 7, 8, 9, 10, 11,12
4 70 3 12 70 6, 7, 8,9 retorna 1
4 3 2 1
www.esab.edu.br 29
Observe que a chamada número 1 da função fará uma nova chamada
para a mesma função, assim como a chamada número 2, chamada
número 3 e chamada número 4. Todas as chamadas recursivas serão
executadas no comando return pesquisar (chave, posição, tamanho).
Saiba mais
Para se aprofundar no conhecimento de métodos
recursivos, você pode acessar um conjunto de
endereços com blogs que tratam do assunto
clicando aqui.
www.esab.edu.br 30
3.3 Variáveis estáticas
O uso das variáveis estáticas está associado ao contexto de visibilidade
das variáveis, que podem ser locais e globais. Como estudado na unidade
anterior na seção de visibilidade das variáveis, as variáveis locais só podem
ser acessadas durante a execução do método, sendo visível somente nesse
método e que no seu encerramento todas as variáveis são desalocadas e
os dados são perdidos. Já as variáveis globais podem ser acessadas por
qualquer método do programa e é destruída somente ao final da sua
execução. O papel das variáveis estáticas declaradas nas funções é garantir
que o valor da variável seja mantido mesmo após o encerramento da
execução do método.
www.esab.edu.br 31
Note que a cláusula static, utilizada na linha 4, é que define uma variável
como estática, e deve ser usada na sua declaração. Cada vez que o
método contar() é executado, a variável contador é incrementada em 1
e impressa na tela. No método main() a função contar é chamada duas
vezes, a primeira chamada na linha 12 e outra na linha 14. Na chamada
da linha 12, a variável contador começará em 0, será incrementada em 1,
portanto, seu valor atual será 1. Mesmo após o encerramento do método,
o valor da variável não será perdido, e na segunda chamada do método,
na linha 14, a variável contador, por ser estática, iniciará de 1, não mais
de 0, depois será incrementada em 1, mudando seu valor atual para 2 e
posteriormente será impresso na tela.
Observe que a variável contador não perdeu seus dados após a primeira
execução, isso só é possível porque as variáveis estáticas são armazenadas
na memória principal em blocos de memória diferentes do espaço
utilizado pelas funções, e no encerramento da função todas as variáveis
são desalocadas com o método. Mas as variáveis estáticas não, pois estão
em outro endereçamento de memória.
www.esab.edu.br 32
Função / Método Função / Método
variáveis variáveis
locais estáticas
www.esab.edu.br 33
Variáveis compostas homogêneas:
4 vetores
Objetivo
Apresentar o conceito, a aplicação e a manipulação de vetores.
www.esab.edu.br 34
Para contextualizar, vamos analisar o problema de armazenamento das
médias de vendas de um determinado produto nos 12 meses do ano. Em
vez de declarar 12 variáveis, é possível a criação de uma variável com 12
posições. A declaração de vetor em C é realizada utilizando-se a seguinte
sintaxe: tipo de dado identificador[n], em que n se refere à quantidade de
posições do vetor, começando em 0 e terminando na posição n-1.
vendas
Emq1
Figura 13 – Alocação de Memória para o vetor de vendas.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 35
do vetor. Porém no uso do vetor para armazenamento ou leitura dos
dados, o programador deverá informar qual a posição a ser utilizada. Mas
como isso é possível?
Posições deslocadas
Bloco de memória ocupado pelo tipo de dados
0 1 2 3 4 5 6 7 8 9 10 11
vendas 128.99
Emq1
ponteiro
Deslocamento: quantidade de posições X 4 bytes
Ponto de partida
Endereço da variável
Figura 14 – Localizando a posição no vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 36
Saiba mais
Para aprofundar os estudos sobre os tipos de
dados em C e o espaço ocupado por cada um deles,
acesse o endereço clicando aqui. Agora que vimos
que os vetores nos permitem criar uma variável
dividida em inúmeras posições, veremos a seguir
como os vetores são aplicados e manipulados.
0 1 2 3 4 5 6 7 8 9 10 11
12
vendas
12 X 4 = 48 bytes
Figura 15 – Manipulando a posição fora do vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 37
Note que a posição 12 do vetor é um bloco de memória que não lhe
pertence, por isso quando acessada pelo programa de computador resultará
em um erro fatal, grave, que resultará no encerramento da aplicação.
www.esab.edu.br 38
Uma característica importante do vetor é que uma vez definida a sua
dimensão (quantidade de elementos) na declaração do vetor, durante
a execução do programa o bloco de memória alocado não poderá ser
gerenciado, independentemente da inserção ou não de itens no vetor.
Cerqueira, Celes e Rangel Netto (2004) destacam o uso da alocação
dinâmica para os casos em que a dimensão do vetor é desconhecida.
Quando sabemos a dimensão, é preferível o uso de vetores.
www.esab.edu.br 39
5 Alocação dinâmica de memória
Objetivo
Apresentar o conceito de alocação dinâmica de memória.
www.esab.edu.br 40
O Quadro 2 a seguir apresenta as vantagens e desvantagens da alocação
estática, observe:
Vantagens Desvantagens
O gerenciamento da memória é realizado O espaço de memória alocado não pode ser
exclusivamente pelo sistema operacional alterado durante a execução do programa.
que é responsável por alocar e desalocar o Caso a definição do espaço solicitado não
espaço de memória solicitado. atenda às necessidades do programa de
computador, não há como gerenciá-lo de
forma a corrigir o problema.
A definição de alocação de memória Caso o espaço solicitado na declaração
é simples, realizada pela instrução de das variáveis seja maior que o espaço
declaração de variável. disponibilizado pelo sistema operacional, o
programa de computador não será iniciado,
indicando um problema de Overflow.
A sua implementação é simples e atende É inviável para solução de problemas
aos problemas triviais de armazenamento em que não se conhece a quantidade de
de dados que exigem pouco espaço de dados que serão armazenados ou o espaço
armazenamento, principalmente quando a ser alocado é muito maior do que o
se conhece a quantidade de dados a serem disponibilizado pelo sistema operacional.
armazenados e manipulados.
www.esab.edu.br 41
5.1 Definição
Na alocação dinâmica as variáveis são declaradas, porém, o espaço de
memória será alocado durante a execução do programa por meio de
instruções específicas por parte do programador, garantindo que ele possa
alocar e liberar espaço de memória a qualquer momento. Isso possibilita
ao programador a construção de estruturas de dados que tenham
tamanhos variados, de acordo com a necessidade de momento, evitando-
se o desperdício de memória.
www.esab.edu.br 42
O Quadro 3 apresenta as vantagens e desvantagens da alocação dinâmica,
observe:
Vantagens Desvantagens
O espaço de memória é definido em tempo Como se trata de um bloco de memória
de execução sem desperdiço de memória, já criado em tempo de execução, toda a
que o espaço pode ser alocado e liberado em responsabilidade em alocar e liberar o
tempo de execução. espaço de memória é do programador.
Pode ser aplicado em soluções triviais ou Caso ele libere um espaço de memória em
em problemas complexos que exigem um um momento inadequado, todos os dados
espaço de memória considerável. serão perdidos. Cabe ao programador saber
É implementado a partir de ponteiros, o momento de executar a instrução que
que são variáveis que apontam para um aloca o espaço de memória, bem como o
endereço de memória. momento em que o espaço é liberado.
www.esab.edu.br 43
5.2 Uso da memória
Basicamente o uso da memória pode ocorrer de forma estática ou
dinâmica. A forma mais comum de definição do espaço de memória se
dá pela declaração de variáveis globais, sendo uma alocação estática, e o
espaço alocado se mantém o mesmo durante toda execução do programa.
As variáveis globais são declaradas no programa e acessíveis a todos os
demais blocos de programas, sejam eles funções ou procedimentos. Outra
forma muito utilizada são as variáveis locais, declaradas dentro dos blocos
de memória e visíveis somente a esses blocos. Nesse caso, as demais funções
e procedimentos, bem como o próprio programa de computador, não
têm acesso a essas variáveis já que são criadas durante a execução do bloco
e liberadas após seu encerramento. Como essas variáveis são criadas em
tempo de execução do método, são alocações dinâmicas.
www.esab.edu.br 44
Variáveis
alocadas
dinamicamente
Variáveis globais
Código do programa
www.esab.edu.br 45
5.3 Alocação contígua e alocação dispersa
Quando uma variável estática é alocada na memória pelo sistema
operacional os blocos são alocados contiguamente, ou seja, um bloco
do lado do outro, de forma organizada. Um vetor com oito posições,
por exemplo, terá os oito blocos de memória que o constituem alocados
um ao lado do outro, facilitando dessa forma o acesso e manipulação da
informação. A Figura 17 apresenta a alocação do vetor de dez posições,
sendo uma alocação estática, veja:
Memória
vendas
Isso é possível, pois o sistema operacional pode alocar o espaço todo antes
mesmo da execução do programa, já que cada item do vetor possui o
mesmo espaço de memória. Por exemplo, se for um vetor de “char”, cada
posição do vetor ocupa 1 byte, resultando em um bloco total de 8 bytes.
www.esab.edu.br 46
Memória
Observe que cada posição do vetor está sendo alocada à medida que
uma nova posição se faz necessária. Caso se desejasse alocar todo o vetor
dinamicamente, ou seja, alocar todas as posições no mesmo momento,
o resultado seria o mesmo da Figura 17, um único bloco de memória
alocado durante a execução do programa, com a vantagem que a
quantidade de posições é conhecida em tempo de execução.
e1
e10
e5
e15
www.esab.edu.br 47
Cada elemento possui um endereço de alocação na memória, além
disso, é armazenado para cada elemento o endereço do próximo
elemento, assim, o elemento do endereço e1 aponta para o elemento
e10, e o elemento do endereço e10 aponta para o endereço e5, assim
sucessivamente até o elemento e15, gerando uma estrutura de dados com
os elementos dos endereços e1, e10, e5 e e15.
www.esab.edu.br 48
6 Alocação dinâmica em C
Objetivo
Apresentar as funções de alocação dinâmica de memória em C.
Para elaboração desta unidade foi usada como referência Cerqueira, Celes
e Rangel Netto (2004).
www.esab.edu.br 49
Para declaração de uma variável ponteiro em C é utilizado o símbolo
de asterisco (*) antes do nome da variável, veja o exemplo a seguir no
Algoritmo 6:
Celles, Cerqueira e Rangel Netto (2004) destacam que para cada tipo
de dados da linguagem de programação C há um tipo ponteiro capaz de
armazenar endereços de memória.
Estudo complementar
Para complementar seus estudos sobre os
conceitos de ponteiros em C, acesse o link e veja
alguns exemplos.
www.esab.edu.br 50
6.2 Comandos para alocação de memória em C
A linguagem C disponibiliza por meio da biblioteca “stdlib” a função malloc
(memory allocation) para alocação de um bloco contíguo de memória do
computador, retornando o endereço desse bloco. Segundo Cerqueira, Celes
e Rangel Netto (2004, p. 66), “[...] a função básica para alocar memória é
malloc. Ela recebe como parâmetro o número de bytes que se deseja alocar e
retorna o endereço inicial da área de memória alocada”.
www.esab.edu.br 51
Algoritmo 8 – Armazenando e consultando dados do ponteiro
01 #include <stdio.h>
02 #include <stdlib.h>
03 int main(){
04 char *ponteiro_char;
05 ponteiro_char = (char *) malloc(1);
06 puts("Informe um Caractere qualquer:\n");
07 scanf("%c",ponteiro_char);
08 printf("O ponteiro de CHAR contem o valor:
%c",*ponteiro_char);
09 system(“pause”);
10 return 0;
11 }
Observe que na linha 7 foi realizada uma entrada de dados via teclado
para a variável ponteiro_char, que armazenará o valor informado pelo
usuário. Já na linha 8 é mostrado na tela o caractere armazenado no
endereço de memória da variável ponteiro_char, que deve ser o mesmo
valor digitado na linha 7.
www.esab.edu.br 52
Para a variável ponteiro_inteiro serão requisitados 4 bytes de memória,
por meio da instrução da linha 5 (malloc(4)). Na linha 6 do algoritmo
é verificado se o valor da variável ponteiro_inteiro após a execução da
função malloc() vale NULL, uma vez que, se houve algum problema na
alocação de memória para a variável, esta não guardará o endereço de
memória e valerá NULL. A palavra reservada NULL indica que a variável
ponteiro ainda não foi alocada, portanto, não pode ser manipulada,
tornando-se uma boa prática de programação sempre antes de usar a
variável ponteiro verificar se ela possui um endereço de memória (é
diferente de NULL).
www.esab.edu.br 53
Algoritmo 10 – Alocação de dados a partir do tipo de dados
01 #include <stdio.h>
02 #include <stdlib.h>
03 int main(){
04 int *ponteiro_inteiro;
05 ponteiro_inteiro = (int*)
malloc(sizeof(int));
06 if (ponteiro_inteiro == NULL){
07 printf ("Erro:Memória Insuficiente para
alocação do Inteiro");
08 }else{
09 printf ("Memória Alocada com Sucesso!\n");
10 printf ("Foram alocados: %d bytes!\
n",sizeof(int));
11 }
system(“pause”);
return 0;
12 }
www.esab.edu.br 54
Algoritmo 11 – Desalocando memória
01 #include <stdio.h>
02 #include <stdlib.h>
03 int main(){
04 int *ponteiro_inteiro;
05 ponteiro_inteiro = (int*)
malloc(sizeof(int));
06 if (ponteiro_inteiro == NULL){
07 printf ("Erro:Memoria Insuficiente para
alocação do Inteiro");
08 }else{
09 printf ("Memoria Alocada com Sucesso!\n");
10 printf ("Liberando Memória Alocada!\n");
11 free(ponteiro_inteiro);
12 }
13 system(“pause”);
14 return 0;
15 }
www.esab.edu.br 55
Fórum
Caro estudante, dirija-se ao Ambiente Virtual de
Aprendizagem da instituição e participe do nosso
Fórum de discussão. Lá você poderá interagir com
seus colegas e com seu tutor de forma a ampliar,
por meio da interação, a construção do seu
conhecimento. Vamos lá?
www.esab.edu.br 56
Resumo
www.esab.edu.br 57
Por fim, na unidade 6, vimos os comandos da biblioteca “stdlib.h” para
alocação de memória (malloc), liberação de memória (free) e espaço de
armazenamento das variáveis ou tipo de dados (sizeof ).
www.esab.edu.br 58
7 Tipos de passagens de parâmetros
Objetivo
Apresentar o funcionamento da passagem de parâmetro nos
métodos.
www.esab.edu.br 59
Segundo Celes, Cerqueira e Rangel (2004, p. 40),
[...] os parâmetros devem ser listados, com os respectivos tipos, entre os parênteses
que seguem o nome da função. Além de receber parâmetros, uma função pode ter um
valor de retorno associado.
Essa função, como pode ser visto na linha 1, possui dois parâmetros:
peso e altura do tipo float. A função retorna um float, o que representa
o resultado do peso dividido pela altura ao quadrado e que é a regra de
negócio de cálculo do índice de massa corporal.
www.esab.edu.br 60
8 imc = calcularIMC(valorpeso,valoraltura);
9 printf(“Seu IMC vale:%f”,imc);
10 system(“pause”);
11 return 0;
12 }
www.esab.edu.br 61
A Figura 19 apresenta a identificação de cada item utilizado no
processamento do cálculo de índice de massa corporal por meio da
função calcularIMC(), observe:
Saiba mais
O Algoritmo 13 pode ser baixado da internet
neste endereço, que é um repositório gratuito de
códigos-fontes e que está sendo utilizado para
a publicação dos algoritmos desenvolvidos pelo
autor desta disciplina.
A forma pela qual os parâmetros podem ser passados para uma função
pode ser por valor ou referência e afetam de que forma os valores do
argumento serão manipulados.
www.esab.edu.br 62
7.2 Tipos de passagem de parâmetro
Ascencio e Araújo (2010, p. 253) destacam que “[...] o parâmetro pode ser
passado por valor ou referência e a forma de declaração deste tipo depende
da sintaxe de cada linguagem de programação”. Ou seja, o funcionamento
independe da linguagem de programação, mas a forma pela qual é
declarada a estrutura do parâmetro depende de cada linguagem.
[...] passagem de parâmetro por valor fornece valores para a função como se fossem
variáveis locais, de forma que os parâmetros são manipulados dentro da função, mas
após o seu encerramento estes valores são liberados e não influenciam nas variáveis
que foram usadas como argumentos.
www.esab.edu.br 63
No Algoritmo 14 e em linguagem C, é declarada a função multiplicar
com passagem de parâmetros por valor. Observe:
www.esab.edu.br 64
O valor do parâmetro num da função multiplicar mudou do valor 10
para 100 durante a execução do método, mas o valor do argumento
representado pela variável x continua valendo 10. Ou seja, a alteração do
parâmetro num não alterou o valor do argumento x, as duas estruturas
de dados são manipuladas separadamente.
www.esab.edu.br 65
O parâmetro pnum é declarado na linha 1 do algoritmo com um
símbolo de asterisco (*) ao lado da definição de seu nome, *pnum.
Essa sintaxe é utilizada para determinar que o parâmetro receba como
argumento o endereço de uma variável. Dessa forma, na utilização do
método, a variável do parâmetro deverá ser informada, precedida por
um símbolo de &, como podemos observar na linha 8 do código, com
somar(&numero). Podemos, portanto, entender que a função aguarda
o endereço da variável, por isso o asterisco ao lado do nome da variável,
e na passagem do argumento para função, devemos colocar um & (e
comercial) ao lado da variável para que o endereço desta seja enviado
para o parâmetro.
www.esab.edu.br 66
Isso só é possível devido ao fato de que na passagem de parâmetro por
referência, o parâmetro recebe o endereço do número e não o seu valor.
Dessa forma, tanto o argumento quanto o parâmetro apontam para o
mesmo lugar ou local da memória, mas com nomes diferentes, de forma
que alterações no parâmetro refletem no argumento.
*pnum
numero em01
20 (20)
em01
www.esab.edu.br 67
Estudo complementar
Acessando este endereço, você poderá
complementar os estudos sobre as formas de
passagem de parâmetro.
Até lá!
www.esab.edu.br 68
8 Tipos estruturados – parte I
Objetivo
Apresentar a definição e o funcionamento dos tipos de variáveis
estruturadas.
[...] os registros, ou tipos estruturados, são uma das principais estruturas de dados,
sendo uma variável composta que engloba um conjunto de dados primitivos, por isso
é conhecida como tipo estruturado heterogêneo”.
www.esab.edu.br 69
A Figura 23 representa uma ficha cadastral de alunos para uma escola.
Observe:
Matrícula: Data:
Nome:
Sexo: M [ ] F [ ]
Data de nascimento: / / Idade:
Nome do Pai:
Nome da Mãe:
Telefone: ( )
Celular: ( )
E-mail:
Note que a ficha do aluno é formada por vários dados que podemos
chamar de campos, alguns para armazenamento de tipos inteiro (como
matrícula e idade), outros para cadastro de tipos alfanuméricos (como
nome, nome do pai e da mãe). No campo sexo será cadastrado um
caractere, M ou F. Como a ficha é constituída por campos de diversos
tipos de dados, para implementar essa ficha utilizando uma linguagem
de programação de computadores será necessária uma estrutura de dados
que também seja heterogênea.
www.esab.edu.br 70
struct nome_da_estrutura{
tipo de dado campo1;
tipo de dado campo2;
tipo de dados campoN;
};
www.esab.edu.br 71
Observe que o tipo estruturado está sendo declarado com o nome
fichaaluno, na linha 1 e formado por um conjunto de campos, que
possuem a declaração na forma de variáveis, apresentados nas linhas 2 a
12, que representam as informações contidas na ficha cadastral de aluno.
A ordem dos campos não precisa ser a mesma ordem da ficha cadastral.
Para que um programa utilize o tipo struct, fichaaluno, que foi criado
anteriormente, é necessária a definição de uma variável com esse tipo, da
seguinte forma:
nome_da_estrutra nome_da_variável;
www.esab.edu.br 72
Algoritmo 18 – Manipulação do tipo estruturado
1 #include <stdio.h>
2 #include <stdlib.h>
3 struct fichaaluno{
4 int matricula;
5 char data[10];
6 char nome[50];
7 char sexo;
8 char datanascimento[10];
9 int idade;
10 char nomepai[50];
11 char nomemae[50];
12 char telefone[14];
13 char celular[14];
14 char email[30];
15 };
16
17 struct fichaaluno aluno;
18
19 void cadastrar(){
20 system(“cls”);
21 printf(“Matricula:\n”);
22 scanf(“%d”,&aluno.matricula);
23 getchar();
24 printf(“Data:\n”);
25 gets(aluno.data);
26 printf(“Nome:\n”);
27 gets(aluno.nome);
28 printf(“Sexo:\n”);
29 aluno.sexo = getchar();
30 getchar();
31 printf(“Data de Nascimento:\n”);
32 gets(aluno.datanascimento);
33 printf(“Idade:\n”);
34 scanf(“%d”,&aluno.idade);
35 getchar();
36 printf(“Nome do PAI:\n”);
37 gets(aluno.nomepai);
38 printf(“Nome da Mae:\n”);
39 gets(aluno.nomemae);
40 printf(“Telefone:\n”);
41 gets(aluno.telefone);
42 printf(“Celular:\n”);
43 gets(aluno.celular);
44 printf(“E-mail:\n”);
45 gets(aluno.email);
www.esab.edu.br 73
46 }
47
48 void listar(){
49 system(“cls”);
50 printf(“Matricula:%d\n”,aluno.matricula);
51 printf(“Data do Cadastro:%s\n”,aluno.data);
52 printf(“Nome:%s\n”,aluno.nome);
53 printf(“Sexo:%c\n”,aluno.sexo);
54 printf(“Data de Nascimento:%s\n”,aluno.datanascimento);
55 printf(“Idade:%d\n”,aluno.idade);
56 printf(“Nome do PAI:%s\n”,aluno.nomepai);
57 printf(“Nome da MAE:%s\n”,aluno.nomemae);
58 printf(“Telefone:%s\n”,aluno.telefone);
59 printf(“Telefone Celular:%s\n”,aluno.celular);
60 printf(“E-mail:%s\n”,aluno.email);
61 }
62 int main(){
63 cadastrar();
64 listar();
65 system(“pause”);
66 return 0;
67 }
www.esab.edu.br 74
A gravação dos dados é realizada na função chamada cadastrar, que limpa
a tela pelo comando cls na linha 20 e solicita que o usuário informe cada
um dos membros da variável aluno, do tipo “tipo_aluno”. A listagem
dos dados é realizada na função listar, que imprime na tela cada um dos
membros cadastrados na função cadastrar(). Como essas duas funções
(cadastrar e listar) manipulam a variável aluno, ela foi declarada de forma
global, na linha 17, na instrução: struct tipo_aluno aluno;
www.esab.edu.br 75
O Algoritmo 19, em linguagem C, demonstra a declaração, atribuição e
o acesso aos dados de um ponteiro para tipo estruturado. Observe:
www.esab.edu.br 76
Nas funções cadastrar() e listar(), os dados são solicitados por meio da
funções entrada de dados gets, para campos do tipo literal, e scanf para
os demais tipos de dados (numéricos e caractere) e apresentados por
meio da função de saída de dados printf, ao usuário e seguem a sintaxe:
variável ponteiro -> nome do campo.
www.esab.edu.br 77
O código completo do Algoritmo 20 pode ser baixado neste endereço.
Procure baixá-lo e executá-lo para verificação dos conteúdos apresentados
sobre o tema.
Saiba mais
Para saber mais sobre os tipos de dados
estruturados em C, acesse este endereço e
complemente seus estudos.
www.esab.edu.br 78
Algoritmo 21 – Alocação dinâmica de tipos estruturados
1 #include <stdio.h>
2 #include <stdlib.h>
3 struct estruturadata{
4 int dia;
5 int mes;
6 int ano;
7 };
8 int main(){
9 struct estruturadata data;
10 struct tipo_data *pontData;
11 pontData = (struct tipo_data*) malloc(sizeof(struct tipo_
data));
12 pontData->dia = 01;
13 pontData->mes = 07;
14 pontData->ano = 1980;
15 system(“cls”);
16 puts(“Resultado\n”);
17 printf(“%d\n%d\n%d”,pontData->dia,pontData->mes,pontData-
>ano);
18 free(pontData);
19 system(“pause”);
20 return 0;
21 }
www.esab.edu.br 79
Na linha 12, o espaço de memória alocado para a variável pontData
é liberado por meio da instrução free(). Essa instrução garante que o
espaço seja liberado já que a variável não será mais utilizada, evitando o
desperdício de memória.
Por fim, você estudou como utilizar os comandos malloc () e free () para
alocar e liberar espaço em tempo de execução para tipos estruturados.
Tarefa dissertativa
Caro estudante, convidamos você a acessar o
Ambiente Virtual de Aprendizagem e realizar a
tarefa dissertativa.
www.esab.edu.br 80
9 Tipos estruturados – parte II
Objetivo
Apresentar a definição e o funcionamento dos tipos de variáveis
estruturadas.
www.esab.edu.br 81
A sintaxe para declaração e novos tipos em C é: typedef tipo nome_
novo_tipo;
Essa técnica de criação de novos tipos de dados visa deixar mais claro
o tipo de informação que será manipulado, podendo ser utilizado para
apelidar um tipo já existente, como no Algoritmo 22, na linha 4, em que
o tipo int foi apelidado de inteiro, por meio da instrução typedef. Outra
aplicação é na definição de um tipo estruturado. Por exemplo, suponha
que um programa tem como finalidade realizar o cadastro dos dados de
uma agenda, com o local do compromisso, data e hora, representado por
um tipo estruturado “fichaagenda”. Usando a definição de um novo tipo,
chamado o tipo estruturado de agenda, por meio da instrução typedef, ficará
mais claro qual o tipo de dados que está sendo manipulado. Consequente
facilitará a compreensão e manutenção no sistema, principalmente se a
pessoa responsável pela manutenção não for a que implementou o programa.
Portanto, trata-se de uma boa prática de programação.
www.esab.edu.br 82
Na linha 3 do Algoritmo 22, foi criado o tipo de dados chamado literal,
que representa uma cadeia de 30 caracteres. Na linha 2 foi criado o
tipo de dados inteiro e na linha 3 o tipo de dados moeda, um float.
Na linha 7 foi declarada a variável nome como sendo do tipo literal. A
variável idade, declarada na linha 8, é do tipo inteiro e a variável salário,
declarada na linha 9, do tipo moeda.
www.esab.edu.br 83
Na linha 3, foi criado o tipo estruturado chamado registro com os
campos código, descrição e preço. Na linha 8, foi declarado o novo tipo
de dados chamado tipo_produto, que, assim como na linha 3, também é
do tipo estruturado registro. Na linha 10, a variável produto foi declarada
como sendo do tipo tipo_produto. Note que a partir da definição de um
novo tipo para um tipo struct, como na linha 8: typedef struct registro
tipo_produto, não será mais necessário que variáveis declaradas com este
novo tipo tenham que utilizar a cláusula struct informando que se trata
de um tipo estruturado. Isso, pois essa informação já está na definição do
novo tipo (implícito), assim a declaração se torna mais simples, como na
linha 10: tipo_produto produto.
Dica
A possibilidade de definição de novos tipos de
dados deve ser utilizada para criar nomes que
facilitem a compreensão dos dados que serão
manipulados, por isso, procure utilizar sinônimos
na declaração dos novos tipos. Por exemplo, para
representar um tipo de dados estruturado que
representa um endereço composto por logradouro,
número, bairro, CEP etc., a boa prática é declarar o
novo tipo como sendo “endereço”.
Da mesma forma que uma variável pode ser declarada como sendo de
um tipo estruturado, um vetor também pode ser declarado da mesma
forma, facilitando o desenvolvimento do programa.
www.esab.edu.br 84
cliente de nome Marcelo, idade 43 anos e sexo masculino na posição 0
do vetor deverá gravar na posição 0 do vetor nomes o valor “Marcelo”,
na posição 0 do vetor idades o valor 43 e no vetor sexos na posição 0
o valor “M”. E quanto maior o número de informações de um cliente,
maior a quantidade de vetores. Ou seja, se houver um dado telefone e
salário, serão necessários mais dois vetores para armazenamento desses
novos dados, e gerenciados de forma simultânea. Note que as operações
de inserção, remoção e alteração terão que manipular todos os vetores de
forma simultânea, pois os dados foram divididos em estruturas de dados
diferentes, tornando o processo complexo.
www.esab.edu.br 85
Algoritmo 24 – Vetor de tipo estruturado
1 #include <stdio.h>
2 #include <stdlib.h>
3 struct registro{
4 int codigo;
5 char descricao[40];
6 float preco;
7 };
8 typedef struct registro tipo_produto;
9 tipo_produto estoque[3];
10 void cadastrar(){
11 system(“cls”);
12 int i=0;
13 for(i=0; i < 3;i++){
14 puts(“Informe o CODIGO:\n”);
15 scanf(“%d”,&estoque[i].codigo);
16 getchar();
17 puts(“Informe a DESCRICAO:\n”);
18 gets(estoque[i].descricao);
19 puts(“Informe o preco:\n”);
20 scanf(“%f”,&estoque[i].preco);
21 }
22 }
23 void listar(){
24 system(“cls”);
25 int i=0;
26 for(i=0; i < 3;i++){
27 printf(“%d - %s - %f\n”,estoque[i].codigo,
28 estoque[i].descricao,
29 estoque[i].preco);
30 }
31 }
32 int main(){
33 cadastrar();
34 listar();
35 system(“pause”);
36 return 0;
37 }
www.esab.edu.br 86
A linha 3 do código apresenta a declaração do tipo estruturado “registro”.
Na linha 8, é criado o tipo de dados chamado tipo_produto. Na linha
9, o vetor chamado estoque é declarado com três posições do tipo “tipo_
produto”. Isso significa que cada posição do vetor terá os campos código,
descrição e preço.
www.esab.edu.br 87
Algoritmo 25 – Vetor de ponteiro para o tipo estruturado
1 #include <stdio.h>
2 #include <stdlib.h>
3 struct registro{
4 int codigo;
5 char descricao[40];
6 float preco;
7 };
8 typedef struct registro tipo_produto;
9 tipo_produto* tabela[3];
www.esab.edu.br 88
estruturado (total = espaço para armazenamento do código + espaço
para armazenamento do nome + espaço para armazenamento do preco).
Para que esse espaço seja alocado, será utilizada a função malloc(). Esse
procedimento pode ser analisado no Algoritmo 27:
www.esab.edu.br 89
Algoritmo 28 – Liberação de memória no vetor de ponteiros
1 void desalocar(){
2 int i=0;
3 for(i=0; i < 3;i++){
4 free(tabela[i]);
5 }
6 }
enum nome_do_conjunto{
valor 1, valor 2, ..., valor N
};
www.esab.edu.br 90
O Algoritmo 29 apresenta um exemplo de utilização de enum, cuja
finalidade é criar um conjunto de países que podem ser escolhidos
pelo usuário. Para deixar o código mais claro, a comparação do
país selecionado pelo usuário utiliza o nome do país declarado na
enumeração, ao invés de um número, observe:
www.esab.edu.br 91
Nesta unidade você pôde estudar como definir novos tipos de dados,
utilizando a cláusula typedef, que pode ser utilizada em variáveis simples
ou compostas, como no caso do vetor. Foi possível também estudar
como criar e manipular vetores de tipos de dados e ponteiros para tipos
de dados. Fechando a unidade, você estudou como definir e utilizar
o tipo enumeração, que permite a criação organizada de conjunto de
constantes.
Atividade
Chegou a hora de você testar seus conhecimentos
em relação às unidades 1 a 9. Para isso, dirija-se
ao Ambiente Virtual de Aprendizagem (AVA) e
responda às questões. Além de revisar o conteúdo,
você estará se preparando para a prova. Bom
trabalho!
www.esab.edu.br 92
10 Matrizes – conceituação
Objetivo
Apresentar o conceito de matrizes.
www.esab.edu.br 93
10.1 Alocação estática versus dinâmica
As matrizes estáticas possuem as mesmas limitações dos vetores
unidimensionais criados estaticamente, que é saber com antecedência
a sua quantidade exata de elementos, e uma vez definido o tamanho
do vetor ele não pode ser alterado, para mais ou menos, durante a
execução do programa. Se essa quantidade não é conhecida em tempo
de execução, é preciso utilizar a alocação dinâmica. Como na maioria
das vezes, durante a execução do programa não se conhece o número
de elementos dessas estruturas de dados, é fundamental o domínio da
estratégia da alocação dinâmica.
www.esab.edu.br 94
Estudo complementar
Para saber mais sobre aplicação das matrizes,
acesse o link e veja alguns exemplo.
Saiba mais
Para saber mais sobre as matrizes, você pode
acessar este endereço.
www.esab.edu.br 95
No caso da matriz da Figura 25, os elementos da diagonal principal são
indexados pelas linhas e colunas: [0][0]; [1][1]; [2][2] e [3][3]. Já os
elementos da diagonal secundária são indexados pelas linhas e colunas:
[0][3]; [1][2]; [2][1] e [3][0].
Agora que você já sabe como declarar a matriz, vamos ver como
preencher, listar e consultar dados nessa estrutura.
www.esab.edu.br 96
Passo 1 Passo 2
Linha 0: percorrer as colunas Linha 1: percorrer as colunas
0 1 2 3 0 1 2 3
0 0
1 1
2 2
3 3
Passo 3 Passo 4
Linha 2: percorrer as colunas Linha 3: percorrer as colunas
0 1 2 3 0 1 2 3
0 0
1 1
2 2
3 3
A cada processo, uma linha é preenchida, sendo que para cada linha
todas as colunas são “visitadas” para o preenchimento da matriz. Ao final
dos processos de cada linha, a matriz estará preenchida por completo.
www.esab.edu.br 97
Algoritmo 30 – Preenchimento da matriz
1 #include <stdio.h>
2 #include <stdlib.h>
3 int matriz[2][2];
4 void preencherMatriz(){
5 system(“cls”);
6 int linha, coluna;
7 for(linha=0; linha<2; linha++){
8 for(coluna=0;coluna<2;coluna++){
9 printf(“Informe o valor para linha:%d e coluna:%d\
n”,linha,coluna);
10 scanf(“%d”,&matriz[linha][coluna]);
11 }
12 }
13 }
14 void main(){
15 preencherMatriz();
16 }
www.esab.edu.br 98
A Figura 27 apresenta esse processamento. Observe:
Linha Coluna
0 0
1
1 0
1
www.esab.edu.br 99
O Algoritmo 31 apresenta a rotina de listagem da matriz:
www.esab.edu.br 100
O Algoritmo 32 apresenta a rotina de pesquisa na matriz:
www.esab.edu.br 101
Por fim, não é possível remover elementos de uma matriz, pois se trata
de uma alocação estática e assim como nos vetores unidimensionais, uma
vez alocado o espaço de memória, não há como liberá-lo em tempo de
execução.
Para que isso seja possível, teremos que criar matrizes dinamicamente
para poder alocar e liberar o espaço de memória em tempo de execução,
usando os comandos malloc() e free(). Mas isso é conteúdo para a
próxima unidade.
www.esab.edu.br 102
11 Matrizes – codificação
Objetivo
Apresentar a codificação e a manipulação de matrizes.
www.esab.edu.br 103
O problema encontra-se na utilização da linguagem C, que só permite a
alocação dinâmica para vetores unidimensionais.
0 1 2
0
1
2
0 1 2 3 4 5 6 7 8
www.esab.edu.br 104
linha 0 linha 1 linha 2
0 1 2 3 4 5 6 7 8
www.esab.edu.br 105
O Algoritmo 33 apresenta a rotina para conversão da matriz em vetor:
www.esab.edu.br 106
A função gerarVetor(), que aparece das linhas 14 à 23, percorre todos os
elementos da matriz e copia os valores para o vetor unidimensional. Para
tanto foi criada a variável p, na linha 15, que representa cada posição do
vetor, começando em 0 e a qual é incrementada a cada inserção do valor
da matriz no vetor.
www.esab.edu.br 107
Observe que se girarmos a matriz de forma que os elementos da coluna
sejam apresentados nas linhas, a matriz ficará exatamente igual. Isso
se dá porque os elementos acima da diagonal principal são os mesmos
elementos abaixo da diagonal principal, conforme pode ser observado na
Figura 33:
Matriz
3 2 4
2 5 8
4 8 6
Diagonal
principal
Figura 33 – Matriz simétrica – diagonal principal.
Fonte: Elaborada pelo autor (2013).
Matriz
3 2 4
2 5 8 3 5 6 2 4 8
4 8 6
Diagonal
principal
Figura 34 – Matriz simétrica – conversão da matriz para vetor unidimensional.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 108
Algoritmo 34 – Conversão da matriz simétrica em vetor
1 void gerarVetor(){
2 int i, j,p;
3 p=0;
4 for(i=0; i < 3; i++){
5 for(j=0; j < 3; j++){
6 if ( i>=j){
7 vetor[p] = matriz[i][j];
8 p++;
9 }
10 }
11 }
12 }
13 void consultar(){
14 int posicao, linha, coluna;
15 puts(“Informe a linha da matriz:”);
16 scanf(“%d”,&linha);
17 puts(“Informe a coluna da matriz:”);
18 scanf(“%d”,&coluna);
19 if(linha >= coluna) posicao = linha * (linha+1)/2+coluna;
20 else posicao = coluna * (coluna+1)/2+linha;
21 printf(“O valor da matriz:%d”, vetor[posicao]);
22 }
da diagonal, temos: .
www.esab.edu.br 109
Nesta unidade você pôde estudar que na linguagem C não é possível
fazer alocação dinâmica de matrizes ou vetores bidimensionais. Mas,
por outro lado, há como fazer isso com vetores unidimensionais, para
tanto é preciso converter uma matriz bidimensional em um vetor
unidimensional para representá-la. Tão importante quanto converter
uma matriz em vetor unidimensional, é criar regras de acesso ao vetor a
partir dos índices da matriz – sendo que a conversão para o vetor deve
analisar o tipo de dados que está manipulando para que, se possível,
nele se ocupe menos espaço de armazenamento que se ocuparia na
matriz. Esse é o caso das matrizes simétricas, as quais possuem elementos
repetidos acima e abaixo da diagonal principal, o que torna desnecessário
o armazenamento de todos os elementos da matriz no vetor.
www.esab.edu.br 110
12 Matrizes – exercícios
Objetivo
Apresentar exercícios comentados sobre matrizes, com a diagonal
principal e diagonal secundária.
www.esab.edu.br 111
A seguir, são apresentados cada um desses passos na forma de funções
implementadas na linguagem C.
www.esab.edu.br 112
Com o vetor alocado na memória, o próximo passo é cadastrar os valores
da matriz na forma de um vetor com os dados informados pelo usuário
do sistema.
www.esab.edu.br 113
Estando a matriz preenchida, é possível listar todos os dados da matriz
que foram armazenados no vetor unidimensional.
www.esab.edu.br 114
A Figura 37 apresenta os elementos da diagonal principal e diagonal
secundária.
0 1 2 0 1 2
0 0
1 1
2 2
Diagonal Diagonal
Principal Secundária
Figura 37 – Diagonal principal e diagonal secundária.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 115
Algoritmo 39 – Listagem dos dados da diagonal principal e diagonal secundária
1 int acessar(int linha,int coluna,int numcolunas){
2 return ponteiro_vetor[linha * numcolunas+coluna];
3 }
4 void listarDiagonalPrincipal(int numlinhas,int numcolunas){
5 puts(“\nElementos da DIAGONAL PRINCIPAL da MATRIZ\n”);
6 if(numlinhas != numcolunas){
7 puts(“\n MATRIZ DEVE SER QUADRADA!\n”);
8 }
9 else{
10 int i=0;
11 for(i=0; i < numcolunas;i++){
12 printf(“%d “,acessar(i,i,numcolunas));
13 }
14 }
15 }
16 void listarDiagonalSecundaria(int numlinhas,int numcolunas){
17 puts(“\nElementos da DIAGONAL SECUNDARIA da MATRIZ\n”);
18 if(numlinhas != numcolunas){
19 puts(“\n MATRIZ DEVE SER QUADRADA!\n”);
20 }
21 else{
22 int i=0;
23 for(i=0; i < numcolunas;i++){
24 printf(“%d “,acessar(i,numcolunas-i-1,numcolunas));
25 }
26 }
27 }
www.esab.edu.br 116
[0,0], [1,1] e [2,2]. Note que as posições são geradas e passadas como
parâmetro para a função acessar(), que retorna o número inteiro dessa
posição (linha, coluna).
www.esab.edu.br 117
iguais, os laços são encerrados, pois a matriz não é simétrica. Dessa
forma, na linha 6, é verificado se o elemento da posição (i, j) é diferente
da posição (j,i). Caso seja verdadeira essa condição, a variável simétrica
recebe falso (0) e o laço é encerrado. A variável simétrica começou
valendo verdadeiro (1).
www.esab.edu.br 118
Resumo
Na unidade 9, você estudou como criar novos tipos de dados por meio
da cláusula typedef e como criar vetores de ponteiros para que essa
estrutura de dados utilize exatamente o espaço de memória necessário.
Nessa unidade você ainda pôde conhecer o tipo enumeração, que
permite o uso de termos para representar valores numéricos.
www.esab.edu.br 119
Na unidade 11, você pôde analisar como converter matrizes para vetores
unidimensionais, já que na linguagem de programação C não é possível a
alocação dinâmica de vetores bidimensionais.
www.esab.edu.br 120
13 Tipos abstratos de dados – parte I
Objetivo
Apresentar a importância da técnica de programação baseada na
definição de Tipos Abstratos de Dados (TAD).
Será que existe uma técnica para que essas estruturas e suas operações
sejam reutilizadas sem a necessidade de novas implementações?
www.esab.edu.br 121
Por exemplo, os tipos de dados básicos, que conhecemos também como
tipos primitivos, não têm uma estrutura sobre seus valores. Isso significa
que não podemos decompor um tipo primitivo em outras partes. A
maioria das linguagens de programação possui o tipo de dados lógico,
que pode assumir os valores true (verdadeiro) e false (falso). No caso da
linguagem C, não existe o tipo de dados lógico, ele é representado pelo
tipo inteiro (int) que pode assumir dois valores: 0 para falso e 1 para
verdadeiro. Tanto o tipo lógico das outras linguagens de programação
quanto o tipo inteiro da Linguagem C não podem ser decompostos em
outros tipos de dados. Já os tipos estruturados permitem a agregação
de mais de um valor à variável, podendo ter todos os valores do mesmo
tipo, no caso de vetores unidimensionais e bidimensionais ou de tipos
diferentes, como nos registros ou faixas de valores das enumerações.
13.1 Definição
Os Tipos Abstratos de Dados são estruturas de dados que representam
dados que não foram previstos pela linguagem de programação e
são divididos em duas partes, os dados e as operações. As definições
de um TAD, o tipo de dado que será manipulado, as regras de
funcionamento e utilização, correspondem à escolha adequada da forma
de armazenamento dos dados e à definição das operações que podem
ser efetuadas sobre eles. Celes, Cerqueira e Rangel Netto (2004, p. 126)
destacam que “[...] o conceito de abstrato no TAD se refere à forma pela
www.esab.edu.br 122
qual o tipo foi implementado, isto deve ser esquecido, já que um TAD é
descrito pela finalidade do tipo e de suas operações, e não pela forma que
foi implementado”.
ContaBancaria conta;
int numero = 1234;
double saldoinicial = 100.00;
Como usar o TAD double valor = 800.00;
abrirConta(conta,saldoinicial);
depositar(conta, valor);
extratoSaldo(conta);
Figura 38 – Tipo Abstrato de Dados: ContaBancaria.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 123
da conta com o número identificador 1, o saldo inicial de 100.00, um
depósito de 800.00 e a impressão do saldo atual. Porém, o programador
não precisa saber como essas funções foram implementadas, é preciso
apenas saber como utilizá-las, quais os parâmetros de entrada, a
ordem dos parâmetros, seus respectivos tipos e o tipo de retorno da
função; o restante – como a forma pela qual os dados são atribuídos
para cada campo do registro que representa conta bancária, como um
valor é adicionado ao saldo quando a operação é de depósito ou como
é implementada a redução do saldo quando a operação é de saque
– é abstraído. Essas informações não precisam ser conhecidas pelo
programador que utiliza o Tipo Abstrato ContaBancaria.
www.esab.edu.br 124
Para Ascencio e Araújo (2010, p. 469), os “[...] TADs são formados por
componentes que são as funções, com objetivos específicos”.
www.esab.edu.br 125
Então, podemos entender que um TAD em C é desenvolvido na forma de
módulos, os quais são arquivos-fonte separados e carregados nos programas
que desejam utilizá-los. E mais, um TAD é composto pelos tipos de dados
e operações relacionadas e específicas com uma finalidade definida.
www.esab.edu.br 126
14 Tipos abstratos de dados – parte II
Objetivo
Apresentar a importância da técnica de programação baseada na
definição de Tipos Abstratos de Dados (TADs).
www.esab.edu.br 127
Algoritmo 41 – Definição do registro para Conta Bancária
01 struct registro{
02 int numero;
03 double saldo;
04 };
www.esab.edu.br 128
O código completo do Tipo Abstrato de Dados ContaBancaria é
apresentado no Algoritmo 43:
[...] estes módulos podem ser utilizados para compor vários programas e, assim,
poupar muito tempo de programação. Isto se deve a característica que estes módulos
podem ser utilizados em vários outros programas sem a necessidade de novas
codificações.
www.esab.edu.br 129
Algoritmo 44 – Regras de negócio da TAD – contabancaria.c
01 #include <stdio.h>
02 #include "conta.h"
03 void abrirConta(ContaBancaria*
contacorrente,int num,double valor){
04 contacorrente->numero = num;
05 contacorrente->saldo = valor;
06 }
07 void depositar(ContaBancaria*
contacorrente,double valor){
08 contacorrente->saldo = contacorrente->saldo
+ valor;
09 }
10 void sacar(ContaBancaria* contacorrente,double
valor){
11 contacorrente->saldo = contacorrente->saldo
- valor;
12 }
13 void extrato(ContaBancaria* contacorrente){
14 printf("R$:%f",contacorrente->saldo);
15 }
www.esab.edu.br 130
Para utilizar o TAD ContaBancaria, basta que, no programa em que ele será
utilizado, seja incluída a instrução #include “contabancaria.c” e o tipo de
dado ContaBancaria. Assim, as operações estarão disponíveis para utilização.
www.esab.edu.br 131
Na linha 5 do código é declarada a variável minhaconta, do tipo
ContaBancaria. Na linha 6 essa variável que representa a conta é aberta
de forma que o número da conta é definido como 1 e o saldo inicial
em 100. Na linha 7 o saldo atual da conta é apresentado na tela, o qual
está em 100. Note que o parâmetro ContaBancaria foi declarado nos
métodos como do tipo por referência, por isso o uso do sinal de & ao
lado da variável (passagem do endereço da variável na memória).
www.esab.edu.br 132
conta.h
usada em interface
contabancaria.c
codificação usada em
main.c
uso (abstração)
Figura 40 – Uso e implementação da interface.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 133
15 Listas encadeadas
Objetivo
Apresentar o conceito de listas encadeadas e seus tipos.
[...] vetor é uma estrutura de dados randômica, já que possibilita o acesso a qualquer
elemento de forma aleatória, mas não é uma estrutura de dados flexível, pois
precisamos dimensioná-lo com um número máximo de elementos.
www.esab.edu.br 134
Essas estruturas dinâmicas são: listas encadeadas, pilhas, filas e árvores.
Nesta unidade começaremos a estudar as listas encadeadas.
Estudo complementar
Para saber mais sobre as listas encadeadas acesse
o link e veja um vídeo explicativo sobre o assunto.
15.1 Definição
A lista encadeada é uma estrutura de dados dinâmica que aloca um
espaço de memória para cada elemento inserido ou libera um espaço
de memória para cada elemento excluído, possibilitando, dessa forma,
o gerenciamento da memória alocada e resolvendo o problema de
desperdício de memória que ocorre com a utilização de um vetor.
Cada elemento da lista é chamado de nó ou nodo, sendo que cada nó é
formado, basicamente, por dois campos: o valor armazenado e o ponteiro
para o próximo elemento da lista.
www.esab.edu.br 135
primeiro elemento/nó se chega ao segundo/nó, do segundo se chega ao
terceiro e sucessivamente até se chegar ao último elemento/nó. Note que,
diferentemente de um vetor, no qual você informa a posição do vetor
(índice) para acessar o elemento desejado, sem necessidade de percorrer
toda a estrutura, para você acessar o elemento na lista encadeada é
preciso percorrer n-1 elementos.
Descritor NULL
Último
end K
www.esab.edu.br 136
Observe que a lista agrupa as informações de forma que estejam
relacionadas entre si com a informação do primeiro e último elemento
da lista na forma de um descritor que armazena o endereço desses
elementos. As operações mais frequentes em listas são: busca, inclusão e
remoção de um determinado elemento. Outras operações possíveis são:
a alteração, a combinação de duas ou mais listas em uma única lista e a
ordenação de elementos.
www.esab.edu.br 137
p p p p
a - anterior
p - próximo
Lista simplesmente encadeada
a p a p a p a p
p p p p
www.esab.edu.br 138
Existe um tipo de lista em que a navegação se dá de forma circular,
estas são chamadas de listas circulares e são estruturas de modo que o
conceito de primeiro e último elemento não existem, uma vez que o
último elemento da lista tem como próximo elemento o primeiro da
lista, formando um ciclo. Dessa forma, para criação e utilização da lista
circular, é necessário apenas conhecer o primeiro elemento da lista, já que
o conceito de último não existe. A Figura 46 apresenta uma lista circular:
p p p p
a p a p a p a p
www.esab.edu.br 139
espaço, o primeiro vídeo é removido para que haja espaço para a gravação
do vídeo mais atual, de forma que as gravações mais atuais sejam
mantidas e as mais antigas removidas.
Por fim, as árvores são listas que armazenam as informações por níveis, o
que muito se assemelha a uma árvore genealógica, em que os elementos são
organizados por níveis e hierarquicamente. Dessa forma, qualquer inserção
ou remoção de elementos deve manter a organização dos dados em níveis,
fazendo com que subam, desçam ou permaneçam no mesmo nível.
www.esab.edu.br 140
A Figura 47 apresenta essas listas específicas:
Inserção
Primeiro Último
Remoção FILA
Topo
Remoção Inserção
PILHA
ÁRVORE
Figura 47 – Fila, pilha e árvore.
Fonte: Elaborada pelo autor (2013).
Nesta unidade você pôde estudar o conceito da lista encadeada, que pode
ser: simplesmente ou duplamente encadeada, dependendo do número de
ponteiros utilizados na ligação entre os elementos, ou circular, quando o
último elemento aponta para o primeiro elemento da lista.
www.esab.edu.br 141
Listas encadeadas simples –
16 conceituação
Objetivo
Apresentar o conceito de listas encadeadas simples.
16.1 Definição
A lista simplesmente encadeada se caracteriza pelo uso de um ponteiro
para cada elemento inserido na lista, o que possibilita que um elemento
saiba quem é seu elemento subsequente, caso ele exista. Mais que isso,
por se tratar de uma estrutura de dados dinâmica, o espaço de memória
de cada elemento é alocado em tempo de execução para que seja utilizado
somente o espaço de memória que realmente se faz necessário, evitando o
desperdício de memória. Cada elemento é alocado no espaço de memória
livre e disponível, e quem gerencia esses espaços é o sistema operacional.
Esse processo não garante que os elementos da lista sejam alocados de forma
contígua, um elemento ao lado do outro (ASCENCIO; ARAÚJO, 2010).
www.esab.edu.br 142
Para a criação da lista simplesmente encadeada, é preciso o uso de um
ponteiro que indique o seu primeiro elemento. Então o percurso da
lista é feito a partir desse ponteiro, seguindo, consecutivamente, pelos
endereços existentes no campo próximo de cada elemento.
e1 e2 e3 e4
10 e2 15 e3 04 e4 01 NULL
e1
Primeiro
Figura 48 – Lista simplesmente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 143
e1 e2 e3 e4
10 e2 15 e3 04 e4 01 NULL
e1 e4
Primeiro Último
Figura 49 – Lista simplesmente encadeada com dois descritores.
Fonte: Elaborada pelo autor (2013).
Com esses dois descritores (o primeiro, que vale e1, e o último, e4), é
possível gerenciar e realizar as operações de inserção, consulta e remoção
de elementos na lista.
www.esab.edu.br 144
A Figura 50 apresenta a lista criada, observe:
primeiro último
NULL NULL
Figura 50 – Lista simplesmente encadeada com dois descritores criada.
Fonte: Elaborada pelo autor (2013).
Observe que a lista foi criada, mas como nenhum elemento foi inserido
até o momento, os descritores foram inicializados com NULL.
www.esab.edu.br 145
Para melhor visualização das regras, observe a Figura 51 que apresenta o
esquema da primeira inserção no final da lista:
primeiro primeiro
e100 e100 e25 e100 e100 e25
10 55 10 e25 55
último último
e100 NULL NULL e100 NULL
Lista não está vazia, possui o elemento 10, O último elemento, referenciado pelo descritor ÚLTIMO,
e o novo valor 55 será inserido no final da lista. que é e100, recebe, no campo próximo, o endereço do
novo elemento e25, que será inserido. Portanto, o último
elemento aponta para quem será inserido na lista (b).
primeiro
e100 e100 e25
10 e25 55
último
e25 NULL
Como se trata de uma inserção no FINAL da lista, o
novo elemento passa a ser o último, assim, o descritor
ÚLTIMO deve apontar para o endereço do elemento
que foi inserido (c).
Figura 52 – Inserção do elemento no final da lista simplesmente encadeada que não está vazia.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 146
Observe que após a inserção do primeiro elemento, os descritores
(primeiro e último) possuem o mesmo valor (e100) e à medida que
novos elementos são inseridos no final da lista, apenas o descritor último
é atualizado com o endereço do elemento inserido.
www.esab.edu.br 147
e99
primeiro <. NULL
último <. NULL
23 NULL
Elemento a
ser inserido.
último e99
e08 e99
98 e99 23 NULL
Lista não está vazia, o novo
primeiro e08 elemento aponta como
próximo para o primeiro
elemento da lista (c).
Figura 54 – Inserção do elemento no início da lista simplesmente encadeada que não está vazia.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 148
Observe que na inserção no final da lista, esta aumentará para a direita, já
na inserção no início da lista, ela aumentará para esquerda. Na inserção
no final, sempre o elemento que será inserido será referenciado como
último e na inserção no início sempre o elemento novo a ser inserido será
referenciado como primeiro. Se a lista está vazia (primeiro vale NULL ou
último vale NULL), o descritor primeiro e último apontarão para o novo
elemento a ser inserido.
www.esab.edu.br 149
A Figura 55 apresenta a sequência de remoção do final quando a lista não
está vazia, portanto, as opções (b), (c), (d), (e) e (f ) serão executadas:
último último
e99 e99
(penúltimo) (penúltimo)
e08 e99 e08 e99
98 e99 23 98 e99 23
Lista não está vazia, então
e08 removerá o último elemento e08
e99. Como primeiro ou último Elemento removido, porém, o
primeiro não valem NULL, operação primeiro elemento anterior AINDA aponta
pode ser realizada. como próximo para o elemento
removido e o descritor ÚLTIMO
penúltimo - elemento que aponta aponta para o elemento que
como próximo para o último. foi removido.
último
último
e99
(penúltimo) e08
e08 e99 (penúltimo)
e08
98 NULL 23
98 NULL O descritor último será
Elemento já foi removido. atualizado para o endereço
e08 do penúltimo.
Penúltimo elemento terá seu e08
primeiro
campo próximo atualizado primeiro
para NULL. Caso contrário,
estará apontando para um
elemento que não existe.
Figura 55 – Remoção do elemento no final da lista simplesmente encadeada que não está vazia.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 150
A Figura 56 apresenta a sequência de remoção do final quando a lista tem
apenas um elemento, portanto, as operações (b) e (c) serão executadas:
último último
e08 e08 último
e08 e08 NULL
98 NULL 98 NULL
NULL
e08 e08
primeiro
primeiro primeiro
Primeiro e último apontam O descritor primeiro e último Primeiro e último apontam
para o mesmo elemento, que continuam apontando para o para NULL, já que a lista, após
será removido. Portanto, a elemento que já foi removido. a remoção, ficou vazia.
lista só possui esse elemento,
não possui anterior e, ao
removê-lo a lista ficará vazia.
www.esab.edu.br 151
As regras para remoção do início são:
www.esab.edu.br 152
A Figura 57 apresenta a sequência de remoção do início quando a lista
não está vazia, portanto, as opções (b), (c), (d), e (e) serão executadas:
(segundo)
primeiro e44 e44 e28
20 e28 05 NULL
último e28
(segundo)
primeiro e44 e44 e28 primeiro e28 e44 e28
20 e28 05 NULL 20 e28 05 NULL
último e28 último e28
www.esab.edu.br 153
Observe que é muito importante que, na remoção dos elementos, os
descritores primeiro e último sejam atualizados para que referenciem
corretamente os dados da lista.
www.esab.edu.br 154
Listas encadeadas simples –
17 codificação
Objetivo
Apresentar a codificação e a manipulação de listas encadeadas
simples.
www.esab.edu.br 155
16 //Operações da Lista
17 void criarLista(Lista*);
18 int listaVazia(Lista*);
19 void inserirFinal(Lista*,int);
20 void inserirInicio(Lista*,int);
21 void removerInicio(Lista*);
22 void removerFinal(Lista*);
23 void listar(Lista*);
24 No* buscar(Lista*,int);
25 int contar(Lista*,No*,int);
26 No* primeiroElemento(Lista*)
www.esab.edu.br 156
Operação Finalidade Parâmetros
Inicializar a lista Recebe como parâmetro o
void criarLista(Lista*); especificando os descritores endereço da lista que será
primeiro e último para NULL. manipulada.
Retornar 1 se a lista estiver
vazia ou 0 caso possua algum Recebe como parâmetro o
int listaVazia(Lista*); elemento. Para tanto, verifica endereço da lista que será
se os descritores primeiro ou manipulada.
último vale NULL.
Recebe como parâmetro o
Inserir um número inteiro no
endereço da lista que será
void inserirFinal(Lista*,int); final da lista simplesmente
manipulada e o valor inteiro
encadeada.
que será inserido.
Recebe como parâmetro o
Insere um número inteiro no
endereço da lista que será
void inserirInicio(Lista*,int); início da lista simplesmente
manipulada e o valor inteiro
encadeada.
que será inserido.
Remover o elemento da
Recebe como parâmetro o
primeira posição da lista
void removerInicio(Lista*); endereço da lista que será
simplesmente encadeada se
manipulada.
esta não estiver vazia.
Remover o elemento da
Recebe como parâmetro o
última posição da lista
void removerFinal(Lista*); endereço da lista que será
simplesmente encadeada, se
manipulada.
esta não estiver vazia.
Listar todos os elementos Recebe como parâmetro o
void listar(Lista*); da lista simplesmente endereço da lista que será
encadeada. manipulada.
Retornar o nó que possui o Recebe como parâmetro o
valor pesquisado e caso o endereço da lista que será
No* buscar(Lista*,int);
valor não seja localizado será manipulada e o valor inteiro
retornado um NULL. que será pesquisado.
Recebe como parâmetro
Retornar a quantidade
o endereço da lista e a
de elementos da lista,
int contar(Lista*,No*,int); contagem de elementos para
realizando um processo
cada execução do processo
recursivo.
recursivo.
Recebe como parâmetro o
Retornar o nó da primeira
No* primeiroElemento(Lista*) endereço da lista que será
posição da lista.
manipulada.
www.esab.edu.br 157
Observe que cada operação contém apenas o cabeçalho da função. A
codificação de cada operação será implementada em outro arquivo-fonte
com extensão”.c” que utilizará o Tipo Abstrato de Dados e deverá fazer
uma referência ao arquivo lista.h por meio da cláusula #include.
www.esab.edu.br 158
Algoritmo 48 apresenta a codificação da operação para verificar se a
lista está vazia:
www.esab.edu.br 159
Você estudou na unidade 16 as regras de negócio para a operação de
inserção no início, portanto, vamos ressaltar as principais características
dessa operação. Sua principal função é inserir um novo elemento no
início da lista, por isso a linha 12 sempre será executada, atualizando o
descritor primeiro com o endereço do novo elemento. As linhas 3 a 6
criam um novo elemento nó, preenchendo-o com o valor passado como
parâmetro e definindo o campo próximo para NULL. Na linha 4 é
alocado um espaço de memória para o novo elemento, por isso o uso da
função malloc().
www.esab.edu.br 160
A função removerFinal() verifica se a lista não está vazia, para, então,
avaliar se o elemento que será removido é o único elemento da lista:
por isso, a condição (lista->primeiro == lista->ultimo) na linha 4. Se
a condição for verdadeira, atualiza os descritores inicio e ultimo para
NULL, pois após a remoção a lista estará vazia. Caso contrário, nas linhas
10 a 13, é realizado um laço que começa no primeiro elemento da lista
até encontrar o penúltimo elemento da lista, aquele elemento cujo campo
próximo vale o endereço do último elemento da lista, ou seja, o elemento
que aponta como próximo para o último elemento da lista: por isso,
na linha 11 a condição penultimo->proximo!= lista->ultimo. Ao final
do laço, na linha 14, a variável penultimo estará valendo o endereço do
último elemento da lista, e na instrução penultimo->proximo = NULL
esse elemento, que antes apontava para o último elemento, passará a
apontar para NULL. Por fim, na linha 17 o espaço de memória alocado
para o último elemento da lista é liberado, por isso podemos entender
que o último elemento é removido da lista, sendo que na linha 3 a
variável item foi inicializada com o endereço do último elemento da lista.
www.esab.edu.br 161
O Algoritmo 51 apresenta a função para buscar o elemento da lista:
www.esab.edu.br 162
Como a lista possui um conjunto de elementos ligados pelo ponteiro
próximo de cada elemento, inclusive o primeiro elemento da lista, é
possível percorrer todos os elementos da estrutura de dados de forma
recursiva fazendo com que o endereço de memória comece no primeiro
elemento da lista e vá até o último elemento da lista, aquele cujo
ponteiro próximo vale NULL.
e2 e88
primeiro último
Figura 59 – Lista simplesmente encadeada com três elementos.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 163
Note que para localizar o valor 100, a cada chamada recursiva a lista
recebe o endereço do elemento a ser pesquisado, começando em e2 que
vale 10. Como valor do endereço (10) é diferente de 100, uma nova
chamada recursiva será realizada com o endereço do próximo de e2 que é
e4 e vale 15. Como 15 não é o valor pesquisado (100), haverá uma nova
chamada da função com o próximo de e4 que é e88 e possui o valor 20,
que não é igual a 100 e, portanto, uma nova chamada da função será
realizada com o próximo de e88 que é NULL e, portanto, a pesquisa será
encerrada, uma vez que o endereço valendo NULL indica que toda a lista
já foi visitada.
www.esab.edu.br 164
Portanto, a lista contará os elementos da lista, percorrendo recursivamente
os elementos desde o primeiro nó até que a lista se encerre.
Tarefa dissertativa
Caro estudante, convidamos você a acessar o
Ambiente Virtual de Aprendizagem e realizar a
tarefa dissertativa.
www.esab.edu.br 165
Lista encadeada simples –
18 exercícios
Objetivo
Apresentar exercícios comentados sobre listas encadeadas simples.
Celes, Cerqueira e Rangel Netto (2004, p 143), destacam que “[...] para
a manutenção da lista ordenada temos que encontrar a posição correta
para inserir o novo elemento”.
www.esab.edu.br 166
Primeiro Primeiro
10 10
Último Último
Lista vazia Insere no início Lista vazia
ou no final
Figura 60 – Inserção com a lista vazia.
Fonte: Elaborada pelo autor (2013).
Observe que como a lista possui um único elemento, ela está ordenada.
Nas demais inserções, já que a lista não está vazia, caso o valor a ser
inserido seja menor ou igual ao valor do primeiro elemento, a inserção
será no início. Caso contrário, será verificado se o elemento a ser inserido
é maior ou igual ao valor do último elemento; sendo verdadeira a
condição, o valor é inserido no final da lista.
Último Último
Inserção do Primeiro
1 10 15
valor 15
Último
Figura 61 – Inserção na lista já preenchida.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 167
Observe que a cada inserção, a lista se manteve ordenada: o valor 1 foi
inserido no início e o valor 10 foi inserido no final da lista.
Caso o valor a ser inserido não seja menor do que o primeiro elemento e
também não seja maior do que o último será necessária a inserção entre
dois elementos da lista. Para a inserção do valor 7, será preciso pesquisar
qual sua posição de inserção dentro da lista, pois o valor 7 não é menor
que o primeiro (1) e não é maior que o último (10).
1 10 15 1 10 15
7
Ponteiro Ponteiro
Primeiro Último Primeiro Insere o valor 7 Último
entre os valores 1 e 10
Ponteiro começa do primeiro elemento.
Valor do próximo elemento (10) é
maior que o valor a ser inserido (7)?
Sim, então encerra a procura.
Vamos verificar como criar essa operação de inserção que mantém a lista
ordenada, realizando pequenas alterações nos arquivos lista.h e lista.c
apresentados na unidade anterior que criam o Tipo Abstrato de Dados.
www.esab.edu.br 168
Algoritmo 53 – Estrutura da lista: lista.h
01 //Estrutura de cada elemento da lista
02 struct tipo_no{
03 int dado;
04 struct tipo_no *proximo;
05 };
06 //tipo No
07 typedef struct tipo_no No;
08 //Estrutura da Lista
09 struct tipo_lista{
10 No *primeiro;
11 No *ultimo;
12 };
13 //Tipo Lista
14 typedef struct tipo_lista Lista;
15 //Operações da Lista
16 void criarLista(Lista*);
17 int listaVazia(Lista*);
18 void inserirFinal(Lista*,int);
19 void inserirInicio(Lista*,int);
20 void removerInicio(Lista*);
21 void removerFinal(Lista*);
22 void listar(Lista*);
23 No* buscar(Lista*,int);
24 int contar(Lista*,No*,int);
25 No* primeiroElemento(Lista*);
26 void inserirOrdenado(Lista*, int);
27 No* ultimoElemento(Lista*);
www.esab.edu.br 169
Primeiro vamos criar a regra da operação ultimoElemento(Lista*), como
pode ser visto no Algoritmo 54:
Com essa nova função já temos como construir a função que realizará a
inserção ordenada na lista, conforme o Algoritmo 55:
www.esab.edu.br 170
Na linha 2 é declarada e inicializada a variável ultimo com o nó que é
retornado pela função ultimoElemento(). Essa variável é utilizada como
condição do comando if da linha 4 para verificar se o elemento que será
inserido não é maior que o último elemento. Na linha 3 é declarada e
inicializada a variável primeiro com o nó que é retornado pela função
primeiroElemento(), utilizada na linha 8 para verificar se o valor a ser
inserido não é menor que o valor do primeiro elemento. Na linha 5, caso
a lista esteja vazia ou o valor inserido seja maior que o último elemento,
a função inserirFinal() é chamada, recebendo como parâmetro a lista
e o valor que será inserido. Na linha 9, se o valor inserido seja menor
que o último elemento, a função inserirInicio() é chamada, recebendo
como parâmetro a lista e o valor que será inserido. Nessas funções o valor
passado como parâmetro é alocado na memória e inserido no início ou
final da lista.
Caso a inserção no início ou final não for realizada, a função fará uma
pesquisa desde o primeiro elemento até localizar um elemento que
possua como próximo um valor maior do que o novo valor a ser inserido.
Por isso na linha 11 a instrução No* ponteiro = primeiro e na linha
12 a condição while((ponteiro->proximo)->dado<valor). A instrução
(ponteiro->proximo)->dado está referenciando o próximo endereço a
partir do ponteiro e então acessando o campo dado daquele endereço, ou
seja, o valor do próximo elemento a partir do elemento atual.
www.esab.edu.br 171
1 2 3 4
Elemento de Novo
Referência (ER) Objetivo: inserir o Novo Novo
novo elemento
Elemento apontado entre o elemento Novo aponta Elemento de referência
como próximo do de referência (ER) como próximo aponta como próximo
elemento de referência. e (posterior). para o posterior. para o novo elemento.
Figura 63 – Regras de inserção entre dois elementos na lista.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 172
Nesta unidade você estudou como implementar uma lista simplesmente
encadeada em C com inserção ordenada, utilizando as funções de
inserção no início, fim e entre elementos.
Atividade
Chegou a hora de você testar seus conhecimentos
em relação às unidades 10 a 18. Para isso, dirija-se ao
Ambiente Virtual de Aprendizagem (AVA) e responda
às questões. Além de revisar o conteúdo, você estará
se preparando para a prova. Bom trabalho!
www.esab.edu.br 173
Resumo
www.esab.edu.br 174
19 Listas circulares – conceituação
Objetivo
Apresentar o conceito de listas circulares.
19.1 Definição
A lista circular é utilizada para representação de processos cíclicos, como
o armazenamento, em um computador, de informações em um arquivo
de log, no qual são registrados o nome do usuário do computador, a data
e hora do acesso ao computador e as operações que foram realizadas.
www.esab.edu.br 175
usuário, será gravada nesse arquivo, e, no futuro, a falta de espaço no
disco rígido para atualização dos dados pode representar um grande
problema de integridade e confiabilidade das informações.
Uma estratégia que pode ser adotada para resolver esse problema é fazer
com que os registros mais antigos sejam substituídos pelos registros
mais atuais. Assim, o arquivo de log armazenará, sempre, no máximo
três registros e caso haja a necessidade de uma quantidade maior, os
elementos mais antigos serão substituídos. A Figura 63 apresenta a lista
circular com os registros de log:
Tamanho máximo da lista
início 1 2 3
01/10/2013 01/10/2013 01/10/2013
08:00:15 08:00:55 08:01:23
Funcionalidade Funcionalidade Funcionalidade
Usuário Usuário Usuário
www.esab.edu.br 176
início 1 2 3
02/10/2013 03/10/2013 05/10/2013
09:00:44 10:00:55 08:01:23
Funcionalidade Funcionalidade Funcionalidade
Usuário Usuário Usuário
1 2
01/10/2013 01/10/2013
08:00:15 08:00:55
Funcionalidade Funcionalidade
Usuário Usuário
Note que dois novos registros foram inseridos na lista, nas posições 1
e 2, em substituição aos antigos elementos dessas posições. A próxima
inserção acontecerá em substituição ao elemento da posição 3, e assim
sucessivamente, começando novamente da posição 1, depois da posição 2
etc., constituindo um processo cíclico.
www.esab.edu.br 177
A criação da lista circular é um pouco mais simples se comparada com a
criação da lista encadeada, já que esse tipo de lista utiliza dois descritores
de início e último elemento, e a lista circular utiliza um único descritor
para o elemento de início, que também começará valendo NULL,
portanto, a lista será considerada vazia.
início
www.esab.edu.br 178
• inserção entre dois elementos: a inserção entre dois elementos
na lista circular possui as mesmas regras de funcionamento da lista
simplesmente encadeada, sem a necessidade de ajustes ou alterações;
• remoção do início: caso a remoção do início da lista resulte em
um único elemento (a), este deverá apontar como próximo para ele
mesmo, caso contrário, o último elemento da lista apontará como
próximo para o novo primeiro elemento da lista (b). A Figura 66
representa essas duas operações, observe:
início início
(a)
início início
(b)
www.esab.edu.br 179
início início
(a)
início início
(b)
www.esab.edu.br 180
Note que essa codificação define a estrutura do elemento que irá compor
a lista e possui um campo que se autorreferencia, já que o campo
próximo aponta para um elemento com a mesma estrutura do tipo_no.
Nesta unidade, você pôde estudar sobre a estrutura de dados lista circular
que por definição não possui o conceito de último elemento, uma vez
que o último elemento sempre aponta como próximo para o primeiro
elemento da lista, formando um processo cíclico. Você pôde também
conhecer e compreender as regras de inserção, remoção e consulta de
dados nesse tipo de lista.
Até Lá!
Fórum
Caro estudante, dirija-se ao Ambiente Virtual de
Aprendizagem da instituição e participe do nosso
Fórum de discussão. Lá você poderá interagir com
seus colegas e com seu tutor de forma a ampliar,
por meio da interação, a construção do seu
conhecimento. Vamos lá?
www.esab.edu.br 181
20 Listas circulares – codificação
Objetivo
Apresentar a codificação e a manipulação de listas circulares.
www.esab.edu.br 182
Note que as linhas 1 a 4 criam tipo estruturado tipo_no, composto por
dois campos, dado e proximo, e que representará cada elemento que será
manipulado na lista circular. Na linha 5 é definido, por meio da cláusula
typedef, o tipo de dado No. As linhas 6 a 8 definem a estrutura da lista
circular formada por um descritor apenas, chamado primeiro, que é do
tipo No. Por fim, na linha 9 é declarado o tipo ListaCircular.
www.esab.edu.br 183
Alocar espaço de memória para o novo elemento;
Gravar nesse espaço de memória o dado;
Se lista está vazia então
Descritor primeiro aponta para o novo elemento;
Novo elemento aponta como próximo para ele mesmo;
fim_se
senão
Novo elemento aponta como próximo para o primeiro elemento da lista;
Percorrer a lista até localizar o elemento (último) que aponta com o próximo para o
primeiro elemento da lista;
O último elemento, que apontava para o primeiro da lista, passa a apontar para o
novo
elemento inserido;
fim_senão;
Descritor primeiro aponta para o novo elemento;
www.esab.edu.br 184
Algoritmo 59 – Inserção no início da lista circular
01 void inserirInicio(ListaCircular* lista, int
valor){
02 No *novo;
03 novo = (No*)malloc(sizeof(No));
04 novo->dado = valor;
05 if(lista->primeiro == NULL){
06 novo->proximo = novo;
07 }else{
08 novo->proximo = lista->primeiro;
09 No* aux = lista->primeiro;
10 while(aux->proximo != lista->primeiro){
11 aux = aux -> proximo;
12 }
13 aux->proximo = novo;
14 }
15 lista->primeiro = novo;
16 }
www.esab.edu.br 185
Passo
(aux)
1
(aux)
2
(aux)
3
Por fim, na linha 15, o novo elemento inserido passa a ser reconhecido
como o primeiro da lista.
www.esab.edu.br 186
último elemento ou até que o valor seja localizado, como é apresentado
no Algoritmo 60:
www.esab.edu.br 187
Algoritmo 61 – Removendo valor no final da lista circular
01 void removerFinal(ListaCircular* lista){
02 if(lista->primeiro != NULL){
03 if((lista->primeiro)->proximo == lista-
>primeiro){
04 No* aux = lista->primeiro;
05 free(aux);
06 lista->primeiro = NULL;
07 }else{
08 No* aux = lista->primeiro;
09 No* anterior = NULL;
10 while(aux->proximo != lista->primeiro){
11 anterior = aux;
12 aux=aux->proximo;
13 }
14 free(aux);
15 anterior->proximo = lista->primeiro;
16 }
17 }
18 }
www.esab.edu.br 188
Passo
(aux)
(anterior <- NULL) 1
(anterior) (aux)
2
(anterior) (aux)
3
www.esab.edu.br 189
21 Listas circulares – exercícios
Objetivo
Apresentar exercícios comentados sobre listas circulares.
www.esab.edu.br 190
Algoritmo 63 – Estrutura da lista circular
01 struct tipo_lista{
02 No *primeiro;
03 int maior,
04 int menor;
05 };
www.esab.edu.br 191
Algoritmo 65 – Inserção no final da lista circular
01 void inserirFinal(ListaCircular* lista, int
valor){
02 No *novo;
03 novo = (No*)malloc(sizeof(No));
04 novo->dado = valor;
05 if(lista->primeiro == NULL){
06 novo->proximo = novo;
07 lista->maior = valor;
08 lista->menor = valor;
09 lista->primeiro = novo;
10 }else{
11 if(valor > lista->maior) lista->maior =
valor;
12 else
13 if(valor < lista->menor) lista->menor =
valor;
14 No* aux = lista->primeiro;
15 while(aux->proximo != lista->primeiro){
16 aux = aux->proximo;
17 }
18 aux->proximo = novo;
19 novo->proximo = lista->primeiro;
20 }
21 }
www.esab.edu.br 192
As instruções das linhas 14 a 17 fazem com que todos os elementos da
lista sejam visitados até que se chegue ao último elemento (aquele que
aponta para o primeiro elemento, aux->proximo!= lista->primeiro).
Ao sair do comando de laço, a variável aux terá o endereço do último
elemento, que na instrução da linha 18 passa a apontar para o novo
elemento e que, por sua vez, na linha 19, passa a apontar para o primeiro
elemento da lista. A Figura 70 representa esse processo de inserção no
final quando a lista não está vazia:
primeiro primeiro
(novo)
(a) (b)
primeiro primeiro
(aux)
(novo) (novo)
(c) (d)
Figura 70 – Inserção no final da lista circular.
Fonte: Elaborada pelo autor (2013).
[...] a função para retirar um elemento da lista é mais complexa. Se descobrimos que
o elemento a ser retirado é o primeiro da lista, devemos fazer o novo valor da lista
passar a ser o ponteiro para o segundo elemento da lista, e então podemos liberar o
espaço para o elemento que desejamos retirar.
www.esab.edu.br 193
O Algoritmo 66 apresenta essa regra de remoção:
www.esab.edu.br 194
caso a condição seja verdadeira, o descritor maior é atualizado com
o valor do segundo elemento da lista. Na linha 14 é verificado se o
elemento que está sendo removido do início da lista é o menor elemento,
caso a condição seja verdadeira, o descritor menor é atualizado com o
valor do segundo elemento da lista. Nas linhas 15 a 22, é realizada uma
pesquisa em todos os elementos da lista, verificando se o valor é o maior
(linha 16) ou o menor (linha 18). Caso o elemento visitado na lista seja
o último (linha 20), o endereço desse elemento é armazenado na variável
ultimo. A instrução da linha 23 especifica que o início da lista passa
a ter o endereço do segundo elemento da lista (o próximo do aux) e a
instrução da linha 24 especifica que o último elemento passa a apontar
para o primeiro da lista. Somente na instrução da linha 25 será removido
o elemento que era o primeiro da lista.
(a) (b)
(c) (d)
Figura 71 – Remoção do primeiro elemento da lista.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 196
Listas duplamente encadeadas –
22 conceituação
Objetivo
Apresentar o conceito de listas duplamente encadeadas.
22.1 Definição
Nas unidades anteriores, pudemos estudar e desenvolver a lista
simplesmente encadeada, que se caracteriza por cada elemento armazenar
um ponteiro para o próximo elemento da lista. Celes, Cerqueira e
Rangel Netto (2004, p. 149) destacam que um elemento com um único
ponteiro “[...] não permite percorrer eficientemente os elementos em
ordem inversa, isto é, do final para o início da lista”. Isso ocorre em
razão da característica da lista simplesmente encadeada, que caminha no
sentido do início para o próximo elemento da lista, até que se chegue ao
último elemento.
www.esab.edu.br 197
lista. Esse processo, que percorre todos os elementos até que se chegue ao
penúltimo, requer um considerável tempo de processamento, que tende a
aumentar com a inclusão de novos elementos.
primeiro último
NULL
NULL
a p a p a p
www.esab.edu.br 198
sentido, primeiro-último
primeiro último
NULL
NULL
a p a p a p
sentido último-primeiro
Figura 73 – Percorrendo a lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 199
Inserção no início da lista: a inserção do início da lista tem como
finalidade garantir que o elemento inserido seja definido como o
primeiro da lista. Na inserção do novo elemento na lista, o que muda
é que o primeiro elemento deverá apontar como anterior para o novo
elemento inserido (b), e o novo elemento deverá apontar como próximo
para o primeiro elemento (a), por fim, o novo elemento deve ser definido
como primeiro da lista (c). A Figura 74 apresenta essas regras:
(a)
(b) (c)
Figura 74 – Inserção no início da lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 200
primeiro último último primeiro (novo)
(a)
(b) (c)
Figura 75 – Inserção no final da lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
(a)
(b) (c)
Figura 76 – Remoção do início da lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 201
Remoção do final da lista: a remoção do final da lista tem como
finalidade garantir que o penúltimo elemento, caso exista, seja definido
como o novo final da lista. Na remoção do último elemento, o novo
último elemento será o elemento anterior apontado pelo último
elemento (a), depois o último elemento será removido (a), e, em seguida,
o novo último elemento deverá mudar o apontador próximo para
NULL (b). Por fim, o penúltimo elemento da lista será referenciado pelo
descritor último (c). A Figura 77 apresenta essas regras:
(a)
(b) (c)
primeiro último
NULL
NULL
a p a p a p
www.esab.edu.br 202
Inserção de elemento entre outros dois elementos: a inserção entre
dois elementos na lista duplamente encadeada é mais simples e rápida. A
Figura 79 apresenta essas regras:
anterior ao REF anterior ao REF
primeiro último primeiro último primeiro último
1 3 1 3 1 3
(REF) (REF) (REF)
(novo) 2 (novo) 2 (novo) 2
(a) (b) (c)
anterior ao REF anterior ao REF
primeiro último primeiro último
1 3 1 3
(REF) (REF)
(novo) 2 (novo) 2
(d) (e)
Figura 79 – Inserção entre dois elementos na lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 203
primeiro último
(REF)
1 2 3
(a)
primeiro último
(REF)
1 2 3
(b)
primeiro último
(REF)
1 2 3
(c)
primeiro último
(REF)
1 2 3
(d)
primeiro último
1 3
(e)
Figura 80 – Remoção do elemento entre dois elementos na lista duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 204
Nesta unidade, você pôde estudar a lista duplamente encadeada, que
recebe esse nome porque cada elemento da lista utiliza dois ponteiros
para interligar os elementos, chamados anterior e posterior. Esses dois
ponteiros possibilitam que a lista possa ser percorrida em dois sentidos,
do primeiro para o último e do último para o primeiro. Além disso, as
operações de inserção e remoção, principalmente no final da lista e entre
elementos, tornam-se mais simples com o uso desses dois ponteiros,
evitando que pesquisas sejam realizadas para localizar os elementos
anterior ou posterior de uma referência na lista.
www.esab.edu.br 205
Listas duplamente encadeadas –
23 codificação
Objetivo
Apresentar a codificação e manipulação de listas duplamente
encadeadas.
www.esab.edu.br 206
• para inicializar a lista, os ponteiros anterior e próximo da lista serão
inicializados com NULL;
• para a inserção na lista, serão implementadas as funções de inserção
no início e no final da lista. As duas funções receberão como
parâmetro a lista a ser manipulada e o valor a ser inserido;
• a remoção da lista será realizada no final, assim, a função de remoção
receberá como parâmetro a lista que será manipulada;
• a rotina de busca de valores na lista será implementada de forma
que receba como parâmetro a lista a ser manipulada e o valor a
ser pesquisado. A função retornará o endereço do elemento, se
localizado, ou valerá NULL, caso o valor não exista na lista.
Veremos a seguir como implementar essas regras e analisar as observações
sobre cada uma das rotinas. Ao final da unidade, você poderá baixar na
internet todo o código desenvolvido.
www.esab.edu.br 207
Algoritmo 67 – Declaração do elemento da lista duplamente encadeada
01 struct tipo_no {
02 int dado;
03 struct tipo_no * proximo;
04 struct tipo_no * anterior; novo campo
05 };
06
07 typedef struct tipo_no No;
www.esab.edu.br 208
Observe que a função de inicialização recebe como parâmetro um
argumento do tipo ListaDupla.
www.esab.edu.br 209
A inserção no início possui o mesmo processo de alocação de memória
e inicialização dos ponteiros anterior e próximo do novo elemento,
porém, a manipulação dos descritores primeiro e último possuem regras
diferentes. O Algoritmo 71 apresenta essas regras:
www.esab.edu.br 210
Algoritmo 72 – Remoção do final da lista duplamente encadeada
47 void inserirFinal (ListaDupla* lista){
48 if(lista->primeiro!= NULL){
49 if(lista->primeiro->proximo == NULL){
50 No* aux = lista->ultimo;
51 lista->primeiro = NULL;
52 lista->ultimo = NULL;
53 free(aux);
54 }else{
55 No* aux = lista->ultimo;
56 lista->ultimo->anterior->proximo=NULL;
57 lista->ultimo = lista->ultimo->anterior;
58 free(aux);
59 }
60 }
61 }
www.esab.edu.br 211
Algoritmo 73 – Consulta do valor na lista duplamente encadeada
77 No* consultar(ListaDupla* lista, int valor){
78 No* aux = lista->primeiro;
79 if (aux != NULL){
80 while((aux != NULL) && (aux->dado !=valor )){
81 aux = aux -> proximo;
82 }
83 return aux:
84 }
85 }
www.esab.edu.br 212
Listas circulares duplamente
24 encadeadas – conceituação
Objetivo
Apresentar o conceito de listas circulares duplamente encadeadas.
24.1 Definição
A proposta da lista circular para representação de processos cíclicos
é mantida nas listas circulares duplamente encadeadas, pois essa é a
característica mais forte desse tipo de lista. Lembre-se de que a lista
circular não possui o conceito de último elemento, independentemente
do tipo (simplesmente encadeada ou duplamente encadeada), pois o que
seria o último elemento aponta como próximo para o primeiro elemento
da lista. A Figura 81 apresenta a lista circular duplamente encadeada:
primeiro
p p p
a a a
Figura 81 – Lista circular duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 213
Observe que cada lista possui dois apontadores, um direcionado para
o próximo elemento (p) e outro para o elemento anterior (a). Puga
(2009, p. 200) destaca que a lista duplamente encadeada determina que
“[...] cada elemento possui um campo que aponta para seu predecessor
(anterior) e outro para seu sucessor (próximo)”. Portanto, a lista circular
duplamente encadeada terá o primeiro elemento da lista apontando
como anterior para o último elemento inserido na lista, e o último
elemento inserido na lista apontando como próximo para o primeiro
elemento. Como se trata de uma lista circular, o conceito de último
elemento da lista não existe, por isso a lista possui um único descritor
para o primeiro elemento da lista (primeiro). Para Celes, Cerqueira e
Rangel Netto (2004, p. 152),
[...] a lista circular também pode ser construída com encadeamento duplo. Nesse
caso, o que seria o último elemento da lista passa a ter como próximo o primeiro
elemento, que por sua vez, passa a ter o último como anterior.
Note que por se tratar de uma lista circular e por não ter o conceito de
final da lista, esta não possui descritor último, apenas o descritor primeiro.
www.esab.edu.br 214
24.2 Função de inicialização, inserção, busca e
remoção
A função de inicialização da lista circular duplamente encadeada
não muda em relação à lista simplesmente encadeada, pois
independentemente do tipo de lista circular, ela terá apenas o descritor
primeiro para ser inicializado com NULL.
www.esab.edu.br 215
primeiro
p (ref) p p
a a a
p
(novo)
a
primeiro
p (ref) p p
a a a
(1) p
(novo)
a
primeiro
p (ref) p p
a a a
(2) p
(novo)
a
primeiro
p (ref) p p
a a a
p (3)
(novo)
a
primeiro
p (ref) p p
a a (4) a
p
(novo)
a
primeiro
p (novo) (ref) p p
a a a a
www.esab.edu.br 217
primeiro
(1) p p
(novo)
a a
primeiro
(1) p p
(novo)
(2) a a
primeiro (3)
(1) p p
(novo)
(2) a a
primeiro (3)
(1) p p
(novo)
(2) a a
(4)
(5)
primeiro (3)
(1) p p
(novo)
(2) a a
(4)
www.esab.edu.br 218
A inserção no final da lista circular duplamente encadeada é semelhante à
inserção no final da lista duplamente encadeada não circular, já estudada.
Porém, para garantir o processo cíclico da lista circular, o novo elemento
inserido no final deve apontar como próximo para o primeiro elemento
da lista. A Figura 84 apresenta essa operação:
primeiro
p p (1) (novo)
a a
primeiro
p p (novo)
a a (2)
primeiro
p p (3) (novo)
a a
primeiro
p p (novo)
a a
(4)
Figura 84 – Inserção no final da lista circular duplamente encadeada.
Fonte: Elaborada pelo autor (2013).
Como a lista circular possui cada elemento com dois ponteiros, a busca
por elementos pode ser realizada em dois sentidos, partindo do último
elemento até chegar ao primeiro elemento ou do primeiro elemento até
chegar ao último da lista. A Figura 85 apresenta essas formas de busca:
www.esab.edu.br 219
primeiro
www.esab.edu.br 220
primeiro
(1)
primeiro
(2)
primeiro
primeiro (3)
www.esab.edu.br 221
primeiro
primeiro (1)
primeiro
(2)
primeiro
(1)
(2)
primeiro
(1)
(2)
www.esab.edu.br 222
Nesta unidade, você pôde analisar e estudar as operações de inicialização,
inserção no início, entre elementos e fim da lista, bem como as operações
de remoção do início, entre elementos e fim da lista. Foi possível também
estudar as duas formas de busca na lista circular duplamente encadeada,
usando o ponteiro anterior de cada elemento, no sentido “último-
primeiro”, e o ponteiro próximo no sentido “primeiro-último”.
www.esab.edu.br 223
Resumo
www.esab.edu.br 224
Na unidade 24 estudamos os conceitos da lista duplamente encadeada
aplicada a uma lista circular, na forma de uma lista circular duplamente
encadeada. Analisamos as regras para implementação das operações de
inserção, remoção, busca e inicialização desse tipo de lista.
www.esab.edu.br 225
Listas circulares duplamente
25 encadeadas − codificação
Objetivo
Apresentar a codificação e a manipulação de listas circulares
duplamente encadeadas.
www.esab.edu.br 226
primeiro
Emq1
anterior anterior
número número
próximo próximo
www.esab.edu.br 227
• o último elemento da lista deverá apontar como próximo para o
novo elemento;
• o novo elemento deverá apontar como anterior para o último;
• o novo elemento deverá apontar como próximo para o primeiro
elemento;
• o primeiro elemento deverá apontar como anterior para o novo
elemento.
Como a lista circular não possui o descritor último, o qual armazena
o endereço de memória do último elemento inserido na lista circular,
é preciso percorrer a lista toda para encontrar o último nó inserido, o
qual é o elemento que aponta como próximo para o primeiro elemento
da lista. Para estabelecer essa regra de pesquisa do último elemento (o
qual aponta como próximo para o primeiro da lista), será implementada
uma função genérica para encontrar um determinado elemento (nó). A
função receberá como parâmetro a lista a ser manipulada e o nó acerca
do qual se deseja saber que elemento aponta para ele na lista e, então,
serão pesquisados todos os elementos da lista, partindo do primeiro
elemento até encontrar o nó que tenha como valor do campo próximo o
nó que está sendo pesquisado. Essa função possui o seguinte cabeçalho:
No* apontaPara(ListaCircular* lista,No* elemento), em que o
parâmetro lista representa a lista a ser manipulada e o parâmetro No* o
nó a ser apontado por algum elemento na lista. A função apontaPara()
retornará o endereço do nó, que aponta para o elemento pesquisado.
Por exemplo, vamos supor que a lista possua os valores A, B, C e D, se
a chamada da função for apontaPara(lista,endereço de ‘D’), a função
retornará o endereço do elemento ‘C’, pois a procura é pelo elemento
que aponta para o ‘D’. Se a chamada da função for para encontrar quais
variáveis apontam para ‘B’, a função retornará o endereço de ‘A’, já que
‘A’ aponta para ‘B’ na lista A, B, C e D.
www.esab.edu.br 228
Na inicialização da lista, como só existe um descritor para o primeiro
nó da lista, este será inicializado com NULL, e a função terá o seguinte
cabeçalho: void inicializar(ListaCircular* lista), em que o parâmetro
lista representa a lista circular duplamente encadeada.
Vamos criar o algoritmo que implementa essas regras de forma que seja
possível inserir, listar, remover e cadastrar elementos na lista circular
duplamente encadeada.
www.esab.edu.br 229
elemento anterior e posterior ao nó, e o valor, um inteiro. Na linha
6 é declarado o tipo de dado No, que representa o registro item_no
e, nas linhas 7 a 9, é declarado o tipo estruturado lista_circular com
apenas um campo chamado primeiro, que armazenará o endereço do
primeiro elemento da lista. Por fim, na linha 10 é criado o tipo de dados
ListaCircular que representa o tipo estruturado lista_circular.
www.esab.edu.br 230
A linha 9 retorna o ponteiro novo com o campo valor previamente
preenchido e os descritores anterior e proximo com NULL para fora do
método. Ou seja, o método cria um novo nó, o preenche com o valor
informado pelo usuário, inicializa os descritores anterior e proximo com
NULL e depois o retorna o ponteiro “novo” já preenchido.
www.esab.edu.br 231
Analisando a função apontaPara, na linha 1, ela declara o cabeçalho da
função apontaPara(ListaCircular* lista,No* elemento) que deve retornar
o endereço do nó que aponta para o elemento procurado. Na linha 2, a
variável p é inicializada com o endereço do primeiro elemento da lista, e
a linha 3 verifica se esse endereço vale NULL, ou seja, se a lista está vazia,
então retorna NULL, pois não existe um nó que aponte para o elemento
procurado, logo, a função é encerrada. Se a lista não está vazia, é realizado,
nas linhas 4 a 6, um laço que percorre cada elemento da lista (p=p-
>proximo), enquanto o nó da lista (p) não aponta como próximo para o
elemento que está sendo procurado (p->proximo!=elemento), e a lista não
tenha terminado (p!=null). Na linha 7 é retornado o endereço do elemento
da lista (p) que aponta como próximo para o elemento procurado.
www.esab.edu.br 232
Algoritmo 77 – Inserção, listagem e remoção
01 void cadastrar(ListaCircular* lista, int pvalor){
02 int achou = buscar(lista, pvalor);
03 if(achou == 1){
04 system("cls");
05 printf("Valor ja foi Cadastrado! , valor repetido
!\n");
06 system("pause");
07 }
08 else{
09 No* item = instanciar(pvalor);
10 if(lista->primeiro == NULL){
11 lista->primeiro = item;
12 item->anterior = lista->primeiro;
13 item->proximo = lista->primeiro;
14 }
15 else{
16 No* ultimo = apontaPara(lista,lista-
>primeiro);
17 ultimo->proximo = item;
18 item->anterior = ultimo;
19 item->proximo = lista->primeiro;
20 lista->primeiro->anterior = item;
21 }
22 }
23 }
24 void listar(ListaCircular* lista,char opcao){
25 system("cls");
26 if(lista->primeiro ==NULL){
27 printf("LISTA VAZIA!\n");
28 }
29 else{
30 printf("LISTAGEM\n");
31 No* p = lista->primeiro;
32 No* ultimo = apontaPara(lista,lista->primeiro);
33 if(opcao == 'D' || opcao == 'd') p=ultimo;
34 do{
35 printf("Valor:%d\n",p->valor);
36 if(opcao=='E' || opcao == 'e') p=p->proximo;
37 else p=p->anterior;
38 }while(((opcao== 'E' || opcao == 'e') &&
(p!=lista->primeiro)) ||
39 ((opcao== 'D' || opcao == 'd') &&
(p!=ultimo)));
40 }
41 system("pause");
42 }
www.esab.edu.br 233
43 void remover(ListaCircular* lista){
44 if(lista->primeiro == NULL){
45 system("cls");
46 printf("Lista Vazia!\n");
47 system("pause");
48 }
49 else{
50 if(lista->primeiro == lista->primeiro->proximo){
51 No* aux = lista->primeiro;
52 lista->primeiro = NULL;
53 free(aux);
54 }else{
55 No* ultimo = apontaPara(lista,lista-
>primeiro);
56 No* penultimo = apontaPara(lista,ultimo);
57 penultimo->proximo = lista->primeiro;
58 lista->primeiro->anterior = penultimo;
59 free(ultimo);
60 }
61 printf("Elemento Removido Com Sucesso!\n");
62 system("pause");
63 }
64 }
www.esab.edu.br 234
inseridos, as instruções das linhas 16 a 21 são executadas, e na linha 16
a variável último é inicializada com o endereço do último elemento da
lista (que aponta como próximo para o primeiro), na instrução No*
ultimo = apontaPara(lista,lista->primeiro). A função apontaPara() foi
implementada de forma que devolva o endereço do nó que aponta
como próximo para o elemento pesquisado e que também é um nó.
As instruções das linhas 17 a 20 realizam as seguintes ações: o último
elemento passa a apontar como próximo para o novo elemento (item),
o novo elemento aponta como anterior para o último da lista, o novo
elemento apontará como próximo para o primeiro elemento da lista e o
primeiro elemento da lista apontará como anterior para o novo elemento,
garantindo, assim, o seu processo cíclico.
Por fim, a função remover pode ser dividida em três regras gerais,
conforme a Figura 90:
void remover(ListaCircular* lista){
if(lista->primeiro == NULL) {
system(”cls”);
printf(”Lista Vazia!\n”); 1
system(”pause”);
}
else{
if(lista->primeiro == lista->primeiro->proximo) {
No* aux = lista->primeiro;
lista->primeiro = NULL; 2
free(aux);
}else{
No* ultimo = apontaPara(lista,lista->primeiro);
No* penultimo = apontaPara(lista, ultimo);
penultimo->proximo = lista->primeiro;
lista->primeiro->anterior = penultimo;
3
free(ultimo);
}
printf(”Elemento Removido Com Sucesso!\n”);
system(”pause”);
}
}
www.esab.edu.br 235
As regras do quadro 1 da Figura 90 são executadas quando a lista estiver
vazia, ou seja, lista->primeiro vale NULL, portanto não é possível
remover o último elemento da lista e a função é encerrada. As regras do
quadro 2 representam as instruções para quando a lista não está vazia,
mas possui um único elemento, por isso o primeiro elemento, que
teve seu endereço armazenado na variável aux, é removido na função
free(aux). O quadro 3 da figura representa a remoção do último elemento
da lista quando esta não está vazia, e possui mais de um elemento em que
a variável ultimo recebe o endereço do último elemento da lista, o qual
será removido, e a variável penultimo recebe o endereço do elemento
que aponta para o último, e essa variável será apontada como anterior ao
primeiro elemento. Ou seja, o último elemento é removido e o primeiro
elemento aponta como anterior para o penúltimo e o penúltimo aponta
como próximo para o primeiro.
Assim, todo o código para manipulação da lista circular duplamente encadeada foi
implementado. Acessando o endereço <http://pastebin.com/M4T48s2U> você pode
fazer o download de todo o código e testá-lo no seu computador.
Até lá.
www.esab.edu.br 236
26 Pilhas – conceituação
Objetivo
Apresentar o conceito de pilhas.
26.1 Definição
Caro aluno, para iniciarmos os estudos com relação à estrutura de dados
pilha, vamos fazer uma analogia com uma pilha de pratos. Veja a Figura 91:
remover inserir
topo topo
base base
Figura 91 – Pilha de pratos.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 237
Analisando essa pilha de pratos, note que podemos fazer a inserção
ou a remoção de um prato, mas tanto a inserção quanto a remoção só
poderão ser realizadas no topo da pilha. Outra informação importante
é que a pilha é formada por duas extremidades, o topo e a base, mas
como as operações só podem ser realizadas no topo, na implementação
dessa estrutura de dados, essa informação não é necessária. Na pilha de
pratos, ela é necessária para manter os pratos em pé e organizados. Se
desejarmos colocar um prato na pilha, colocamos pelo topo, se a opção
for por remover um prato da pilha, o retiramos do topo. Para ter acesso
a um determinado prato, é preciso remover o prato do topo até chegar
ao elemento desejado. Portanto, quando um elemento é inserido na
pilha ele passa a ser o topo, e quando um elemento é removido da pilha,
o elemento abaixo, caso exista, passa a ser o topo da pilha. A Figura 92
apresenta a sequência de duas remoções e uma inserção em uma pilha
que inicialmente possuía cinco elementos:
remover
remover
inserir
topo
topo topo
topo
Note que todo acesso à pilha se dá pelo topo, tanto para inserção quanto
para remoção, logo, os elementos da pilha são retirados na ordem inversa
à ordem de inserção, assim, o primeiro a sair é o último que entrou.
Celes, Cerqueira e Rangel Netto (2004, p. 160) conceituam a pilha
como uma “[...] estrutura de dados conhecida como LIFO (last in first
out), do inglês, o último a entrar é o primeiro a sair”.
www.esab.edu.br 238
Estudo complementar
Leio o texto que apresenta o jogo Torre de Hanoi,
ele utiliza os conceitos de empilhar e desempilhar
(inserir e remover na pilha).
Uma vez que as operações são realizadas somente no topo, a pilha possui
duas operações básicas: empilhar um novo elemento, inserindo-o no
topo e desempilhar, removendo o elemento do topo. Celes, Cerqueira e
Rangel Netto (2004, p. 160), destacam que “[...] normalmente estas duas
operações são identificadas como push (empilhar) e pop (desempilhar)”.
A Figura 93 apresenta o resultado das operações push(10), push(20),
pop() e push(15) em uma pilha:
20 topo 20 15 topo
base 10 topo base 10 base 10 topo base 10
www.esab.edu.br 239
Como na pilha, as inserções e remoções são feitas por uma única
extremidade (topo), não sendo necessário que se conheça o elemento da
outra extremidade, diferentemente do que ocorre com a lista encadeada,
em que as inserções e remoções podiam ocorrer no início, entre
elementos e no final da estrutura de dados.
Produto A Produto D
Produto B Produto C
Produto C Produto B
Produto D Produto A
www.esab.edu.br 240
Observe que a ordem de armazenamento dos produtos é inversa à ordem
das entregas, de forma que o produto que será entregue primeiro seja
armazenado por último no contêiner.
Pilha
0 1 2 3 4
Figura 95 – Pilha implementada com vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 241
topo
(0)
Pilha Push(23)
0 1 2 3 4
topo
(0)
Pilha 23
0 1 2 3 4
Figura 96 – Operação de push no vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 242
Como as posições da pilha variam de 0 a 4, a variável topo valerá -1 para
representar que nenhum elemento foi inserido, assim, para saber se a
pilha está vazia basta verificar se a posição do topo vale -1, sendo que na
primeira inserção na pilha a variável será incrementada em 1, mudando,
portanto, para o valor 0. Na remoção, quando o topo estiver valendo 0, o
topo é o primeiro elemento da lista e a variável será decrementada em 1,
portanto, mudando para -1, o que representa que a pilha está vazia.
topo
(2)
topo topo
(-1) Push(44) 44 Pop() (-1) 44
0 1 2 3 4 0 1 2 3 4 0 1 2 3 4
Figura 98 – Controle da posição do topo.
Fonte: Elaborada pelo autor (2013).
Tarefa dissertativa
Caro estudante, convidamos você a acessar o
Ambiente Virtual de Aprendizagem e realizar a
tarefa dissertativa.
www.esab.edu.br 243
27 Pilhas – codificação – parte I
Objetivo
Apresentar a codificação e a manipulação de pilhas.
www.esab.edu.br 244
A instrução int pilha [5] define o tamanho do vetor pilha com cinco
posições e que será manipulado na forma de uma pilha, com as operações
de push() para inserção e pop() para remoção. A variável qtde representa
a quantidade máxima de elementos da pilha, e a variável topo a posição
do vetor que representa o topo da pilha.
Para verificar se a pilha está vazia basta verificar se a variável topo vale
-1. A função que verifica se a pilha está vazia retorna verdadeiro (1) se
o topo vale -1 ou falso (0) caso o topo seja diferente de -1. Observe no
Algoritmo 79:
www.esab.edu.br 245
valor da variável dado (linha 4). Se a pilha está cheia, ou seja, atingiu
a quantidade máxima de elementos, uma mensagem é apresentada ao
usuário informando que a pilha está cheia (linhas 6 a 9).
Como a função pop controla a variável topo, ela não recebe parâmetros e
é importante que o usuário receba uma mensagem avisando que a pilha
está vazia de forma a mantê-lo informado sobre a sua situação.
www.esab.edu.br 246
Algoritmo 82 – Função Listar
01 void listar(int *pilha){
02 if(pilha_vazia()==1){
03 system("cls");
04 printf("Pilha Vazia!\n");
05 }
06 else{
07 system("cls");
08 do{
09 printf("%d\n",pilha[topo]);
10 pop();
11 }while(pilha_vazia()==0);
12 }
13 system("pause");
14 }
Caro aluno, acessando o endereço clicando aqui você poderá efetuar o download do
algoritmo elaborado anteriormente e executá-lo no seu computador.
Agora que sabemos como implementar uma pilha como vetor, vamos
verificar como implementar a estrutura de dados pilha usando a lista
encadeada.
www.esab.edu.br 247
27.2 Implementação de pilha com lista
A principal vantagem da implementação da pilha usando a lista
encadeada, está no fato de não ser necessário conhecer previamente o
número de elementos que serão manipulados. Celes, Cerqueira e Rangel
Netto (2004, p. 164) destacam que “[...] quando o número máximo de
elementos que serão armazenados na pilha não é conhecido, devemos
implementar a pilha usando uma estrutura de dados dinâmica, no caso,
uma lista encadeada”.
Note que cada nó da pilha, definido como struct item_no nas instruções
das linhas 1 a 4, é formado por um valor inteiro, armazenado pelo
campo dado e pelo campo anterior, que armazena o endereço do nó
predecessor. Na linha 5 é criado o tipo de dados No para representar
a estrutura de cada elemento da pilha. As instruções das linhas 6 a 8
definem a estrutura da pilha, formada por um único descritor, o topo
da pilha. Na linha 9 é criado o tipo de dados pilha, que representa a
estrutura de dados que será manipulada no programa em C. As funções
que criam a pilha e verificam se ela está vazia podem ser visualizadas no
Algoritmo 84:
www.esab.edu.br 248
Algoritmo 84 – Funções Pilha_Vazia e Criar
01 int pilha_vazia(Pilha* pilha){
02 if(pilha->topo == NULL) return 1;
03 else return 0;
04 }
05 Pilha* criar(){
06 Pilha* pilha = (Pilha*)
malloc(sizeof(Pilha));
07 pilha->topo = NULL;
08 return pilha;
09 }
Observe que a função push recebe como parâmetro a lista que será
manipulada e o valor que será inserido na pilha. Na linha 2 é criado,
dinamicamente, o novo elemento que será inserido na pilha, que tem o
campo dados preenchido com o valor do parâmetro “info” na linha 3.
A linha 4 define que, por padrão, todo novo elemento inserido tem o
campo anterior valendo NULL.
www.esab.edu.br 249
Na linha 5 verifica-se se a pilha não está vazia, ou seja, se já existe um
elemento inserido na pilha. Logo, o novo elemento inserido deve apontar
como anterior para o topo da pilha (novo->anterior = pilha ->topo, na
linha 6), e, tratando-se de uma inserção na pilha, todo elemento inserido
passa a ser o topo da pilha, por isso a instrução da linha 8 é sempre
executada, estando a pilha vazia ou não. A Figura 99 apresenta esse
processo de inserção no topo:
Cria novo elemento Novo aponta para topo como interior Novo é topo
www.esab.edu.br 250
Algoritmo 86 – Funções Pop e Listar
01 void pop(Pilha *pilha){
02 if(pilha_vazia(pilha)==1){
03 system("cls");
04 printf("Pilha Vazia!\n");
05 system("pause");
06 }
07 else{
08 No* aux = pilha->topo;
09 if(aux->anterior == NULL){
10 pilha->topo = NULL;
11 }else{
12 pilha->topo = pilha->topo->anterior;
13 }
14 free(aux);
15 }
16 }
17 void listar(Pilha *pilha){
18 if(pilha_vazia(pilha)==1){
19 system("cls");
20 printf("Pilha Vazia!\n");
21 system("pause");
22 }
23 else{
24 system("cls");
25 printf("__________
LISTAGEM__________\n");
26 do{
27 printf("%d\n",pilha->topo->dado);
28 pop(pilha);
29 }while(pilha_vazia(pilha)==0);
30 system("pause");
31 }
32 }
www.esab.edu.br 251
do topo está armazenado no campo anterior do topo, assim, o descritor
topo passa a conter o endereço do campo anterior do topo (instrução
da linha 12) e, na linha 13, o antigo topo é removido. Por exemplo, se o
topo da pilha é o elemento X e o elemento abaixo do topo é o elemento
Z, então, Z é o anterior de X. Como o elemento X será removido, o topo
passará a ser o anterior de X, que é o elemento Z, e, após a remoção do
elemento X, teremos a pilha com Z no topo.
Puga (2009, p. 231) destaca que “[...] a função desempilhar não recebe
outro parâmetro que não seja a pilha que está sendo manipulada, pois a
remoção é sempre do elemento que está no topo”. A função listar mostra
todos os elementos do topo da pilha, desde que ela não esteja vazia (por
isso a condição da linha 18), não estando vazia, cada elemento do topo é
mostrado na tela, e, em seguida, o topo é removido pela função pop() da
linha 28, até que a pilha esteja vazia.
Bem, dessa forma você pode analisar as rotinas para inserção, remoção e listagem em
uma pilha implementada por meio da lista encadeada, e o código do algoritmo pode
ser baixado clicando aqui.
Nesta unidade, você pôde analisar como desenvolver uma pilha na forma
de vetor e lista encadeada, com as operações de inserção (push), remoção
(pop) e listagem dos elementos da pilha, sendo que por característica da
estrutura de dados, a listagem de todos os dados da pilha resulta em uma
pilha vazia.
Atividade
Chegou a hora de você testar seus conhecimentos
em relação às unidades 19 a 27. Para isso, dirija-se ao
Ambiente Virtual de Aprendizagem (AVA) e responda
às questões. Além de revisar o conteúdo, você estará
se preparando para a prova. Bom trabalho!
www.esab.edu.br 252
28 Pilhas – codificação – parte II
Objetivo
Apresentar a codificação e a manipulação de pilhas.
2 B topo 2 B
qtde qtde qtde qtde
Pilha
0 Vazia 1 1 A topo 2 1 A 1 1 A topo
www.esab.edu.br 253
Observe que, inicialmente, a quantidade (qtde) de elementos da pilha
começa valendo 0, a cada inserção do produto é gerado um número de
ordem (1, 2,..., N) e a quantidade é incrementada em 1. Com a remoção
do elemento do topo, a quantidade é decrementada em 1, de forma que
se tenha, no descritor qtde, o valor que representa a quantidade atual
de elementos da pilha. Vamos começar criando os tipos estruturados de
dados que serão usados na implementação da pilha. O Algoritmo 87
apresenta a declaração do tipo estruturado No e o tipo estruturado Pilha:
www.esab.edu.br 254
O Algoritmo 88 apresenta a função de inserção de elementos na pilha:
A função de push recebe como parâmetro dois valores, a pilha que será
manipulada e o nome do produto. Na linha 2 é declarada a variável
novo, que aloca dinamicamente um espaço na memória. Na linha 3,
a ordem de inserção do produto é determinada pelo valor da variável
item, que, na linha 7, é incrementado em 1. Na linha 4 é atribuído ao
campo produto o nome do produto, e na linha 5 o campo anterior é
inicializado com NULL. Na linha 6, o campo que controla a quantidade
de elementos da pilha é incrementado em 1 (pilha->qtde = pilha->qtde
+ 1). A instrução da linha 8 faz a ligação entre o novo elemento inserido
e o topo da pilha, ligando-os pelo campo anterior do novo elemento
(novo->anterior = pilha->topo), desde que a pilha tenha pelo menos um
elemento, e na linha 9 o novo elemento é definido como topo da pilha.
Saiba mais
Acessando o endereço clicando aqui é possível
revisar o funcionamento da função strcpy,
responsável por atribuir um valor a uma variável
do tipo literal.
www.esab.edu.br 255
Algoritmo 89 – Função Pop
01 void pop(Pilha *pilha){
02 if(pilha->topo != NULL){
03 No* aux = pilha->topo;
04 pilha->topo = pilha->topo->anterior;
05 pilha->qtde=pilha->qtde-1;
06 free(aux);
07 }
08 }
Vamos ver como esvaziar toda a pilha. A próxima função tem como
finalidade limpar a pilha removendo todos os elementos, desde que a
pilha não esteja vazia.
www.esab.edu.br 256
Observe que na linha 2 verifica-se se a pilha está vazia (pilha->topo ==
NULL) e, caso a condição seja verdadeira, uma mensagem “PILHA
VAZIA!” é exibida ao usuário. A pilha não estando vazia, é realizado um
laço condicional que remove o elemento do topo, por meio da função
pop(pilha), enquanto a pilha não estiver vazia (while(pilha->topo !=
NULL)). Após o encerramento do laço, na linha 9, uma mensagem
“PILHA ESVAZIADA!” é mostrada ao usuário.
Você pode fazer o download de todo o código implementado nesta unidade clicando aqui.
Nesta unidade, você pôde estudar como construir uma pilha que utiliza
dois descritores, o topo, responsável por identificar o elemento no topo
da pilha, e a quantidade (qtde), para controlar o número de elementos
existentes na pilha. Como regras de manipulação da pilha foram
implementadas as funções de push (inserção), pop (remoção), listagem e
esvaziamento da pilha.
www.esab.edu.br 257
29 Filas – conceituação
Objetivo
Apresentar o conceito de filas.
Na unidade anterior, você pôde estudar como construir uma pilha que
utiliza dois descritores, o topo, responsável por identificar o elemento
no topo da pilha, e a quantidade (qtde), para controlar o número de
elementos existentes na pilha. Como regras de manipulação da pilha
foram implementadas as funções de push (inserção), pop (remoção),
listagem e esvaziamento da pilha.
Vamos iniciar fazendo uma analogia da estrutura de dados fila com uma
fila de banco.
29.1 Definição
Quem nunca enfrentou uma fila em um banco ou restaurante? A fila faz
parte das nossas atividades no dia a dia, ainda mais neste mundo cada vez
mais corrido e competitivo, não é mesmo?
www.esab.edu.br 258
A Figura 101 representa uma fila:
início final
Saída Entrada
Figura 101 – Fila de pessoas.
Fonte: Elaborada pelo autor (2013).
Estudo complementar
Ficou curioso e quer saber mais sobre o
funcionamento de uma fila de banco? Então
acesse o link.
Já para Puga (2009, p. 219), a pilha pode ser definida como a estrutura
na qual “[...] os elementos são atendidos ou utilizados, sequencialmente,
na ordem em que são armazenados, cujas operações de inserção são feitas
por uma extremidade, e as de remoção, por outra”.
www.esab.edu.br 259
Independentemente do tipo de fila que será utilizado, a estrutura de
dados fila visa garantir a prioridade de chegada, quem chega mais cedo
é atendido primeiro, portanto, não é permitida a inserção de elementos
que não no final da fila e muito menos a remoção que não ocorra no
início da fila.
• inserir no final;
• remover do início;
• listar os itens da fila.
www.esab.edu.br 260
Diferentemente da estrutura de dados pilha, a listagem dos elementos da fila não
exige que os elementos sejam removidos, portanto, após listar os elementos a fila não
estará vazia. A Figura 102 apresenta a visão das duas estruturas de dados, na forma
de uma pilha de pratos e de uma fila de atendimento em banco:
Pilha Fila
Figura 102 – Visão da pilha e fila.
Fonte: Elaborada pelo autor (2013).
Observe que a pilha pode ser representada como uma estrutura na vertical, já que
possui apenas uma extremidade, o topo, e quando olhamos, a visão se dá de cima
para baixo, então visualizamos apenas o primeiro prato, e para saber se há outro,
temos que remover o prato do topo. Já a fila pode ser representada como uma
estrutura na horizontal, pois possui duas extremidades, início e final, o que permite a
visualização de todos os elementos sem a necessidade de remoção de elementos.
www.esab.edu.br 261
Para ajudar na compreensão das regras de inserção e remoção, veja a
Figura 103:
• criar a fila;
• inserir no final;
• remover do início;
• listar os elementos da fila.
Diferentemente do vetor, não há necessidade de controle de posições de
inserção e remoção, mas é preciso que a lista possua dois descritores para
armazenamento do endereço do primeiro elemento e do último elemento
da fila, como pode ser visualizado na Figura 104:
www.esab.edu.br 262
B
A B C A B C D
insere “D”
Remover (A)
primeiro último
A B C D
www.esab.edu.br 263
• remover: verifica se a fila está vazia ou não (para vetor ou lista) e,
sendo um vetor, remove o elemento da posição 0, realizando uma
operação de repetição trazendo todos os elementos que sobraram
na fila para a posição atual -1. No caso da lista, remove o elemento
apontado pelo descritor início e define o novo início como sendo o
próximo elemento apontado pelo antigo início;
• listar: verifica se a fila está vazia ou não (para vetor ou lista) e
percorre todos os elementos da estrutura de dados, sem a necessidade
de remoção dos elementos.
Nesta unidade, você pôde estudar o funcionamento e a definição da
estrutura de dados fila, que pode ser implementada por meio de um
vetor ou lista simplesmente encadeada com as operações de inserção no
final, remoção no início e listagem dos elementos.
www.esab.edu.br 264
30 Filas – codificação
Objetivo
Apresentar a codificação e a manipulação de filas.
www.esab.edu.br 265
A fila será representada por um vetor de dez posições de inteiros e a variável
pi é inicializada com 0, já que a primeira inserção será nessa posição.
www.esab.edu.br 266
Algoritmo 94 – Função de listagem
01 void listar(){
02 system("cls");
03 if(pi == 0){
04 printf("Fila Vazia\n");
05 }else{
06 int i=0;
07 for(i=0; i < pi;i++){
08 printf("%d ",fila[i]);
09 }
10 printf("\n");
11 }
12 system("PAUSE");
13 }
próxima
(pi) inserção
0 1 2 3
1 15
Figura 105 – Inserção na fila como vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 267
Por fim, a próxima função consiste na remoção do elemento do início da
fila, posição 0, desde que ela não esteja vazia, o que pode ser observado
no Algoritmo 95:
www.esab.edu.br 268
sucessivamente até a posição pi-1. Após a cópia dos elementos da posição
“i+1” para “i”, deslocando os elementos da fila para o seu início, a
variável pi tem posição de inserção decrementada em 1, de forma que a
próxima inserção, caso aconteça, ocorra no lugar do valor da posição 2,
que contém uma cópia do valor que estava nessa posição, mas que já foi
deslocado para a posição anterior (à esquerda).
Saiba mais
Você pode complementar seus estudos sobre a
exclusão lógica clicando aqui.
Note que a exclusão lógica se faz necessária devido ao uso do vetor que
não possibilita a remoção de uma posição específica da memória, já que,
uma vez alocado, o bloco de memória se mantém até que o programa
se encerre. No caso da fila implementada com uso da lista encadeada
a remoção será física, removendo o elemento da memória durante a
execução do programa.
www.esab.edu.br 269
Resumo
www.esab.edu.br 270
que são armazenados, e cujas operações de inserção são feitas por uma
extremidade e as de remoção, por outra. Assim, as operações possíveis
em uma fila são: a inserção no final, a remoção no início e a listagem dos
elementos da fila.
www.esab.edu.br 271
31 Filas – exercícios
Objetivo
Apresentar exercícios comentados sobre filas.
Nesta unidade, vamos desenvolver uma fila por meio de uma lista
simplesmente encadeada, com as operações de inserção, remoção e
listagem dos dados. Criaremos uma lista que simule a lista de arquivos
para impressão utilizada pelo sistema operacional, em que cada item da
lista deverá conter o nome do arquivo que será impresso e seu tamanho
em bytes.
31.1 Exercícios
A fila que programaremos simula uma fila de arquivos, uma estrutura de
dados muito utilizada pelo sistema operacional para controle dos arquivos
que são enviados para a impressora. Por se tratar de uma fila, o primeiro
arquivo inserido será o primeiro arquivo a ser impresso e removido da
fila, garantindo o conceito FIFO (First In First Out – primeiro a entrar,
primeiro a sair). A Figura 107 representa a fila que será implementada:
www.esab.edu.br 272
inserir
próximo próximo próximo
nomearquivo nomearquivo nomearquivo
remover tamanho tamanho tamanho
www.esab.edu.br 273
na linha 03, na declaração do campo nomearquivo do tipo estruturado
item_no, que representa cada item da fila, com os campos nomearquivo,
tamanho e próximo. Na linha 07 é criado um novo tipo chamado No
para representar cada item da fila do tipo estruturado item_no. Nas
linhas 08 a 11, é declarado o tipo estruturado fila que representa a fila
que será manipulada, com os descritores inicio e final, ambos do tipo
ponteiro de No. Na linha 12 é criado o tipo de dados FilaArquivos que
representa o tipo estruturado fila, com seus dois campos, inicio e final.
www.esab.edu.br 274
Algoritmo 98 – Inserção na fila
01 void inserir(FilaArquivos* arquivos, literal
nome, int tam){
02 No* novo = (No*) malloc(sizeof(No));
03 strcpy(novo->nomearquivo,nome);
04 novo->tamanho = tam;
05 novo->proximo = NULL;
06 if(arquivos->inicio == NULL){
07 arquivos->inicio = novo;
08 }else{
09 arquivos->final->proximo = novo;
10 }
11 arquivos->final = novo;
12 }
www.esab.edu.br 275
Algoritmo 99 – Remoção na fila
01 void remover(FilaArquivos* arquivos){
02 if(arquivos->inicio!=NULL){
03 No* aux = arquivos->inicio;
04 printf(“O valor %s foi removido da fila\
n”,aux->nomearquivo);
05 arquivos->inicio = arquivos->inicio-
>proximo;
06 if(arquivos->inicio == NULL) {
07 arquivos->final = NULL; //fila ficará
vazia
08 }
09 free(aux);
10 }
11 }
www.esab.edu.br 276
Todo o código para manipulação da fila de impressão do exercício desenvolvido nesta
unidade pode ser acessado clicando aqui, no qual você pode fazer o download de
todo o código e testá-lo no seu computador.
Fórum
Caro estudante, dirija-se ao Ambiente Virtual de
Aprendizagem da instituição e participe do nosso
Fórum de discussão. Lá você poderá interagir com
seus colegas e com seu tutor de forma a ampliar,
por meio da interação, a construção do seu
conhecimento. Vamos lá?
www.esab.edu.br 277
32 Fila dupla
Objetivo
Apresentar o conceito, a aplicação e a manipulação de fila dupla.
www.esab.edu.br 278
32.1 Definição
O que diferencia a fila que utiliza a lista simplesmente encadeada da fila
com lista duplamente encadeada é apenas a quantidade de descritores
para cada nó, mudando de um descritor (apenas o próximo) na lista
simples, para dois descritores (anterior e próximo) na duplamente
encadeada. Porém, as operações continuam sendo as mesmas, inserção no
final da fila, remoção no início da fila, listagem e consulta aos dados, de
forma que a estrutura de dados fila mantenha a regra de FIFO (primeiro
a entrar, primeiro a sair). Para Celes, Cerqueira e Rangel Netto (2004,
p. 181), nesse tipo de fila, com lista duplamente encadeada “[...] em
cada nó da fila, além da referência para o próximo elemento, há também
uma referência para o elemento anterior, para que seja possível acessar o
elemento posterior e adjacente de cada elemento da fila”.
www.esab.edu.br 279
Algoritmo 100 – Estrutura da fila com lista duplamente encadeada
01 struct item_no{
02 int numeropedido;
03 struct item_no *proximo;
04 struct item_no *anterior;
05 };
06 typedef struct item_no No;e
07 struct fila{
08 No* inicio;
09 No* final;
10 };
11 typedef struct fila FilaPedidos;
www.esab.edu.br 280
Vamos analisar a próxima rotina de inserção no final da fila duplamente
encadeada, representada pelo Algoritmo 102:
www.esab.edu.br 281
Vamos analisar agora a rotinas de remoção do primeiro elemento da fila
duplamente encadeada, apresentada no Algoritmo 103:
www.esab.edu.br 282
Caso não se trate da remoção do único elemento da fila, a instrução da
linha 10 será executada, fazendo com que no início da fila o primeiro
elemento tenha como anterior o valor NULL, pois antes dele não existirá
nenhum outro elemento da fila.
www.esab.edu.br 283
Em princípio, a listagem começará no início da fila, por isso, na linha
03 a variável item é inicializada com o endereço do primeiro elemento
da fila (pedidos->inicio). Na linha 05 é verificado se o tipo escolhido é
decrescente (D) e em caso verdadeiro, a variável item é inicializada com
o endereço do último elemento da fila (pedidos->final). As instruções das
linhas 06 a 11 representam as rotinas para percorrer cada elemento da
fila e mostram o número do pedido cadastrado. Sendo que, se o tipo é
crescente (C), a listagem utilizará o descritor próximo de cada elemento
para percorrer a fila (if (tipo == ‘C’ || tipo ==’c’) item = item->proximo).
Caso contrário, será utilizado o descritor anterior de cada item da fila
(else item = item->anterior).
Dessa forma, a fila pode ser listada nos dois sentidos, sem a necessidade
de alterações na organização dos elementos. Por exemplo, para listar
crescentemente a chamada da função será listar(Fila,’C’), e para listar
decrescentemente a chamada será listar(Fila,’D’), onde Fila representa a
fila dinamicamente alocada que será manipulada.
Você pode baixar todo o código do exemplo desenvolvido nesta unidade, com
exemplos para a chamada da função listar de forma crescente e decrescente, assim
como as rotinas de inserção, remoção e inicialização, acesse clicando aqui.
Nesta unidade, por meio de um exemplo que cria uma fila de controle
de pedidos, com as operações de inicialização, remoção e listagem
dos elementos em dois sentidos, crescente e decrescente, você pôde
desenvolver e analisar as diferenças entre uma fila simplesmente
encadeada e outra duplamente encadeada.
Até lá!
www.esab.edu.br 284
33 Árvores – parte I
Objetivo
Apresentar o conceito de árvores.
33.1 Definição
Nas unidades anteriores, pudemos abordar o estudo das estruturas
de dados lista, filas e pilhas, que são consideradas estruturas de dados
lineares, assim como a estrutura de dados vetor. A conotação de
linear está associada à forma pela qual os dados são representados
sequencialmente, com cada elemento um ao lado do outro. A Figura 108
apresenta as estruturas de dados estudadas anteriormente.
www.esab.edu.br 285
Vetores Listas encadeadas saída Fila
0 1 2 3
(simples) entrada
NULL NULL
(dupla)
Pilha
NULL NULL
pop push
(circular)
topo
Vamos agora imaginar uma estrutura que possua uma representação não
linear, hierárquica, como uma lista de diretórios do computador, em
que um diretório pode estar dentro de outro e assim subsequentemente,
como apresentado na Figura 109:
www.esab.edu.br 286
Figura 109 – Lista de diretórios.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 287
Unidade
C:
Windows
Boot
cs-CZ DA-DK
www.esab.edu.br 288
estrutura de dados não linear, que possui propriedades especiais e admite
muitas operações, como pesquisa, inserção, remoção, e são úteis para
implementação de algoritmos que necessitam de estruturas hierárquicas”.
A árvore é uma estrutura de dados bem peculiar, por isso, possui uma
terminologia própria para identificação de seus elementos e grupo de
elementos, como veremos a seguir.
Níveis
A 0
B C 1
D E F G H 2
I 3
www.esab.edu.br 289
Analisando a árvore da Figura 111, podemos concluir que:
Uma árvore pode ser classificada como “[...] genérica ou binária, dependendo
do número máximo de filhos para cada nó” (PUGA, 2009, p. 186).
www.esab.edu.br 290
33.3 Árvore binária
As árvores binárias são assim nomeadas por possuírem no máximo dois
filhos para cada nó. Segundo Puga (2009, p. 234), as árvores binárias
“[...] possuem nós com grau menor ou igual a 2, isto é, nenhum nó
possui mais que dois descendentes diretos (dois filhos)”.
08 21 Nível - 1
04 09 55 Nível - 2
Figura 112 – Árvore binária.
Fonte: Elaborada pelo autor (2013).
Note que cada nó possui no máximo dois filhos (grau <= 2). A raiz da
árvore é o nó com valor 10 e a árvore possui como folha os nós 04, 09 e
55, pois eles não possuem filhos. Já os nós 10, 08 e 21 são considerados
nós internos, pois possuem filhos. A altura da árvore é 2, já que é este o
maior nível da árvore (nível 0 possui o nó 10, nível 1 possui os elementos
08 e 21, e nó 2 possui os elementos 04, 09 e 55).
www.esab.edu.br 291
A árvore é uma estrutura de dados bem peculiar. Por isso, possui uma
terminologia própria para identificação de seus elementos e grupo de
elementos, como: nó, raiz, folha, grau do nó, grau e altura da árvore,
trajetória e nó interior.
Você viu também que uma árvore pode ser classificada como generalizada
ou binária, dependendo do número máximo de filhos para cada nó. As
árvores binárias são assim nomeadas porque apresentam no máximo
dois filhos para cada nó e possuem uma característica muito importante
quanto à posição dos nós: os valores menores que os do pai sempre estão
à esquerda e os valores maiores que os do pai sempre à direita.
www.esab.edu.br 292
34 Árvores – parte II
Objetivo
Codificação de árvores em C.
Vamos utilizar essa propriedade para desenvolver uma árvore binária, que
armazena os valores informados pelo usuário, valores estes expressos em
números inteiros, de forma que os valores menores fiquem à esquerda
e os valores maiores à direita. A Figura 113 apresenta o processo de
inserção na árvore binária.
www.esab.edu.br 293
Valores a serem inseridos:
[15, 21, 55, 08, 12, 33]
15 (raiz) 55 12 55
21 33
Insere 55 (3) Insere 12 (5)
se for maior -> direita se for maior -> direita
se for menor -> esquerda se for menor -> esquerda
15 (raiz) 15 (raiz)
21 08 21
55 12 55
Observe que a primeira inserção define o valor que será a raiz da árvore.
A cada inserção é verificado se o valor é maior ou menor do que o valor
da raiz. Se o valor for maior é inserido à direita, se for menor é inserido
à esquerda. Porém, essas posições já podem ter sido ocupadas por
outra inserção anterior, logo, é preciso que o processo de inserção faça
uma nova verificação do valor que está na posição que seria a inserção,
avaliando se o valor a ser inserido é maior ou menor que esse valor já
inserido: se for maior que o valor já inserido, o novo valor é inserido
à direita, se for menor que o valor já inserido, o novo valor é inserido
à esquerda do valor já inserido. O processo se repete para cada nó que
já está ocupado até que se ache uma posição livre para inserção. Por
exemplo, a Figura 113 apresenta o processo para inserção do valor 33.
Como ele é maior que o nó 15 (raiz), deve ser inserido à direita do nó
15, mas nessa posição já existe o nó 21. Então, pergunta-se ao nó 21 se o
www.esab.edu.br 294
valor 33 é maior ou menor que ele (21), como é maior, deve ser inserido
à direita, mas já existe o nó 55 nessa posição, então, se pergunta ao nó 55
se 33 é maior ou menor do que ele, como é menor, é inserido à esquerda
do nó 55.
Como a árvore deve iniciar vazia, sem elementos, vamos criar uma
função que inicialize a árvore com NULL. O Algoritmo 106 apresenta
essa função.
www.esab.edu.br 295
Algoritmo 106 – Inicializar a árvore binária
01 void inicializar(ArvoreBinaria *arvore){
02 *arvore = NULL;
03 }
www.esab.edu.br 296
Algoritmo 107 – Inserção na árvore binária
01 void inserir(ArvoreBinaria *arvore,int
novovalor){
02 if ((*arvore) = = NULL) {
03 *arvore = (No *) malloc(sizeof(No));
04 (*arvore)->esq = NULL;
05 (*arvore)->dir = NULL;
06 (*arvore)->valor = novovalor;
07 }
08 if (novovalor < (*arvore)->valor) {
09 inserir((&(*arvore)->esq, novovalor);
10 }
11 else{
12 if (novovalor > (*arvore)->valor) {
13 inserir(&((*arvore)->dir, novovalor);
14 }
15 }
16 }
www.esab.edu.br 297
Na chamada recursiva, com o endereço da esquerda ou da direita, junto
com o valor a ser inserido, a função verificará novamente na linha 02
se essa nova referência está livre (NULL). Se estiver livre, o nó é criado,
com esquerda e direita valendo NULL e o novo valor gravado no nó.
Caso contrário, o processo recursivo se repete (descendo na árvore) até
que um nó vazio seja localizado.
Por fim, vamos desenvolver uma função para listar os nós da árvore,
que deverá ser recursiva, de forma que a listagem comece na raiz e vá
descendo à esquerda ou à direita de cada nó que não seja NULL. De
acordo com Celes, Cerqueira e Rangel Netto (2004, p. 190) a função
para exibição dos elementos da árvore “[...] percorre recursivamente a
árvore, visitando todos os nós e imprimindo sua informação”.
www.esab.edu.br 298
Essa regra é apresentada no Algoritmo 108:
Você pode fazer o download de todo o código apresentado nesta unidade clicando
aqui. Procure baixá-lo e executá-lo no seu computador. Ele foi desenvolvido
utilizando a ferramenta DevC++, mas pode ser testado em qualquer compilador da
linguagem C.
www.esab.edu.br 299
Nesta unidade você pôde estudar como implementar uma árvore binária
utilizando a linguagem de programação C. Primeiramente, foi preciso
definir a estrutura de cada nó da árvore, formada por duas referências,
esquerda e direita, que apontam para os nós filhos da esquerda e da
direita de cada nó da árvore. Como cada nó armazenará um inteiro, o
tipo estruturado nó foi composto ainda por um campo chamado valor,
do tipo inteiro.
Por fim, você pôde analisar que a impressão dos valores de uma árvore
também utiliza a recursividade, listando o valor armazenado em cada nó,
desde que ele seja diferente de NULL. Em seguida, a função para listar os
dados é chamada recursivamente com a referência da esquerda e da direita
do nó pai. Ou seja, a listagem percorre a árvore listando o valor do pai,
depois o valor do filho da direita e em seguida do filho da esquerda.
www.esab.edu.br 300
35 Árvores – parte III
Objetivo
Apresentar o conceito, a aplicação e a manipulação de árvores.
www.esab.edu.br 301
35.1 Ordens de percurso em árvores binárias
(prefixado, central e pós-fixado)
Na unidade anterior, a implementação dos elementos da árvore binária
utilizou como regra a impressão dos dados do nó pai, depois dos nós
filhos da esquerda e depois dos nós filhos da direita. Essa forma de
imprimir os nós da árvore é chamada de percurso prefixado ou pré-
ordem, mas não é a única forma de listar os elementos da árvore.
2 6
1 3 5 7
www.esab.edu.br 302
(1) R
(2)
E Pai (5)
4
(3) D E
2 6
E (4) (6) D (7)
1 3 5 7
www.esab.edu.br 303
prefixado que recebe o endereço do nó da esquerda, caso ele não seja
NULL. Depois é chamada recursivamente a função prefixado que recebe
o endereço do nó da direita, gerando assim o percurso R, E e D.
(4) R
4
(2) (6)
R R
(1) D E
2 6
E (3) (5) D (7)
1 3 5 7
www.esab.edu.br 304
O Algoritmo 110 apresenta a lógica de listagem dos elementos pelo
percurso central:
www.esab.edu.br 305
4 4 4
2 6 2 6 2 6
1 3 5 7 1 3 5 7 1 3 5 7
(1) nó mais à esquerda (2) nó mais à direita (3) nó pai
4 4 4
2 6 2 6 2 6
1 3 5 7 1 3 5 7 1 3 5 7
(4) nó mais à esquerda (5) nó mais à direita (6) nó pai
2 6
1 3 5 7
(7) nó pai
Figura 117 – Percurso pós-fixado na árvore binária.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 306
Algoritmo 111 – Percurso pós-fixado
01 void posfixado(ArvoreBinaria no){
02 if (no != NULL){
03 if (no->esq != NULL) posfixado (no->esq);
04 if (no->dir != NULL) posfixado (no->dir);
05 printf("%d\n",no->valor);
06 }
07 }
Você pode fazer o download de todo o código apresentado nesta unidade clicando
aqui. Procure baixá-lo e executá-lo no seu computador.
www.esab.edu.br 307
Na próxima unidade, abordaremos outro tipo de árvore, que se chama
genérica, e estudaremos o conceito, a aplicação e a manipulação desse
tipo de árvore.
Tarefa dissertativa
Caro estudante, convidamos você a acessar o
Ambiente Virtual de Aprendizagem e realizar a
tarefa dissertativa.
www.esab.edu.br 308
36 Árvores – parte IV
Objetivo
Apresentar o conceito, a aplicação e a manipulação de árvores.
www.esab.edu.br 309
36.1 Árvores genéricas
Nas unidades anteriores sobre árvores, foi possível estudar que as árvores
binárias possuem no máximo dois nós filhos para cada nó pai, ou seja, o grau
de cada nó pode ser 0, 1 ou 2. A Figura 118 apresenta uma árvore binária.
08 21 Nível - 1
04 09 55 Nível - 2
Figura 118 – Árvore binária.
Fonte: Elaborada pelo autor (2013).
B C D
subárvore
E F G
H
subárvore subárvore
subárvore
www.esab.edu.br 310
Observe que a árvore é formada pelos elementos: A, B, C, D, E, F, G
e H, mas também é constituída de três subárvores, com os elementos
A, B e E na primeira subárvore, apenas o nó C na segunda subárvore
e os nós D, F, G e H, na terceira subárvore. Segundo Celes, Cerqueira
e Rangel Netto (2004, p. 186), “[...] a subárvore é um conjunto de
elementos interligados da árvore”. Portanto, podemos também definir os
elementos D, F e G como uma subárvore, pois qualquer conjunto de nós
interligados é uma subárvore, independentemente de ser binária ou não.
Assim, a árvore genérica pode ser entendida como uma árvore que possui
um número variável de filhos e maior que dois.
www.esab.edu.br 311
Algoritmo 113 – Nó da árvore quaternária
01 struct noarvore {
02 struct noarvore* esq;
03 struct noarvore* dir;
04 struct noarvore * centro;
05 struct noarvore * proximo;
06 int valor;
07 };
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
NULL NULL NULL NULL NULL NULL NULL
NULL NULL NULL NULL NULL NULL NULL
www.esab.edu.br 312
A Figura 121 apresenta essa árvore.
Banda
Música Música Música Música Música Música Música Música Música Música Música
1 2 3 4 1 2 3 1 2 3 4
www.esab.edu.br 313
Note que a estrutura representa hierarquicamente as informações,
iniciando na banda, passando pelo álbum e chegando às músicas.
Até lá!
Atividade
Chegou a hora de você testar seus conhecimentos
em relação às unidades 26 a 36. Para isso, dirija-se ao
Ambiente Virtual de Aprendizagem (AVA) e responda
às questões. Além de revisar o conteúdo, você estará
se preparando para a prova. Bom trabalho!
www.esab.edu.br 314
Resumo
www.esab.edu.br 315
tem como sequência: E, D e R. Para implementação desses percursos na
forma de funções, é necessária a utilização da recursividade, ou seja, a
função chama a si própria.
www.esab.edu.br 316
37 Árvores – parte V
Objetivo
Apresentar exercícios comentados sobre árvores.
www.esab.edu.br 317
No final desta unidade, você terá o endereço de internet para fazer o
download de todo o código desenvolvido.
Vamos lá!
www.esab.edu.br 318
Algoritmo 115 − Inserção na Árvore Binária
01 void inserir(ArvoreBinaria *arvore,int
novovalor){
02 if ((*arvore) = = NULL) {
03 *arvore = (No *) malloc(sizeof(No));
04 (*arvore)->esq = NULL;
05 (*arvore)->dir = NULL;
06 (*arvore)->valor = novovalor;
07 }
08 if (novovalor < (*arvore)->valor) {
09 inserir((&(*arvore)->esq), novovalor);
10 }
11 else{
12 if (novovalor > (*arvore)->valor) {
13 inserir(&((*arvore)->dir),
novovalor);
14 }
15 }
16 }
www.esab.edu.br 319
Valores a serem inseridos:
[15, 21, 55, 08, 12, 33]
15 (raiz) 55 12 55
21 33
Insere 55 (3) Insere 12 (5)
se for maior -> direita se for maior -> direita
se for menor -> esquerda se for menor -> esquerda
15 (raiz) 15 (raiz)
21 08 21
55 12 55
www.esab.edu.br 320
Algoritmo 116 – Pesquisa na árvore binária
01 int verificar(ArvoreBinaria arvore,int chave){
02 if (arvore != NULL){
03 if(arvore->valor == chave){
04 return 1;
05 }
06 else{
07 if(arvore->valor < chave){
08 return verificar(arvore->dir,chave);
09 }
10 else{
11 return verificar(arvore-
>esq,chave);
12 }
13 }
14 }else{
15 return 0;
16 }
17 }
www.esab.edu.br 321
recursivamente até que o valor seja localizado (arvore->valor == chave),
retornando 1, ou até que se chegue em um endereço que vale NULL
(arvore == NULL). Isso significa que o valor não foi localizado e não há
mais nós à esquerda ou à direita (chegou a um nó folha da árvore), logo o
valor não será localizado e a função retornará 0 (falso).
www.esab.edu.br 322
Se o maior valor da árvore está à direita, o menor valor estará à esquerda
da árvore, uma vez que o menor valor está sempre à esquerda do nó
pai. Assim, muito semelhante ao acharMaior(), a função para achar o
menor valor começará a verificar, a partir da raiz, se existe um elemento
à esquerda do nó referência. Se existir, esse elemento à esquerda será
visitado recursivamente até que não exista um elemento à esquerda.
Logo, o último nó à esquerda representa o menor elemento inserido
na árvore. O Algoritmo 118 apresenta a lógica da função para achar o
menor valor.
Caro aluno, você pode fazer o download dos códigos desenvolvidos nesta unidade,
com as funções de inserção, verificação e localização do maior e menor valor clicando
aqui. Faça o download dos códigos e execute-os no seu computador, usando
a ferramenta de sua preferência, sendo que todo o código foi desenvolvido na
ferramenta DEV C++.
www.esab.edu.br 323
Nesta unidade, você pôde implementar as rotinas de inserção, consulta
e localização do maior e menor valor da árvore binária, sendo que todas
essas funções se caracterizam por ser funções recursivas, que chamam a
si próprias. Na função de consulta para verificação de um determinado
valor na árvore binária, a lógica aplicada foi descer à esquerda quando
o valor a ser localizado é menor que o valor do nó pesquisado, e à
direita quando o valor pesquisado é maior que o valor do nó visitado.
Esse processo de descida na árvore se repete até que o valor pesquisado
seja localizado ou até que o nó visitado não tenha mais filhos a serem
visitados. Já a localização do maior valor da árvore é realizada de forma
recursiva a partir do nó raiz – um percurso na árvore que chegue até o
elemento mais à direita, consequentemente o maior valor da árvore. Para
localização do menor valor da árvore, é realizado um percurso de forma
recursiva a partir do nó raiz na árvore que chegue até o elemento mais à
esquerda, consequentemente o menor valor da árvore.
www.esab.edu.br 324
Métodos de pesquisa e ordenação:
38 conceituação
Objetivo
Apresentar os conceitos dos métodos de pesquisa e ordenação.
www.esab.edu.br 325
38.1 Definição de pesquisa
Nesta unidade, vamos nos focar na discussão de algoritmos muito
utilizados, a pesquisa e a ordenação de dados. Começaremos pela
operação de busca que é muito comum em aplicações computacionais,
independentemente da estrutura de dados que é utilizada. Nós mesmos
implementamos rotinas de pesquisa de dados em vetores, listas
simplesmente encadeada, listas duplamente encadeadas e em árvores
binárias, nas unidades 17, 22 e 25.
www.esab.edu.br 326
(p)
0 1 2 3 0 1 2 3
10 4 12 7 10 4 12 7 vetor[p] == 12?
Localizar [ 12 ] Localizar [ 12 ]
(p) NÃO
0 1 2 3
10 4 12 7 vetor[p] == 12?
Localizar [ 12 ]
(p) NÃO
0 1 2 3
10 4 12 7 vetor[p] == 12?
Localizar [ 12 ]
SIM - ACHOU
www.esab.edu.br 327
Podemos notar que o algoritmo de pesquisa linear é realmente simples.
Este precisa apenas de uma variável para representar cada elemento
da estrutura de dados. Se o valor pesquisado não for localizado,
desloca-se para o próximo elemento da estrutura de dados. Também
podemos destacar que usando essa forma de pesquisa linear, quanto
maior o número de elementos da estrutura de dados, mais demorada
será a pesquisa, já que é preciso percorrer todos os elementos até que
se localize o elemento desejado ou é preciso que a estrutura de dados
seja percorrida por inteiro. Celes, Cerqueira e Rangel Netto (2004, p.
257), destacam que “[...] o desempenho computacional deste algoritmo
varia linearmente em relação ao tamanho do problema”, isto porque o
algoritmo terá que percorrer todos os elementos da estrutura de dados
para verificar se um determinado elemento existe ou não. Logo, é preciso
criar estratégias que possam acelerar o processo de pesquisa e uma delas é
ordenar os elementos da estrutura de dados, como veremos a seguir.
www.esab.edu.br 328
Elementos para inserção: 10, 15, 12, 44
Insere o 10
Insere o 10 no ínicio
10
Insere o 15
Insere o 15 no final
10 15
Insere o 12
Insere o 12 entre o 10 e o 15
10 12 15
Insere o 12
Insere o 44 no final
10 12 15 44
Figura 124 – Ordenação na inserção.
Fonte: Elaborada pelo autor (2013).
10 08 18 23 24
08 10 18 23 24
08 10 18 23 24
www.esab.edu.br 329
Observe que o vetor foi previamente preenchido e os valores estão
desordenados. Para a ordenação de forma crescente, a cada passo da
ordenação, pelo menos um dos valores existentes no vetor será ordenado,
os maiores valores serão deslocados para o final do vetor e os menores
para o início do vetor. Ao final do processo de ordenação, todos os
valores estarão classificados, mas até lá o vetor esteve desordenado em
alguns momentos. Essa forma de ordenação é conhecida como algoritmo
de troca, uma vez que os valores são trocados durante o processo de
classificação. Por exemplo, na ordenação do valor 23, esse valor foi
trocado de posição com os valores 10, 8, 18, sucessivamente, até que o
valor 23 ficasse na posição correta (antes do valor 24).
www.esab.edu.br 330
disso, a ordenação pode ocorrer no momento da inserção do elemento na
estrutura de dados, de forma que a estrutura esteja sempre ordenada, ou
a partir de um conjunto de elementos já inseridos na estrutura de dados.
www.esab.edu.br 331
Métodos de ordenação: bubble sort
39 – parte 1
Objetivo
Apresentar o conceito e o funcionamento do algoritmo bubble sort.
39.1 Definição
O algoritmo de ordenação bolha, ou bubble sort, recebe esse nome
como representação da sua forma de funcionamento, que empurra os
maiores valores para o final da estrutura de dados quando da ordenação
crescente. Caso a ordenação seja decrescente, o sentido da bolha muda
e os maiores valores são deslocados para o início da estrutura de dados.
A ideia fundamental do algoritmo é realizar uma série de comparações
dos elementos da estrutura de dados, em pares. Quando esses dois
www.esab.edu.br 332
elementos comparados estão fora de ordem (crescente ou decrescente), há
uma troca de posição entre eles, de forma que o primeiro elemento seja
comparado com o segundo, o segundo elemento com o terceiro e assim
sucessivamente, até que seja comparado o penúltimo elemento com o
último (CELES; CERQUEIRA; RANGEL NETTO, 2004). A Figura
127 apresenta esse processo de comparação.
0 1 2 3
0 1 2 3
0 1 2 3
0 1 2 3
Figura 127 – Ordenação do método de bolha.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 333
PASSO 1 PASSO 2 PASSO 3
0 1 2 3 0 1 2 3 0 1 2 3
0 1 2 3 0 1 2 3 0 1 2 3
(maior)
0 1 2 3 0 1 2 3
(maior)
0 1 2 3
(maior)
Figura 128 – Ordenação do método de bolha: passo a passo.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 334
39.2 Funcionamento do algoritmo
Como foi visto na seção anterior, o algoritmo do método de bolha
(bubble sort) consiste em percorrer os elementos do vetor, em pares,
comparando se esses elementos do par estão fora de ordem. Se a
ordenação for crescente, para que os elementos sejam considerados fora
de ordem, o elemento da esquerda do par deve ser maior que o elemento
da direita do par. Já se a ordenação for decrescente, o par é considerado
fora de ordem se o elemento da esquerda do par é menor que o elemento
da direita. A Figura 129 apresenta essa regra:
Exemplo 1 Exemplo 2
0 1 2 3 0 1 2 3
10 8 ordem crescente 18 21 ordem decrescente
www.esab.edu.br 335
Agora que sabemos como o método de bolha identifica se os elementos
do par estão em desordem, podemos nos questionar: como ele faz para
organizá-los?
Note que o primeiro passo (1) para ordenação do par formado pelos
valores das posições 0 e 1, que são 10 e 8, respectivamente, é copiar o
valor da posição 0 do vetor para uma variável auxiliar, pois no momento
em que o 8, menor valor do par, for copiado para posição 0, o valor
10 será perdido. Após a cópia do valor da posição da esquerda para a
variável auxiliar, o valor da posição da direita do par pode ser copiado
para a posição da esquerda do par, conforme o passo (2) na Figura 130.
Por fim, o valor armazenado na variável auxiliar é copiado para a posição
da direita do par (3), resultando em um novo par com o menor valor à
esquerda e o maior valor do par à direita. Pronto, o par estará ordenado,
mas não significa que todo o vetor esteja ordenado. Logo, o processo
precisa continuar até que todos os elementos estejam ordenados, como
pode ser observado na Figura 131.
www.esab.edu.br 336
Processo 1 Processo 2 Processo 3
0 1 2 3 0 1 2 3 0 1 2 3
10 8 5 21 8 5 10 21 5 8 10 21
0 1 2 3 0 1 2 3 0 1 2 3
8 10 5 21 5 8 10 21 5 8 10 21
(ordenados)
0 1 2 3 0 1 2 3
8 5 10 21 5 8 10 21
(ordenados)
0 1 2 3
8 5 10 21
(ordenado)
www.esab.edu.br 337
foram realizadas três comparações das posições: 0 com 1, 1 com 2 e 2
com 3. Ou seja, n(4) – processo (1) = 3 comparações. Já no processo
dois, como um elemento já estará ordenado antes de o processo começar
(ordenado no processo um), as comparações foram das posições: 0 com
1, e 1 com 2, ou seja duas comparações (n vale 4 e o processo vale 2,
então n-2 = 2). Por fim, no processo três foi realizada uma comparação
das posições 0 e 1 (n=4 – processo = 3, resultado: 1 comparação).
www.esab.edu.br 338
Métodos de ordenação: bubble sort
40 – parte 2
Objetivo
Apresentar a codificação do algoritmo bubble sort.
www.esab.edu.br 339
Vamos iniciar codificando o algoritmo de ordenação do método de
bolha, na forma de uma função, e depois vamos desenvolver um
algoritmo como inserção, listagem e ordenação de um vetor para
validação do algoritmo apresentado.
www.esab.edu.br 340
Por fim, o valor da variável aux é copiada para a posição da direita do
vetor, na instrução vetor[dir] = aux da linha 04 do algoritmo.
www.esab.edu.br 341
Celes, Cerqueira e Rangel Netto (2004, p. 242), com relação ao uso da
variável houve_troca destacam que essa técnica “[...] evita que o processo
continue mesmo depois de o vetor estar ordenado, o que permite
interromper o processo quando houver uma passagem inteira sem trocas”.
www.esab.edu.br 342
40.2 Exercícios comentados
Para inserir os elementos no vetor, será desenvolvida uma função que
recebe como parâmetro o vetor que será manipulado e o tamanho do
vetor, conforme o Algoritmo 121.
www.esab.edu.br 343
A função listar recebe como parâmetros o vetor que será manipulado (int
vetor[]) e o tamanho desse vetor (int tamanho), e a partir da posição 0
do vetor (int i=0) são percorridos e listados todos os valores cadastrados
no vetor, da posição 0 até a última posição, tamanho-1 (instrução
for(i=0;i<tamanho;i++) da linha 03).
www.esab.edu.br 344
Você pode baixar e testar o algoritmo com todas as funções apresentadas nesta
unidade clicando aqui. Para testar com um novo tamanho de vetor, basta alterar o
tamanho do vetor dados na sua definição, na linha 02 do algoritmo, só não se esqueça
de alterar na chamada das funções inserir(), listar e ordenar_bolha() o tamanho, que
era 5, para o novo tamanho que foi definido.
www.esab.edu.br 345
Métodos de ordenação: quick sort
41 – parte 1
Objetivo
Apresentar o conceito e o funcionamento do algoritmo quick sort.
Por fim, você pôde implementar uma função de menu() para melhorar a
interação do usuário como o sistema.
www.esab.edu.br 346
41.1 Definição
O algoritmo de ordenação quick sort possui como principal característica
a ordenação do vetor em partes, tendo como referência um elemento
chamado de pivô, que pode ser qualquer elemento já existente no vetor.
O pivô pode ser o primeiro ou o último, ou seja, qualquer elemento já
cadastrado no vetor, sendo muito comum a utilização do vetor como
sendo o elemento do meio do vetor. A cada execução do processo de
ordenação, os elementos do vetor são comparados com o elemento pivô,
de forma que os valores menores que o pivô fiquem à sua esquerda e os
elementos maiores que o pivô, à sua direita, o que resulta na ordenação
do pivô, ou seja, o pivô está na posição correta. Estando o pivô ordenado,
o vetor será dividido em duas partes, a parte à esquerda do pivô e a parte
à direita do pivô. Cada uma dessas partes define um pivô como referência
e os valores são ordenados, até que se tenham os menores à esquerda do
pivô e os maiores à direita. Esse processo se repete até que todo o vetor
esteja ordenado.
www.esab.edu.br 347
A definição da posição do elemento pivô se dá pela soma da posição
inicial com a posição final do segmento, dividido por dois. Como não há
posição de vetor com número real, é considerada apenas a parte inteira
do resultado da divisão como valor de posição do pivô. Como o primeiro
segmento é o vetor inteiro, a posição inicial é 0 e a posição final 5, já que
temos um vetor de seis posições.
0 1 2 3 4 5
5 8 3 1 6 2
pivô
(parte 1) (parte 2)
Figura 133 – Vetor particionado.
Fonte: Elaborada pelo autor (2013).
0 1 2 3 4 5 0 1 2 3 4 5
5 8 3 1 6 2 2 8 3 1 6 5
pivô pivô
(parte 1) (parte 2) (parte 1) (parte 2)
Figura 134 – Ordenação por quick sort.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 348
valor maior valor menor trocar
0 1 2 3 4 5 0 1 2 3 4 5
2 8 3 1 6 5 2 1 3 8 6 5
pivô pivô
(parte 1) (parte 2) (parte 1) (parte 2)
Figura 135 – Ordenação por quick sort.
Fonte: Elaborada pelo autor (2013).
0 1 3 4 5
2 1 8 6 5
(pivô) (pivô)
trocar trocar
0 1 3 4 5
1 2 5 6 8
(pivô) (pivô)
Figura 136 – Ordenação por quick sort.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 349
2. No segmento representado pelos valores 3 a 5, o pivô foi definido
como sendo o elemento da posição 4 (pivô = 3 + 5/2, pivô vale 4) e o
maior valor à esquerda do pivô é o valor 8, e o menor valor à direita é o
valor 5. Fazendo a troca desses valores, o segmento estará ordenado: 5,
6 e 8. Com a ordenação do segmento um (valores 1 e 2), do primeiro
pivô (valor 3), e do segundo segmento (valores 5, 6, 8 ) o vetor estará
ordenado:1, 2, 3, 5, 6 e 8.
Pode nos parecer que o algoritmo de quick sort seja demorado pelo
fato de termos vários processos e novos segmentos, mas é exatamente
essa característica que o torna eficiente ao ordenar vários segmentos ao
mesmo tempo, de forma recursiva, como veremos a seguir.
www.esab.edu.br 350
A ideia principal do algoritmo é particionar o vetor em dois e ordenar os
valores de forma que os valores menores que o pivô fiquem à sua esquerda
e os maiores que o valor do pivô, à sua direita. A implementação do quick
sort é realizada de forma recursiva para tornar mais eficiente a ordenação
dos dois segmentos gerados a partir do vetor original.
0 1 2 3 4 5
2 1 3 8 6 5 (0 a 5)
0 1 3 4 5
(0 a 1) 2 1 8 6 5 (3 a 5)
0 1 3 4 5
1 2 5 6 8
0 1 3 4 5
1 2 5 6 8
(0 a 0) (1 a 1) (3 a 3) (4 a 4)
www.esab.edu.br 351
Nesta unidade, você pôde conhecer as características que definem o
método de ordenação quick sort, cuja ideia principal é particionar
o vetor em duas partes e ordenar os valores de forma que os valores
menores que o valor do pivô fiquem à sua esquerda e os maiores que o
valor do pivô à sua direita. A implementação do quick sort é realizada
de forma recursiva para tornar mais eficiente a ordenação dos dois
segmentos gerados a partir do vetor original, e pode ser representada por
três ações importantes: dividir, conquistar e combinar.
www.esab.edu.br 352
Métodos de ordenação: quick sort
42 – parte 2
Objetivo
Apresentar a codificação do algoritmo quick sort.
www.esab.edu.br 353
42.1 Codificação do algoritmo
O algoritmo de quick sort pode ser dividido em duas operações
principais, a troca dos elementos que estão fora de ordem, e a ordenação
da estrutura de dados que executa várias divisões da estrutura de dados,
de forma recursiva. A operação de troca de valores na estrutura de dados
já foi utilizada por nós no algoritmo de ordenação bubble sort, o método
de bolha, por isso vamos apenas apresentá-la.
Essa função trocar será utilizada na função de ordenação, como pode ser
visto no Algoritmo 125.
www.esab.edu.br 354
Algoritmo 125 – Ordenação por quick sort
01 void ordenar_quick(int vetor[], int inicial,
int final){
02 int i,j,pivo;
03 i = inicial;
04 j = final;
05 pivo = vetor[(inicial+final)/2];
06 while (i <= j){
07 while (vetor[i] < pivo && i < final)
i++;
08 while (vetor[j] > pivo && j > inicial)
j--;
09 if (i <= j){
10 trocar(vetor,i,j);
11 i++;
12 j--;
13 }
14 }
15 if( j > inicial) ordenar_
quick(vetor,inicial,j);
16 if( i < final) ordenar_
quick(vetor,i,final);
17 }
www.esab.edu.br 355
Saiba mais
Caro aluno, você pode conhecer um pouco mais
sobre as operações aritméticas em C clicando aqui.
www.esab.edu.br 356
Na linha 08, a instrução while (vetor[j] > pivo && j > inicial) j--; tem
como finalidade percorrer os elementos do vetor, posição por posição,
a partir da posição final do vetor, até que seja encontrado um valor
menor ou igual ao valor do pivô ou até que se chegue no início do vetor.
A instrução tem como objetivo armazenar na variável “j” a posição do
menor valor à direita do pivô.
www.esab.edu.br 357
42.2 Exercícios comentados
Para a ordenação do vetor utilizando o algoritmo de quick sort, é
necessário que sejam informados o vetor que será manipulado e as
posições de início e final do vetor. O Algoritmo 126 apresenta utilização
da função ordenar_quick para um vetor de inteiros com cinco posições.
www.esab.edu.br 358
e à direita do elemento pivô serão trocados até que o valor do pivô esteja na
posição correta. A partir da ordenação do pivô, dois novos segmentos do
vetor serão gerados, da posição inicial do vetor até a posição do pivô -1, e
da posição do pivô +1 até a posição final do vetor.
www.esab.edu.br 359
Resumo
www.esab.edu.br 360
Na unidade 40 foram implementadas duas regras fundamentais para o
funcionamento do algoritmo de bolha: a função trocar, que tem como
finalidade inverter o par de elementos do vetor que estão fora de ordem;
e a função ordenar_bolha(), que controla o número de processos e
comparações que serão realizados para que o vetor fique ordenado.
www.esab.edu.br 361
43 Métodos de ordenação: merge sort
Objetivo
Apresentar o conceito e o funcionamento do algoritmo merge sort.
www.esab.edu.br 362
43.1 Definição
O algoritmo de ordenação merge sort se fundamenta em três processos:
divisão, conquista e combinação, de forma que a estrutura de dados
a ser ordenada seja dividida em partes menores (segmentos), que são
ordenadas em separado e, posteriormente, unidas, resultando em uma
estrutura de dados ordenada. A Figura 139 representa o processo de
ordenação por merge sort.
Vetor
Merge
Merge Merge
www.esab.edu.br 363
43.2 Funcionamento do algoritmo
O funcionamento do algoritmo visa garantir os três passos para a
ordenação da estrutura de dados: dividir, conquistar e combinar, e, para
isso, possui algumas regras de funcionamento, apresentadas na Figura 140.
MERGE SORT
Dividir o Vetor
inicial em dois
segmentos.
Divida o
COMBINAR Segmento
AS PARTES no Meio.
SEGMENTADA
Número de Ordenar a
NÃO SIM PRIMEIRA
elementos do
Vetor Segmento > 1 METADE
ORDENADO. recursivamente.
Ordenar a
SEGUNDA
METADE
recursivamente.
www.esab.edu.br 364
Vamos aplicar essas regras para um hipotético vetor com os elementos
38, 16, 27, 39, 12 e 27, representado na Figura 141.
0 1 2 3 4 5
38 16 27 39 12 27
0 1 2 3 4 5
38 16 27 39 12 27
Note que ainda há segmentos com mais de um elemento, e que, por isso,
vamos realizar mais uma divisão, conforme consta na Figura 143.
0 1 2 3 4 5
38 16 27 39 12 27
0 1 2 3 4 5
38 16 27 39 12 27
Figura 143 – Segunda segmentação do vetor.
Fonte: Elaborada pelo autor (2013).
www.esab.edu.br 365
Note que ainda há segmentos com mais de um elemento, e que, por isso,
vamos realizar mais uma divisão, conforme a Figura 144.
0 1 2 3 4 5
38 16 27 39 12 27
0 1 2 3 4 5
38 16 27 39 12 27
0 1 3 4
38 16 39 12
0 1 2 3 4 5
16 38 27 12 39 27
0 1 3 4
Ordenação 38 16 39 12 Ordenação
www.esab.edu.br 366
Note que os últimos segmentos criados estão ordenados crescentemente.
Agora, vamos combinar o segmento ordenado com o segmento não
ordenado, de forma que o vetor gerado fique ordenado, conforme a
Figura 146.
0 1 2 3 4 5
16 38 27 12 39 27
16 27 38 12 27 39
Note que cada segmento está ordenado, porém, ainda temos dois
segmentos formados pelos elementos das posições 0 a 2 e os elementos
das posições 3 a 5. Vamos combinar os elementos dos dois segmentos
para gerar um único vetor ordenado.
entrada saída
7 5 2 4 1 6 3 0 0 1 2 3 4 5 6 7
7 5 2 4 1 6 3 0 2 4 5 7 0 1 3 6
dividir combinar
7 5 2 4 1 6 3 0 5 7 2 4 1 6 0 3
7 5 2 4 1 6 3 0 7 5 2 4 1 6 3 0
www.esab.edu.br 367
do primeiro segmento e o valor 27 (posição p2) do segundo segmento,
sendo, consequentemente, o valor 16 colocado na posição correta. Dessa
forma, sempre que um valor é colocado na posição correta, o elemento
à frente desse segmento será utilizado para a próxima comparação,
representado por p1 no primeiro segmento e por p2 no segundo
segmento. Isso possibilita que se saiba quais valores serão comparados
(elementos da posição p1 com elementos da posição p2), sendo que o
menor valor entre eles será colocado na posição correta.
www.esab.edu.br 368
44 Métodos de ordenação: merge sort
Objetivo
Apresentar a codificação do algoritmo merge sort.
www.esab.edu.br 369
Primeiramente, vamos conhecer a codificação da rotina de combinação,
representada no Algoritmo 127.
www.esab.edu.br 370
As instruções das linhas 10 a 12 copiam os elementos ordenados do
primeiro segmento (os quais não precisaram ser trocados, pois já estavam
ordenados) para o vetor auxiliar, assim como as instruções das linhas 13
a 15 copiam os elementos já ordenados do segundo segmento para o
vetor auxiliar. Por fim, as instruções das linhas 16 a 18 copiam todos os
elementos que já estão ordenados no vetor auxiliar para o vetor principal,
deixando-o ordenado.
www.esab.edu.br 371
44.2 Exercícios comentados
Vamos desenvolver um exercício no qual o usuário informa um
conjunto de valores inteiros que serão armazenados no vetor, e que,
posteriormente, serão ordenados por meio do algoritmo de merge sort.
Além de cadastrar os valores no vetor e ordená-lo, o usuário da aplicação
também poderá listar os elementos do vetor. Para a interação do
usuário com o sistema, criaremos um menu com as opções de inserção,
ordenação, listagem e sair do sistema.
Observe que os dois vetores devem ter o mesmo tamanho, pois todos os
valores do vetor dados serão copiados para o vetor auxiliar (aux).
www.esab.edu.br 372
Como o vetor de dados possui tamanho valendo 10, o usuário poderá
cadastrar 10 elementos do tipo inteiro no vetor. Para listar os elementos
do vetor, faremos uma função para percorrer e mostrar, na tela, todos os
10 elementos do vetor dados, conforme o Algoritmo 131.
Vamos criar uma função de menu para que o usuário possa escolher qual
operação ele deseja utilizar no sistema. O Algoritmo 132 apresenta a
função menu.
www.esab.edu.br 373
Algoritmo 132 – Função menu
01 void menu(){
02 int op=0;
03 do{
04 system("cls");
05 printf("1-Inserir\n2-Listar\n3-Ordenar\n4-
Sair\nEscolha uma das opcoes:\n");
06 scanf("%d",&op);
07 switch(op){
08 case 1:inserir();break;
09 case 2:listar();break;
10 case 3:segmentacao(dados,aux,0,9);
11 printf("\nOrdenado com
Sucesso!\n");
12 system("pause");
13 break;
14 }
15 }while(op!=4);
16 }
www.esab.edu.br 374
Observe que, na função main, a função menu é chamada para que sejam
apresentadas as opções para o usuário, quem poderá preencher, listar e
ordenar o vetor várias vezes e, para sair do sistema, basta escolher a opção
4 do menu. Não se esqueça de incluir, no algoritmo, nas duas primeiras
linhas, as chamadas para as bibliotecas stdio.h e stdlib.h. Para isso, insira as
instruções, #include “stdio.h” e #include “stdlib.h”, uma em cada linha.
Caro aluno, você pode fazer o download dos códigos desenvolvidos nesta unidade
clicando aqui. Faça o download do código e execute-o no seu computador, usando
a ferramenta de sua preferência, sendo que todo o código foi desenvolvido na
ferramenta DEV C++.
www.esab.edu.br 375
Métodos de pesquisa: busca em
45 vetor
Objetivo
Apresentar os métodos de pesquisa utilizando vetor.
Você viu que a função segmentação tem como objetivo dividir o vetor
em dois segmentos, e que cada um desses segmentos será ordenado por
meio da função combinação.
Nesta unidade, vamos estudar as formas de busca no vetor, que pode se dar
por meio de dois tipos de pesquisa, sequencial ou binária. Esta unidade foi
desenvolvida com base em Celes, Cerqueira e Rangel Netto (2004).
www.esab.edu.br 376
45.1 Busca sequencial
A forma mais simples de busca em um vetor consiste em percorrer o
vetor, elemento a elemento, para verificar se o elemento de interesse
existe no vetor, ou seja, se algum dos elementos do vetor é igual ao
elemento desejado (CELES; CERQUEIRA; RANGEL NETTO, 2004).
O Algoritmo 134 apresenta uma rotina de pesquisa sequencial.
www.esab.edu.br 377
Uma estratégia para tornar o algoritmo mais eficiente é considerar que
a estrutura de dados já está ordenada. Por exemplo, se temos um vetor
com elemento 3, 8, 15 e 21 e desejamos buscar o elemento 12, não será
preciso pesquisar todos os elementos do vetor. Isso acontece porque,
como o valor 12 é menor que 15, se ele não for achado até a posição do
valor 15, não é preciso continuar a busca – o valor (12) não tem como
existir nas posições à frente em um vetor ordenado. O Algoritmo 135
apresenta essa nova regra para a busca sequencial.
www.esab.edu.br 378
“[...] apresenta um desempenho ligeiramente superior ao primeiro, mas
a ordem dessa versão do algoritmo continua sendo linear [...]”, o que o
torna ainda demorado.
www.esab.edu.br 379
buscar (99)
10, 15, 21, 44, 66, 77, 88, 99
meio procura
para frente
não será
pesquisado
Figura 148 – Busca sequencial.
Fonte: Elaborada pelo autor (2013).
Note que o valor desejado, 99, encontra-se após o valor do meio (66);
dessa forma, todos os elementos anteriores ao meio, inclusive o meio
(66), não farão parte da nova busca, que será realizada apenas com os
elementos 77, 88 e 99. Será gerado um novo meio, que será o valor 88, e,
como o valor 99 é maior que 88, a pesquisa continuará a partir do valor
99, descartando todos os elementos anteriores ao meio e o próprio meio,
sobrando apenas o valor 99 a ser pesquisado. Como só há o valor 99, ele é
o meio do segmento, e, ao compará-lo com o valor desejado, o resultado
será de valor localizado. Caso o valor desejado não estivesse na posição do
valor 99, isso indicaria que o valor desejado não existe no vetor.
www.esab.edu.br 380
Algoritmo 136 – Busca binária
01 int pesquisa_binaria(int vetor[],int pi,int
pf,int chave){
02 if (pi > pf) return 0;
03 int meio = (pi+pf) / 2;
04 if (vetor[meio] == chave){
05 return 1; //achou
06 }
07 else{
08 if(vetor[meio] > chave){
09 return pesquisa_
binaria(vetor,0,meio-1, chave);
10 }else{
11 return pesquisa_
binaria(vetor,meio+1,pf,chave);
12 }
13 }
14 }
Note que, caso a posição inicial do vetor seja maior que a posição final,
o valor pesquisado (chave) não existe no vetor e a função retornará
0 (falso). Se o valor da posição do meio do vetor for igual ao valor
pesquisado (chave), a função retornará 1 (verdadeiro). Caso contrário,
se o valor do meio do vetor for maior que o valor procurado, a função
pesquisa_binária será chamada, recursivamente, na linha 9 para pesquisar
os elementos anteriores ao meio do vetor. Ou, ainda, se o valor do meio
do vetor for menor que o valor procurado, a função pesquisa_binária
será chamada, recursivamente, na linha 11 para pesquisar os elementos
posteriores ao meio do vetor.
Caro aluno, você pode fazer o download dos códigos desenvolvidos nesta unidade
clicando aqui. Faça o download do código e execute-o no seu computador usando
a ferramenta de sua preferência, sendo que todo o código foi desenvolvido na
ferramenta DEV C++.
www.esab.edu.br 381
Nesta unidade, você pôde estudar a busca sequencial, entendendo que
a forma mais simples de busca em um vetor consiste em percorrê-lo,
elemento a elemento, para verificar se o elemento de interesse existe
no vetor, ou seja, se algum dos elementos do vetor é igual ao elemento
desejado. Trata-se de um algoritmo bastante simples, desenvolvido com
um comando de laço e um comando de decisão, o que o torna fácil de
ser implementado e entendido. Porém, a sua eficiência é baixa, já que é
preciso percorrer todos os elementos do vetor, desde a primeira posição,
até que seja localizado o valor desejado ou o vetor se encerre. Logo,
quanto maior o vetor, mais demorada será a pesquisa sequencial. Uma
estratégia para tornar o algoritmo de pesquisa sequencial mais eficiente é
considerar que a estrutura de dados já está ordenada; assim, o algoritmo
ficará mais rápido. Porém, como o algoritmo continuará fazendo uma
busca sequencial, elemento por elemento, ainda será demorado.
Estudo complementar
Para saber mais sobre pesquisa binária, acesse o
vídeo explicativo.
Você pôde estudar, ainda, que, para vetores ordenados, deve-se usar a busca
binária, que tem como ideia buscar o elemento desejado comparando-o
com o elemento do meio do vetor. Se o valor do meio do vetor for igual
ao elemento desejado, podemos parar a busca, pois o valor foi localizado.
No entanto, se o valor do meio do vetor for maior que o valor desejado,
uma nova procura será realizada da posição inicial até a posição anterior ao
meio do vetor. Se o valor do meio do vetor for menor que o valor desejado,
a busca será da posição do meio + 1 até a posição final do vetor. Se o valor
não for localizado, para cada novo segmento em que se continuará a busca
será definido um novo meio e todo o processo se repete, dividindo o vetor
e descartando uma das partes da divisão até que se ache o valor desejado,
ou seja, sinalizado que ele não existe no vetor.
www.esab.edu.br 382
Métodos de pesquisa: árvore
46 binária de pesquisa
Objetivo
Apresentar o método de pesquisa utilizando árvore binária de
pesquisa.
www.esab.edu.br 383
46.1 Busca em árvore binária de pesquisa
Na unidade 33, realizamos o estudo da estrutura de dados árvore binária,
assim nomeada por possuir, no máximo, dois filhos para cada nó. Segundo
Puga (2009, p. 234), as árvores binárias “[...] possuem nós com grau
menor ou igual a 2, isto é, nenhum nó possui mais que dois descendentes
diretos (dois filhos)”. A Figura 149 apresenta uma árvore binária.
A árvore binária é “[...] uma estrutura de dados não linear, que possui
propriedades especiais e admite muitas operações, como; pesquisa,
inserção, remoção, e são úteis para implementação de algoritmos
que necessitam de estruturas hierárquicas” (PUGA, 2009, p. 230). A
árvore é uma estrutura de dados bem peculiar e, por isso, possui uma
terminologia própria para a identificação de seus elementos e grupo de
elementos, por exemplo:
www.esab.edu.br 384
Uma árvore binária pode ser classificada como balanceada ou não
balanceada. Uma árvore binária balanceada se caracteriza por ter,
para cada nó, a seguinte regra: os valores menores que o nó pai ficam
sempre à esquerda, enquanto que os valores maiores que o nó pai ficam
sempre à direita. Consequentemente, na árvore binária não balanceada
os valores não possuem regra de organização e os valores são inseridos
aleatoriamente na estrutura de dados. A Figura 150 apresenta os dois
tipos de árvores binárias.
(-) (+)
8 4
3 10 13 3
1 6 14 1 6 14
4 7 13 10 7 8
www.esab.edu.br 385
A forma de se buscar um elemento na árvore balanceada é diferente da
busca na árvore não balanceada. Nesta unidade, abordaremos a busca na
árvore não balanceada e, na próxima unidade, faremos o estudo da busca
em árvores balanceadas. Na árvore binária, a busca é mais inteligente, já
que os valores estão organizados. Já na árvore não balanceada, é preciso
percorrer todos os seus elementos para localizar o valor desejado. A busca
na árvore corresponde à trajetória (caminho percorrido de um nó origem
até um nó destino) do nó raiz até se chegar ao valor desejado ou até que
seja sinalizado que o valor não existe.
www.esab.edu.br 386
K
A L
M P
www.esab.edu.br 387
Note que o processo é muito semelhante ao da busca sequencial, pois
todos os elementos são visitados, um a um, até que se localize o elemento
desejado. Dessa forma, quanto mais elementos tivermos na árvore, mais
demorada tende a ser a pesquisa. Portanto, utilizar as formas de percurso
na árvore binária não balanceada não se trata de uma boa estratégia para
a busca de elementos, principalmente se a estrutura tende a armazenar
muitos deles.
www.esab.edu.br 388
Métodos de pesquisa: árvore
47 balanceada
Objetivo
Apresentar o método de pesquisa utilizando árvores balanceadas.
www.esab.edu.br 389
47.1 Busca em árvores balanceadas
Vamos começar revisando o conceito de árvore binária balanceada e
como essa árvore é gerada. Na unidade 34, abordamos esse assunto
definindo o balanceamento da árvore como sendo uma importante
propriedade na qual os valores menores que o seu pai ficam à sua
esquerda, enquanto que os valores maiores que seu pai ficam à sua direita
(CELES; CERQUEIRA; RANGEL NETTO, 2004). Vamos utilizar
essa propriedade para desenvolver a busca na árvore binária. A Figura
152 apresenta uma árvore binária balanceada que armazena os valores
informados pelo usuário, números inteiros, de forma que os valores
menores fiquem à esquerda e, os maiores, à direita.
15 (raiz) 55 12 55
21 33
Insere 55 (3) Insere 12 (5)
se for maior -> direita se for maior -> direita
se for menor -> esquerda se for menor -> esquerda
15 (raiz) 15 (raiz)
21 08 21
55 12 55
www.esab.edu.br 390
Observe que a primeira inserção define o valor que será a raiz da árvore.
A cada inserção, é verificado se o valor é maior ou menor que o valor da
raiz. Se for maior, o valor é inserido à direita; se for menor, é inserido à
esquerda. Porém, essas posições já podem ter sido ocupadas por outra
inserção anterior, logo, é preciso que o processo de inserção faça uma nova
verificação do valor que está na posição que seria a inserção, avaliando
se o valor a ser inserido é maior ou menor que esse valor já inserido. Se
for maior que o valor já inserido, o novo valor é inserido à direita; se
for menor que o valor já inserido, o novo valor é inserido à esquerda. O
processo se repete para cada nó que já está ocupado até que se ache uma
posição livre para inserção. Por exemplo, a Figura 152 apresenta o processo
para a inserção do valor 33. Como ele é maior que o nó 15 (raiz), deve ser
inserido à direita do nó 15, porém, nessa posição já existe o nó 21. Então,
é perguntado ao nó 21 se o valor 33 é maior ou menor que ele (21). Como
é maior, deve ser inserido à direita. No entanto, já existe o nó 55 nessa
posição, então, é perguntado ao nó 55 se 33 é maior ou menor que ele.
Como é menor, é inserido à esquerda do nó 55.
www.esab.edu.br 391
Algoritmo 137 – Inserção na árvore binária
01 void inserir(ArvoreBinaria *arvore,int
novovalor){
02 if ((*arvore) = = NULL) {
03 *arvore = (No *) malloc(sizeof(No));
04 (*arvore)->esq = NULL;
05 (*arvore)->dir = NULL;
06 (*arvore)->valor = novovalor;
07 }
08 if (novovalor < (*arvore)->valor) {
09 inserir((&(*arvore)->esq, novovalor);
10 }
11 else{
12 if (novovalor > (*arvore)->valor) {
13 inserir(&((*arvore)->dir, novovalor);
14 }
15 }
16 }
www.esab.edu.br 392
15
8 21
12 55
33
8 21
12 55
33
www.esab.edu.br 393
15
subárvore
8 21
12 55
33
Dessa forma, para buscar o valor 55, a árvore com os elementos 15, 8,
12, 21, 55 e 33 pesquisou os elementos 15, 21 e 55 e obteve sucesso. Se a
pesquisa fosse sequencial, seriam pesquisados os valores 15, 8, 12, 21 e 55
para que fosse obtido o mesmo resultado, ou seja, o valor existe na árvore.
www.esab.edu.br 394
for menor, é inserido à esquerda do valor já inserido. O processo se repete
para cada nó que já está ocupado, até que se ache uma posição livre para
a inserção. Assim, podemos concluir que todos os nós possuem os valores
menores que ele à esquerda e maiores à direita.
Tarefa dissertativa
Caro estudante, convidamos você a acessar o
Ambiente Virtual de Aprendizagem e realizar a
tarefa dissertativa.
www.esab.edu.br 395
48 Exercícios de fixação
Objetivo
Abordar exemplos de exercícios resolvidos e comentados.
www.esab.edu.br 396
Exercícios de fixação
www.esab.edu.br 397
ou maior, sendo os valores menores inseridos à esquerda e os maiores à
direita do nó pesquisa. Caso a posição de inserção já esteja preenchida,
é preciso “descer” nos níveis da árvore, até localizar uma posição de
inserção disponível. O Algoritmo 139 apresenta a função de inserção na
árvore binária.
www.esab.edu.br 398
avaliado (&((*arvore)->esq) e também o novo valor a ser inserido. Caso
o novo valor não seja menor que o valor do nó que está sendo verificado,
porém, seja maior (linha 12), função inserir é chamada, recursivamente,
na linha 13, recebendo, como parâmetro, o endereço do filho da direita
do nó que está sendo avaliado (&((*arvore)->dir) e também o novo valor
a ser inserido. Na chamada recursiva, com o endereço da esquerda ou da
direita, junto com o valor a ser inserido, a função verificará, novamente
na linha 02, se essa nova referência está livre (NULL). Se estiver, o nó é
criado, com esquerda e direita valendo NULL e o novo valor gravado no
nó. Caso contrário, o processo recursivo se repete (descendo na árvore)
até que um nó vazio seja localizado.
Vamos desenvolver uma função para listar os nós da árvore, a qual deverá
ser recursiva, de forma que a listagem comece no elemento da esquerda,
passe pelo elemento da raiz e, por fim, pelo elemento da direita de cada nó
que não seja NULL. O Algoritmo 140 apresenta essa forma de listagem.
www.esab.edu.br 399
Algoritmo 141 – Função buscar
01 int buscar(ArvoreBinaria no, int chave){
02 if(no == NULL){
03 return 0;
04 }
05 else{
06 if (no->valor == chave){
07 return 1;
08 }else{
09 if (no->valor > chave){
10 return buscar(no->esq,chave);
11 }else{
12 return buscar(no->dir,chave);
13 }
14 }
Por fim, vamos desenvolver uma função de menu que permita ao usuário
escolher qual função ele deseja executar na aplicação. O Algoritmo 142
apresenta essa função.
www.esab.edu.br 400
Algoritmo 142 – Função menu
01 void menu(){
02 ArvoreBinaria binaria;
03 criarArvore(&binaria);
04 int op = 0,valor;
05 do{
06 system("CLS");
07 printf("1-Inserir\n2-Listar\n3-Buscar\
n4-Sair\nEscolha uma opcao:");
08 scanf("%d",&op);
09 switch(op){
10 case 1:printf("Valor:\n");
11 scanf("%d",&valor);
12 inserir(&binaria,valor);
13 break;
14 case 2:
15 exibirNos(binaria);
16 system("pause");
17 break;
18 case 3:
19 printf("Valor:\n");
20 scanf("%d",&valor);
21 int achou =
buscar(binaria,valor);
22 if(achou){
23 printf("\nValor Localizado na
Arvore\n");
24 }else{
25 printf("\nValor Nao Existe na
Arvore\n");
26 }
27 system("pause");
28 break;
29 }
30 }while(op != 4);
31 }
Você pode baixar todo o código do exemplo desenvolvido nesta unidade, com
exemplos para a chamada da função listar de forma crescente e as rotinas de inserção
e busca clicando aqui.
www.esab.edu.br 401
Nesta unidade, você pôde desenvolver um algoritmo que permite ao
usuário cadastrar, listar e consultar valores inteiros armazenados em uma
árvore binária balanceada. No primeiro código, você implementou o tipo
estruturado, que representará cada nó da árvore, e a função que cria a
árvore binária. Para a inserção dos elementos na árvore binária, foi criada
a função inserir, que recebe, como parâmetro, a referência da árvore que
será manipulada e o valor a ser inserido. Como regra, caso a árvore esteja
vazia (vale NULL), será inserido o valor como sendo a raiz da árvore.
As demais inserções verificarão se o valor a ser inserido é menor que o
valor do nó pesquisado ou maior, sendo os valores menores inseridos
à esquerda e, os maiores, à direita do nó pesquisa. Caso a posição de
inserção já esteja preenchida, é preciso “descer” nos níveis da árvore, até
localizar uma posição de inserção disponível.
www.esab.edu.br 402
Com esse assunto, finalizamos a nossa disciplina, e desejo que o
conhecimento adquirido com o estudo das estruturas de dados,
principalmente no que se refere ao gerenciamento de memória,
implementação de métodos de ordenação e pesquisa, bem como a
construção de algoritmos mais eficientes e seguros, possa representar
um diferencial profissional para você, caro aluno, em um mercado de
Tecnologia da Informação cada vez mais competitivo e desafiador. Meus
parabéns por mais essa vitória!
Atividade
Chegou a hora de você testar seus conhecimentos
em relação às unidades 37 a 48. Para isso, dirija-se ao
Ambiente Virtual de Aprendizagem (AVA) e responda
às questões. Além de revisar o conteúdo, você estará
se preparando para a prova. Bom trabalho!
www.esab.edu.br 403
Resumo
www.esab.edu.br 404
Na unidade 46, você pôde revisar os conceitos básicos da estrutura de
dados árvore binária e viu que a árvore binária pode ser classificada
como balanceada ou não balanceada. Uma árvore binária balanceada
se caracteriza por ter, para cada nó, os filhos com valores menores à
esquerda do nó pai e os filhos com valores maiores à direita do nó pai.
Para buscar um elemento na árvore binária não balanceada, deve ser
adotada uma das três possíveis ordens de percurso na árvore prefixado,
central ou pós-fixado. O que muda de uma ordem de percurso para
a outra é a sequência dos elementos visitados. O processo de busca
na árvore binária não balanceada é muito semelhante ao de busca
sequencial, pois todos os elementos são visitados, um a um, até que
se localize o elemento desejado. Dessa forma, quanto mais elementos
tivermos na árvore, mais demorada tende a ser a pesquisa.
www.esab.edu.br 405
Glossário
Arquivo de log
É um arquivo temporário para registro de informações, na forma de
um processo passivo, sem a interferência do usuário de computador, e
que armazena eventos e ações executadas no computador, de forma a
monitorar as operações executadas. R
Arquivo-fonte
Arquivos-texto que contêm o código-fonte dos programas de
computadores, o qual é constituido pelas linhas de programação em
linguagem de programação específica. Algumas linguagens de programação
necessitam converter as linhas de programação em linguagem de máquina,
isto é, aquele que o computador entende, como: C, Pascal, Java. R
Árvore genealógica
É uma representação das pessoas que tiveram participação na existência
de uma pessoa, é o histórico de dados sobre seus ancestrais de forma que
fiquem conhecidas as conexões estabelecidas entre estes. R
Atribuição
Define um valor para a variável, que pode ser definida pelo programador
ou por meio de um comando de entrada de dados, todas as linguagens
de programação possuem um comando de atribuição, que no caso da
linguagem C é representada pelo sinal de igual (variável = valor;). R
www.esab.edu.br 406
Bibliotecas
Conjunto de funções pré-codificadas por outros programadores que
resolvem determinados problemas para que o usuário não precise
implementá-las, nem saber como foram implementadas. São exemplos
de bibliotecas: OpenGL (funções para desenhar), OpenCV (funções para
processamento de imagem). R
Bubble
O termo bubble, ou bolha, foi escolhido para representar esse algoritmo
devido ao fato das bolhas de ar, em um tanque com água, ao subirem
para a superfície, procurarem seu lugar se encaixando uma ao lado da
outra, de forma organizada. R
Cabeçalho
Nas linguagens de programação representa a declaração de uma função
ou método, omitindo a codificação, apenas especificando o nome da
função, seu tipo de retorno e os parâmetros com seus nomes e os tipos de
dados, representando, assim, a especificação da interface da função. R
Combinação
Consiste em gerar um novo conjunto de valores com elementos
combinados de dois ou mais conjuntos diferentes. R
Constantes
Consiste em uma técnica de armazenamento de dados em linguagens
de programação em que a informação não pode ser alterada durante a
execução do programa. R
Contêiner
Um contentor ou contêiner é um recipiente de metal ou madeira,
geralmente de grandes dimensões, destinado ao acondicionamento e
transporte de carga em navios, trens etc. R
www.esab.edu.br 407
Descritor
Variável que armazena endereços de elementos da estrutura de dados,
como o endereço do primeiro e do último elemento da lista. Os
descritores podem armazenar o endereço de qualquer informação da lista,
como o endereço do maior e do menor valor inserido na lista. R
Desempenho computacional
É a avaliação de um algoritmo com relação ao seu custo computacional,
que é o tempo necessário para a sua execução e o consumo de memória
principal que será utilizado durante a execução do algoritmo. Quanto
mais rápido o algoritmo e menos espaço de memória alocado, melhor
será seu desempenho computacional. R
Desfazer
Em computação, a função desfazer é uma operação muito comum em
aplicativos de editores de textos e reverte a última edição ou alteração
realizada no texto. R
Disco rígido
É um periférico do computador cuja utilidade é o armazenamento
de dados. Basicamente todas as informações do computador são
armazenadas no disco rígido, também conhecido como HD (a abreviação
de Hard Disk, disco rígido, em inglês). R
Do-while
É um comando de laço, uma instrução em linguagem de programação,
conhecido como “repita até”. Nessa instrução os processos são executados
pelo menos uma vez, já que primeiramente os processos são executados
para depois ser avaliada a condição para que o laço se repita ou se encerre.
R
Estruturas hierárquicas
Estrutura hierárquica é um sistema com organização de cima para baixo e
normalmente representado em forma de diagrama. R
www.esab.edu.br 408
Fila de arquivos
Também conhecida como fila de impressão ou spool, é um espaço na
memória principal do computador, gerenciado pelo sistema operacional,
onde ficam armazenados em fila os arquivos enviados para impressão. R
Gerenciar
No contexto desta disciplina, o sistema operacional realiza/gerencia
os processos que compartilham os recursos do computador para que
problemas não aconteçam, como falta de memória ou demora no
processamento de dados. R
Heap
Corresponde a uma área na memória principal do computador destinada
à alocação e liberação de espaços para as variáveis locais e globais que
são criadas e manipuladas nos programas de computadores. As variáveis
alocadas dinamicamente ou estaticamente também são armazenadas
nesse espaço de memória. R
Índices
Representam as posições de acesso em uma tabela de banco de dados
ou em um vetor unidimensional (usa apenas um índice) ou vetores
bidimensionais (usam dois índices). R
Inicialização
Define o valor inicial das variáveis declaradas nos programas de
computadores, a maioria das linguagens de programação já define um
valor padrão para as variáveis, de forma que o programador não precise
inicializá-las. Por exemplo, variáveis numéricas em C são inicializadas
com 0 automaticamente. R
Interação
Consiste em criar mecanismos ou interfaces que permitam ao usuário
manipular o sistema computacional, sendo o ponto de contato entre o
usuário e o aplicativo. R
www.esab.edu.br 409
Interface
São arquivos de código-fonte que apresentam as funções, especificando
apenas os cabeçalhos, mas não implementam o código dessas funções.
As regras de negócio serão implementadas nos arquivos que estenderem
as interfaces, os quais, obrigatoriamente, devem usar a mesma estrutura
definida no cabeçalho das funções. R
Inviabilizar a operação
A resposta da pesquisa em uma estrutura de dados, indicando se o valor
pesquisado existe ou não na estrutura de dados, deve ser dada em tempo
hábil, que permita a utilização da informação por parte do usuário do
sistema. É fundamental que durante os processos de pesquisa o usuário
seja informado visualmente de que a busca está em execução. Caso
contrário, o usuário poderá cancelá-la indevidamente acreditando que a
operação estava inativa. R
Jargão
É um tipo de comunicação específica limitada a um determinado grupo
de especialistas ou profissionais, e que tem por objetivo padronizar
a troca de informações. Por exemplo, o jargão jurídico utilizado por
advogados e juízes. R
Main
É a principal função de um programa em C, sendo a primeira função a
ser executada em um programa escrito em linguagem C que pode ou não
chamar outras funções. R
Método
São procedimentos ou funções, também conhecidas como sub-rotinas
– que com a utilização do paradigma orientado a objetos passaram a ser
chamadas de métodos –, pois são executadas por um objeto e determinam
o seu comportamento. Na programação estruturada é chamada de função
ou sub-rotina, na programação orientada a objetos de método. R
www.esab.edu.br 410
Mnemônicos
Termos associados à memória; aquilo que é de memorização fácil devido
à associação da palavra com o objeto ou pessoa. R
Não linear
Os dados que não seguem uma sequência física, em que os elementos
estão um ao lado do outro, sequencialmente. R
Navegador
O termo navegador é utilizado desde o início da internet, já que os
usuários visitavam os sites na forma de uma navegação pelos conteúdos.
Na prática, o termo representa o programa instalado no computador que
possibilita o acesso aos sites. Também é muito conhecido como browser.
R
Offset
É o termo utilizado para representar o deslocamento de um ou vários
valores em uma estrutura de dados. Um offset pode ocorrer para a
direita, quando os valores são deslocados uma posição à frente, ou para
a esquerda, quando os valores são deslocados para uma posição anterior.
R
Overflow
É um erro de execução do programa quando é requisitada mais memória
do que há disponível pelo sistema operacional. Esse erro é considerado
fatal já que encerra inesperadamente a execução do programa. R
Particionado
Consiste em dividir o vetor em, pelo menos, duas partes, ou dois
segmentos, mantendo as caraterísticas do vetor original, como tipo de
dados e posições (índices). R
www.esab.edu.br 411
Percurso
É um caminho constituído por uma partida, uma série de locais visitados
e identificados, unidos por linhas. R
Pilha
A pilha (stack ou heap) é um tipo de vetor dinâmico. Suas operações
são limitadas em inserção e remoção, que são gerenciadas pela mesma
extremidade (topo). Essa estrutura é conhecida como LIFO (Last In -
First Out), pois os itens são inseridos e retirados inversamente. Ou seja, o
primeiro item que entra é o último a sair. R
Processos cíclicos
São processos contínuos que se repetem em uma ordem predefinida,
retornando para o estágio inicial. R
Referência
O termo é utilizado para indicar o endereço da variável na memória
principal. R
Segmentação
É um termo genérico para designar a divisão de algo em partes separadas
ou segmentos. R
Segmentos
São partes da estrutura de dados, formadas por um conjunto de
elementos da estrutura de dados, com posição inicial e final. Um
segmento possui o mesmo tipo de dados e características da estrutura de
dados original. R
Sinalizado
Consiste em enviar algum tipo de aviso ao usuário de que alguma ação
foi executada no sistema. R
www.esab.edu.br 412
Sistema de segurança
Conjunto de mecanismos de monitoramento remoto com uso de
câmeras que possibilitam a gravação e visualização de vídeos em tempo
real e armazenamento em equipamentos para visualização posterior. R
Subprogramas
São trechos de programas que possuem uma funcionalidade específica
dentro do programa principal. São chamados pelo nome a partir do
programa principal. Quando retornam valor, são chamados de função.
Caso contrário, são conhecidos como procedimento. R
Regras de negócio
Representam as regras de funcionamento do sistema, o que ele deve fazer
seguindo o modo como seus processos são executados no mundo real.
Essas regras de negócio devem ser documentadas na forma de requisitos e
futuramente implementadas no sistema. R
Tipo Básico
As linguagens de programação possuem os tipos de dados predefinidos,
também conhecidos como tipos básicos ou tipos primitivos,
representados pelos inteiros, reais, lógicos e caracteres. R
Tipos de dados
Os tipos de dados são utilizados pelas linguagens de programação para
definição do domínio dos dados (valores que podem ser armazenados) e
o espaço de memória que será alocado. R
www.esab.edu.br 413
Tipos primitivos
São os tipos de dados básicos das linguagens de programação,
normalmente representados pelos tipos: byte, int, float e char. R
Unidimensionais
Termo utilizado para representação de vetores que possuem uma linha
por várias colunas. R
URL
Uniform Resource Locator (Localizador Padrão de Recursos)
é o endereço virtual de um recurso disponível em uma rede de
computadores, como a internet. A URL pode indicar onde está o
arquivo, computador, site ou qualquer outro recurso que o usuário de
computador desejar acessar. R
www.esab.edu.br 414
Referências
www.esab.edu.br 415