UNIVERSIDADE DO ALGARVE FACULDADE DE CIÊNCIAS E TECNOLOGIA Departamento de Engenharia Electrónica e Informática Relatório da disciplina: Estruturas de dados 2º semestre 2004/2005 Helder Aniceto Amadeu de Sousa Daniel Faro Julho de 2005 Estruturas de Dados (2004-2005) 1 Estruturas de Dados (2004-2005) 2 Índice 1 2 3 4 5 Introdução ............................................................................................................................... 1 Objectivos pedagógicos ........................................................................................................... 1 Conteúdos programáticos ......................................................................................................... 1 Programa ................................................................................................................................. 1 Bibliografia.............................................................................................................................. 2 5.1 Referências principais: ..................................................................................................... 2 5.2 Bibliografia complementar sobre C: ................................................................................. 3 5.3 Resumos das aulas ........................................................................................................... 3 6 Procedimento de avaliação ....................................................................................................... 3 6.1 Avaliação contínua ........................................................................................................... 3 6.2 Exame época normal e recurso ......................................................................................... 3 6.3 Aprovação na disciplina ................................................................................................... 3 7 Calendarização ........................................................................................................................ 3 7.1 Teórica ............................................................................................................................. 3 7.2 Prática .............................................................................................................................. 5 8 Assiduidade ............................................................................................................................. 6 8.1 Teóricas ........................................................................................................................... 6 8.2 Práticas ............................................................................................................................ 7 9 Avaliação................................................................................................................................. 7 Enunciados dos momentos de avaliação......................................................................................... 11 Estruturas de Dados (2004-2005) i Estruturas de Dados (2004-2005) ii 1 Introdução Neste relatório encontra-se definido o conteúdo, objectivos, programa, bibliografia, métodos de avaliação da disciplina Estruturas de Dados, leccionada no 2º semestre do ano lectivo de 2004/2005, às licenciaturas em Engenharia de Sistemas e Informática, Ensino de informática e Informática, da Faculdade de Ciências e Tecnologia da Universidade do Algarve. O programa abaixo apresentado, o qual foi seguido este ano, é idêntico ao desenvolvido pelo Professor Doutor José Valente de Oliveira no ano lectivo 2003/2004, podendo ser consultado todo o material lectivo em: http://w3.ualg.pt/~jvo/ed0304. A matéria teórica e as fichas práticas referidas no ponto 7, foram idênticas às leccionadas no ano lectivo 2003/2004, da autoria do Professor Doutor José Valente de Oliveira, salvo menção explícita nesse mesmo ponto. O material lectivo referente ao ano 2004/2005 pode ser consultado em: http://w3.ualg.pt/~hdaniel/ed. No plano curricular das licenciaturas acima referidas, esta disciplina insere-se no 2º semestre do 1º ano e correspondem-lhe 3 créditos. A disciplina foi ainda leccionada ao curso de Engenharia de Sistema e Computação. 2 Objectivos pedagógicos Dotar os alunos com a capacidade de desenvolver programas multi-modulares em ANSI C, recorrendo a estruturas de dados elementares e algoritmos básicos de ordenação e procura. 3 Conteúdos programáticos Tipos de dados e tipos de dados abstractos. Listas, pilhas, filas de espera e árvores binárias. Algoritmos de ordenação elementares e algoritmo quicksort. 4 Programa Abaixo apresenta-se o programa previsto para o semestre, conforme apresentado nas aulas teóricas: I. Conceitos fundamentais a) Tipos básicos b) Arrays, estruturas e uniões c) Ponteiros i. Ponteiros para constantes ii. Ponteiros constantes iii. Ponteiros constantes para constantes iv. Ponteiros para estruturas v. Ponteiros para funções vi. Arrays de ponteiros e os argumentos da função main II. Reserva dinâmica de memória a) Funções para reserva e libertação dinâmica de memória b) Reserva dinâmica de memória para matrizes e tensores III. Primeiras noções de Projecto, de Projecto por Contrato e Aspectos sobre a documentação de código. IV. Listas ligadas a) Cadeias b) Listas circulares simplesmente ligadas Estruturas de Dados (2004-2005) 1 c) Listas duplamente ligadas 5 V. Tipos de dados abstractos a) Motivação e definição b) A pilha (stack) como tipo de dados abstracto i. Programa cliente de Stack: calculadora de expressões aritméticas em notação sufixa ii. Implementação do ADT Stack usando uma lista ligada iii. Implementação do ADT stack usando arrays iv. Programa cliente de stack para conversão de expressões algébricas de notação infixa para notação sufixa. c) O tipo de dados abstracto fila (FIFO queue): Interface e implementações d) Noção de fila generalizada e) Noção de deque (double-ended queue) f) Implementação em C de tipos de dados abstractos com múltiplas instâncias: O ADT Point e o ADT Queue g) O tipo de dados abstracto polinómio i. interface ii. Implementação baseada em arrays e em listas ligadas h) O ADT Matriz Esparsa i. Funções seleccionadas de interface ii. Implementação baseada em arrays e em listas ligadas VI. Árvores a) Noções de recursividade b) Generalidades, terminologia e representação de árvores ordenadas c) Árvores binárias i. Funções de atravessamento ii. Propriedades básicas e funções elementares recursivas VII. Algoritmos elementares de ordenação a) Terminologia e definição do problema de ordenação b) Algoritmos de ordenação por selecção c) Algoritmos de ordenação por inserção e troca (bubble sort) d) Ordenação por selecção em listas ligadas VIII. O algoritmo Quicksort a) Versão probabilística e sua implementação em C b) Versão clássica e sua implementação em C c) Desempenho d) Melhoramentos do quicksort: subcolecções pequenas e partição por mediana e) A função qsort do ANSI C. IX. Filas privadas e Acervos: Noções elementares Bibliografia 5.1 Referências principais: Ellis Horowitz, Sartaj Sahni, Susan Anderson-Freed (1993). “Fundamentals of Data Structures in C”, Computer Science Press, New York R. Sedgewick (1999). “Algorithms in C (3rd Searching, Structures, Sorting and Searching”, Addison-Wesley. Estruturas de Dados (2004-2005) edition) Parts 1-4: Fundamentals, Data 2 5.2 Bibliografia complementar sobre C: Brian W. Kernigham, M. Dennis Ritchie (1991). “The C Programming Language, (2nd edition)”. Prentice-Hall. P. Guerreiro (2001). “Elementos de Programação com C”, FCA. 5.3 Resumos das aulas Resumos das aulas, assim como outros elementos relativos à disciplina, estão disponíveis on-line em http://w3.ualg.pt/~hdaniel/ed/ 6 Procedimento de avaliação 6.1 Avaliação contínua Teste: Trabalho prático: 60 % da nota final 40 % da nota final Prova complementar para alunos com notas no intervalo [8.0; 9.5[. 6.2 Exame época normal e recurso Os exames consistem de prova escrita, sem consulta, podendo incluir questões sobre conhecimentos necessários à realização do trabalho prático. A nota do exame constituí a nota final da disciplina Todos os alunos regularmente inscritos são admitidos a exame. 6.3 Aprovação na disciplina Serão aprovados na disciplina alunos com nota igual ou superior a 10 valores, na avaliação contínua ou nos exames. 7 Calendarização A disciplina é composta por componente teórica semanal de 2 horas e componente prática de 3 horas. A componente teórica divide-se em dois períodos não consecutivos de 1 hora. 7.1 Teórica A exposição da matéria segundo o programa acima foi calendarizada para 28 aulas teóricas, com duração aproximada de 50 minutos: 1. Apresentação 2. Estruturas de Dados Elementares: Tipos básicos, estruturas e uniões 3. Arrays e ponteiros I 4. Arrays e ponteiros II: Relacionamento entre ponteiros e arrays 5. Arrays de ponteiros; Args. função main; Reserva dinâmica de memória I 6. Reserva dinâmica de memória para matrizes. Introdução às listas ligadas 7. Noções de Projecto 8. Funções atómicas sobre cadeias. Aspectos de documentação de código 9. Mais funções sobre cadeias. Listas circulares 10. Listas duplamente ligadas 11. Tipos de Dados Abstractos. Interface para um ADT Stack 12. Cliente de ADT Stack: calculadora para expressões em notação sufixa 13. Primeiras implementações de um ADT Stack 14. Conversor de expressões algébricas Estruturas de Dados (2004-2005) 3 15. O ADT FIFO queue 16. Filas aleatórias, generalizadas e deques. ADTs com múltiplas instâncias 16bRevisitando ADTs 17. O ADT polinómio: Interface e implementação baseada em arrays 18. O ADT polinómio: Implementação baseada em listas ligadas 19. O ADT matriz esparsa 20. Árvores e recursividade 21. Representação de árvores ordenadas 22. Funções e propriedades fundamentais das árvores binárias 23. Algoritmos elementares de ordenação I 24. Algoritmos elementares de ordenação II 25. Quicksort I 26. Quicksort II 27. Filas com prioridades e acervos Relativamente à calendarização do ano lectivo de 2003-2004 foi introduzida a aula 16b, sobre ADTs genéricos, e adicionada documentação sobre árvores binárias de busca à aula 22. Na aula 19 não foi tão aprofundada a implementação do ADT matriz esparsa baseado em listas ligadas, tendo sido apresentadas conceitos sobre implementações alternativas deste ADT. EDa 2004 / 2005 Teóricas T2 Semana 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Inicio 14-Fev-05 21-Fev-05 28-Fev-05 07-Mar-05 14-Mar-05 21-Mar-05 28-Mar-05 04-Abr-05 11-Abr-05 18-Abr-05 25-Abr-05 02-Mai-05 09-Mai-05 16-Mai-05 23-Mai-05 30-Mai-05 06-Jun-05 Fim 18-Fev-05 25-Fev-05 04-Mar-05 11-Mar-05 18-Mar-05 25-Mar-05 01-Abr-05 08-Abr-05 15-Abr-05 22-Abr-05 29-Abr-05 06-Mai-05 13-Mai-05 20-Mai-05 27-Mai-05 03-Jun-05 10-Jun-05 TER 1 2/3 5 7 9 11 13 15 16b 18 20 21/22 23 24 Teste T1 QUI TER 2 1 4 2/3 6 5 8 7 10 9 Páscoa 12 11 14 13 16 15 17 16b 19 18 21 20 Semana académica 22 21 FER 22 25 24 Teste 26 SEX 2 4 6 8 10 12 14 16 17 19 FLT 21/22 23 25 FER Os alunos dos cursos aos quais a cadeira é leccionada foram divididos em dois turnos, T1 e T2, sendo distribuído o serviço docente do seguinte modo: T1 T2 Helder Daniel Helder Daniel (professor auxiliar DEEI) As aulas foram apresentadas conforme mostra a calendarização acima. Como as aulas teóricas 21 e 22 foram leccionadas em 3 períodos de 1 hora, mais uma hora do que o previsto, não houve tempo para apresentar a aula teórica 27. Estruturas de Dados (2004-2005) 4 A aula teórica prevista para o turno T1 no dia 6 de Maio de 2005, não foi leccionada por ausência do docente. Foi marcada uma aula de compensação para o dia 9 de Junho de 2005, de modo a equilibrar o número de aulas de ambos os turnos teóricos. As aulas teóricas previstas para os turnos T1 e T2 no dia 7 de Junho de 2005, não foram leccionadas por coincidir com o dia do teste. 7.2 Prática Nas aulas práticas, além do tempo dedicado aos trabalhos práticos, foram resolvidos exercícios sobre os temas apresentados nas aulas teóricas, distribuídos por 11 fichas: 1. Exercícios de aquecimento 2. Estruturas, uniões, arrays e apontadores 3. Reserva dinâmica de memória 4. Listas simplesmente ligadas e diagramas de estrutura 5. Listas circulares 6. O Stack como tipo de dados abstracto 7. Filas de espera, deques e filas aleatórias 8. ADTs com múltiplas instâncias 9. Matrizes esparsas 10. Árvores 11. Algoritmos elementares de ordenação EDa 2004 / 2005 Semana 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Inicio 14-Fev-05 21-Fev-05 28-Fev-05 07-Mar-05 14-Mar-05 21-Mar-05 28-Mar-05 04-Abr-05 11-Abr-05 18-Abr-05 25-Abr-05 02-Mai-05 09-Mai-05 16-Mai-05 23-Mai-05 30-Mai-05 06-Jun-05 Práticas S Fim P4 18-Fev-05 25-Fev-05 1 04-Mar-05 2 11-Mar-05 3 18-Mar-05 4a / TRB 25-Mar-05 01-Abr-05 Páscoa 08-Abr-05 4b 15-Abr-05 5 22-Abr-05 6 FER 29-Abr-05 06-Mai-05 7 13-Mai-05 20-Mai-05 8 27-Mai-05 9 03-Jun-05 10 10-Jun-05 11 T Q S S P3 Q P5 P2 P1 Não se efectuam aulas práticas 1 1 1 1 2 2 2 2 3 3 3 3 4a / TRB 4a / TRB 4a / TRB 4a / TRB Páscoa 4b 4b 4b 4b 5 5 5 5 6 6 6 6 CC 7 7 7 7 8 8 8 8 9 9 9 Semana académica 9 10 10 10 FER 10 11 11 11 11 TRB TRB Teste FER TRB Os alunos dos cursos aos quais a cadeira é leccionada foram divididos em cinco turnos, P1 – P5, sendo distribuído o serviço docente do seguinte modo: P1 P2 P3 P4 P5 Ana Paula Costa Helder Daniel Carlos Fonseca Ana Paula Costa Ana Paula Costa (assistente DEEI) (professor auxiliar DEEI) (professor auxiliar DEEI) Estruturas de Dados (2004-2005) 5 As aulas foram apresentadas conforme mostra a calendarização acima. Na primeira semana não foram efectuadas aulas práticas por não ter sido leccionada matéria teórica. A aula prática prevista para o turno P3 no dia 19 de Abril de 2005, não foi leccionada por ausência do docente, pois coincidiu com uma reunião da Comissão Científica do DEEI. No entanto como este turno tinha mais uma aula prevista que os restantes a calendarização não foi afectada. A dessincronização do número de aulas para os diferentes turnos, devido a feriados e outros acontecimentos eventuais, fez com que aos turnos P1, P2 e P5 coubesse mais uma aula que aos restantes. Assim as aulas de 2, 3 e 9 de Junho de 2005 foram dedicadas apenas ao trabalho prático e abertas a alunos inscritos em todos os turnos práticos. O trabalho prático deste ano consistiu na implementação de uma aplicação de gestão de contas à ordem de um só titular em ANSI C. Foi requerido que implementação fosse baseada em Tipos de Dados abstractos e que fosse efectuada a separação do interface de utilizador do motor da aplicação. O trabalho prático foi faseado, ao longo do semestre, à medida que foram introduzidos os conceitos e estruturas de dados necessárias para a implementação. 8 Assiduidade 8.1 Teóricas Alunos inscritos na disciplina segundo os serviços académicos: Curso ESC EI I ESI Total 2 54 119 121 296 alunos 100 90 80 70 60 T1 50 T2 40 Total 30 20 10 0 1 3 5 7 9 11 13 15 17 19 21 23 25 27 aula nº Fig. 1 – Assiduidade às aulas teóricas No gráfico acima têm-se a assiduidade à componente teórica por aula, para cada turno e total. Estruturas de Dados (2004-2005) 6 8.2 Práticas Alunos inscritos nos turnos práticos: Turno P1 P2 P3 P4 P5 33 40 30 44 33 alunos 120 100 P1 80 P2 P3 Total 180 60 P4 P5 40 Total 20 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 aula nº Fig. 2 – Assiduidade às aulas práticas No gráfico acima têm-se a assiduidade à componente prática por aula, para cada turno e total. 9 Avaliação Para avaliação contínua, exame de época normal e exame de recurso está indicada a percentagem de aprovação baseada nos alunos que se propuseram a cada época de avaliação. Assim para avaliação contínua será utilizado o número de alunos que compareceram ao teste e não a totalidade de inscritos segundo os serviços académicos. ESC 0 ESI 13 EI 6 I 9 Total 28 Entregaram o Trabalho: 0 12 6 11 29 Aprovados por avaliação continua: % aprovação 0 7 1 5 13 0,00% 53,85% 16,67% 55,56% 46,43% 0 0 15 9 4 4 12 5 31 18 0,00% 60,00% 100,00% 41,67% 58,06% 1 1 5 1 1 0 9 2 16 4 100,00% 20,00% 0,00% 22,22% 25,00% Assiduidade ao teste: Assiduidade ao exame época normal: Aprovados no exame época normal: % aprovação: Assiduidade ao exame recurso: Aprovados no exame recurso: % aprovação: Total de alunos aprovados Estruturas de Dados (2004-2005) 35 7 Os seguintes histogramas mostram a distribuição de notas para as épocas de avaliação acima indicadas: Distribuição de notas de frequência 7 6 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 nota Distribuição de notas de exame época normal 5 4 4 3 3 2 2 1 1 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 nota Distribuição de notas de exame de recurso 5 4 3 2 1 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 nota Fig. 3 – Distribuição de notas Estruturas de Dados (2004-2005) 8 ____________________________________________________ Helder Daniel Responsável pela disciplina de Estrutura de Dados no ano lectivo 2004/2005 Estruturas de Dados (2004-2005) 9 Estruturas de Dados (2004-2005) 10 Enunciados dos momentos de avaliação Teste Exame época normal Prova Complementar Exame recurso Trabalho prático Estruturas de Dados (2004-2005) 11 Estruturas de Dados (2004-2005) 12 UNIVERSIDADE DO ALGARVE Faculdade de Ciências e Tecnologia Departamental de Engenharia Electrónica e Informática Estruturas de dados (2004/2005) Licenciaturas em Engenharia de Sistemas e Informática, Ensino de Informática e Informática Teste 7 de Junho 2005 Duração: 2h00m 1. Seja a seguinte declaração: int ai[] = { 1, 2, 3, 4, 5 }; a) (0.5v) Justifique o que é impresso após executado o seguinte segmento de código: int i, *pi = &ai[4]; for (i=0; i<=4; i++) printf ("%d ", *(pi-i)); b) (1v) Escreva um segmento de código que troque aleatoriamente as posições dos elementos do array ai. 2. Considere uma lista simplesmente ligada (ou cadeia) de números inteiros, onde o ponteiro plist aponta o nó que está à cabeça: plist 1 2 3 4 NULL e a estrutura do nó define-se como: typedef struct node *link; struct node { int item; link next; }; a) (1v) Implemente uma função que retorne o item à cabeça da lista. Se necessário indique as pré-condições. b) (1v) Implemente uma função que insira um nó, já existente passado como argumento, no fim da lista. c) (1.5v) Implemente uma função que remova o último nó. Indique pré e pós-condições. d) (1v) Supondo que a lista acima suporta a implementação de um Stack, onde o topo está à cabeça e que a função que substituí o item no topo pelo argumento t define-se como: void STACKchgtop (link plist, int t) { plist->item = t; } Justifique sucintamente se a lista plist poderá ser considerada um tipo de dados abstracto. 3. Seja a seguinte matriz esparsa de números inteiros e sua transposta: ⎡0 0 0 0 ⎤ A = ⎢⎢0 0 7 0⎥⎥ ⎢⎣0 8 0 9⎥⎦ ⎡0 ⎢0 T A =⎢ ⎢0 ⎢ ⎣0 0 0⎤ 0 8⎥⎥ 7 0⎥ ⎥ 0 9⎦ a) (0.75v) Declare uma estrutura de dados que possa ser usada para guardar informação sobre os elementos não nulos das matrizes, isto é um triplo. b) (0.75v) Para a matriz A represente esquematicamente como poderia guardar a informação dos elementos não nulos (os triplos) numa lista simplesmente ligada. c) (1v) Escreva um segmento de código que transforme os triplos da lista de elementos não nulos da matriz A nos triplos da matriz transposta de A, i.e.: A i, j = A T j,i Teste ED 2004 / 2005 - 1/2 4. Na sequência TES*T*E uma letra representa uma operação de put numa fila de espera FIFO e um * significa uma operação de get. a) (0.75v) Qual a primeira letra na fila após as operações indicadas na sequência acima terem sido efectuadas? Justifique. b) (0.75v) Mostre o que sucederia se após o último put fossem efectuados mais 4 get consecutivos, isto é a sequência de operações: TES*T*E**** . 5. Seja a seguinte árvore binária ordenada (ou de pesquisa), onde cada nó guarda um inteiro, e o valor do item de um nó é maior que todos os itens na subárvore esquerda e menor ou igual que todos os itens na subárvore direita. a) (0.75v) Declare um ponteiro e uma estrutura que permitam referenciar e definir um nó da árvore binária. b) (1.5v) Implemente uma função que percorra uma árvore binária segundo um percurso posorder e imprima os itens de cada nó. Para a árvore acima indique a ordem pela qual os nós são percorridos. c) (1.5v) Escreva uma função recursiva que verifique se uma árvore binária, onde os itens são inteiros, é ou não ordenada (de pesquisa). d) (1.5v) Represente a árvore binária acima usando um array, e explique como sabendo o índice de um nó nesse array se pode chegar aos índices dos seus filhos esquerdo e direito. 6. Seja a função que implementa o algoritmo de ordenação por inserção (ascendente) para um array a de N inteiros: void insertion(int *a, int N) { int i, j, t; for(i=1; i<N; i++) for(j=i; j>0; j--) if (a[j] < a[j-1]) { t = a[j]; a[j] = a[j-1]; a[j-1] = t; } } a) (1.5v) Para os array seguintes indique o estado (ordem dos elementos do array) após ser completada cada iteração do ciclo for exterior (variável i): i) ai[ ] = { 5, 4, 3, 2, 1 }; ii) bi[ ] = { 1, 2, 3, 4, 5 }; b) (0.75v) Como poderia ser alterada a função de modo que os inteiros sejam ordenados por ordem inversa (descendente)? c) (1.5v) Explique as diferenças entre o algoritmo de ordenação por inserção e o algoritmo bubble sort. Indique quais as alterações na função acima para que passe a implementar o algoritmo bubble sort. d) (1v) Descreva sucintamente como poderia ser melhorado, em termos médios, o desempenho do algoritmo de inserção apresentado acima. Teste ED 2004 / 2005 - 2/2 UNIVERSIDADE DO ALGARVE Faculdade de Ciências e Tecnologia Departamento de Engenharia Electrónica e Informática Estruturas de dados (2004/2005) Licenciaturas em Engenharia de Sistemas e Informática, Ensino de Informática e Informática Exame época normal 2h00m 6 de Julho 2005 Duração: 1. (1v) Para as alíneas seguintes, justifique qual o valor impresso: a) int a = 1; int *p = &a; *p += 1; printf ("%d", a); b) char s[] = "abcdef"; char *p = &s[2]; p ++; printf ("%c", *p); 2. Considere uma lista simplesmente ligada onde cada item é um caracter, e o ponteiro plist aponta o nó que está à cabeça: plist A C D E NULL a) (0.75v) Declare um ponteiro e uma estrutura que permitam referenciar um nó da lista. b) (1.25v) Implemente uma função que remova o item à cabeça da lista e liberte a memória. Se necessário indique as pré-condições. c) (1v) Implemente uma função que retorne o comprimento da lista. d) (1v) Implemente uma função que imprima os itens da lista por ordem inversa, isto é do último nó para o primeiro. e) (1v) Discuta sucintamente as vantagens e desvantagens das listas duplamente ligadas quando comparadas com listas simplesmente ligadas. f) (1v) Supondo que já existe alocado na memória um nó com o item 'B', apontado por nnode, implemente um segmento de código que o insira na lista entre o nó com item 'A' e o nó com item 'C'. 3. Seja o polinómio: P(x) = 3x10 + x2 + 5 a) (1.5v) Descreva como poderia ser guardado um polinómio como o acima: i) num array, onde mesmo os coeficientes nulos são armazenados. ii) numa lista, que minimize o uso de memória, armazenando apenas os coeficientes não nulos. b) (1v) Implemente um segmento de código que realize a operação R(x) = P(x) x 2. Considere que os coeficientes do polinómio acima já estão guardados no array P. O resultado será guardado no array R, que deverá ser alocado na memória dinâmica (e validada a sua alocação) com a mesma dimensão de P (11 elementos), antes de se efectuar a operação. 4. Considere a sequência E*X*AME*. a) (0.75v) Se uma letra representar uma operação put numa fila de espera FIFO e um * uma operação get, quais as letras na fila, e a sua ordem, após as operações indicadas na sequência acima terem sido efectuadas? Justifique. b) (0.75v) Se uma letra representar uma operação push numa pilha, ou stack (LIFO), e um * uma operação pop, quais as letras na pilha, e a sua ordem, após as operações indicadas na sequência acima terem sido efectuadas? Justifique. Exame época normal ED 2004 / 2005 - 1/2 5. Pretende-se implementar o ADT, com suporte para múltiplas instâncias, Ponto, com duas coordenadas inteiras x e y. Considerando o ficheiro ponto.h que declara o interface do ADT e o ficheiro de implementação do ADT ponto.c, escreva todo o código necessário (incluindo protótipos de funções), nos ficheiros correctos, de acordo com: a) (0.75v) Estrutura do ADT e ponteiro para esta (identificado por Ponto). b) (1v) Função: Ponto PONTOnovo(int x0, int y0), que cria uma nova instância do ADT na memória dinâmica e retorna um ponteiro para esta ou NULL se não existir memória disponível. c) (1v) Função: Ponto PONTOsoma(Ponto A, Ponto B), que retorna uma nova instância do ADT Ponto, com a soma dos pontos argumentos A e B, isto é a soma das coordenadas: C (x0+x1, y0+y1) <=> A (x0, y0) + B (x1, y1) d) (0.75v) Escreva um segmento de código cliente que crie os pontos A(1,2), B(3,2) e guarde no ponto C a soma de A com B. 6. Seja a seguinte árvore binária: A B C D G E onde cada nó é definido por: typedef struct node *link; struct node { char item; link left, right; }; a) (1v) Escreva uma função que conte os nós internos da árvore binária. b) (0.75v) A árvore binária é completa? Justifique. c) (0.5v) Represente a árvore binária acima usando um array. 7. (1v) Explique sucintamente o funcionamento do algoritmo de ordenação por selecção (ascendente) se a colecção de itens a ordenar estiver guardada numa lista simplesmente ligada. 8. Seja a seguinte implementação da versão probabilistica do algoritmo de ordenação quicksort (descendente), para um array a de n inteiros, onde a macro exchange troca o valor dos seus dois argumentos: void qs(type* a, int n){ int i; int partition(type* a, int n){ int i, last=0; int p=rand()%n; if (n<2) return; i=partition(a, n); qs(a, i); qs(a+i+1, n-i-1); exchange(a[0], a[p]); for (i=1; i<n; i++) if (a[i] > a[0]) { ++last; exchange(a[i], a[last]); } exchange(a[0], a[last]); return last; }; }; a) (0.75v) Como poderia ser modificado o algoritmo para que a ordenação passe a ser ascendente? b) (0.75v) Para implementar a versão clássica, qual das duas funções acima deveria ser modificada. Justifique. c) (0.75v) Indique no que consiste o melhoramento do algoritmo quicksort denominado partição pela mediana de 3 amostras. Exame época normal ED 2004 / 2005 - 2/2 UNIVERSIDADE DO ALGARVE Faculdade de Ciências e Tecnologia Departamento de Engenharia Electrónica e Informática Estruturas de dados (2004/2005) Licenciaturas em Engenharia de Sistemas e Informática, Ensino de Informática e Informática Exame época recurso 21 de Julho 2005 Duração: 2h00m Ao longo do enunciado considere o array de inteiros: int ai[ ] = { 4, 6, 2, 5, 1, 3, 7 }; 1. a) (0.5v) Explique o que é impresso pelo seguinte segmento de código: int i; for (i=0; i<7; i+=2) printf ("%d ", *(ai+i)); b) (0.5v) Quais as alterações e efectuar ao código acima para que sejam impressos os elementos de índice impar do array ai, isto é: 6 5 3 2. a) (1.25v) Implemente uma função que reserve memória dinamicamente para um array de n inteiros, e retorne um ponteiro para esse array ou NULL em caso de insuficiência de memória. Indique pré-condições e excepções se necessário. b) (1v) Escreva um segmento de código que reserve memória dinamicamente para um array de 7 inteiros e copie para esse array os elementos do array ai pela mesma ordem. 3. Neste exercício considere que lista refere-se a uma lista simplesmente ligada de inteiros. a) (0.75v) Declare um ponteiro e uma estrutura que permitam referenciar um nó da lista. b) (1.5v) Escreva um segmento de código que crie uma nova lista, copiando os elementos do array ai para essa lista pela mesma ordem. c) (1v) Escreva um segmento de código que remova da lista criada na alínea anterior (e da memória) o 2º nó, isto é o nó cujo elemento é o número 6. d) Supondo que se pretende implementar um stack baseado numa lista: i. (1v) Em que ponto da lista se devem fazer as operações de inserção e remoção de elementos de modo que o tempo de execução destas seja constante e o menor possível? Justifique. ii. (1v) Implemente a função int STACKtop (link s), que retorna (mas não elimina) o elemento no topo do stack s. e) (1v) Supondo que existe um ponteiro para o ultimo nó da lista, identificado por plast, escreva uma função que adicione em tempo constante, um nó já existente ao fim da lista. 4. Pretende-se implementar o ADT com suporte para múltiplas instâncias Date, para manipulação de datas de acordo com o calendário Gregoriano. Cada instância guarda uma data em 3 inteiros: o dia, o mês e o ano. Considerando que no ficheiro date.h será declarado o interface do ADT e o ficheiro date.c terá a implementação, escreva todo o código necessário (incluindo protótipos de funções), nos ficheiros correctos, de acordo com: Exame época recurso ED 2004 / 2005 - 1/2 a) (0.75v) Estrutura do ADT e ponteiro para esta (identificado por Date). A estrutura contém 3 inteiros para guardar o dia, mês e ano. b) (1.25v) Supondo que está disponível a função void sysdate (int *day, int *month, int *year) que retorna nos seus argumentos a data actual do sistema, declare e defina a função Date DATEnow (void) que cria uma nova instancia deste ADT e que retorna um ponteiro para esta ou NULL no caso de insuficiência de memória. c) (1v) Declare e defina a função void DATEprint (Date d) que escreve na consola a instância d no formato: dia / mes / ano. Indique pré e pós condições se necessário. d) (0,75v) Escreva um segmento de código que usando este ADT mostre no monitor a data actual do sistema e) (0.75v) Proponha uma estrutura alternativa para o ADT que permita poupar memória no armazenamento do dia, mês e ano. 5. Seja a definição de um nó numa árvore binária: typedef struct node *link; struct node { int item; link left, right; }; a) (1.5v) Supondo que está disponível a função: /* Descrição: se root == NULL é criado o nó raiz da árvore com o item t, senão um novo nó com o item t é adicionado à árvore binária de pesquisa com raiz root, onde o item do filho esquerdo é menor que o item do pai e o item do filho direito é maior ou igual que o item do pai. POS: Adiciona novo nó à árvore de pesquisa e retorna a raiz da árvore */ link btree (link root, int t); escreva um segmento de código que percorra o array ai desde o índice 0 para o final, e adicione cada elemento do array a uma árvore binária de pesquisa (inicialmente vazia). b) (0.75v) Represente esquematicamente a árvore binária de pesquisa criada na alínea anterior. c) (1v) Implemente uma função que retorne a altura de uma árvore binária. Considere que uma árvore com apenas um nó, a raiz, tem altura 0. 6. Considere o código do algoritmo de ordenação Quicksort para inteiros: void qs(int* a, int n){ int i; int partition (int *a, int n) { /* ... */ if (n<2) return; i=partition(a, n); qs(a, i); qs(a+i+1, n-i-1); } } a) (0.75v) Modifique o algoritmo de modo que se os elementos numa partição forem menores que 10 sejam ordenados pela função: void selection (int *a, int n). b) (1.25v) Seja a função void swap (int *a, int *b) que troca o valor dos seus dois argumentos, defina a função: void selection (int *a, int n), que implementa o algoritmo de ordenação por selecção. c) (0.75) O algoritmo de ordenação por selecção é adequado para ordenar colecções guardadas em ficheiros (por exemplo no disco rígido). Porquê? Exame época recurso ED 2004 / 2005 - 2/2 UNIVERSIDADE DO ALGARVE Faculdade de Ciências e Tecnologia Departamento de Engenharia Electrónica e Informática Estruturas de dados (2004/2005) Licenciaturas em Engenharia de Sistemas e Informática, Ensino de Informática e Informática Prova complementar 19 de Julho 2005 Duração: 1h00m 1. Considere uma lista simplesmente ligada (ou cadeia) de números inteiros, onde o ponteiro plist aponta o nó que está à cabeça: plist 1 2 3 4 NULL a) Declare um ponteiro e uma estrutura que possa referenciar e definir um nó da lista na memória dinâmica. b) Implemente uma função que remova o item à cabeça da lista da memória dinâmica. Se necessário indique as pré-condições. c) Implemente uma função que percorra a lista e imprima o item do nó. d) Supondo que a lista acima suporta a implementação de um Stack, onde o topo está à cabeça, escreva uma função que remova e retorne o item no topo do stack. 2. Seja a seguinte árvore binária onde cada nó guarda um inteiro: a) Declare um ponteiro e uma estrutura que permitam referenciar e definir um nó da árvore binária na memória dinâmica. b) Implemente uma função que percorra uma árvore binária segundo um percurso inorder e imprima os itens de cada nó. Para a árvore acima indique a ordem pela qual os nós são percorridos. c) A árvore binária é completa? Justifique. 3. Seja a função que implementa o algoritmo de ordenação por selecção (ascendente) para um array a de N inteiros: void selection(int *a, int N) { int t, i, j, im; for(i=0; i<N-1; i++) { im=i; for(j=i+1; j<N; j++) if ([j] < a[im]) im=j; t = a[i]; a[i] = a[im]; a[im] = t; } } a) Para o array ai [ ] = { 3, 1, 2, 5, 4 }, indique o estado (ordem dos elementos do array) após ser completada cada iteração do ciclo for exterior (variável i): b) Como poderia ser alterada a função de modo que os inteiros sejam ordenados por ordem inversa (descendente)? c) Descreva sucintamente o principio de funcionamento do algoritmo de ordenação por inserção quando aplicado a um array de inteiros. Prova Complementar ED 2004/2005 - 1/1 Trabalho Prático EDa 2004/2005 Gestão de contas à ordem Desenvolver uma aplicação que permita gerir contas à ordem de um só titular, em ANSI C, a qual deverá suportar os seguintes serviços: i) ii) iii) iv) v) vi) vii) Criação de contas Levantamentos e depósitos numa dada conta Transferências manuais entre contas Eliminação de movimentos Extracto de movimentos de uma dada conta, ordenado por data e por descrição Consulta de saldo de uma dada conta Leitura e escrita em disco das contas e seus movimentos. Todas as operações deverão ser efectuadas na memória principal, no entanto toda a informação deverá ser também guardada na memória secundária por pedido explicito do utilizador. Para executar o programa deve ser digitado na linha de comandos do Linux o seu nome e o nome de um ficheiro que guarda a informação: > contas <nome do ficheiro para guardar a informação sobre as contas> Os argumentos da linha de comandos devem ser validados e verificado se o ficheiro especificado como primeiro argumento é valido. Se tal não suceder deve ser emitida uma mensagem de erro correspondente e o programa deve abortar. Se o ficheiro não existir deverá ser criado um novo. As contas à ordem devem ser identificadas pelo número de conta, que deverá ser único. É também necessário manter um registo de todos os movimentos efectuados numa conta, desde a sua criação. Considere que um movimento é caracterizado por um número de ordem único, sequencial e independente da conta; tipo, que poderá ser levantamento ou depósito; data; descrição e valor positivo. No caso de uma transferência, deve ser efectuado na conta fonte um levantamento e na conta destino um depósito, ambos com o mesmo número de movimento, data, descrição e valor, sendo também indicado qual a conta fonte e destino, como mostra o exemplo de extractos de conta seguinte: Nº de Conta: 1 Nº data 0 01/09/04 1 10/09/04 3 01/10/04 4 10/10/04 tipo descrição depósito Vencimento Out levantamento depósito CHK CGD 123456 levantamento valor (euros) 1000 100 100 50 Nº Conta Destino 2 Nº de Conta: 2 Nº data 2 05/09/04 4 10/10/04 tipo depósito depósito valor (euros) 1000 50 Nº Conta Fonte 1 descrição Abertura Enunciado do trabalho prático de Estruturas de dados 2004/5 1-2 No saldo deve constar a seguinte informação: Nº de Conta: 2 Saldo: € 1050 A aplicação deve ser dividida em dois módulos principais: o motor, que pode ser visto como um servidor que presta os serviços acima indicados e o interface com o utilizador, que permitirá que um utilizador possa aceder aos serviços do motor. Este interface poderá ser simples, no entanto terá de respeitar algumas especificações de modo que possa no futuro ser substituído por outro mais elaborado. Especificações sobre estruturas de dados e estrutura da aplicação que deverão ser obrigatoriamente seguidas serão apresentadas gradualmente ao longo do semestre em 4 fases. Entrega do trabalho i) Data A data limite de entrega é terça-feira 14 de Junho de 2005. ii) Código fonte O código fonte deve ser acompanhado de um makefile para gerar o executável, que deverá chamadar-se contas. (Todos os ficheiros devem ter como comentário os Nomes, Nos. e Curso dos elementos do grupo) Cada módulo deve ser dividido em interface (<nome do módulo>.h) e implementação (<nome do módulo>.c) excepto o programa principal. Todas as funções devem ser documentadas (Descrição, Pré e Pós condições, Excepções). Pode ser entregue por e-mail ou em disquete ao docente do turno prático em que esta inscrito o grupo. ii) Relatório Com o código fonte terá der ser entregue um relatório, que incluirá um pequeno manual do utilizador e um manual de referência. Este último deverá incluir um diagrama de estrutura da aplicação. Um template para este relatório será disponibilizado na página da disciplina: O relatório deverá ser entregue em papel, ao docente do turno prático em que está inscrito o grupo. Avaliação De acordo com o indicado na aula teórica 1. Será tomada especial atenção à correcção do programa e à eficiência da implementação. Será avaliada também a decomposição da aplicação em módulos coesos e bem documentados como estudado nas aulas teóricas e a implementação e utilização das estruturas de dados. Nota: Funcionalidades não pedidas nos enunciados não serão avaliadas. Bibliografia recomendada Além da bibliografia indicada para a disciplina, as aulas teóricas e práticas disponíveis em http://w3.ualg.pt/~hdaniel/Ed/, pode ser também consultada a resolução dos trabalhos práticos de EDa, ano lectivo 2003/2004, já enviados para a mailing list da disciplina: [email protected]. Enunciado do trabalho prático de Estruturas de dados 2004/5 2-2 Trabalho de Estruturas de Dados 2004/2005 Gestão de contas à ordem Pormenores sobre a implementação para a 1ª fase 1 Preliminares O programa será dividido em 2 módulos principais. Um motor, que efectuará todas as operações de gestão de contas à ordem, e um interface de utilizador. Estes módulos serão implementados como 2 ADTs (tipos de dados abstractos). O ADT accountmanager será o motor e o ADT amui o interface de utilizador. Este último módulo aceitará comandos de utilizador e irá transformá-los em pedidos de serviços ao motor (gestor): memória principal Aplicação accountmanager comandos do utilizador utilizador chamadas a funções do motor amui retorno das funções do motor informação para utilizador sistema de ficheiros 2 Função main O programa terá que seguir algumas normas, de modo que numa fase posterior o ADT amui possa ser substituído por outro compatível, em termos de interface com o o gestor de contas (accountmannager). O programa principal terá obrigatoriamente o nome contas.c e o seguinte layout: /* Gestor de contas à ordem TP ED 2004-2005 Nomes, Nºs e curso dos elementos do grupo */ #include "accman.h" /* account manager: header do ADT gestor de contas (motor)*/ #include "amui.h" /* account manager user interface: header do ADT interface de utilizador */ /* incluir outros headers necessários. */ int main(int argc, char** argv) { accountmanager m; /* instância do ADT motor */ Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 1-7 amui i; /* instância do ADT interface de utilizador */ /* Valida linha de comandos do S.O. de modo a garantir que tem 1 argumento! Senão deve imprimir no stderr a mensagem: "Chamada: contas <nome de ficheiro para guardar informação>" e terminar */ /* Reserva memória e inicializa o ADT motor */ m = ACCMANinit(argv[1]); /* Se m == NULL deve imprimir no stderr a mensagem: "Não foi possível inicializar o motor" e terminar */ /* Reserva memória e inicializa o ADT interface de utilizador e associa-o ao motor */ i = AMUIinit(m); /* Se i == NULL deve imprimir no stderr a mensagem: "Não foi possível inicializar o interface de utilizador" e terminar */ /* Executa interface de utilizador (e o programa)*/ AMUIrun (i); /* Liberta memória reservada às instâncias dos ADTs*/ AMUIdel (i); ACCMANdel (m); return 0; } As únicas alterações necessárias consistem em substituir os comentários a vermelho pelo código C respectivo. (e claro colocar no cabeçalho o nome, nº e curso dos elementos do grupo - comentário a azul). A partir dos dois includes no topo do programa verifica-se que os headers do ADT motor (accountmanager) e interface de utilizador (amui) terão obrigatoriamente de ter os nomes: accman.h e amui.h. Os ficheiros com a implementação das funções do interface destes ADTs terão de ter os nomes: accman.c e amui.c. No inicio do programa deve ser introduzido código para validar a linha de comandos do Sistema Operativo. A chamada consiste no nome do executável e no caminho para um ficheiro que guarda os dados sobre as contas: contas <caminho do ficheiro de dados> Se este ficheiro não existir assume-se que será criado um novo, mas isso será efectuado pelo ADT accountmanager. Na função main será necessário garantir que o programa é chamado com um argumento na linha de comandos, como indica o 1º comentário a vermelho. Pelo layout pode-se observar que os ADTs utilizados são primeiro inicializados: m = ACCMANinit(argv[1]); Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 2-7 i = AMUIinit(m); Estas funções retornam um ponteiro para a instância do ADT criada. Se retornarem NULL assumese que por algum motivo não foi possível criar e inicializar a instância do ADT, e o programa termina imprimindo a mensagem de erro indicada nos comentários imediatamente abaixo das chamadas às funções. Toda a operação será efectuada após se executar o interface de utilizador, a instância i do ADT amui: AMUIrun (i); Assim este é responsável por enviar os comandos do utilizador ao motor do gestor (accountmanager), tendo por isso sido associado à instância m desse ADT, inicializada acima. Após o utilizador pedir para terminar o interface, os ADTs são libertos da memória e o programa termina: AMUIdel (i); ACCMANdel (m); Repare-se que para cada ADT existe uma função para o inicializar e outra para o terminar. Esta é uma boa prática quando se programa ADTs. Mesmo que numa dada altura do desenvolvimento não sejam necessárias, estas funções devem existir para futura expansão – mesmo que não tenham instruções no corpo. Por outro lado tarefas comuns destas funções são libertar a memória alocada à instância do ADT passada como argumento, fecho de ficheiros ou streams, etc.. Nota: A especificação dos interfaces (ficheiro *.h) destes 2 ADTs, que deverá obrigatoriamente ser seguida será apresentada na 2ª fase. Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 3-7 2.1 Teste da função main Para testar se o programa faz o que se pretende, isto é validar a linha de comando do S.O. e a inicialização dos ADTs accountmanager e amui pode-se implementar temporariamente nos headers as funções necessária. Atenção que isto é uma implementação temporária só para testar a função main. A programação destes ADTs será efectuada numa fase posterior. Assim o ficheiro accman.h poderá ser: #ifndef _ACCMAN_ #define _ACCMAN_ typedef void* accountmanager; accountmanager ACCMANinit(const char* fn){ return (void *) 0; /* 0 == NULL e qq valor != 0 é !NULL */ }; void ACCMANdel (accountmanager ui){ }; #endif E o ficheiro amui.h: #include "accman.h" typedef void* amui; amui AMUIinit(accountmanager m){ return (void *) 1; /* 0 == NULL e qq valor != 0 é !NULL*/ }; void AMUIrun (amui ui){ }; void AMUIdel (amui ui){ }; Repare-se que foram definidas apenas as funções utilizadas em main. As funções de libertação de memória não têm instruções no corpo. As únicas funções com algum código no corpo são as de inicialização. Assim é possível fazer com que estas funções retornem um ponteiro NULL – i.e. zero - ou não (modificando o valor retornado e recompilando o programa) de modo a testar a validação da criação das instâncias de ambos os ADTs na função main. Para testar a validação da linha de comandos basta executar o programa com um número de argumentos diferente de um. 2.2 Compilação do programa Para compilar o programa, nesta fase de desenvolvimento, basta compilar o ficheiro contas.c. No entanto é obrigatório desenvolver um makefile. O makefile será um ficheiro com esse nome, semelhante aos utilizados nas aulas práticas e pode ter o seguinte layout: #TP Eda 2004-5 CC OUT = gcc = contas Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 4-7 LIBS = -lm #-lncurses #necessario se UI usa biblioteca ncurses CFLAGS = -g -ansi -Wall FILES = $(OUT).o #accman.o amui.o #outros modulos necessarios #link $(OUT): $(FILES) $(CC) $(FILES) -o $(OUT) $(LIBS) #a linha acima inicia-se com um <TAB> #compile c.o: $*.c $*.h $(CC) $(CFLAGS) -c $*.c -o $*.o #a linha acima inicia-se com um <TAB> clean: rm -f *.o rm -f $(OUT) rm -f $(OUT).exe #as 3 linhas acima iniciam-se com um <TAB> Assim o nome do executável será contas (variável OUT), a biblioteca a incluir será para já apenas m (variável LIBS), embora ainda não seja necessária poderá vir a ser na fase seguinte. Numa fase posterior poderá ser necessário utilizar a biblioteca ncurses, dai que esteja em comentário. Nesta fase como só temos um ficheiro, a variável FILES será apenas: FILES = $(OUT).o isto é: contas.o Durante o desenvolvimento da aplicação, quando se adicionar mais módulos ao programa (leia-se ficheiros com extensão *.c) basta adicionar o nome desse ficheiros, mas com extensão *.o à variável FILES. Assim para compilar o programa basta colocar todos os ficheiros no mesmo directório (incluindo o makefile) e digitar: make na linha de comandos do S.O.. Repare-se que se se digitar: make clean serão apagados todos os ficheiros *.o e o executável, de modo que se possa recompilar todos os módulos novamente. 3 Data do sistema Para lançar movimentos é necessário ler a data do sistema. O seguinte programa ilustra como esta pode ser acedida e transformada numa string no formato DD/MM/AAAA: Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 5-7 #include <time.h> #include <stdio.h> /* DEFINED in <time.h> struct tm { int int int int int int int int int }; tm_sec; tm_min; tm_hour; tm_mday; tm_mon; tm_year; tm_wday; tm_yday; tm_isdst; seconds minutes hours day of the month month year day of the week day in the year daylight saving time The members of the tm structure are: tm_sec The number of seconds after the minute, normally in the range 0 to 59, but can be up to 61 to allow for leap seconds. tm_min The number of minutes after the hour, in the range 0 to 59. tm_hour The number of hours past midnight, in the range 0 to 23. tm_mday The day of the month, in the range 1 to 31. tm_mon The number of months since January, in the range 0 to 11. tm_year The number of years since 1900. tm_wday The number of days since Sunday, in the range 0 to 6. tm_yday The number of days since January 1, in the range 0 to 365. tm_isdst A flag that indicates whether daylight saving time is in effect at the time described. The value is positive if daylight saving time is in effect, zero if it is not, and negative if the information is not available. */ int main () { time_t t; struct tm* ts; char datestr[11]; t = time(NULL); ts = localtime(&t); printf ("%s\n\n", ctime (&t)); sprintf (datestr, "%02d/%02d/%d", >tm_year); printf ("%s\n\n", datestr); getchar(); return 0; } ts->tm_mday, ts->tm_mon+1, 1900+ts- Repare-se que a função: t = time(NULL); não retorna uma estrutura com o tempo discriminado, para isso é necessário converter o retorno da função acima numa estrutura tm, com: ts = localtime(&t); Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 6-7 Após se obter essa estrutura, hora e data podem ser lidas dos seus membros de dados, como mostra o programa. Nota: A função ctime (&t) retorne uma string com a data, maii precisamente no formato: Sun Apr 10 09:48:48 2005 No entanto não se tem controlo sobre a formatação dessa string. 4 Funções sobre listas ligadas Como serão obrigatoriamente utilizadas listas ligadas, deve-se começar a organizar o código da biblioteca para listas ligadas. Se bem que não seja obrigatório usar listas duplamente ligadas circulares, estas estruturas vão facilitar a implementação de algumas funções do gestor de contas, de modo que se recomenda o seu uso (aulas teóricas 9 e 10 e ficha prática 5). Podem ser utilizadas as funções sobre listas ligadas desenvolvidas nas aulas teóricas ou nas práticas, no entanto funções com excepções devem ser modificadas para indicar a excepção retornando por exemplo NULL, mas não abortar o programa nem imprimir mensagem de erro. O módulo cliente é que deve decidir o que fazer ao detectar a excepção. Por exemplo indicar ao interface de utilizador a excepção, de modo este trate dela (p. ex. emitindo uma mensagem de erro para o utilizador) 5 Compilador a utilizar Deve ser usada o compilador gcc 3. Este encontra-se instalado nos sistemas Linux. Alternativamente pode ser usado o ambiente cygwin, que emula o S.O. Linux sobre Windows, e correntemente incluí a versão 3.3.3 do gcc. Também pode ser usado o ambiente de desenvolvimento BloodShed Dev-C++ para Windows, que incluí uma versão do compilador gcc para este S.O. No entanto não esquecer que deve ser entregue junto com o código fonte um makefile que permita compilar a aplicação num S.O. Linux. Pormenores de implementação da 1ª fase do trabalho prático de Estruturas de dados 2004/5 7-7 Trabalho de Estruturas de Dados 2004/2005 Gestão de contas à ordem Pormenores sobre a implementação para a 2ª fase 1 Implementação de ADT para guardar informação sobre cada movimento Nota: Pode-se utilizar o template apresentado como anexo da aula teórica 16b 2004/2005. Mais informação sobre ADTs múltipla instância podem também ser encontradas nessa aula. Este tipo de dados abstracto servirá para guardar a informação sobre cada movimento, por isso terá de suportar múltiplas instâncias. Cada instância ou variável deste ADT deverá guardar informação sobre: Nº de movimento Data do movimento tipo descrição valor Nº de Conta - poderá ser um inteiro. - a data actual do sistema quando lançado o movimento. - depósito ou levantamento. (poderá ser um tipo enumerado) - uma cadeia de caracteres (string) - valor positivo, que representa um valor monetário em Euros, arredondado aos cêntimos de Euro (2ª casa decimal) - um número de conta. Só utilizado no caso de uma transferência, isto é um par levantamento/depósito, onde todos os campos são idênticos com excepção do tipo e deste. No levantamento será a conta destino, no depósito será a conta fonte. Esta é a informação que deve constar na estrutura de cada instância do ADT, definida no ficheiro de implementação, por exemplo movimento.c. Nas funções do interface, por exemplo movimento.h, deverão existir funções para: - Criar uma nova instância e inicializar todas as variáveis dessa (as variáveis acima). Deverá por isso ter argumentos para passar esses valores. Poderá não ser necessário passar a data, pois como esta é a data do sistema na altura do lançamento, a função pode lê-la, como mostrado na 1ª fase, e guardá-la no campo correspondente da estrutura. - Eliminar uma instância da memória. - Escrever a informação de uma instância do movimento num stream em modo de texto formatado, como indicado no enunciado. - Escrever a informação de uma instância do movimento num stream em modo binário. - Leitura da informação de uma instância do movimento num stream em modo binário. Estas duas ultimas serão utilizadas em conjunto com outras para guardar e recuperar toda a informação do gestor de contas, isto é todas as contas e seus movimentos, no ficheiro indicado como argumento de linha de comando do Sistema Operativo. (serão utilizadas na 3ª fase) Sugere-se que se siga a implementação do ADT complexo da aula teórica 16b de 2004/2005. Pormenores de implementação da 2ª fase do trabalho prático de Estruturas de dados 2004/5 1-4 Para testar este ADT pode-se criar um programa cliente que: #1 incialize uma instância do ADT (e as suas variáveis de instância) #2 guarde-a em modo binário #3 lê-a em modo binário #4 escreve-a em modo de texto no stdout #5 liberta-a da memória Pode ser tomado como exemplo o programa cliente do ADT complexo da aula teórica 16b 2004/2005. 2 Definir item de cada nó da lista ligada Os movimentos terão de ser guardados em memória numa lista ligada, de modo que o item de cada nó da lista deverá ser do tipo do ponteiro para a estrutura do ADT movimento. (recomenda-se que a lista seja duplamente ligada circular): Instância do ADT conta ... Instância do ADT movimento Instância do ADT movimento Instância do ADT movimento Assim se o ponteiro para instância do ADT movimento definido no interface deste ADT fôr, por exemplo: typedef struct MOVst* mov; isto é: mov, e se existir um ficheiro type.h que define o tipo do item de cada nó de uma lista ligadas, este ultimo ficheiro deverá ser definido: #ifndef _TYPE_ #define _TYPE_ typedef mov item; printstring "%p" #endif /* opcional. Indica que deve ser impresso o endereço do item*/ Pormenores de implementação da 2ª fase do trabalho prático de Estruturas de dados 2004/5 2-4 3 Demonstração de entrada-saída de dados através de streams Usando streams pode-se aceder ao sistema de ficheiros e ao monitor, i.e. o stdout. O seguinte programa apresenta algumas funções que permitem esse acesso: /* Stream I/O demo */ #include <stdio.h> #include <stdlib.h> #define MAX_SIZE 20 typedef struct { float n; char s[MAX_SIZE]; } record; /* Tipo de dados definido pelo utilizador */ /* Não é abstracto pois os membros de dados da /* estrutura estão disponíveis para o cliente (main) */ int main () { FILE *f; record rec = { -1234.56, "uma string" }; record inrec; /* Stream Output (texto formatado) Se f == stdout escreve no monitor */ f = fopen("file.txt", "w"); if (!f) { fprintf (stderr, "can not open file"); exit (1); } if (fprintf (f, "%f | %s\n", rec.n, rec.s) < 0) { fprintf (stderr, "I/O error"); exit (1); } fclose(f); /* Stream I/O em (binário) */ f = fopen("file.bin", "w"); if (!f) { fprintf (stderr, "can not open file"); exit (1); } /* retorna o numero de items escritos*/ if (fwrite (&rec, sizeof (record), 1, f) != 1) { fprintf (stderr, "I/O error"); exit (1); } fclose(f); f = fopen("file.bin", "r"); if (!f) { fprintf (stderr, "can not open file"); exit (1); } /* retorna o numero de items lidos. se menor que o pedido ocorreu um erro ou chegou-se sucedeu erro de leitura ou chegou-se ao fim do ficheiro*/ if (fread (&inrec, sizeof (record), 1, f) < 1) { fprintf (stderr, "I/O error"); exit (1); } fclose(f); printf ("%f | %s\n", inrec.n, inrec.s); getchar(); return 0; } A função: Pormenores de implementação da 2ª fase do trabalho prático de Estruturas de dados 2004/5 3-4 int fprintf (FILE* f, formatstring, ...); Escreve no stream f as variáveis formatadas em modo texto de acordo com o 2º argumento formatstring. Retorna um número negativo se existiu erro de escrita ou positivo caso contrário. Se no parâmetro f fôr passado o stream para a saída padrão - stdout - escreve no monitor (se a saída não tiver sido previamente redireccionada). int fputc (char c, FILE *f); Escreve no stream f o caracter c. Retorna o carácter caso tenha sucesso, ou EOF (-1) caso contrário. int fwrite (void* p, int size, int count, FILE* f) int fread (void* p, int size, int count, FILE* f) Escreve\lê do\no stream f, os dados no endereço dado por p com comprimento size bytes, count vezes. Em caso de sucesso retorna count, caso contrário um número menor que count. Para associar um stream a um ficheiro pode ser usada a função: FILE* fopen (const char* fn, const char* mode); Retorna um ponteiro para o stream associado ao ficheiro fn. Caso não tenha sucesso retorna NULL. O parâmetro mode permite abrir o ficheiro para: escrita: leitura: leitura e escrita: "w" "r" "rw" Para encerrar o stream f, descarregando dados ainda na memória para o ficheiro associado ao stream deve-se usar: fclose (File* f); Quando se usa uma função de escrita num ficheiro como por exemplo fwrite ou fprintf, os dados poderão não ser imediatamente guardados nesse ficheiro mas sim num buffer na memória. Por questões de eficiência o sistema operativo encarregar-se-á de descarregar os dados nesse buffer para o ficheiro posteriormente. Deste modo, para garantir que tudo o que foi pedido escrever é efectivamente escrito no ficheiro, deve-se antes de terminar um programa fechar todos os ficheiros com fclose. Se se pretender apenas descarregar dados ainda no buffer para o ficheiro associado ao stream f, mantendo-o aberto para leitura ou escrita, pode-se usar: fflush (File* f); Pormenores de implementação da 2ª fase do trabalho prático de Estruturas de dados 2004/5 4-4 Trabalho de Estruturas de Dados 2004/2005 Gestão de contas à ordem Pormenores sobre a implementação para a 3ª fase 1 Estrutura do gestor de contas - As contas devem ser itens de uma lista ligada. Deverá pois existir um ADT Conta que suporte múltiplas instâncias. - Para cada conta deverá existir uma colecção de movimentos, isto é de instâncias do ADT movimento desenvolvido na 2ª fase. Esta colecção deverá ser implementa sobre uma lista ligada, ordenada por data do movimento. Recomenda-se que seja duplamente ligada circular de modo a facilitar implementação de funções de visualização de movimentos ordenados pelos movimentos mais recentes ou pelos mais antigos. Nota: Não é necessário que a lista seja implementada como um ADT. Podem inclusive ser utilizadas as funções sobre listas apresentadas nas aulas teóricas. - A ordenação dos movimentos pela sua descrição será ascendente, indirecta e efectuada recorrendo a uma árvore binária de indexação, que será descrita na 4ª fase. Lista de instâncias do ADT Conta (apontada pelo ponteiro contas, variável de accountmamager) (apontadas pelo ponteiro mov, variável de Conta) Lista de instâncias do ADT Movimento contas Conta 1 saldo mov Conta 2 saldo mov mov 1 NULL ... Conta 3 saldo mov NULL mov 3 mov 2 mov 3 ADT accountmanager (gestor de contas ou motor) Assim o ADT accountmanager terá um ponteiro para uma lista de contas e por sua vez cada instância do ADT conta terá um ponteiro para uma lista de movimentos. Estes ADT podem ser implementados de modo semelhante ao ADT polinómio baseado em listas ligadas introduzido na aula teórica 18. De notar que o item de cada nó da lista deverá ser um ponteiro para uma instância de um ADT. Como se terá duas listas com ADTs diferentes, sugere-se que este ponteiro seja void*: Pormenores de implementação da 3ª fase do trabalho prático de Estruturas de dados 2004/5 1-3 struct node { void * item; link next; }; Nº de conta e Nº de movimento terão de ser únicos e sequenciais. Para isso poderão existir duas variáveis de instância do ADT accountmanager que serão incrementadas quando criado um novo movimento e uma nova conta. Estas variáveis deverão ser inicializadas com o valor 1, quando o ficheiro passado como primeiro argumento, na linha de comandos, não existir. Será assim criado um novo ficheiro para guardar os dados das contas. 2 Interfaces dos ADTs Os interfaces dos ADTs accountmanager (motor) e amui (interface de utilizador) que deverão obrigatoriamente ser seguidos é dado em anexo, ficheiros accman.h e amui.h As funções desses ADTs deverão ser programadas de acordo com a sua descrição, pré-condições, pós-condições e excepções. O ADT amui aceitará comandos do utilizador e chamará funções correspondentes do motor accountmanager. Deste modo a estrutura deste ADT incluirá uma instância de accountmanager: /* amui.c */ #include "amui.h" #include "accman.h" /* ... */ struct AMUIstruct { accountmanager m; /* outras variáveis que possam ser necessárias*/ }; Esta instância do motor será passada como argumento da função que inicializa o ADT amui, por exemplo: amui AMUIinit(accountmanager m){ amui ui = (amui) malloc (sizeof *ui); if (ui) ui->m=m; return ui; }; A operação do interface de utilizador pode ser simples, mas deve validar as entradas do utilizador. Se o menu fôr: 123456- Gravar Criar Conta Depositar Levantar Transferir Saldo ... S – Sair Pormenores de implementação da 3ª fase do trabalho prático de Estruturas de dados 2004/5 2-3 A função AMUIrun poderá ser: void AMUIrun (amui ui){ int c; /* ... */ do { /* ... Imprime menu variável c tem tecla pressionada pelo utilizador ... */ switch(c) { /* ... */ case '6': saldo(ui); break; /* ... */ } } while(c!='S'); }; e a função auxiliar em amui.c: static void saldo (amui ui) { int nc; printf ("\nNº Conta: "); scanf("%d", &nc); ACCMANbalance(ui->m, nc, stdout); /* ... */ }; Nota: O saldo e o extracto devem ser apresentados como indicado no enunciado. Atenção: Mesmo que não seja necessário implementar alguma função indicada nos interfaces destes dois ADTs, esta terá de existir no ficheiro de implementação mas sem código no seu corpo: int func (int a) { /* corpo vazio */ } Pormenores de implementação da 3ª fase do trabalho prático de Estruturas de dados 2004/5 3-3 Trabalho de Estruturas de Dados 2004/2005 Gestão de contas à ordem Pormenores sobre a implementação para a 4ª fase 1 Extracto de movimentos de uma conta ordenados por descrição A ordenação ascendente dos movimentos de cada conta, por descrição, será efectuada através de uma árvore binária. Será pois ordenação indirecta, isto é a posição dos movimentos na lista não será alterada, será sim criada uma árvore binária que indica as posições relativas destes movimentos segundo o critério de ordenação acima indicado. Instância do ADT conta lista movimentos Inst. movimento número == 2 descrição == "D" ... raiz Inst. movimento número == 4 descrição == "A" ... Inst. movimento número == 5 descrição == "T" ... Inst. movimento número == 10 descrição == "C" ... (D) (T) (A) (C) Tal como na lista de movimentos de uma conta, cada nó da árvore terá como item um ponteiro para uma instância do ADT movimento. Na figura acima, para simplificação mas sem perda de generalidade, assume-se que a descrição de cada movimento é apenas um caracter. Nota: As letras a azul no nós da árvore binária não representam dados do nó mas apenas uma indicação para qual dos movimentos aponta o item do nó. Assim para cada conta deverá existir uma árvore de ponteiros para os movimentos além da lista de movimentos. Esta árvore deverá ser actualizada sempre que se adiciona um movimento. Pormenores de implementação da 4ª fase do trabalho prático de Estruturas de dados 2004/5 1-5 Deste modo quando fôr pedido o extracto ordenado por descrição basta percorrer a árvore e apresentar todos os movimentos. No entanto ocupa mais memória. Esta implementação requer no entanto um espaço considerável de memória. Alternativamente a árvore binária poderá ser criada temporariamente apenas quando se precisa de mostrar o extracto de conta ordenado por descrição, poupando memória à custa de algum desempenho. Para isso: #1 percorrer a lista de movimentos da conta para a qual se pretende visualizar o extracto e adicionar o ponteiro para o movimento (o item de cada nó da lista) como um item de cada nó da árvore. #2 percorrer a árvore segundo um percurso inorder e mostrar os movimentos apontados por cada item da árvore. #3 libertar a árvore da memória. Qualquer das opções é válida mas deve ser justificado no relatório o porquê da escolha. Podem-se usar as funções para árvores binárias ordenadas apresentadas na aula teórica 22. Um ficheiro de demonstração destas funções, com o nome btree_ord.c pode ser encontrado em: http://w3.ualg.pt/~hdaniel/ed/teorica/T22_code/ Uma função semelhante a btree(...) poderá ser utilizada para criar a árvore e uma função semelhante a btlist(...) para imprimir os extractos. O critério de ordenação poderá continuará a ser: Chave do filho esquerdo menor que a chave do pai e chave do filho direito maior ou igual que a chave do pai. No entanto a chave é uma string, a descrição do movimento, de modo que pode ser comparada usando a função: #include <string.h> int strcmp(const char *A, const char *B); Que retorna um número > 0 (zero) quando *A se ordena lexicograficamente após *B, retorna um número < 0 (zero) quando *A se ordena lexicograficamente antes de *B e retorna 0 (zero) se as duas strings forem iguais. Se mov representar um ponteiro para cada instância do ADT movimento, pode-se adicionar uma função ao ADT movimento, por exemplo: int MOVless (mov m0, mov m1) que compara dois movimentos em termos de descrição usando strcmp(...), e retorna 1 caso a descrição do movimento m0 se ordene lexicograficamente antes da descrição do movimento m1, ou 0 caso contrário. Pormenores de implementação da 4ª fase do trabalho prático de Estruturas de dados 2004/5 2-5 Esta função poderá agora ser utilizada na função btree(...), onde é necessário determinar qual a menor chave, isto é nas linhas: ... if (t < root->item) root->left=r; => if (MOVless (t, root-item)) ... ... if (r < root->item) return btree (r, r->left, t); => if (MOVless (r, root-item)) ... ... Na função btlist(...) basta alterar a linha que imprime o item para chamar a função que imprime o movimento, por exemplo: ... btlist (root->left); printf( ... btlist (root->right); ... => MOVprint (root->item, stdout); 1.1 Integração na aplicação No gestor (ou motor da aplicação) ficheiro accman.h está definida a função: /* Escreve o extracto da conta número n, do gestor de conta m no stream f em modo texto formatado. Retorna 1 se a operação teve sucesso ou -2 se a conta número n não existe. Se mode < 0 ordena os movimentos por data descendentemente Se mode == 0 ordena os movimentos por descrição ascendentemente Se mode > 0 ordena os movimentos por data ascendentemente PRE: m != NULL && f != NULL Excepção: Se ocorrer erro de escrita retorna 0 */ int ACCMANtransactions (accountmanager m, int n, int mode, FILE *f); Como mostra a descrição, esta função deve ser chamada a partir do interface de utilizador amui.c, com o número de conta da qual se pretende visualizar o extracto e uma variável que indica o tipo de extracto, mode. Uma boa opção de implementação será delegar no ADT conta a impressão do extracto, já que este ADT contém toda a informação necessária para a tarefa: int ACCMANtransactions (accountmanager m, int n, int mode, FILE *f); { account pacc=ACCMANexist (m, n); if (!pacc) return -2; return ACCOUNTprint(pacc, f, mode); }; Pormenores de implementação da 4ª fase do trabalho prático de Estruturas de dados 2004/5 3-5 Assim se a função auxiliar ACCMANexist(m, n), implementada em accman.c, retornar um ponteiro para a conta com número n, do gestor m, ou NULL se esta não existir, pode-se delegar o trabalho de escolha do tipo de extracto e sua impressão no stream f na função: ACCOUNTprint(pacc, f, mode) do ADT conta, que recebe o ponteiro pacc para a instância da conta, o stream f e o modo de impressão mode. Nota: Não é obrigatório que no ADT conta exista uma função que apresente todos os tipos de extractos. Também é válido existir neste ADT uma função para apresentar cada tipo de extracto. De qualquer forma, na implementação do interface de utilizador amui.c, para que seja mostrado o extracto de uma conta a pedido do utilizador, pode-se usar um segmento de código semelhante a: /* Pedir nºconta e modo de apresentação do extracto (por data ascendente ou por data descendente ou por descrição ascendente) ao utilizador */ n = ACCMANtransaction (ui->m, nºconta, modo, stdout); /* caso n diferente de 1 imprime mensagem de erro correspondente */ Se a instância do interface de utilizador fôr apontada por ui e o motor (ou gestor) associado a essa seja apontado por m: struct AMUIstruct { accountmanager m; /* ... */ }; 2 Construir a aplicação com interface de utilizador alternativo O código objecto do interface de utilizador alternativo (amui.o) está disponível para 3 compiladores diferentes, em: http://w3.ualg.pt/~hdaniel/ed/avaliacao/tpfase4/cygwin http://w3.ualg.pt/~hdaniel/ed/avaliacao/tpfase4/Dev-C++ (windows)/ http://w3.ualg.pt/~hdaniel/ed/avaliacao/tpfase4/linux-x86/ - Seguindo do o primeiro link enconta-se a versão de amui.o para gcc v3.3.3 em ambiente cygwin. - Através do segundo link chega-se à versão de amui.o para Dev-C++ v4.9.9.2 (ou v4.9.9.0). Este ambiente de desenvolvimento de aplicações corre em windows e usa como compilador uma versão apropriada do gcc. - Pelo terceiro link têm-se a versão de amui.o para gcc v2.95.4 e superiores sobre Linux (intel x86) Pormenores de implementação da 4ª fase do trabalho prático de Estruturas de dados 2004/5 4-5 Para construir a aplicação com o interface de utilizador alternativo: #1 Copiar todo o código fonte, e também o makefile, com excepção de amui.c, para um novo directório. #2 Colocar nesse novo directório um dos ficheiros amui.o indicado acima (que seja apropriado para o compilador e sistema operativo usados) #3 executar o makefile Convém lembrar que para que o interface de utilizador alternativo possa ser compilado, os ficheiros amui.h e accman.h deverão ser os fornecidos na 3ª fase (e não poderão ser alterados!) Nos três links referidos acima encontram-se também os executáveis da aplicação final para o sistema operativo e ambiente indicados. Estes demonstram não apenas a operação do interface de utilizador mas toda a operação esperada da aplicação de gestão de contas. Dependendo do sistema operativo o nome do executável é contas ou contas.exe. 3 Relatório Fazer relatório de acordo com o template que será disponibilizado em: http://w3.ualg.pt/~hdaniel/ed/avaliacao/ Pormenores de implementação da 4ª fase do trabalho prático de Estruturas de dados 2004/5 5-5