Baixe o app para aproveitar ainda mais
Prévia do material em texto
Algoritmos e Estruturas de Dados II Turmas A1 e A2 Gisele L. Pappa Cris;ano Arbex 2018/01 Algoritmos • Presentes em todas as áreas da computação na resolução dos mais diversos ;pos de problemas • Permitem que problemas do mundo real possam ser trabalhados de forma estruturada e depois resolvidos por um computador Estrutura de Dados • Uma estrutura de dados é uma forma de se armazenar e organizar os dados de um determinado problema de forma a facilitar o acesso e modificações por um algoritmo • Diferentes estruturas de dados se aplicam a diferentes problemas e algoritmos Abstração Problema do Mundo Real Modelo Abstrato Resolução do Problema Solução no Mundo Real Algoritmos + Estruturas de Dados Ementa • Tipos Abstratos de Dados (TADs) • Análise de Algoritmos O(n), O(n log n), )(n!), ... • Estruturas de dados listas, filas, pilhas e árvores • Métodos de ordenação quick, heap, merge, select, etc • Métodos de pesquisa hash, árvores binárias, árvores digitais Parte 1 Parte 2 Prova – 26/04 TP1 (10 ptos) Parte 3 Prova 2 – 21/06 TP2 – 15 ptos Avaliação • 2 provas (total 60 pontos) • 3 trabalhos prá;cos (5+15+20 pontos) • Implementação • Documentação • Teste • Lista de exercícios (5 pontos) Bibliografia Livro Texto: Ziviani, N., Projeto de Algoritmos com Implementações em Pascal e C, 3ª Edição, Cengage Learning, 2011. Cormen , T., Leiserson, C, Rivest R., Stein, C. Introduc8on to Algorithms, Third Edi;on, MIT Press, 2009. Bibliografia Bibliografia de Referência: Celes, W, Cerqueira, R, Rangel, J. Introdução à Estrutura de Dados, Editora Campus, 2004. Feofiloff, P. Algoritmos em Linguagem C, Editora Campus, 2009. Sedgewick, R. Algorithms in C, Parts 1-4: Fundamentals, Data Structures, Sor8ng, Searching (3rd Edi;on), Addison-Wesley, 1997 Aho, A., Hopocron J., Ullman J. Data Structure and Algorithms, Addison-Wesley, 1983 Detalhes • Linguagem: C • Plataformas: IDE Recomendada Code::Blocks (usando GCC/MinGW) • Não u;lizar bibliotecas especificas • Sistema operacional recomendado: Linux • Alta carga extra classe Moddle • Todas informações relacionadas ao curso, incluindo notas de aulas, estarão disponíveis na METATURMA do Moodle • Submissão de trabalhos prá;cos via Moodle • Monitor e monitorias • Moodle e presencial Programação em C recomendações e boas prá;cas Clodoveu Davis Gisele L. Pappa Tópicos • Indentação • Comentários • Modularização • Entrada e saída • Vetores e Strings • Passagem de parâmetros • Structs Boas Prá;cas • Com um pequeno esforço extra, programas escritos em C podem se tornar mais legíveis e mais facilmente “debugáveis” • No caso de disciplinas de programação, isso ainda ajuda no entendimento das idéias e na correção de trabalhos Indentação • É usual empregar TABS para indicar blocos de programação – Em geral, 1 tab equivale a 8 espaços, MAS NÃO USAR ESPAÇOS para alinhar • Há vários es;los • Quando o bloco é longo, é usual colocar um pequeno comentário após o fechamento indicando o que está sendo fechado Indentação • K&R: Kernighan & Ritchie Indentação • 1TBS: One True Brace Style Indentação • Allman Comentários • Importantes para compreensão do código • Mais importantes em código mais complexo • Úteis para explicar o conteúdo de variáveis, mas não subs;tuem um bom critério de atribuição de nomes • Não exagerar! Comentários • No início de cada módulo de código (arquivos .c, .h) • Uma linha em cada função, explicando o que ela faz – Não é necessário explicar COMO ela faz, o código deve ser claro o bastante para permi;r esse entendimento em uma função razoavelmente pequena – Se necessário, considerar a quebra em outras funções • Comentário na linha da declaração, se necessário, para esclarecer o significado/o uso de uma variável • Comentário na linha do fecha-chave, para ajudar a entender blocos e loops Comentários • No início de um bloco/arquivo fonte Comentários • Em função (simples) Comentários • Em funções (arquivo .h) Comentários • Em variáveis • Em structs Comentários • No fim de um bloco de programa • No código Modularização • Planejar a quebra de um programa em módulos – Um módulo não significa necessariamente um arquivo fonte; muitas vezes, é um par de arquivos .c / .h – Existe sempre um arquivo fonte para o programa principal (main), e outros para funções adicionais ou componentes • Montar módulos especificamente para ;pos abstratos de dados [aula: TAD] • Procurar dar independência aos módulos, para que possam eventualmente ser reaproveitados Modularização Modularização Modularização Modularização Atenção: incluir timer.h e timer.c no projeto Modularização transformação em biblioteca ;mer.lib Copiar timer.h para o diretório “includes” do compilador Copiar timer.lib para o diretório lib do compilador Constantes e #define • Não usar “números mágicos” no código • Algumas constantes usadas no programa podem ter que ser modificadas, e é mais fácil fazer isso em um lugar só • Sempre que for tornar o código mais legível, usar #define para dar um nome à constante – Em geral, nomes de constantes são em maiúsculas #define PI 3.141592 #define MAX_VETOR 1000 Nomes de variáveis • Algumas variáveis merecem nomes significa;vos: MAX_VETOR, numClientes, listaAlunos • Variáveis auxiliares em geral recebem nomes curtos: i, j, aux, x – Cuidado para não fazer confusão – Não abusar: i, ii, iii, aux1, aux2, aux3... – Variáveis inteiras: i, j, k – Variáveis reais: x, y, z – Strings: s1, s2 – Booleanas: nome do teste (existe, valido, ocorre) Nomes de variáveis • Es;los variados: – Só minúsculas (i, num, conta) – Só maiúsculas (constantes: PI, E, MAX) – CamelCase (numMat, anguloEntrada) – Indicação do ;po no início do nome (iNum, iValor, fRaio, fAltura, dVolume) • Há quem prefira inserir comentários e usar nomes de variáveis em inglês, por ficar mais próximo da linguagem de programação Organização e limpeza • Procurar dar um aspecto organizado ao código, ajuda na compreensão • Entender o código fonte como um instrumento de comunicação • Comentar excessivamente código mal escrito não ajuda • Dar nomes adequados a variáveis ajuda bastante Parênteses e espaçamento • Usar espaços antes de parênteses, depois de vírgulas, ao redor de operadores binários if (x == 10) y = 5; for (i = 0; i < 10; i++) { x += a; a = f(b); } • Cuidado com notações compactas demais, e com comandos embu;dos em outros if (x++ == b) y = 5; Correção e robustez • Testes: prever todo ;po de problema e variações na entrada de dados – Limites de vetores – Valores inteiros e de ponto flutuante – Contadores e incremento – Testes de fim de arquivo – Teste de disponibilidade de memória para alocação Compilação • LER as mensagens de erro e ENTENDER a origem do problema • Warnings: indicam problemas potenciais, devem ser resolvidos • Muitas vezes a mensagem de erro não reflete o que está ocorrendo – Observar a linha em que o erro foi indicado, a linha anterior, o bloco de código em que ocorreu, e o corpo da função em que ocorreu Debugger • Ajuda a acompanhar os valores das variáveis ao longo da execução – Observaro valor de variáveis (watches) – Posicionar pontos de interrupção (breakpoints) – Executar passo a passo • Vide http://wiki.codeblocks.org/index.php?title=Debugging_with_Code::Blocks • Documentação do CodeBlocks http://wiki.codeblocks.org/index.php?title=Main_Page ENTRADA E SAÍDA I/O em C • Formalmente, ro;nas de entrada e saída não fazem parte da linguagem, e sim de bibliotecas que acompanham os compiladores – Felizmente, são padronizadas – Exige-se a linha #include <stdio.h> para usá-las I/O em C • prin (string [, valor , valor, ...]); – O string contém uma “máscara” (template) com lacunas reservadas para os valores que serão impressos printf(“O valor de x eh %d”, x); printf(“Area: %f\n”, PI*d*d/4); printf(“Nome: %s”, nomeAluno); prin • Especificadores de formato – %c (char) – %s (string) – %d (int) – %ld (long int) – %f (float) – %lf (double) – %e (float notação cienfica) – %g (e ou f, ou seja, notação cienfica se necessário) Caracteres de escape • Acrescentados à máscara para provocar reposicionamento do cursor – \n: nova linha – \t: tabulação – \r: backspace – \\: caractere da barra inver;da Entrada • Com máscara: scanf(string, *var [, *var, ...]); – Mesmos especificadores de formato do prin – A função precisa receber o endereço da variável à qual o valor digitado será atribuído scanf(“%d”, &num) scanf(“%c%d”, &letra, &num); scanf(“%c”, &ch); scanf(“%s”, s); // string scanf(“%13c”, s); //le 13 caracteres scanf(“ %c”, &ch); //pula brancos Entrada Entrada • Observe que scanf interrompe a leitura de um string quando encontra um branco, se usado com %s • Uso de %[]: – %[aeiou]: apenas as vogais são permi;das – %[^aeiou]: as vogais não são permi;das – %[^\n]: interrompe quando recebe um [enter] – %60[^\n]: admite até 60 caracteres, e para quando encontra um enter Entrada • Linhas inteiras: gets(string) – Lê uma linha inteira, excluindo \n, e coloca \0 no final – Com limite de tamanho: • fgets(string, tamMax, stdin) Entrada • Caracteres individuais: getchar() – O caractere digitado é o valor de retorno da função I/O para arquivos • A entrada do teclado e saída para o monitor são realizadas internamente considerando três disposi;vos virtuais: stdin, stdout e stderr – Como são disposi;vos padrão, referências a stdin e stdout eles são omi;das dos comandos • I/O para arquivos é muito semelhante à operação com stdin e stdout, mas um handle ao arquivo tem que ser fornecido I/O para arquivos • O handle é ob;do no momento da abertura do arquivo FILE *inFile; // variável handle FILE *outfile; ... //abre o arquivo para leitura (r) ou gravacao (w) inFile = fopen(“arquivo.txt”, “r”); outFile = fopen(“saida.txt”, “w”); ... fscanf(inFile, “%d”, &num); ... fprintf(outFile, “O valor lido eh %8.2d\n”, num); ... fclose(inFile); fclose(outFile); I/O para arquivos • fopen: modos de abertura I/O para arquivos • Se o handle retornar nulo do comando fopen, então ocorreu erro (arquivo não encontrado, arquivo travado contra gravação, permissão negada pelo sistema operacional, etc.) • Testar: if ((inFile = fopen(“arquivo.txt”, “r”)) == NULL) { printf(“Nao consegui abrir.\n”); exit(1); } I/O para arquivos • gets() à fgets(arq, tamMax, string); • getchar() à fgetc(arq); • putc(ch) à fputc(arq, ch) • prin à fprin (arq, string, valor) • scanf à fscanf(arq, string, endereço) • feof(arq) – Retorna booleano indicando se o fim do arquivo foi a;ngido – Neste caso, o fgetc retorna a constante EOF, definida em stdio como 0xFFFF I/O para arquivos • Exemplo Exercício (POSCOMP 2009) #include<stdio.h> #include<string.h> int main (void) { char texto[]= "foi muito facil"; int i; for (i = 0; i < strlen(texto); i++){ if (texto[i] == ' ') break; } i++; for ( ; i < strlen(texto); i++) printf("%c", texto[i]); return 0; } O que será impresso quando o programa for executado? I/O para arquivos • Exemplo (variação) VETORES E STRINGS Alocação está;ca de memória • Ao se declarar uma variável qualquer, o compilador deixa reservado um espaço suficiente na memória para armazená-la int a; // 2 bytes long b; // 4 bytes float x; // 4 bytes double y; // 8 bytes char c; // 1 byte Alocação está;ca de memória • Ao fazer a alocação está;ca, apenas o espaço necessário na memória é reservado • O conteúdo de cada posição não é alterado, e portanto uma variável apenas declarada pode conter qualquer coisa • Inicializar as variáveis, atribuindo valores, antes do uso – Inclusive vetores, matrizes e strings Vetores • Quando se declara um vetor, o valor entre chaves indica quantas vezes o espaço de memória necessário para o ;po básico será alocado int v[100]; // 100 * sizeof(int) = 200 bytes long vl[200]; // 200 * sizeof(long) = 800 bytes double z[1000]; // 1000 * sizeof(double) = 8000 bytes Vetores • A referência a uma posição de um vetor indica o cálculo de uma posição de memória a par;r do início do vetor float x[1000]; // x[20] está na posição x + 20*sizeof(float) • Na verdade, o símbolo x é um apontador para o início da região de memória reservada para o vetor Vetores • C NÃO AVISA NEM PRODUZ ERRO QUANDO O LIMITE DE UM VETOR OU MATRIZ FOR EXCEDIDO y = x[2000]; // não dá erro, mas vai acessar // uma parte inesperada da memória – Erro mais comum (run;me): segmentation fault • É responsabilidade do programador verificar os limites, e garan;r que não sejam excedidos Matrizes = Vetores de mais de uma dimensão • Na verdade, a alocação na memória é linear • Muda apenas o cálculo da posição de memória do elemento referenciado int M[5][5]; ... M[0][0] = 15; M[2][3] = 2; // posicao: M + (2*5 + 3)*sizeof(int) Strings • Um string é um vetor do ;po char • Para manipulação do string, atribuindo e recuperando valores, e realizando operações sobre ele, é importante entender essa caracterís;ca • Quando o conteúdo de um string não ocupa todo o espaço disponível, usa-se um caractere ‘\0’ (ou NULL, código ASCII 0) como delimitador Strings • Strings constantes aparecem no código entre aspas printf(“%s”, “Bom dia!”); • O delimitador \0 está incluído: Strings • Não é possível fazer atribuições diretas para strings • Usar a função strcpy ou a função strncpy Strings • Na inicialização, pode-se usar o mesmo recurso disponível para vetores • char nome[] = {‘A’, ‘n’, ‘a’, ‘\0’}; • Ou • char nome[] = “Ana”; Strings • Como o nome do string representa o endereço onde começa o string, é possível manipulá-lo diretamente – Cuidado! • Exemplo char nome[] = “Alexandre”; printf(“%s\n”, nome + 3); // imprime “xandre” Strings • Funções – strlen(st): retorna o comprimento do string (com exceção do \0) – strcat(st1, st2): concatena o s2 no s1 (s1 tem que ter espaço) – strcmp(s1, s2): retorna <0 se s1 é menor que s2, ==0 se s1 é igual a s2, e >0 se s1 é maior que s2 (ordem alfabé;ca) • A comparação entre strings também tem que ser feita caractere a caractere, portanto não se pode usar s1==s2; isso só compara os endereços Strings • strncat, strncmp, strncpy: idem aos anteriores, mas especifica o número de caracteres que serão considerados Strings • strtok: extrai “tokens”, substrings delimitados • Exemplo long int num; char linha[256]; char *p1 = NULL; while(!feof(infile)) { fgets(linha, 256, infile); p1 = strtok(linha, " \n"); //delim: branco ou fim de linha while ((p1 != NULL) && (!feof(infile))) { num++; fprintf(outfile, "%s\n", p1); p1 = strtok(NULL, " \n"); } } printf("O arquivo de entrada tinha %ld palavras.\n", num); fclose(infile); Strings • Vetores de strings podem ser criados • Exemplo char DiaSemana[7][14] = {“Domingo”, “Segunda”, “Terça”, “Quarta”, “Quinta”, “Sexta”, “Sabado”}; ... printf(“%s\n”, DiaSemana[3]); Algoritmos e Estrutura de Dados II Passagem de Parâmetros • Em pascal e C++, parâmetros para função podem ser passados por valor ou por referência – Por valor: o parâmetro formal (recebido no procedimento) é uma cópia do parâmetro real (passado na chamada) – Por referência: o parâmetro formal (recebido no procedimento) é uma referência para o parâmetro real (passado na chamada) • As modificações efetuadas acontecem no parâmetro real • Em C só existe passagem por valor, logo deve-se implementar a passagem por referência u;lizando-se apontadores Algoritmos e Estrutura de Dados II Passagem de Parâmetros (C) void SomaUm(int x, int *y) { x = x + 1; *y = (*y) + 1; printf("Funcao SomaUm: %d %d\n", x, *y); } int main() { int a=0, b=0; SomaUm(a, &b); printf("Programa principal: %d %d\n", a, b); } 1 1 0 1 Algoritmos e Estrutura de Dados II Passagem de Parâmetros • E para alocar memória dentro de um procedimento? – Em pascal, basta passar a variável (apontador) como referência. – Em C também, mas como não há passagem por referência as coisas são um pouco mais complicadas void aloca(int *x, int n) { x=(int *)malloc(n*sizeof(int)); x[0] = 20; } int main() { int *a; aloca(a, 10); a[1] = 40; } Error! Access Violation! Endereço Valor Algoritmos e Estrutura de Dados II Passagem de Parâmetros • E para alocar memória dentro de um procedimento? – Em pascal, basta passar a variável (apontador) como referência. – Em C também, mas como não há passagem por referência as coisas são um pouco mais complicadas void aloca(int *x, int n) { x=(int *)malloc(n*sizeof(int)); x[0] = 20; } int main() { int *a; aloca(a, 10); a[1] = 40; } Error! Access Violation! void aloca(int **x, int n) { *x=(int *)malloc(n*sizeof(int)); *x[0] = 20; } int main() { int *a; aloca(&a, 10); a[1] = 40; } OK Parâmetros para o programa • Chamada: ./prog arg1 arg2 arg3 • Declaração completa da função main int main(int argc, char *argv[]) • argc: número de parâmetros passados na linha de comando • argv: um vetor de argc strings – argv[0]: nome do programa – argv[1]: primeiro parâmetro – argv[argc – 1]: úl;mo parâmetro – argv[argc] é sempre NULL Parâmetros para o Programa Exemplo: Programa data, que recebe mês, dia e ano como inteiros Chamada: data 19 04 99 void main(int argc, char *argv[]) { int mes; char *nomemes [] = {"Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho”,"Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"}; if(argc == 4) /*O primeiro parametro e' o nome do programa, o segundo o dia, o terceiro o mes e o quarto os dois ultimos algarismos do ano */ { mes = atoi(argv[2]); /* Transforma string mes em inteiro*/ if (mes<1 || mes>12) /* Testa se o mes e' valido */ printf("Erro!\nUso: data dia mes ano, todos inteiros"); else printf("\n%s de %s de 19%s", argv[1], nomemes[mes-1], argv[3]); } else printf("Erro!\nUso: data dia mes ano, todos inteiros"); } Algoritmos e Estrutura de Dados II Exercícios 1. Faça um programa que leia um valor n, crie dinamicamente um vetor de n elementos e passe esse vetor para uma função que vai ler os elementos desse vetor. 2. Declare um TipoRegistro, com campos a inteiro e b que é um apontador para char. No seu programa crie dinamicamente uma váriavel do TipoRegistro e atribua os valores 10 e ‘x’ aos seus campos. Algoritmos e Estrutura de Dados II Respostas (1) void LeVetor(int *a, int n){ int i; for(i=0; i<n; i++) scanf("%d",&a[i]); } int main(int argc, char *argv[]) { int *v, n, i; scanf("%d",&n); v = (int *) malloc(n*sizeof(int)); LeVetor(v,n); for(i=0; i<n; i++) printf("%d\n",v[i]); } Apesar do conteúdo ser modificado Não é necessário passar por referência pois todo vetor já é um apontador... Algoritmos e Estrutura de Dados II Respostas (2) typedef struct { int a; char *b; } TRegistro; int main(int argc, char *argv[]) { TRegistro *reg; reg = (TRegistro *) malloc(sizeof(TRegistro)); reg->a = 10; reg->b = (char *) malloc(sizeof(char)); *(reg->b) = 'x'; printf("%d %c",reg->a, *(reg->b)); } É necessário alocar espaço para o registro e para o campo b. *(reg->b) representa o conteúdo da variável apontada por reg->b Referências • Mizrahi, V. V. Treinamento em Linguagem C. Pearson, 2008. • Kernighan B.W., Ritchie, D.M. C A linguagem de programação (padrão ANSI). Campus, 1989. • Vide Moodle para links (inclusive cursos online), exemplos e exercícios
Compartilhar