Documente Academic
Documente Profesional
Documente Cultură
Estrutura de Dados
Londrina
2015.2
Estrutura de Dados
Faculdade
PROGRAMA DE GRADUAO EM ENGENHARIA
DEPARTAMENTO DE ENGENHARIA
C++
LONDRINA-PR
2015.2
Estrutura de Dados
Estrutura de Dados
INTRODUO
Este material apresenta uma coleo de algoritmos sobre uma variedade de estruturas de
dados de memria principal, estudadas na disciplina de Estrutura de Dados Algoritmos e
Estruturas de Dados.
Estrutura de Dados
1 Aula.
1.1 Uma breve histria do C++
O C++ foi inicialmente desenvolvido por Bjarne Stroustrup dos Bell Labs, durante a dcada de
1980 com o objetivo de implementar uma verso distribuda do ncleo do Unix. Como o Unix
era escrito em C, dever-se-ia manter a compatibilidade, ainda que adicionando novos recursos.
O C foi escolhido como base de desenvolvimento da nova linguagem, pois possua uma
proposta de uso genrico, era rpido e tambm portvel para diversas plataformas. Algumas
outras linguagens que tambm serviram de inspirao para o cientista da computao foram
ALGOL 68, Ada, CLU e ML.
Ainda em 1983 o nome da linguagem foi alterado de C with Classes para C++. Antes
implementada usando um pr-processador, a linguagem passou a exigir um compilador
prprio, escrito pelo prprio Stroustrup.
Novas caractersticas foram adicionadas, como funes virtuais, sobrecarga de operadores e
funes, melhorias na verificao de tipo de dado e estilo de comentrio de cdigo de uma
linha (//).
Em 1985 foi lanada a primeira edio do livro The C++ Programming Language, contendo
referncias para a utilizao da linguagem, j que ainda no era uma norma oficial.
A primeira verso comercial foi lanada em outubro do mesmo ano.
Em 1989 a segunda verso foi lanada, contendo novas caractersticas como herana mltipla,
classes abstratas, mtodos estticos, mtodos constantes e membros protegidos,
incrementando o suporte a orientao a objeto.
Assim como a linguagem, sua biblioteca padro tambm sofreu melhorias ao longo do tempo.
Sua primeira adio foi a biblioteca de E/S, e posteriormente a Standard Template Library
(STL); ambas tornaram-se algumas das principais funcionalidades que distanciaram a
linguagem em relao a C.
Criada primordialmente na HP por Alexander Stepanov no incio da dcada de 1990 para
explorar os potenciais da programao genrica, a STL foi apresentada a um comit unificado
ANSI e ISO em 1993 convite de Andrew Koenig.
Aps uma proposta formal na reunio do ano seguinte, a biblioteca recebe o aval do comit.
Pode-se dizer que C++ foi a nica linguagem entre tantas outras que obteve sucesso como uma
sucessora linguagem C, inclusive servindo de inspirao para outras linguagens como Java, a
IDL de CORBA e C#.
Estrutura de Dados
1.2 Compiladores
Um compilador um programa de sistema que traduz um programa descrito em uma
linguagem de alto nvel para um programa equivalente em cdigo de mquina para um
processador. Em geral, um compilador no produz diretamente o cdigo de mquina, mas sim
um programa em linguagem simblica (assembly) semanticamente equivalente ao programa
em linguagem de alto nvel. O programa em linguagem simblica ento traduzido para o
programa em linguagem de mquina atravs de montadores.
Para desempenhar suas tarefas, um compilador deve executar dois tipos de atividade. A
primeira atividade a anlise do cdigo fonte, onde a estrutura e significado do programa de
alto nvel so reconhecidos. A segunda atividade a sntese do programa equivalente em
linguagem simblica. Embora conceitualmente seja possvel executar toda a anlise e apenas
ento iniciar a sntese, em geral estas duas atividades ocorrem praticamente em paralelo. Para
apresentar um exemplo das atividades que um compilador deve desempenhar, considere o
seguinte trecho de um programa em C:
Para o compilador, este segmento nada mais do que uma seqncia de caracteres em um
arquivo texto. O primeiro passo da anlise reconhecer que agrupamentos de caracteres tm
significado para o programa, por exemplo, saber que int uma palavra-chave da linguagem e
que a e b sero elementos individuais neste programa. Posteriormente, o compilador deve
reconhecer que a seqncia int a ,corresponde a uma declarao de uma varivel inteira cujo
identificador recebeu o nome a.
As regras de formao de elementos e frases vlidas de uma linguagem so expressas na
gramtica da linguagem. O processo de reconhecer os comandos de uma gramtica
conhecido como reconhecimento de sentenas.
Estrutura de Dados
As duas primeiras linhas so o cabealho do programa. Todo programa deve ter um cabealho
desse tipo para definir quais as bibliotecas ele utilizar. Bibliotecas so arquivos que
normalmente so instalados juntos com o compilador e que possuem os comandos e funes
pertencentes linguagem.
#include<>
Serve para indicar ao compilador todas as bibliotecas que este programa utilizar. Na maioria
dos programas que escreveremos durante esta apostila, s utilizaremos o
#include <iostream>
,que serve para incluir a biblioteca iostream em nossos programas.
Esta biblioteca contm as principais funes, comandos e classes de entrada e sada de C++,
necessrias para realizar programas que, por exemplo, recebam dados via teclado e enviem
dados via monitor.
A segunda linha do cabealho,
using namespace std;
, um aviso ao compilador que estaremos utilizando os comandos e funes padro de C++.
Ele necessrio porque em C++ podemos criar vrias bibliotecas para serem utilizveis em
vrios programas.
Cada uma dessas bibliotecas contm comandos, classes e funes prprias, e para evitar
confuses e problemas com os nomes destes comandos, utilizamos o cabealho using
namespace ...; para definir qual o campo de nomes que estamos utilizando.
Num programa normal, que no utiliza outras bibliotecas alm do padro de C++, utilizamos o
namespace std como nosso campo de nomes de comandos e funes. Assim, sempre que
utilizamos um comando prprio de C++, o compilador reconhecer automaticamente este
comando como sendo pertencente biblioteca padro de C++.
Assim como em C++, tudo o que acontece durante a execuo do programa est
contido dentro de uma funo principal, chamada main. Declaramos a funo main com:
Estrutura de Dados
int main ( )
Todos os comandos executados pelo programa esto contidos entre as chaves { }
da funo main.
O encerramento de um programa geralmente feito da mesma maneira para todos eles. As
duas ltimas linhas antes do fecha-chaves so dois comandos normalmente utilizados ao
fim de um programa.
system(PAUSE > null)
uma chamada de funo prpria de C++.
A funo system( ) recebe argumentos como o PAUSE que na verdade so comandos para o
sistema operacional. Neste caso, ela recebe o comando PAUSE > null para pausar a
execuo do programa at que o usurio aperte uma tecla qualquer. Utilizamos este recurso
para que a tela do programa no seja terminada automaticamente pelo sistema, impedindo
que vejamos os resultados do programa.
Finalmente, o comando
return 0
a resposta da funo main para o sistema.
A linguagem oferece ainda dois tipos bsicos para a representao de nmeros reais (ponto
flutuante): float e double. A tabela abaixo compara estes dois tipos.
Estrutura de Dados
1.5.3 Constantes.
Quando temos que usar valores constantes, que no se alteram durante o processo.
A diferena bsica em relao s variveis, como os nomes dizem (variveis e constantes),
que o valor armazenado numa rea de constante no pode ser alterado.
Estrutura de Dados
A constante x no poder deixar de ter valor igual a 4. Qualquer tentativa de modificar o valor
da constante ao longo do programa ser reportada como erro pelo compilador.
<algorithm>
<fstream>
<functional>
<iostream>
<locale>
<map>
<set>
<sstream>
<string>
<vector>
<Math.h>
Estrutura de Dados
1.7.1 Trigonomtricas.
sin (): Retorna o valor do seno. Recebe como argumento o valor dos graus em double.
cos (): Retorna o valor do co-seno. Recebe como argumento o valor dos graus em
double.
tan (): Retorna o valor da tangente. Recebe como argumento o valor dos graus em
double.
1.7.2 Logartmicas.
log (): Retorna o valor do logaritmo na base 2. Exige um argumento do tipo double.
log10 (): Retorna o valor do logaritmo na base 10. Exige um argumento do tipo double.
1.7.3 Potncia.
sqrt (): Retorna o valor da raiz quadrada. Recebe como argumento um double do qual
ele deve extrair a raiz.
pow (): Retorna o valor da base elevada ao expoente. Recebe dois argumentos do tipo
double, o primeiro a base e o segundo o expoente. Por exemplo: se quisermos saber
o resultado da operao 210,faramos pow (2, 10).
1.8 Operadores.
A linguagem C++ oferece uma gama variada de operadores, entre binrios e unrios. Os
operadores bsicos so apresentados a seguir.
Estrutura de Dados
A operao feita na preciso dos operandos. Assim, a expresso 5/2 resulta no valor 2, pois a
operao de diviso feita em preciso inteira, j que os dois operandos (5 e 2) so constantes
inteiras. A diviso de inteiros trunca a parte fracionria, pois o valor resultante sempre do
mesmo tipo da expresso. Conseqentemente, a expresso 5.0/2.0 resulta no valor real 2.5,
pois a operao feita na preciso real (double, no caso).
O operador mdulo, %, no se aplica a valores reais, seus operandos devem ser do tipo
inteiro. Este operador produz o resto da diviso do primeiro pelo segundo operando. Como
exemplo de aplicao deste operador, podemos citar o caso em que desejamos saber se o
valor armazenado numa determinada varivel inteira x par ou mpar. Para tanto, basta
analisar o resultado da aplicao do operador %, aplicado varivel e ao valor dois.
Estrutura de Dados
n++;
incrementa de uma unidade o valor de n (anlogo para o decremento em n--). O aspecto no
usual que ++ e -- podem ser usados tanto como operadores pr-fixados (antes da varivel,
como em ++n) ou ps-fixados (aps a varivel, como em n++). Em ambos os casos, a varivel n
incrementada. Porm, a expresso ++n incrementa n antes de usar seu valor, enquanto n++
incrementa n aps seu valor ser usado. Isto significa que, num contexto onde o valor de n
usado, ++n e n++ so diferentes. Se n armazena o valor cinco, ento:
x = n++;
atribui 5 a x, mas:
x = ++n;
atribuiria 6 a x. Em ambos os casos, n passa a valer 6, pois seu valor foi incrementado em
uma unidade. Os operadores de incremento e decremento podem ser aplicados somente em
variveis; uma expresso do tipo x = (i + 1)++ ilegal.
Estrutura de Dados
Mesmo para programadores experientes, o uso das formas compactas deve ser feito com
critrio. Por exemplo, os comandos:
a = a + 1;
a += 1;
a++;
++a;
so todos equivalentes e o programador deve escolher o que achar mais adequado e simples.
Em termos de desempenho, qualquer compilador razovel capaz de otimizar todos estes
comandos da mesma forma.
1.8.6.1 Relacionais.
Estes operadores comparam dois valores. O resultado produzido por um operador relacional
zero ou um. Em C++, no existe o tipo booleano (true ou false). O valor zero interpretado
como falso e qualquer valor diferente de zero considerado verdadeiro. Assim, se o resultado
de uma comparao for falso, produz-se o valor 0, caso contrrio, produz-se o valor 1.
1.8.6.2 Lgicos.
Estrutura de Dados
Operador sizeof.
Armazena o valor 4 na varivel a, pois um float ocupa 4 bytes de memria. Este operador
pode tambm ser aplicado a uma varivel, retornando o nmero de bytes do tipo associado
varivel.
Estrutura de Dados
1.10.1 Sada.
cout << Exemplo<<endl; C++.
cout o dispositivo de sada padro no C++ (geralmente o monitor), e a frase completa
insere uma seqncia de caracteres no dispositivo de sada. O cout declarado no
arquivo de cabealho <iostream.h>, ento pra que seja possvel utiliza-lo, esse arquivo
precisa ser incluso. Note que a frase termina com um caractere ponto-e-vrgula (Esse
caractere significa o fim da instruo e precisa ser includo aps toda instruo em
qualquer programa em C++ )Um dos erros mais comuns dos programadores de C++
devido ao fato de esquecerem de incluir um ponto-e-vrgula; no final de cada
instruo.
printf (formato, lista de constantes/variveis/expresses...); C;
O primeiro parmetro uma cadeia de caracteres, em geral delimitada com aspas, que
especifica o formato de sada das constantes, variveis e expresses listadas em seguida.
Para cada valor que se deseja imprimir, deve existir um especificador de formato
correspondente na cadeia de caracteres formato. Os especificadores de formato variam
com o tipo do valor e a preciso em que queremos que eles sejam impressos. Estes
especificadores so precedidos pelo caractere % e podem ser, entre outros:
isto , alm dos especificadores de formato, podemos incluir textos no formato, que
so mapeados diretamente para a sada. Assim, a sada formada pela cadeia de
caracteres do formato onde os especificadores so substitudos pelos valores
correspondentes.
Existem alguns caracteres de escape que so freqentemente utilizados nos formatos
de sada. So eles:
A funo printf retorna o nmero de campos impressos. Salientamos que para cada
constante, varivel ou expresso listada devemos ter um especificador de formato
apropriado.
1.10.2 Entrada.
cin>>varivel; C++
A entrada padro no C++ feita aplicando-se o operador sobrecarregado de extrao
(>>) no comando cin. Isso precisa ser seguido pela varivel que ir guardar o dado que
ser lido.
Declarando a varivel como desejada, ento espera por uma entrada do cin (teclado)
para que possa guard-la em um espao reservado da memria ROM. O comando cin
s pode processar a entrada do teclado depois que a tecla ENTER for pressionada.
Sendo assim, mesmo que voc pea um nico caractere, o cin no ir processar a
entrada at que o usurio pressione ENTER depois que o caractere tenha sido digitado.
Voc precisa sempre considerar o tipo da varivel que voc est usando para guardar
o valor extrado pelo cin. Se voc pedir um inteiro, voc receber um inteiro, se voc
Estrutura de Dados
A principal diferena que o formato deve ser seguido por uma lista de endereos de
variveis (na funo printf passamos os valores de constantes, variveis e expresses).
Na seo sobre ponteiros, este assunto ser tratado em detalhes. Por ora, basta saber
que, para ler um valor e atribu-lo a uma varivel, devemos passar o endereo da
varivel para a funo scanf. O operador & retorna o endereo de uma varivel. Assim,
para ler um inteiro, devemos ter:
int n;
scanf ("%d", &n);
Para a funo scanf, os especificadores %f, %e e %g so equivalentes. Aqui, caracteres
diferentes dos especificadores no formato servem para cercar a entrada. Por exemplo:
scanf ("%d:%d", &h, &m);
obriga que os valores (inteiros) fornecidos sejam separados pelo caractere dois pontos
(:).
Um espao em branco dentro do formato faz com que sejam "pulados" eventuais
brancos da entrada. Os especificadores %d, %f, %e e %g automaticamente pulam os
brancos que precederem os valores numricos a serem capturados. A funo scanf
retorna o nmero de campos lidos com sucesso.
Estrutura de Dados
1.11.1 Deciso.
Os mtodos de tomada de deciso no C++ esto presentes para as tarefas mais corriqueiras
que o programa deve executar.
1.11.1.1
Estrutura IF/ELSE.
Estrutura de Dados
possvel observar que podemos criar um programa com quantas condies queremos,
restringindo a cada condio, uma ao ou um conjunto de aes.
Alguns exemplos para serem refeitos.
Elabore um programa que diga qual ao, ou aes foram escolhidas:
a) Ao 1: caso o nmero seja maior ou igual a 2.
b) Ao 2: caso o nmero seja maior que 1.
c) Ao 3: caso no seja satisfeita nenhuma condio.
Observe que o erro encontra-se no uso do else if, com ele, exclumos possibilidades possveis
de respostas. Por exemplo, se digitarmos o nmero 3 no programa acima, o compilador nos
dar como sada apenas a primeira condio ("Ao 1 escolhida), onde na verdade temos
duas respostas, pois satisfaz a ao 1 e 2 simultaneamente. Se substituirmos o if no lugar do
else if, o compilador nos dar as 2 respostas possveis ("Ao 1 escolhida e "Ao 2
escolhida), com isso, corrigiramos o problema da redundncia do nosso exemplo. Com o uso
apenas do if e do else possvel o compilador executar vrias condies que ocorram
simultaneamente.
Exemplos de Aplicao.
1. Dados dois nmeros reais quaisquer, desenvolva um programa que diga se eles so
iguais ou diferentes.
Soluo:
Estrutura de Dados
Estrutura de Dados
Observe que o comando pow serve para realizar operaes com exponenciais. No nosso caso:
a = pow(n,0.5), estamos atribuindo varivel a, a seguinte expresso exponencial n elevado a
0.5. De forma genrica, no comando pow (A,B), teremos a funo exponencial, onde A a base
e B o expoente.
1.11.2 Repetio.
As estruturas de repeties so muito importantes para a soluo de problemas na
programao, pois muitas vezes os mesmo procedimentos tm que ser executados mais de
uma vez, ou um nmero de vezes varivel.
Em C/C++, basicamente existem trs tipos de estrutura de repetio: for, while e do while.
1.11.2.1
Estrutura FOR.
Para o for, como qualquer iterao (repetio), precisa de uma varivel para controlar os loops
(voltas). No for, essa varivel dever ser iniciada, indicando pelo seu critrio de execuo, e
forma de incremento ou decremento. Ou seja, o for precisa de trs condies. Vale salientar
que essas condies so separadas por ponto-e-vrgula. O comando deve ser inserido no
compilador da seguinte forma:
Um dos exemplos mais utilizados o clculo da potncia de um nmero, ou o fatorial de um
nmero, para a potncia o usurio informa ao programa a base e o expoente. O programa
deve fazer o nmero de interaes iguais o nmero x do expoente. Considerando o nmero do
expoente natural, positivo e maior que zero, temos o seguinte programa:
Estrutura de Dados
Observe que em for (int i=0; i<x; i++ ), temos as trs condies dentro do parnteses.
Exemplos de Aplicao.
1. Elabore um programa que calcule o fatorial de um nmero dado.
Soluo:
2. Um nmero dito perfeito quando a soma de seus divisores (exceto ele mesmo), ele
prprio. Exemplo 28, divisores = 14+7+4+2+1 =28. Elabore um programa que diga se o
nmero digitado perfeito ou no.
Soluo:
Estrutura de Dados
1.11.2.2
Estrutura WHILE.
Outra forma de iterao (repetio) em C/C++ o WHILE. O while executa uma comparao
com a varivel. Se a comparao for verdadeira, ele executa o bloco de instrues, quantas
vezes forem necessrias, at a comparao se tornar falsas.
Exemplos de Aplicao.
1. Elabore um programa que imprima os termos de uma progresso aritmtica cujo
primeiro termo 3 e a razo 5. Parar o processamento quando for impresso um termo
maior que 100.
Soluo:
Estrutura de Dados
Exemplo de Aplicao em C:
muito comum, em programas computacionais, termos procedimentos iterativos, isto
, procedimentos que devem ser executados em vrios passos. Como exemplo, vamos
considerar o clculo do valor do fatorial de um nmero inteiro no negativo. Por
definio:
Estrutura de Dados
Mais compacta e amplamente utilizada, atravs de laos for. Faa este mesmo exemplo em
FOR.
1.11.2.3
Estrutura DO/WHILE.
A estrutura de repetio DO/WHILE parte do princpio de que se deve fazer algo primeiro e s
depois comparar uma varivel para saber se o loop ser executado mais uma vez. A estrutura
do/while parecida com a do while, no aspecto de possuir apenas uma condio, e ambas so
estruturas de repetio, porm o do/while, diferentemente do while, informa a condio ao
compilador apensa no final da estrutura.
Estrutura de Dados
1.11.2.4
Outra forma de estrutura seletiva o switch. Dentro do switch h o case (que significa caso).
Ou seja, quase que um if com vrias possibilidades, mas com algumas diferenas
importantes.
Primeira diferena: Os cases no aceitam operadores lgicos. Portanto, no possvel fazer
uma comparao. Isso limita o case a apenas valores definidos.
Segunda diferena: O switch executa seu bloco em cascata. Ou seja, se a varivel indicar para
o primeiro case e dentro do switch tiver 5 cases, o switch executar todos os outros 4 cases a
no ser que utilizemos o comando para sair do switch. (Nos referimos ao BREAK).
Agora, que conhecemos diferenas importantes, vamos ver como proceder com o switch /
case.
O primeiro comando switch deve-se colocar entre parnteses a varivel na qual est guardado
o valor que ser avaliado pelo case. Ento, abre-se o bloco de dados.
Dentro do bloco de dados colocamos o comando case e logo aps um valor terminando a
linha com dois pontos (:). Preste ateno no tipo de varivel que ser colocado, pois h
diferenas entre um dado e outro. Por exemplo: 1 no a mesma coisa que '1' e 'a' no a
mesma coisa que 'A'. Ento, estruturado o comando que ser executado pelo case.
Estruturalmente, seria isso:
Estrutura de Dados
Exemplo de Aplicao:
1. Elabore uma simples calculadora que realize as operaes de adio, subtrao,
multiplicao e diviso, utilizando a estrutura switch/case.
Soluo:
Perceba que no final de cada case h um break. Porque se no houvesse, o switch continuaria
executando at o final. Por exemplo, no exerccio anterior, havia 4 casos (case), e escolhemos
a operao 2, caso no houvesse o break, o programa executaria os casos seguintes, no nosso
caso, a operao 3 e 4.
Estrutura de Dados
Default
Default, do ingls padro, o case que ativado caso no tenha achado nenhum case
definido. Ou seja, o que aconteceria em ltimo caso. Vamos imaginar o seguinte
cenrio: Seu programa pede para que o usurio digite apenas duas opes (S ou N)
para reiniciar o programa. Mas, propositalmente ou por engano, o usurio digita uma
opo totalmente diferente. E agora? O que seu programa deve fazer? aqui que o
default entra. Geralmente o default quando previsto um erro, uma entrada de
dado incorreta ou no de acordo com o contexto. O default tem seu papel parecido
com o else, da estrutura if/else, caso nenhuma condio for feita, far os comandos
definidos no default.
Como podemos ver, h dois casos: S para reiniciar ou N para sair. Se por acaso algum digitar
algo diferente disso, executa-se o default, que informa que a opo escolhida invalida e
repete a pergunta se o usurio deseja sair do programa, at que seja digitada uma opo
valida (S ou N). Como a linguagem C/C++ case sensitive (diferencia maisculas de minsculas)
usamos uma funo para deixar a letra maiscula (toupper da biblioteca ctype). Agora, no
importa o que o usurio digitar, pois o programa est preparado para reagir qualquer
entrada de dado.
Lista de exerccios de fixao lista03
Estrutura de Dados
1.11.3.1
Break.
1.11.3.2
Continue.
gera a sada:
0 1 2 3 4 6 7 8 9 fim
Estrutura de Dados
1.12 Funes.
As funes dividem grandes tarefas de computao em tarefas menores. Os programas em
C/C++ geralmente consistem de vrias pequenas funes em vez de poucas funes de maior
tamanho. A criao de funes evita a repetio de cdigo, de modo que um procedimento que
repetido deve ser transformado numa funo que, ser chamada diversas vezes. Um
programa bem estruturado deve ser pensado em termos de funes, e estas, por sua vez,
podem (e devem, se possvel) esconder do corpo principal do programa detalhes ou
particularidades de implementao. Em C/C++, tudo feito atravs de funes. Os exemplos
anteriores utilizam as funes da biblioteca padro para realizar entrada e sada. Nesta seo,
discutiremos a codificao de nossas prprias funes.A forma geral para definir uma funo :
Neste exemplo, que a funo fat recebe como parmetro o nmero cujo fatorial deve ser
impresso. Os parmetros de uma funo devem ser listados, com seus respectivos tipos, entre
os parnteses que seguem o nome da funo. Quando uma funo no tem parmetros,
colocamos a palavra reservada void entre os parnteses. Devemos notar que main tambm
uma funo; sua nica particularidade consiste em ser a funo automaticamente executada
aps o programa ser carregado. Como as funes main que temos apresentado no recebem
parmetros, temos usado a palavra void na lista de parmetros.
Estrutura de Dados
Alm de receber parmetros, uma funo pode ter um valor de retorno associado. No
exemplo do clculo do fatorial, a funo fat no tem nenhum valor de retorno, portanto
colocamos a palavra void antes do nome da funo, indicando a ausncia de um valor de
retorno.
A funo main obrigatoriamente deve ter um valor inteiro como retorno. Esse valor pode
ser usado pelo sistema operacional para testar a execuo do programa. A conveno
geralmente utilizada faz com que a funo main retorne zero no caso da execuo ser bem
sucedida ou diferente de zero no caso de problemas durante a execuo.
Por fim, salientamos que C exige que se coloque o prottipo da funo antes desta ser
chamada. O prottipo de uma funo consiste na repetio da linha de sua definio
seguida do caractere (;). Temos ento:
Estrutura de Dados
Estrutura de Dados
Ento, podemos analisar passo a passo a evoluo do programa mostrado acima, ilustrando
o funcionamento da pilha de execuo.
Estrutura de Dados
Isto ilustra por que o valor da varivel passada nunca ser alterado dentro da funo.
A seguir, discutiremos uma forma para podermos alterar valores por passagem de
parmetros, o que ser realizado passando o endereo de memria onde a varivel est
armazenada.
int a
Declaramos uma varivel com nome a que pode armazenar valores inteiros.
Automaticamente, reserva-se um espao na memria suficiente para armazenar valores
inteiros (geralmente 4 bytes).
Da mesma forma que declaramos variveis para armazenar inteiros, podemos declarar
variveis que, em vez de servirem para armazenar valores inteiros, servem para armazenar
valores de endereos de memria onde h variveis inteiras armazenadas. C/C++ no reserva
uma palavra especial para a declarao de ponteiros; usamos a mesma palavra do tipo com
os nomes das variveis precedidas pelo caractere *. Assim, podemos escrever:
int *p
Neste caso, declaramos uma varivel com nome p que pode armazenar endereos de
memria onde existe um inteiro armazenado. Para atribuir e acessar endereos de memria,
a linguagem oferece dois operadores unrios ainda no discutidos. O operador unrio &
(endereo de), aplicado a variveis, resulta no endereo da posio da memria reservada
para a varivel. O operador unrio * (contedo de), aplicado as variveis do tipo ponteiro,
acessa o contedo do endereo de memria armazenado pela varivel ponteiro. Para
Estrutura de Dados
Aps as declaraes, ambas as variveis, a e p, tem armazenadas como valores "lixo", pois
no foram inicializadas. Podemos fazer atribuies como exemplificado nos fragmentos de
cdigo da figura a seguir:
Estrutura de Dados
Alguns exemplos.
imprime o valor 2.
Estrutura de Dados
Mostrando graficamente.
1.15 Recursividade.
As funes podem ser chamadas recursivamente, isto , dentro do corpo de uma funo
podemos chamar novamente a prpria funo. Se uma funo A chama a prpria funo A,
dizemos que ocorre uma recurso direta. Se uma funo A chama uma funo B que, por
sua vez, chama A, temos uma recurso indireta. Diversas implementaes ficam muito mais
fceis usando recursividade. Por outro lado, implementaes no recursivas tendem a ser
mais eficientes em relao ao tempo.
Para cada chamada de uma funo, recursiva ou no, os parmetros e as variveis locais so
empilhados na pilha de execuo. Assim, mesmo quando uma funo chamada
recursivamente, cria-se um ambiente local para cada chamada. As variveis locais de
chamadas recursivas so independentes entre si, como se estivssemos chamando funes
diferentes.
Estrutura de Dados
1.16.1 #include.
Uma das diretivas reconhecidas pelo pr-processador, e j utilizada nos nossos exemplos,
#include. Ela seguida por um nome de arquivo e o pr-processador a substitui pelo
corpo do arquivo especificado. como se o texto do arquivo includo fizesse parte do
cdigo fonte.
Uma observao: quando o nome do arquivo a ser includo envolto por aspas
("arquivo"), o pr-processador procura primeiro o arquivo no diretrio atual e, caso no o
encontre, o procura nos diretrios de include especificados para compilao. Se o arquivo
colocado entre os sinais de menor e maior (<arquivo>), o pr-processador procura somente no
no diretrio de include.
Estrutura de Dados
1.16.2 #define.
muito utilizada e que ser agora discutida a diretiva de definio. Por exemplo, uma funo
para calcular a rea de um crculo pode ser escrita da seguinte forma:
Neste caso, antes da compilao, toda ocorrncia da palavra PI (desde que no envolvida
por aspas) ser trocada pelo nmero 3.14159.
C permite ainda a utilizao da diretiva de definio com parmetros. vlido escrever, por
exemplo:
Estas definies com parmetros recebem o nome de macros. Devemos ter muito cuidado
na definio de macros. Mesmo um erro de sintaxe pode ser difcil de ser detectado, pois
ocompilador indicar um erro na linha em que se utiliza a macro e no na linha de definio
da macro (onde efetivamente encontra-se o erro).
Outros efeitos colaterais de macros mal definidas podem ser ainda piores. Por exemplo, no
cdigo abaixo:
Estrutura de Dados
Portanto, conclumos que, na regra bsica para a definio de macros, devemos envolver cada
parmetro, e a macro como um todo, com parnteses.
1.17 Vetores.
A forma mais simples de estruturarmos um conjunto de dados por meio de vetores. Como
a maioria das linguagens de programao, C/C++ permite a definio de vetores. Definimos um
vetor em C/C++ da seguinte forma:
int v[10]
A declarao acima diz que v um vetor de inteiros dimensionado com 10 elementos, isto
, reservamos um espao de memria contnuo para armazenar 10 valores inteiros. Assim,
se cada int ocupa 4 bytes, a declarao acima reserva um espao de memria de 40 bytes,
como ilustra a figura abaixo.
Estrutura de Dados
Ateno todo programa aqui apresentado ser escrito em C, para que o aluno
refaa o mesmo em C++. Pois as quetes em prova sro em C e/ou C++.
Devemos observar que passamos para a funo scanf o endereo de cada elemento do
vetor (&v[i]), pois desejamos que os valores capturados sejam armazenados nos
elementos do vetor. Se v[i] representa o (i+1)-simo elemento do vetor, &v[i] representa
o endereo de memria onde esse elemento est armazenado.
Estrutura de Dados
Estrutura de Dados
Note que, ao ser passado para a funo o endereo do primeiro elemento do vetor (e no os
elementos propriamente ditos), podem alterar os valores dos elementos do vetor dentro da
funo. O exemplo abaixo ilustra:
A sada sera 2 4 6, pois os elementos do vetor sero incrementados dentro da funo.
Estrutura de Dados
Estrutura de Dados
1.17.4.1
malloc.
a funo bsica para alocar memria, recebe como parmetro o nmero de bytes que se
deseja alocar e retorna o endereo inicial da rea de memria alocada.
Exemplificando, consideremos a alocao dinmica do vetor de inteiros com 10
elementos. Como a funo malloc retorna o endereo da rea alocada e, desejamos
armazenar valores inteiros nessa rea, temos que declarar um ponteiro de inteiro para receber
o endereo inicial do espao alocado. O trecho de cdigo ento seria:
int *v;
v = malloc(10*4);
Feito isso, v armazenar o endereo inicial de uma rea contnua de memria suficiente para
armazenar 10 valores inteiros. Deve-se tratar v como tratamos um vetor declarado
estaticamente, pois, se v aponta para o inicio de uma rea alocada, pode-se dizer que v[0]
acessa o endereo do primeiro elemento que armazenaremos, e
segundo, at
v[9].
Na exemplificao acima, trabalhamos com um inteiro, que ocupa 4 bytes. Devemos ficar
independentes de compiladores e mquinas, usando o operador sizeof(
).
v = malloc(10*sizeof(int));
Segue ilustrao de maneira esquemtica o que ocorre na memria:
Estrutura de Dados
Caso no houver espao livre suficiente para realizao da alocao, ser retornado um
endereo nulo (representado pelo smbolo NULL, definido em stdlib.h). Devemos prevenir
o erro na alocao do programa verificando o valor de retorno da funo malloc. Deve-se
imprimir uma mensagem e abortar o programa usando a funo exit, tambm definida na
stdlib.
v = (int*) malloc(10*sizeof(int));
if (v==NULL)
{
printf("Memoria insuficiente.\n");
exit(1); /* aborta o programa e retorna 1 para o sist. operacional */
}
Para liberao da memria alocada dinamicamente, usa-se a funo free. Esta funo recebe
como parmetro o ponteiro da memria a ser liberada. Assim, para liberar o vetor v, fazemos:
free (v);
Devemos passar para a funo free um endereo de memria que tenha sido alocado
dinamicamente. Deve-se lembrar de que no podemos acessar o espao na memria
depois que o liberamos.
Como exemplo do uso da alocao dinmica, usaremos o programa para o clculo da
mdia e da varincia. Agora, o programa l o nmero de valores que sero fornecidos, aloca
um vetor dinamicamente e faz os clculos. Somente a funo principal precisa ser alterada,
pois as funes para calcular a mdia e a varincia anteriormente apresentadas independem
do fato de o vetor ter sido alocado esttica ou dinamicamente.
Estrutura de Dados
Estrutura de Dados
Exemplo: Suponhamos que queremos escrever uma funo para testar se uma
varivel caractere c um dgito (um dos caracteres entre '0' e '9'). Esta funo pode ter
o prottipo:
int digito(char c);
e ter como resultado 1 (verdadeiro) se c for um dgito, e 0 (falso) se no for.
A implementao desta funo pode ser dada por:
1.18.2 String.
Cadeias de caracteres (strings), em C/C++, so representadas por vetores do tipo char
terminadas, obrigatoriamente, pelo caractere nulo ('\0'). Portanto, para armazenarmos uma
cadeia de caracteres, devemos reservar uma posio adicional para o caractere de fim da
cadeia. Todas as funes que manipulam cadeias de caracteres (e a biblioteca padro de C/C++
oferece vrias delas) recebem como parmetro um vetor de char, isto , um ponteiro para o
primeiro elemento do vetor que representa a cadeia, e processam caractere por caractere, at
encontrarem o caractere nulo, que sinaliza o final da cadeia.
O prximo passo entender as strings. Strings em C/C++ so tratados como vetores de
tamanho determinado que podem armazenar qualquer caracter. Diferentemente de declarar
apenas uma varivel do tipo char (que armazena apenas um caracter) a string uma cadeia de
caracteres, ou seja, pode guardar quantos caracteres ns determinarmos.
Portanto, para declararmos uma string, basta ns criarmos um vetor de caracteres dessa
forma:
char minhaString [50];
O nico problema das strings so o seu consumo de recursos. Por exemplo, se levarmos em
conta o vetor de caracteres que acabamos de criar, apesar dele conter 50 posies, ns s
poderemos digitar at 49 letras. Isso ocorre porque toda string deve ter um caracter terminal,
que geralmente indicado pelo NULL (nulo). Isso quer dizer que um vetor de caracteres
(string) de 50 posies ter 49 caracteres efetivos e um NULL indicando seu final.
1.18.2.1
Entrada de String.
Para entrarmos com uma String no sistema usamos a mesma funo de entrada padro - cin.
Ou seja, se quisermos que o usurio digite seu nome faramos da seguinte forma:
Estrutura de Dados
Agora, outro problema ao tratarmos de strings em C/C++. Embora a funo cin consiga obter a
string, ela sempre termina assim que pressionarmos o espao a primeira vez, ou seja, ele s
consegue pegar uma palavra por vez.
Ento, como vamos obter uma linha inteira?
Bem, para obtermos uma linha inteira ns devemos fazer uso de um dos mtodos encontrados
dentro de cin - o mtodo getline.
O mtodo getline obtm uma linha de acordo com o tamanho definido no mtodo. Ento, o
mtodo getline utiliza dois parmetros: 1. O nome da string; 2. O tamanho mximo que ser
preenchido.
Ento, usando o mesmo exemplo, apenas mudaramos a 6 linha. Vejamos:
1.18.3.1
Para obter o tamanho de uma string usamos a funo strlen (que a juno do
ingls String Length, que quer dizer, largura de string). Essa funo retorna o nmero de
caracteres utilizados (incluindo os espaos se houver). Ela recebe como argumento apenas a
string que deve ser verificada e retorna o nmero de caracteres encontrados.
Estrutura de Dados
1.18.3.2
H tambm uma forma de compararmos duas strings para ver ser ambas so iguais. A funo
que determina isso strcmp. Embora C/C++ case sensitive, ou seja, diferencia maisculas de
minsculas, isso no ir influenciar nessa funo. Essa funo retorna 0 se h igualdade entre
as strings ou um nmero diferente de zero se no houver igualdade. Portanto, se quisermos
fazer uma comparao de duas strings, procedemos da seguinte forma: strcmp (string1,
string2). Vejamos o exemplo:
1.18.3.3
Para copiar uma string para outra string usamos strcpy (que vem de String copy). Essa funo
recebe dois argumentos: 1. a string para onde ser armazenada a cpia; 2. a string que ser
copiada. Resumidamente, ele copia a segunda string para a primeira. Exemplo:
Estrutura de Dados
1.18.3.4
Concatenar uma String que dizer juntar. De uma forma mais simplria, como se dissssemos
que a juno da palavra passa mais a palavra tempo igual a passatempo. Ou seja, essa funo
- strcat - pega duas strings e junta o que tiver na primeira com o que tiver na segunda.
Tome cuidado: Se concatenarmos duas strings e uma delas ou ambas forem vazias ocorrer
um erro.
No exemplo abaixo, faremos o seguinte: vamos obter o valor de duas strings e concaten-las
formando uma nova string.
Lidar com strings extremamente importante para a programao, pois a string a base de
qualquer arquivo e principalmente controles de rotina (por exemplo: rotinas e procedimentos
de banco de dados, o SQL; endereamento de arquivos; modificao de configuraes...).
Abaixo esta um exemplo com todas as funes bsicas de manipulao de string vistas at
aqui.
Estrutura de Dados
Estrutura de Dados
struct data {
int dia;
int mes;
int ano;
};
Portanto, a varivel hoje declarada como sendo um tipo de dado data. Data uma estrutura
de dados que tem trs caractersticas (ou trs membros) inteiras: dia, mes e ano. Como hoje
um tipo de dado data, ele obtm os mesmos trs membros. Para acessar cada membro,
usamos a varivel e depois o nome do membro que queremos acessar separados por ponto (.).
Estrutura de Dados
O problema aqui est no seguinte, como acessaremos um membro de uma estrutura de dados
usando um ponteiro? Pois simples. Para isso, basta usarmos o que chamamos de "seta". A
"seta" consiste de um sinal de menos e um maior (->).
Portanto, podemos criar nosso struct do mesmo jeito de sempre e nosso ponteiro tambm.
Mas, quando formos acessar um membro dessa estrutura usando um ponteiro ns no
usaremos um ponto, mas uma seta.
Vejamos:
Estrutura de Dados
float mat[4][3];
Estrutura de Dados
O nmero de elementos da linha pode ser omitido numa inicializao, mas o nmero de
colunas deve, obrigatoriamente, ser fornecido:
Uma segunda opo declarar o parmetro como matriz, podendo omitir o nmero de linhas.
Estrutura de Dados
No entanto o vetor no uma estrutura de dados muito flexvel, pois precisamos dimensionlo com um nmero mximo de elementos. Se o nmero de elementos que precisarmos
armazenar exceder a dimenso do vetor, teremos um problema, pois no existe uma maneira
simples e barata (computacionalmente) para alterarmos a dimenso do vetor em tempo de
execuo. Por outro lado, se o nmero de elementos que precisarmos armazenar no vetor for
muito inferior sua dimenso, estar subutilizando o espao de memria reservado.
A soluo para esses problemas utilizar estruturas de dados que cresam medida que
precisarmos armazenar novos elementos (e diminuam medida que precisarmos retirar
elementos armazenados anteriormente). Tais estruturas so chamadas dinmicas e
armazenam cada um dos seus elementos usando alocao dinmica.
Ento, discutiremos a estrutura de dados conhecida como lista encadeada.
As listas encadeadas so amplamente usadas para implementar diversas outras estruturas de
dados com semnticas prprias, que sero tratadas nos captulos seguintes.
Numa lista encadeada, para cada novo elemento inserido na estrutura, alocamos um
espao de memria para armazen-lo. Desta forma, o espao total de memria gasto
pela estrutura proporcional ao nmero de elementos nela armazenado. No entanto, no
podemos garantir que os elementos armazenados na lista ocuparo um espao de
memria contguo, portanto no temos acesso direto aos elementos da lista. Para que
seja possvel percorrer todos os elementos da lista, devemos explicitamente guardar o
encadeamento dos elementos, o que feito armazenando-se, junto com a informao de
cada elemento, um ponteiro para o prximo elemento da lista. A Figura abaixo ilustra o
arranjo da memria de uma lista encadeada.
Estrutura de Dados
Nota-se que se trata de uma estrutura de auto-referencia, pois, alm do campo que armazena
a informao (no caso, um nmero inteiro, info), h um campo que um ponteiro,(prox) para
uma prxima estrutura do mesmo tipo. Tambm uma boa estratgia definirmos o tipo Lista
como sinnimo de struct lista. O tipo Lista representa um n da lista e a estrutura da lista
encadeada representada pelo ponteiro para seu primeiro elemento (tipo Lista*).
Estrutura de Dados
Uma das implementaes dessa funo mostrada a seguir. Devemos notar que o ponteiro que
representa a lista deve ter seu valor atualizado, pois a lista ser representada pelo ponteiro para o
novo primeiro elemento. Por esta razo, a funo de insero recebe como parmetros de
entrada a lista onde ser inserido o novo elemento e a informao do novo elemento, e tem
como valor de retorno a nova lista, representada pelo ponteiro para o novo elemento.
Esta funo aloca dinamicamente o espao para armazenar o novo n da lista, guarda a
informao no novo n e faz este n apontar para (isto , ter como prximo elemento) o
elemento que era o primeiro da lista. A funo ento retorna o novo valor que representa a
lista, que o ponteiro para o novo primeiro elemento. A Figura abaixo ilustra a operao de
insero de um novo elemento no incio da lista.
Um trecho de cdigo que cria uma lista inicialmente vazia e insere nela dois novos elementos.
Observe que no podemos deixar de atualizar a varivel que representa a lista a cada
insero de um novo elemento.
Estrutura de Dados
Essa funo pode ser reescrita de forma mais compacta, conforme mostrado abaixo:
elemento seguinte, e ento podemos liberar o elemento que queremos retirar. Devemos
notar que, no segundo caso, precisamos do ponteiro para o elemento anterior para
podermos acertar o encadeamento da lista. As Figuras a seguir ilustram as operaes de
remoo.
Estrutura de Dados
Mais uma vez, observe que no podemos deixar de atualizar a varivel que representa a
lista a cada insero e a cada remoo de um elemento. Esquecer-se de atribuir o valor de
retorno varivel que representa a lista pode gerar erros graves. Se, por exemplo, a
funo retirar o primeiro elemento da lista, e varivel que representa a lista, no fosse
atualizada, estaria apontando para um n j liberado. Como alternativa, poderamos
fazer com que as funes insere e retira recebessem o endereo da varivel que
representa a lista. Nesse caso, os parmetros das funes seriam do tipo ponteiro para
lista (Lista** l) e seu contedo poderia ser acessado/atualizado de dentro da funo
usando o operador contedo (*lst).
1.22 Pilha.
Uma das estruturas de dados mais simples a pilha. Possivelmente por essa razo, a
estrutura de dados mais utilizada em programao, sendo inclusive implementada
diretamente pelo hardware da maioria das mquinas modernas. A idia fundamental da pilha
que todo o acesso a seus elementos feito atravs do seu topo. Assim, quando um elemento
novo introduzido na pilha, passa a ser o elemento do topo, e o nico elemento que pode ser
removido da pilha o do topo. Isto faz com que os elementos da pilha sejam retirados na
ordem inversa ordem em que foram introduzidos: o primeiro que sai o ltimo que entrou
(a sigla LIFO last in, first out usada para descrever esta estratgia).
Existem duas operaes bsicas que devem ser implementadas numa estrutura de pilha: a
operao para empilhar um novo elemento, inserindo-o no topo, e a operao para
desempilhar um elemento, removendo-o do topo. comum nos referirmos a essas duas
operaes pelos termos em ingls push (empilhar) e pop (desempilhar).
Estrutura de Dados
Estrutura de Dados
A funo para criar a pilha aloca dinamicamente essa estrutura e inicializa a pilha como
sendo vazia, isto , com o nmero de elementos igual a zero.
Para inserir um elemento na pilha, usamos a prxima posio livre do vetor. Devemos
ainda assegurar que exista espao para a insero do novo elemento, tendo em vista
que
trata-se de um vetor com dimenso fixa.
A funo pop retira o elemento do topo da pilha, fornecendo seu valor como retorno.
Podemos tambm verificar se a pilha est ou no vazia.
A funo que verifica se a pilha est vazia pode ser dada por:
Estrutura de Dados
Finalmente, a funo para liberar a memria alocada pela pilha pode ser:
A funo cria aloca a estrutura da pilha e inicializa a lista como sendo vazia.
O primeiro elemento da lista representa o topo da pilha. Cada novo elemento inserido no
incio da lista e, conseqentemente, sempre que solicitado, retiramos o elemento tambm do
incio da lista. Desta forma, precisamos de duas funes auxiliares da lista:
para inserir no incio e
para remover do incio.
Ambas as funes retornam o novo primeiro n da lista.
Estrutura de Dados
As funes que manipulam a pilha, (Inserir e excluir) fazem uso dessas funes de lista:
Por fim, a funo que libera a pilha deve antes liberar todos os elementos da lista.
Estrutura de Dados
1.23 Filas.
Estrutura de Dados
Referncias Bibliogrficas.
1. http://www.gsmfans.com.br/index.php?topic=67407.0;
2. http://pt.wikipedia.org/wiki/C%2B%2B;
3. Lista de exerccio Prof. Alexandre Ribeiro FEIS;
4. Lista de exerccio Prof. Anirio Salles Filho FEIS;
5. Lista de exerccio Profa. Erica Regina Marani Daruichi Machado FEIS;
6. Apostila: Introduo Cincia da Computao e Teoria e Desenvolvimento de
Algoritmos, Profa. Erica M. Daruichi Machado FEIS;
7. Curso Bsico de Lgica de Programao, Unicamp - Centro de Computao DSC,
Autor: Paulo Srgio de Moraes;
8. http://www.dca.fee.unicamp.br/cursos/EA876/apostila/HTML/node37.html;
9. http://www.linhadecodigo.com.br/Artigo.aspx?id=1114;
10. http://www.vivaolinux.com.br/artigo/Substituindo-a-biblioteca-conio.h-no-Linuxusando-ncursescurses.h/;
11. http://allanlima.wordpress.com/;
12. http://pt.wikipedia.org/wiki/C_(linguagem_de_programao);
13. http://www.apostilando.com/download.php?cod=3149&categoria=C%20e%20C++;
14. http://pt.wikibooks.org/wiki/Programar_em_C%2B%2B;
15. http://www.tiexpert.net/programacao;
16. http://pt.wikibooks.org/wiki/Programar_em_C/Vetores;
17. http://pt.wikipedia.org/wiki/C%2B%2B;
18.
Estrutura de Dados