Você está na página 1de 113

Algoritmos e Estruturas de Dados

LEEC
Estruturas de Dados e Operações
Dados e Algoritmos: o que são?
 Dados
 Conjunto de valores, que representam elementos informativos (ex: o
valor inteiro 74 pode indicar o peso de uma pessoa, o ano da revolução
do 25 de Abril, ...)
 Os valores podem estar armazenados de forma permanente -
constantes (ex:p), ou podem ser substituídos em localizações
variáveis (ex: saldo da conta bancária, luz do semáforo, ...)
 Algoritmo
 Sequência de instruções que, a partir de vários dados (previamente
armazenados, ou indicados por entidades externas ao programa como
utilizadores ou ficheiros), calcula novos dados.
 Ex: cálculo do maior divisor comum, determinação do valor das compras
num supermercado, ...

2 AED (IST/DEEC)
Que estrutura de dados escolher?
 A escolha da forma como se armazenam os dados depende de
 Operações que sejam mais frequentes no algoritmo a implementar
 Quando o acesso e modificação são a base do algoritmo, as tabelas deverão
ser escolhidas
 Quando a inserção e remoção são mais frequentes
 Usar listas simples, se estas operações ocorrem sempre no início
(“STACK”)
 Usar listas simples com ponteiro adicional para o fim da lista, se a inserção
se fizer no fim e a remoção no início (“QUEUE”)
 Previsibilidade sobre a memória necessária:
 O uso de tabelas pressupõe que se saiba que memória é suficiente, ou que se
defina um valor suficientemente alto que comporte todas as instâncias de
interesse.
 Quando tal valor é incomportável, o único recurso razoável consiste em gerir
a memória dinamicamente.

3 AED (IST/DEEC)
Tipologia de dados [1]
 Simples
 Inteiros (int Index);
 Reais (float Stock, double Income);
 Caracteres (char Answer)
 Estruturados
 Tabelas (‘‘array’’) – estáticas ou dinâmicas: vários elementos do mesmo
tipo
 Unidimensionais (Array1[N])
 Multidimensionais (Array2[N1]...[Nk] ou Array2[N1,...,Nk])
 Acervos (Heap[N])
 Estruturas (‘‘struct’’): elementos de tipos possivelmente distintos

4 AED (IST/DEEC)
Tipologia de dados [2]
 Dinâmicos: volume dos dados pode ser modificado durante a
execução do algoritmo
 Listas
 Lista simples
 Pilha (“stack”), Fila (“queue”), Fila Prioritária (“priority queue”)
 Duplamente ligadas, anéis
 Árvores
 Grafos
 Composições
 listas de listas, listas de estruturas, lista de tabelas, tabelas de listas, ...

5 AED (IST/DEEC)
Tabelas
 Definição: Uma tabela (“array”) é uma série de localizações,
todas do mesmo tipo, acedidas por índices numéricos a partir
de 0.
 Sintaxe: após a declaração base inserir uma das opções
 {[Dimensão]}+
 Restrições:
 as dimensões são inteiros positivos.
 Exemplos
int Tab1[4]; /* tabela de 4 inteiros */
char Tab2[3][2]; /* tabela de char 3 por 2 */
char Tab3[5][14][2]; /* tabela de char 5 por 14 por 2 */

Tab1[0] Tab1[1] Tab1[2] Tab1[3]

6 AED (IST/DEEC)
Estruturas [1]
 As tabelas apenas permitem alocar, estática ou dinamicamente,
“caixas” de memória para objectos do mesmo tipo;
 Só inteiros, só reais, só caracteres...
 Para agrupar objectos de tipos diferentes numa mesma
variável existem as estruturas
 Exemplo:

#define SIZE 80
struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;

?????? ... ???? ?? ??


pessoa

7 AED (IST/DEEC)
Estruturas [2]
 A partir do momento que se definem estruturas, mais ou
menos complexas, podem definir-se novos tipos
 Exemplo:

typedef struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;

 Pode-se declarar variáveis do tipo “pessoa”


pessoa aluno, *aed, ip[400];
como já se fazia com os tipos básicos

8 AED (IST/DEEC)
Estruturas [3]
 Uma estrutura pode ter qualquer número de campos e cada
campo pode ser qualquer um dos tipos definidos e/ou
ponteiros para esses tipos.
struct {
int numero;
pessoa aluno;
float *notas_de_cadeiras;
} aluno_IST;

 Inclusivamente pode fazer-se o seguinte:


typedef struct _no_elementar{
unsigned int numero;
pessoa aluno;
struct _no_elementar * proximo;
} no;

9 AED (IST/DEEC)
Tipos de dados e Variáveis
 Qualquer variável, em qualquer linguagem, tem um tipo de
dados associado
 O tipo de dados define os valores e as operações possíveis
sobre esses valores
 define portanto os valores que uma variável desse tipo pode tomar e as
operações que sobre ela podem ser feitas
 Mas um tipo de dados é uma definição genérica, abstracta
 a sua definição não corresponde a nenhuma variável
 ao definir um tipo de dados não estamos a instanciar nenhuma variável

10 AED (IST/DEEC)
Tipos básicos [1]
 Definição: Um tipo de dados define os valores e as
operações possíveis sobre esses valores.
 Inteiros
 int (o tipo mais usado; valores dependem da máquina:16 bits entre -32768 e
32767, 32 bits entre -215 e 215-1)
 short (2 Bytes entre -32768 e 32767)
 long (32 bits)
 Os literais podem ter base decimal (ex: 27, -15), octal (iniciados por 0, ex:
031) ou hexadecimal (iniciados por 0x ou 0X, ex: 0x1f5, 0XFF).
 Reais
 double (precisão dupla) : o mais usado
 float (precisão simples)
 Os literais possuem duas partes
 inteira e fraccionária, separadas por ponto (ex: -27.3, 9.5) ou
 base e expoente, separadas por E (ex: 2.3E-2, 4E3).

11 AED (IST/DEEC)
Tipos básicos [2]
 Caracteres
 Char
 Os literais são delimitados por “plicas”
 carácter impresso (ex: 'a', '}')

 caracteres especiais formados por ‘\letra’


 '\n' : mudança de linha (``newline´´)
 '\t' : tabulador
 '\\' : barra à esquerda (``backslash´´)

 código ASCII em base octal


'\0' : carácter nulo (``NULL´´)
- não confundir com o carácter ´0´ (código ASCII 48)

12 AED (IST/DEEC)
Organização em C das definições
de tipos
 Em C podem ser definidos novos tipos
 A definição dos tipos, no C, é dividida em dois ficheiros:
 A interface, num ficheiro «header» (.h):
 Definição do tipo.
 Protótipos das funções usadas para manipular objectos deste tipo, na forma
 tipo-retorno nome(tipo par1, tipo par2,...);

 A implementação, num ficheiro com extensão «.c»:


 Código das funções
 Cada utilização do tipo é feita num ficheiro «cliente» onde o
header é incluído:
#include <stdio.h> /*biblioteca*/
#include “ficheiro.h” /*declarações de projecto, no próprio directório*/

13 AED (IST/DEEC)
Definição de tipo: exemplo [1]

Definição do tipo Implementação do tipo


Ficheiro Num.h : Ficheiro Point.c:
typedef float Number; #include <math.h>
Ficheiro Point.h : #include “Point.h”
#include “Num.h” Number distance(Point pA, Point pB)
typedef struct { {
Number x; Number dx = pA.x – pB.x;
Number y; Number dy = pA.y – pB.y;
} Point; return(sqrt(dx*dx + dy*dy));
}
Number distance(Point pA, Point pB); Point create_point(Number x, Number y)
Point create_point(Number x, Number {
y); Point pt;
pt.x = x; pt.y = y;
return pt;
}

14 AED (IST/DEEC)
Definição de tipo: exemplo [2]
 Utilização do tipo (CLIENTE)

#include “Point.h”
main ()
{
Point Pt1, Pt2;
Number Numb;
Pt1 = create_point(1.0, 2.0);
Pt2 = create_point(3.7, 4.5);
Numb = distance(Pt1, Pt2);
...
}

15 AED (IST/DEEC)
Ponteiros
 A linguagem C permite aceder a posições de memória
específicas por meio de ponteiros;
 Para cada tipo é possível criar ponteiros;
 Exemplo:
int N1;
 Reserva uma posição de memória onde a variável “N1” irá morar;
 &N1 é o endereço de memória onde o valor de “N1” é armazenado;
int *N2;
 Reserva uma posição de memória, de nome “N2”, onde se poderá colocar o
endereço de um inteiro, ou do primeiro inteiro de uma tabela de
inteiros;
 &N2 é o endereço de memória onde se poderá colocar um endereço

16 AED (IST/DEEC)
Listas [1]
 Definição: uma lista é uma sequência de nós compostos por
 um item, contendo informação específica do nó.
 um apontador para o nó seguinte.
 Um nó é uma estrutura recursiva (“recursive” ou “recurrent”)
 Último elemento (terminal) aponta para vazio (NULL)
 Os restantes elementos (recorrentes, ou indutivos), entre o primeiro e o
penúltimo, contêm um apontador para um elemento do mesmo tipo.

17 AED (IST/DEEC)
Listas [2]
 Estrutura conceptual

item item item


Cabeça
(‘‘head’’)

 Disposição em memória
“head”
&#1 &#2 &#3 NULL
&#1
&#2
&#3

item item item

18 AED (IST/DEEC)
Listas [3]
 Definição em C – Cada nó da lista é composto por :
 Um item, que contém a informação específica ao elemento,
normalmente definido por declaração tipo:
typedef xxx Item;
 Um apontador para o próximo elemento da lista.

typedef struct _list


{
Item item;
struct _list * next;
} list;

 O último elemento da lista tem o seu campo next inicializado com


NULL.

19 AED (IST/DEEC)
Listas [4]
 Declaração de uma instância
 uma lista é representada por um apontador para uma estrutura de tipo
list
list *l;
 Alocação de um elemento de lista

l = (list *) malloc(sizeof(list));

if (l == NULL) {
fprintf(stderr, “Não há mais memória...!\n”);
}

20 AED (IST/DEEC)
Listas [5]
 Acesso a informação
list *l;
(*l).item e (*l).next permitem aceder aos dois campos da
estrutura.
 Mais simplesmente, podemos usar o operador -> e escrever:
l->item, l->next
 Elementos seguintes na lista:
l->item – item do primeiro elemento da lista,
l->next->item – item do segundo elemento da lista,
l->next->next->item – item do terceiro elemento da lista

21 AED (IST/DEEC)
Interface para Listas [1]
 A interface do tipo list, é constituída por:
 A definição do tipo list:
Ficheiro contendo a
#include “defs.h”
definição de Item
typedef struct _list
{
Item item;
struct _list *next;
} list;

 Alocação de um novo elemento:


list * NewElement(Item);

 Libertação da memória associada a um elemento:


Void FreeElement(list *);

22 AED (IST/DEEC)
Interface para Listas [2]
 Inserção de um elemento a seguir a outro
void InsertNext(list *, list *);

 Remoção de um elemento a seguir a outro:


void DeleteNext(list *);

 Função para aceder ao elemento seguinte:


list * Next(list *); /* acessor*/

 Função de acesso a um item:


Item Item(list *); /* acessor */

23 AED (IST/DEEC)
Interface para Listas [3]
 No ficheiro “defs.h” define-se o item
 Por exemplo

typedef int Item;

24 AED (IST/DEEC)
Implementação para Listas [1]
 Implementação do tipo list:
 Alocação de um novo elemento:
list * NewElement(Item N)
{
list *El = (list *) malloc(sizeof(list));
El ->next = NULL;
El->item = N;
return(El);
}

 Libertação da memória associada a um elemento:


void FreeElement(list *pEl)
{
free(pEl);
}

25 AED (IST/DEEC)
Implementação para Listas [2]
 Inserção de um elemento a seguir a outro
void InsertNext(list *pEl1, list *pEl2)
{
pEl1->next = pEl2->next;
pEl2->next = pEl1;
}

pEl2
item item item etc.

item

pEl1 pEl2
item item item etc.

depois item primeiro

pEl1
26 AED (IST/DEEC)
Implementação para Listas [2]
 Inserção de um elemento a seguir a outro
void InsertNext(list *pEl1, list *pEl2)
{
pEl1->next = pEl2->next;
pEl2->next = pEl1;
}

pEl2
item item item etc.

item

pEl1 pEl2
item item item etc.

depois item primeiro

pEl1
27 AED (IST/DEEC)
Implementação para Listas [3]
 Remoção de um elemento a seguir a outro
void DeleteNext(list *pEl)
{
list *Aux;
Aux = pEl->next->next;
FreeElement(pEl->next);
pEl->next = Aux;
}

pE1 aux
item item item etc.

primeiro

pE1 aux
item item item etc.

depois

28 AED (IST/DEEC)
Implementação para Listas [3]
 Remoção de um elemento a seguir a outro
void DeleteNext(list *pEl)
{
list *Aux;
Aux = pEl->next->next;
FreeElement(pEl->next);
pEl->next = Aux;
}

pE1 aux
item item item etc.

primeiro

pE1 aux
item item item etc.

depois

29 AED (IST/DEEC)
Implementação para Listas [4]
 Função para aceder ao elemento seguinte
list * Next(list *pEl)
{
return pEl->next;
}

 Função de acesso a um item


Item Item(list *pEl)
{
return pEl->item;
}

30 AED (IST/DEEC)
Implementação para Listas [5]
 Processamento de listas
 Para percorrer uma lista,
for (Ptr = pFirst; Ptr != NULL; Ptr = Next(Ptr))

assumindo que pFirst aponta para o primeiro elemento da lista.


O último elemento da lista deverá ter o seu campo next inicializado
com NULL.

 Exemplo: Para imprimir todos os elementos da lista


for (Ptr = pFirst; Ptr != NULL; Ptr = Next(Ptr))
PrintItem(Item(Ptr));

31 AED (IST/DEEC)
Implementação para Listas [6]
 Função para reverter uma lista

list *reverse(list *pL)


{
list *Next = pL, *Cur = pL, *Res = NULL;

while (Cur != NULL) {


Next = Cur->next;
Cur ->next = Res;
Res = Cur;
Cur = Next;
}
return Res;
}

 Fica como exercício para casa verificar a correcção desta função.

32 AED (IST/DEEC)
Síntese da Aula 1 de Tipologia e
Operações
 Introdução aos Dados e Algoritmos
 Listagem de tipos de dados mais vulgares e critérios de
escolha
 Tipologia de dados
 Simples, Tabelas, Estruturas
 Tipos de dados e variáveis
 Tipos básicos
 Organização em C das definições de tipos
 Ponteiros
 Listas simples
 Interface para listas
 Implementação para listas

33 AED (IST/DEEC)
Tipos abstractos [1]
 Balanço dos tipos de dados no C (básicos, estruturados,
dinâmicos e composições)
 Vantagens:
 muito próximos dos tipos das linguagem máquina (Bit,Word)
 manipulação eficiente
 Inconvenientes:
 Porque os algoritmos são expressos por instruções simples (=, if, do,...)
 resultam muitas vezes em falhas de implementação
 tipo de linguagem muito afastada do raciocínio dos analistas
 Mesmo que o algoritmo seja igual, a alteração do tipo de dados obriga a
modificar o código (exemplo: se nas listas da aula anterior for alterado o tipo
do campo item, de int para char, a função newElement tem de ser modificada)
 Solução: usar abstracções que permitem descrever o problema, deixando
os detalhes de implementação para depois!

34 AED (IST/DEEC)
Tipos abstractos [2]
 Um tipo abstracto é
 um tipo genérico de dados, dos quais não se conhece os valores
 uma interface que define os acessos e as operações sobre os dados
 O conjunto das propriedades algébricas da interface, que delimitam as
possíveis implementações.
 A implementação transcreve o tipo abstracto, codificando
os programas que efectuam as operações definidas na
interface (satisfazendo as propriedades).
 O uso permite compilar a implementação, instanciando o
tipo genérico para um dos tipos de C
 Um programa que usa um tipo abstracto é um cliente. O
cliente não tem acesso à implementação.

35 AED (IST/DEEC)
Tipos abstractos [3]
 Exemplo dos passos no desenvolvimento de um programa
 Análise:
 [Especificação] entram números codificados, sendo depois devolvido o valor
decifrado.
 [Tipo abstracto de dados] operações: entrada e remoção de elementos de
uma lista genérica, cifra e descodificação.
 Implementação:
 Opção A: Tipo de dados por tabela, com índice para o primeiro índice livre
 Opção B: Tipo de dados por lista simplesmente ligada
 Cifra/descodificação por chave pública
 Uso
 (Opção B) O tipo de dados é uma lista simplesmente ligada de inteiros.
 Compilação
 0’s e 1’s, do processador

36 AED (IST/DEEC)
Contentores
 Muitos tipos abstractos representam contentores.
 Os contentores servem para armazenar um conjunto de items: listas,
pilhas, anéis, tabelas de dispersão (‘‘hash-tables’’), ...
 Para definir um tipo abstracto para um contentor deve-se:
 Definir a interface do tipo abstracto em função do tipo Item.
A interface inclui o ficheiro “Item.h” (que contém a definição do tipo);
 Definir a implementação;
 Cada cliente cria um ficheiro “Item.h”, consoante as suas
necessidades;
 Compilar.

37 AED (IST/DEEC)
Exemplo: Pilha [1]
 Operações abstractas
 inicialização.
 limpeza da pilha.
 colocação de um elemento na pilha.
 remoção de um elemento (o último colocado).
 Indica se pilha está vazia.
 Pilha.h Usando a mesma interface, podem
typedef enum {FALSE,TRUE} BOOL; ser definidas várias implementações ...
void Init(unsigned pNumb);/*parâmetro é comprimento máximo*/
int Empty();
void Push(Item pI);
Item Pop();
BOOL IsEmpty();

38 AED (IST/DEEC)
Exemplo: Pilha [2]
 Algumas propriedades algébricas da pilha
TODO n: IsEmpty(Init(n)) = TRUE – pilha é sempre inicializada vazia
TODO i: IsEmpty(Push(i))=FALSE – depois de inserido um elemento, nunca a pilha fica vazia
TODO i: Pop(Push(i))=i – elemento removido da pilha é sempre o último a ser inserido

 Para os mais curiosos, apenas! Na definição algébrica da pilha,


ela própria é um tipo genérico Stack. A interface e as
propriedades são um pouco diferentes.
 A implementação é um pouco mais complexa, pelo que não é
considerada aqui.

39 AED (IST/DEEC)
Exemplo: Pilha [3]
 Implementação do tipo pilha usando uma tabela
 Pilha.c
#include “Item.h” /* aqui o cliente define os dados */
#include “Pilha.h” /* aqui está definida a interface */
static Item *gP;
static int gN; /*primeiro índice livre*/

void Init(unsigned pSize) {


gP = (Item *)malloc(pSize*sizeof(Item));
gN = 0;}

void Push(Item pI) {


gP[gN++] = pI;}

BOOL IsEmpty() {
return gN==0? TRUE : FALSE; }
...
40 AED (IST/DEEC)
Exemplo: Pilha [4]
 Implementação do tipo pilha usando uma tabela
 Exemplo

Push(i3)
i3
i2 i2
gN=2 i1 gN=3 i1

 Vantagens:
 Simplicidade.
 Desvantagens:
 Tamanho máximo limitado à partida.

41 AED (IST/DEEC)
Exemplo: Pilha [5]
 Implementação do tipo pilha usando uma lista
 Pilha.c
#include “Item.h” /* aqui o cliente define os dados */
#include “Pilha.h” /* aqui está definida a interface */
typedef struct pilha {
Item item;
struct pilha *next;
} pilha;
static pilha *gP;
void Init(unsigned pNumb) {
gP = NULL;}
void Push(Item pI) {
pilha *Top = (pilha *)malloc(sizeof(pilha));
Top->item = pI; Top->next = gP;
gP = Top;}
BOOL IsEmpty() {
return gP==NULL? TRUE : FALSE;} ...

42 AED (IST/DEEC)
Exemplo: Pilha [6]
 Implementação do tipo pilha usando uma lista
 Exemplo
gP i2 i1
Push(i3)

gP i3 i2 i1
 Vantagens:
 Cresce à medida que novos elementos são postos na pilha
 Decresce quando se removem elementos
 Desvantagens:
 Ocupa mais memória (para os apontadores)
 Mais lento (gestão de memória)
43 AED (IST/DEEC)
Exemplo: Pilha [7]
 Utilização do tipo abstracto pilha por clientes
 Vários clientes poderão usar diferentes definições para o tipo Item,
de modo a usar, por exemplo

Uma pilha de inteiros Uma pilha de alunos


Item.h Item.h
typedef int Item typedef struct {
unsigned numero;
enum {MEEC,MEIC,MERC} curso;
char *nome;
} Item;

44 AED (IST/DEEC)
Exemplo: Pilha [8]

Ciente 1 – Instack.c Cliente 2 – Alunos.c


#include “Item.h” #include “Item.h”
#include “Pilha.h” #include “Pilha.h”
main() Item InicializarAluno(char *pName,int pNumb)
{ {...}
Item Val=100; main()
Init(19); {
Push(Val); Item Aluno;
} init(0);
Aluno=InicializarAluno(“José”,44562);
Push(Aluno);
}
Atenção! O ficheiro “Item.h” é incluído antes do ficheiro “Pilha.h” de modo a definir
o tipo Item referido neste ficheiro.
45 AED (IST/DEEC)
Exemplo: Pilha [9]
 Comparando os dois clientes, as diferenças resumem-se a:
 Ficheiro de definições Item.h
 Programa principal
 Os tipos de dados abstractos permitem reutilizar funções (a
nível de programas fonte).
 Atenção: não é o mesmo que a reutilização de bibliotecas
(‘‘library’’) objecto.
 Nestas, os tipos de dados não podem ser definidos pelos clientes.

46 AED (IST/DEEC)
Exemplo: Pilha [10]
 Exemplo de aplicação do tipo abstracto de pilha: pretende-se validar
expressões aritméticas em notação pós-fixada (considerados apenas operadores
soma e multiplicação).
 Explicação: Os operadores aritméticos binários, são colocados segundo a
notação:
 In-fixada: entre os operandos (usada pelos humanos, mas obriga a definir prioridades-multiplicação
maior que soma, e usar parêntesis para tornear as prioridades.
Ex: 2 + 3 *4 [resultado 14]; (2 +3)*4 [resultado 20]
 Pós-fixada: depois dos operandos (usada pelos computadores, cálculo efectuado logo que se
encontra o operador o que evita definição de prioridades e parêntesis)
Ex: 2 3 4 * + [resultado 14]; 2 3 + 4 * [resultado 20]
Método de cálculo de 2 3 4 * +
(a) inserir 2 na pilha
(b) inserir 3 na pilha
(c) inserir 4 na pilha
(d) detectada a multiplicação, retirar os dois operandos da pilha «4»,«3» e inserir o resultado
«12»
(e) detectada a soma, retirar os dois operandos da pilha «2»,«12» e inserir o resultado «14»
(f) Só há um operando na pilha: a expressão 2 3 4 * + é válida!

47 AED (IST/DEEC)
Exemplo: Pilha [11]
#include <stdio.h>
#include <string.h>
#include “Item.h” /* O cliente usa pilha de inteiros */
#include “Pilha.h”

main(int pArgc, char **pArgv)


{
int Idx, Res, Dim = pArgc-1;

Init(Dim);
for (Idx = 1; Idx <= Dim; Idx ++) {
if (!strcmp(pArgv[Idx],”+”)) Push(Pop() + Pop());
else if (!strcmp(pArgv[Idx],”*”)) Push(Pop() * Pop());
else Push(atoi(pArgv[Idx]));
}
Res = Pop();
if (IsEmpty()) printf(“Valido com resultado=%d\n”, Res);
else printf(“Expressao invalida!\n”);
}

48 AED (IST/DEEC)
Implementações
 O cliente não depende da implementação.
 O cliente não tem de saber como a pilha foi implementada.
 Escolha-se a implementação na altura da edição de ligações
(‘‘link’’) do programa.
 A escolha deve ser feita em função da aplicação.
 Em muitos casos as diferenças estão na gestão da memória:
 Uma gestão mais rígida (baseada em tabelas) tem o defeito que é preciso
conhecer o tamanho máximo à partida. O espaço ocupado em memória
corresponde sempre ao tamanho máximo.
 Uma gestão mais flexível (baseada em listas, por exemplo) tem o defeito
de ser menos eficiente (tempo de computação) mas permite o
tratamento de problemas maiores devido a uma gestão mais apertada da
memória.

49 AED (IST/DEEC)
Tipos de 1ª classe
 Os tipos abstractos que já vimos têm um defeito:
 Só pode existir uma única instância do tipo por programa
 Definida como variável estática na parte da implementação
 O ideal seria poder usar os tipos abstractos da uma maneira
semelhante aos tipos básicos (int, float, char etc..).
 Definição: Um tipo de 1ª classe é um tipo para o qual pode
haver muitas instâncias que podem ser atribuídas a variáveis do
programa. Os tipos de 1ª classe podem ser usados nas
declarações como os tipos básicos.

50 AED (IST/DEEC)
Exemplo: tipo complexo [1]
 O tipo de 1ª classe complexo é definido pela interface:
complex.h:
typedef struct
{
float Re;
float Im;
} complex;

complex initComplex (float pRe, float pIm);


float Re(complex pC);
float Im(complex pC);
complex multComplex(complex pC1, complex pC2);

 O ficheiro complex.c contém a implementação...

51 AED (IST/DEEC)
Exemplo: tipo complexo [2]
#include “complex.h”
complex InitComplex (float pRe, float pIm)
{
complex Res;
Res.Re = pRe;
Res.Im = pIm;
return Res;
}

float Re (complex pC) { return pC.Re; }


float Im (complex pC) { return pC.Im; }

complex multComplex(complex pC1, complex pC2)


{
return initComplex(Re(pC1)* Re(pC2)– Im(pC1)* Im(pC2), Re(pC1)*
Im(pC2)+Im(pC1)* Re(pC2));
}

52 AED (IST/DEEC)
Exemplo: tipo complexo [3]
 O programa cliente calcula as raízes complexas da equação
𝑧𝑵 = 1. As raízes são dadas pela fórmula : cos(2pk/N) + i sin(2pk/N).
#include <math.h> Compilação requer biblioteca de funções
#include “complex.h” matemáticas: usar
main (int pArgc, char **pArgv) { gcc –o roots roots.c complex.c -lm
int Idx1, Idx2, pN;
complex t, x;
if (pArgc < 2) exit(1);
pN = atoi(pArgv[1]);
printf(“raízes para N=%d\n”, pN);
for (Idx1=0; Idx1 < pN; Idx1++) {
float r = 2.0 * M_PI * Idx1 / pN;
t = initComplex (cos(r), sin(r));
printf(“Z%d: %6.3f %6.3f\t“, Idx1, Re(t), Im(t));
for (x=t, Idx2=0; Idx2 < pN-1; Idx2++) x = mult(t,x);
printf(“Z%d^%d = %6.3f %6.3f\n”, Idx1, pN, Re(x),Im(x));}}

53 AED (IST/DEEC)
Exemplo: tipo complexo [4]
 Neste exemplo, podemos observar que:
 O tipo complex é usado como se fosse um tipo predefinido,
 Certas linguagens de programação (por exemplo o Java) permitem a
definição de operadores (+,-,*,/, etc...) para tipos novos.
 Em Java podíamos ter definido um novo * em vez de mult
 Apesar de o cliente ser independente da implementação, tem acesso à
forma como foi implementado o tipo (a definição do tipo está visível na
interface)
 Perigo potencial ...

54 AED (IST/DEEC)
Exemplo: tipo complexo [5]
 Se o cliente tiver acesso à definição do tipo:
 Tem a possibilidade de aceder directamente aos campos da
estrutura:
 Pode usar t.Re em vez de Re(t).
 Os programas clientes tornam-se dependentes da
implementação.
 se a implementação for alterada (usando, por exemplo a
notação polar ou angular para os complexos), é necessário
alterar todos os ficheiros clientes.
 A solução consiste em esconder a definição do tipo.

55 AED (IST/DEEC)
Complexos: 2ª versão [1]
 A interface passa a ser: complex.h
typedef struct _complex complex;

complex *initComplex(float pRe, float pIm);


float Re(complex *pC);
float Im(complex *pC);
complex *multComplex(complex *pC1, complex *pC2);

 A diferença reside na utilização de ponteiros para acesso e


processamento de qualquer variável do tipo complex.
 A implementação do tipo complex não está visível neste
ficheiro.

56 AED (IST/DEEC)
Complexos, 2ª versão [2]
 A implementação é alterada: complex.c
struct _complex {float Re; float Im;};

complex *initComplex (float pRe, float pIm)


{
complex *Res =(complex *) malloc(sizeof(complex));
Res->Re = pRe;
Res->Im = pIm;
return Res;
}

 A diferença reside na utilização de ponteiros para acesso e


processamento de qualquer variável do tipo complex.
 A implementação do tipo complex não está visível neste
ficheiro.

57 AED (IST/DEEC)
Complexos, 2ª versão [3]
 O cliente não precisa de ser alterado.
 Resumindo:
 A implementação do tipo é 100% privada
 Escondida na implementação
 O uso é feito através de um ponteiro explícito
 Todas as variáveis do tipo complexo são referências para o objecto
 O interface funcional esclarece o mesmo
 Não era possível implementar esta solução sem recorrer a um
apontador
 O apontador permite criar um tipo sem especificar os pormenores
 O compilador pode compilar um programa (interface + cliente) porque
um apontador tem um tamanho definido: 32bits (64 bits nas máquinas
recentes).

58 AED (IST/DEEC)
Síntese da Aula 2 de Tipologia e Operações
 Tipos abstractos
 Contentores
 Pilhas
 Implementação em tabela
 Implementação em lista

 Critérios para escolha da implementação


 Tipos abstractos de 1ª classe
 Exemplo para Complexos
59 AED (IST/DEEC)
Exemplo: Filas [1]
 Definição: Na fila, ou FIFO-First In/First Out, o primeiro
elemento a sair é o primeiro elemento entrado. Em relação às
pilhas, as alterações relevantes são:
 A inserção de um elemento é feita no fim da lista.
 Passa a haver dois ponteiros, um para o início da lista (para retirar
elementos) outro para o fim da lista (para inserir elementos).
 Fila.h
typedef struct fila F;
void FilaDump(F*);
F *FilaInit(int);
int FilaEmpty(F*);
void FilaPut(F*, Item);
Item FilaGet(F*);

60 AED (IST/DEEC)
Exemplo: Filas [2]
 Implementação (usando uma lista):
 Fila.c
A estrutura element define o nó base da fila
#include <stdlib.h> (formato de lista) e a estrutura fila, referida
#include <stdio.h> na interface, é definida
#include “Item.h”
#include “Fila.h”

typedef struct _element


{
Item item;
struct _element * next;
} element;

struct fila
{
element * primeiro;
element * ultimo;
};

/* continua */

61 AED (IST/DEEC)
Exemplo: Filas [3]
 A função new serve para alocar e inicializar um novo
elemento na lista:
element *FilaNew(Item vItem, element *pNext)
{
element *x = (element *) malloc(sizeof (element));
x->item = vItem;
x->next = pNext;
return x;
}

 Função de inicialização de uma fila.


 Dado que está implementada com uma lista, não estamos limitados
no número de elementos.
F *FilaInit(int maxN)
{
F *pFila = (F*) malloc(sizeof(F));
pFila->primeiro = pFila->ultimo = NULL;
return pFila;
}

62 AED (IST/DEEC)
Exemplo: Filas [4]
 Função que coloca um item na fila:
void FilaPut(F *pFila, Item vI)
{
if (pFila->primeiro == NULL) { /* a fila está vazia */
pFila->ultimo = FilaNew(vI, pFila->primeiro);
pFila->primeiro = pFila->ultimo; return; }
/* o item é colocado no fim da lista */
pFila->ultimo->next = FilaNew(vI, NULL);
pFila->ultimo = pFila->ultimo->next;
}

Fila após inicialização Fila com um elemento Fila com dois elementos

primeiro 0 primeiro &#1 23 0 primeiro &#1 23 &#2 17 0

último último item next último item next item next


0 &#1 &#2

63 AED (IST/DEEC)
Exemplo: Filas [5]
 A função get remove o primeiro elemento da fila:

Item FilaGet(F *pFila)


{
Item item = pFila->primeiro->item;
element *elem = pFila->primeiro->next;
free(pFila->primeiro);
pFila->primeiro = elem;
return item;
}

 À medida que os elementos são removidos da lista, a memória


é libertada.

64 AED (IST/DEEC)
Contentores
 O exemplo do tipo fila demonstra a possibilidade de criar
tipos de 1ª classe para novos contentores.
 Os contentores criados até agora dependem de um tipo
Item que representa o tipo de dados que se quer armazenar
no contentor
 Um defeito desta abordagem é que num programa o tipo
Item só pode ser definido uma única vez.
 Em consequência não podemos ter no mesmo programa, uma
fila de inteiros e uma fila de elementos de tipo «aluno».

65 AED (IST/DEEC)
Items
 A solução consiste, como no caso dos tipos de 1ª classe, em
usar um ponteiro de modo a não especificar o tipo dos items.
 Podemos manter a interface e indicar explicitamente a
utilização de pointeiros, ou
 Redefinir o tipo Item é definido como:
typedef void *Item;
 O tipo void * corresponde a um apontador para um
objecto cujo tipo é desconhecido.
 Vamos agora, criar filas de vários tipos.

66 AED (IST/DEEC)
Filas de complexos
 Para criar filas de complexos, o cliente pode usar o tipo
complex já definido:
F *pFilaC = FilaInit(10); /* declaração da fila */
complex *z = initComplex (1,1); /* declaração de um complexo */

 Para colocar o complexo na fila:


FilaPut(pFilaC, (Item) z);

 A variável z é um apontador, o cast permite evitar obter


warnings do compilador.
 Para remover um elemento da fila:
z = (complex *) FilaGet(pFilaC);
É preciso converter os Complex * para
Item ao escrever na fila e converter os
Item para Complex * ao ler da fila.

67 AED (IST/DEEC)
Filas de matrizes [1]
 Para definir filas de matrizes, temos de definir primeiro um
tipo abstracto matrix
 Interface – Matrix.h
typedef struct _matrix matrix;
matrix *NewMatrix(int, int);
matrix *MatrixMult(matrix *, matrix *);
float MatrixElement(matrix *,int,int);
etc...

 Implementação – Matrix.c
struct _matrix {
float ** valores;
int cols;
int rows;
};
etc...

68 AED (IST/DEEC)
Filas de matrizes [2]

matrix *NewMatrix(int pRows, int pCols)


{
int Idx;
matrix *Mat = (matrix *) malloc(sizeof(matrix));
Mat->valores = (float **) malloc(pRows * sizeof (float *));
for (Idx =0; Idx < pRows; Idx++)
Mat->valores[Idx] = (float *) malloc(pCols * sizeof(float));
Mat->rows=pRows; Mat->cols=pCols; return Mat;
}

matrix *MatrixMult(matrix *pA, matrix *pB) {


matrix *Res = NewMatrix(Rows(pA), Cols(pB));
int IdxI,IdxJ,IdxK;
...
return Res;
}

float MatrixElement(Matrix *pA, int pI, int pJ)


{
return pA->valores[pI][pJ];
}

69 AED (IST/DEEC)
Filas de matrizes [3]
 Uma vez o tipo matrix definido, o cliente pode criar uma fila
de matrizes:
F *pFilaM = FilaInit(10);
matrix *m = NewMatrix(10,10);

 Colocar m na fila:
FilaPut(pFilaM,(Item) m);

 Para remover uma matriz da fila:


matrix *a = (matrix *) FilaGet(pFilaM);

 As filas de matrizes e as filas de complexos podem coexistir no mesmo


programa.
 O Item aponta para um complexo ou para uma matriz.

70 AED (IST/DEEC)
Listas duplamente ligadas [1]
 As filas duplamente ligadas são usadas quando é preciso
percorrer a lista do início até ao fim e do fim até ao início.
 Interface – dlist.h:

typedef struct dlist Dl;


Dl *NewDList();
Dl *AddAtEnd(Item, Dl*);
Dl *AddAtHead(Item, Dl*);
Dl *Concat(Dl*,Dl*);
Item First(Dl*);
Item Last(Dl*);
Item Next(Dl*);
Item Previous(Dl*);
Item Nth(Dl*, int n);
Item Find(Item, Dl*, int test(Item,Item));
void Apply( void f(Item), Dl*);

71 AED (IST/DEEC)
Listas duplamente ligadas [2]
 Implementação – dlist.c
 Definição do tipo dlist

struct dlist {
Item item;
struct dlist *next;
struct dlist *previous;
};

 A lista vazia é representada por um ponteiro para NULL

Dl *NewDlist() {
return ((Dl*) NULL);
}

72 AED (IST/DEEC)
Listas duplamente ligadas [3]
 Inserção de um Item no início da lista

Dl *AddAtHead(Item item, Dl *list)


{
Dl *n = (Dl *) malloc(sizeof(dlist));
n->previous = n->next = NULL;
n->item = item;
if (list != NULL) {
n->next = list;
list->previous = n;
}
return n;
}

73 AED (IST/DEEC)
Listas duplamente ligadas [4]
 Inserção de um Item no final da lista pela função AddAtEnd

Dl *last(Dl *lst)
{
Dl *ultimo = lst;
while (ultimo->next != NULL)
ultimo = ultimo->next;
return ultimo;
}

Dl *AddAtEnd(Item i, Dl *list) {
Dl *ultimo = last(list);
ultimo->next = malloc(sizeof(dlist));
ultimo->next->item = i;
ultimo->next->previous = ultimo;
ultimo->next->next = NULL;
return list;
}

74 AED (IST/DEEC)
Listas duplamente ligadas [5]
 Para concatenar duas listas
Dl *Concat(Dl *lst1,Dl *lst2)
{
Dl *lst3 = last(lst1);
lst3->next = lst2; lst2->previous = lst3;
return lst1;
}

 Vários acessores
Item First(Dl *lst) {return lst->item;}
Item Last(Dl *lst) {return last(lst)->item;}
Item Next(Dl *lst) {return lst->next->item;}
Item Previous(Dl *lst) {return lst->previous->item;}

 Nth devolve o n-ésimo da lista


Item Nth(Dl *lst, int n) {
int i;
for (i=0; i<n; i++) lst = lst-> next; return lst->item;
}

75 AED (IST/DEEC)
Listas duplamente ligadas [6]
 A função Find ilustra a utilidade de passar funções no argumento
Item Find(Item i, Dl *lst, int test(Item,Item)) {
while (!(test(i,lst->item)) && lst->next != NULL)
lst = lst->next;
if (lst->next == NULL)
if (test(i,lst->item))
return lst->item;
else return (Item)NULL; O terceiro argumento da função é uma função de
return lst->item; comparação entre 2 items. O utilizador escreve a
} função de teste em conformidade com o objectivo da
procura.
 A função Apply aplica a função f a todos os elementos da lista

void Apply( void f(Item), Dl *lst)


{
while (lst != NULL) {
f(lst->item);
lst = lst-> next;
}
} A vantagem para o cliente é que não tem de escrever
muitos dos ciclos que iteram sobre a lista.

76 AED (IST/DEEC)
Síntese da Aula 3 de Tipologia e
Operações
 Tipos abstractos de 1ª Classe

 Exemplo para Filas

 Filas de Complexos

 Filas de matrizes

 Listas Duplamente Ligadas

77 AED (IST/DEEC)
Apêndice

Estruturas de Dados e Operações


Descrição
 Colecção de acetatos com informação complementar ao
capítulo
 Essencialmente, os tópicos aqui cobertos constituem revisão
de conceitos já discutidos na disciplina de Introdução à
Programação
 Existem dois tipos de subconjuntos
 Continuação de tópicos discutidos na Aula 1 - Iniciados pela repetição
do acetato da Aula 1 que lhe está associado,
 Tópicos omitidos na Aula 1

79 AED (IST/DEEC)
Conversão de tipos
 A linguagem C permite alterar o tipo de variáveis, quando
conveniente e/ou necessário
 Conversão implícita:
int Numb;
float Inflation;
Inflation = Numb / 3.5;
 O operador / converte Numb para real antes de efectuar a divisão.
 Conversão explícita (‘‘casting’’):
int Numb1, Numb2;
float Price;
Price = ((float) Numb1) / Numb2;
 Para que o resultado final seja um float, é necessário converter
explicitamente um dos inteiros para float
80 AED (IST/DEEC)
Ponteiros
 A linguagem C permite aceder a posições de memória
específicas por meio de ponteiros;
 Para cada tipo é possível criar ponteiros;
 Exemplo:
int N1;
 Reserva uma posição de memória onde a variável “N1” irá morar;
 &N1 é o endereço de memória onde o valor de “N1” é armazenado;
int *N2;
 Reserva uma posição de memória, de nome “N2”, onde se poderá colocar o
endereço de um inteiro, ou do primeiro inteiro de uma tabela de
inteiros;
 &N2 é o endereço de memória onde se poderá colocar um endereço

81 AED (IST/DEEC)
Ponteiros [2]
 Exemplo (continuação):
int N1;
N1
??

N1 = 3;
N1
3

&N1
N1
3

&N1

82 AED (IST/DEEC)
Ponteiros [3]
 Exemplo (continuação):
int *N2;
N2
?? 3

N2 = &N1;
N2
&N1 3
A alocação de
&N1 memória ocor-
re em qualquer
N2 = (int *) malloc(3*sizeof(int)); sítio, podendo
ser acima ou a-
N2 baixo do local
onde “mora” o
&N2[0] 3 ?? ?? ?? ponteiro.
&N1
&N2[0]

83 AED (IST/DEEC)
Ponteiros [4]
 Exemplo (continuação):
N2[1] = 5;
N2
&N2[0] 3 ?? 5 ??
&N1
&N2[1]
printf(“%d”, *(N2+1));
•5
• porque N2+1 é &N2[1]: quando a variável for de tipo
ponteiro para tabela, + adiciona espaço de cada célula
printf(“%d”, *N2+1);
• ??+1
• porque *N2 é o conteúdo de N2[0] que contém ??.

84 AED (IST/DEEC)
Tabelas [1]
 Definição: Uma tabela (“array”) é uma série de localizações,
todas do mesmo tipo, acedidas por índices numéricos a partir
de 0.
 Sintaxe: após a declaração base inserir uma das opções
 {[Dimensão]}+
 Restrições:
 as dimensões são inteiros positivos.
 Exemplos
int Tab1[4]; /* tabela de 4 inteiros */
char Tab2[3][2]; /* tabela de char 3 por 2 */
char Tab3[5][14][2]; /* tabela de char 5 por 14 por 2 */

Tab1[0] Tab1[1] Tab1[2] Tab1[3]

85 AED (IST/DEEC)
Tabelas [2]
 Os índices são valores entre 0 e largura-1, para cada dimensão.
 Exemplos:
Tab1[0]=0; /* correcto*/
Tab1[4]=-25; /* incorrecto: limite máximo é 3 */
Tab2[2][0]='?'; /* correcto*/

 É responsabilidade do programador garantir que os índices


estão dentro dos limites!!!

86 AED (IST/DEEC)
Tabelas [3]
 As tabelas são inicializadas por ciclos de varrimento ou lista de
valores delimitada por { }.
 Exemplos
/* tabela Tab1 inicializada com ciclo */
for (Idx=0; Idx<4; Idx++)
Tab1[Idx]=Idx;
/* tabela Tab1 inicializada com lista de valores */
Tab1 = {0,1,2,3};

/* inicialização de Tab2 com ciclo */


for (Idx1=0; Idx1<3; Idx1++)
for (Idx2=0; Idx2<2; Idx2++)
Tab2[Idx1][Idx2] = 'a'+2*Idx1+Idx2;
/* inicialização de Tab2 com lista de valores */
Tab2 = { {'a','b'}, {'c','d'}, {'e','f'} };

87 AED (IST/DEEC)
Tabelas [4]
 Em C, existe uma correspondência entre tabelas e ponteiros:
*Array  Array[0]
*(Array + 1)  Array[1]
*(Array + Idx)  Array[Idx]

 As instruções seguintes:
int Array[10]; int *Ptr;
Array[2] = 3;
Ptr = Array + 2;
printf(“%d\n”, *Ptr);

 3
Escrevem no terminal o valor .....

88 AED (IST/DEEC)
Tabelas [5]
 Quando o tamanho da tabela é conhecido apenas na altura da
execução deve-se alocar a memória de maneira dinâmica

#include <stdlib.h>
#include <stdio.h>
main (int pArgc, char *pArgv[])
{
int Numb = atoi(pArgv[1]);
int *Space = (int *) malloc(Numb * sizeof (int));
if (Space == NULL)
fprintf(stderr, “Memória insuficiente.\n”);

89 AED (IST/DEEC)
Tabelas multidimensionais [1]
 A declaração
float Vect[10][20];
cria um vector de 10 elementos, em que cada um deles é um vector de 20
objectos do tipo float.
Esta tabela é gerada na compilação
 Alocação dinâmica (gerada na execução do programa)
float ** Newmatrix(int pRows, int pColumns)
{
int Idx;
float **mat; /* mat é um apontador para uma tabela que contém float ! */

mat = (float **)malloc(pRows * sizeof (float *));

for (Idx=0; Idx<pRows; Idx++)


mat[Idx]=(float *)malloc(pColumns * sizeof(float));

return mat;
}

90 AED (IST/DEEC)
Tabelas multidimensionais [2]
 Exemplo:
float ** mat;
??
mat
mat = (float **) malloc(3*sizeof(float *));
&mat[0] ?? ?? ??
mat
&mat[0]

mat[0] = (float *) malloc(4*sizeof(float));


mat[0] &mat[0][0] ?? ?? ?? ?? ?? ??
mat
&mat[0][0]

Há que fazer malloc para mat[1] e mat[2] como acima


91 AED (IST/DEEC)
Tabelas multidimensionais [3]
 Exemplo (continuação):
 Depois de se usar a matriz “mat” pode ser necessário libertar a
memória.
 Tal faz-se pela ordem inversa da alocação
 Liberta-se “mat[0]”, “mat[1]” e “mat[2]” e só depois se liberta
“mat”
 A sequência
free(mat[0]);
free(mat[1]);
free(mat[2]);
free(mat);
para efeitos de posterior utilização de memória, produz
??
mat
92 AED (IST/DEEC)
Tabelas multidimensionais [4]
 O que aconteceria se, depois de alocar “mat[0]” se fizesse a
libertação de “mat”?
 Ou seja,
mat = (float **) malloc(3*sizeof(float *));
mat[0] = (float *) malloc(4*sizeof(float));
free(mat);
Como fica a memória?

?? ?? ?? ?? ??
mat

O espaço de memória que havia sido reservado para “mat[0][0-3]”


permanece, nunca mais podendo ser usado por outras alocações
durante a execução do programa corrente!!!

93 AED (IST/DEEC)
Tabelas multidimensionais [5]
 Exemplo: multiplicação de matrizes
float **Mult(float **pA, float **pB, unsigned pLA, unsigned pCB, unsigned
pS)
{
int IdxI,IdxJ,IdxK;
float **Res = Newmatrix(pLA,pCB);

for (IdxI=0; IdxI < pLA; IdxI++)


for (IdxJ=0; IdxJ < pCB; IdxJ++)
for (IdxK=0; IdxK < pS; IdxK++)
Res[IdxI][IdxJ] += pA[IdxI][IdxK] * pB[IdxK][IdxJ];
return(Res);
}

/* uso da função */
#define LINEA 10
#define COLB 15
#define SIZE 20
float **ArrA = Newmatrix(LINEA,SIZE), **ArrB = Newmatrix(SIZE,COLB), **ArrC;
ArrC = Mult(ArrA, ArrB, LINEA, COLB, SIZE);

94 AED (IST/DEEC)
Cadeias de caracteres [1]
 Definição: Uma cadeia de caracteres (“string”) é uma tabela
de caracteres, sendo obrigatoriamente '\0' o último
caracter.
 Uma cadeia pode ser indicada entre aspas (ex: “perigo!” é uma
cadeia de 7+1=8 caracteres).
 Numa tabela inicializada, o compilador de C determina a dimensão.
 Exemplo
char Msg[]=“texto”; /* tabela 5+1=6 caracteres */
int Tab[]={-4,17,5}; /* tabela 3 inteiros */
 Os ponteiros e a gestão dinâmica de memória facilitam o tratamento de
cadeias de caracteres.

95 AED (IST/DEEC)
Cadeias de caracteres [2]
 Processamento de cadeias de caracteres
 Exemplo: procura numa cadeia de caracteres

char * Procura (char C, char *pString)


{
int Idx;

for (Idx=0; (pString[Idx] != ‘\0’) && (pString[Idx] != C); Idx++);


return pString+Idx; /* retorna uma cadeia de caracteres que */
/* começa pelo símbolo contido em C ou */
/* por uma cadeia vazia. */
}

96 AED (IST/DEEC)
Cadeias de caracteres [3]
 A biblioteca do C string.h oferece um conjunto de
funções para manipular cadeias de caracteres:
 Função para copiar o conteúdo de uma cadeira de caracteres:
char *strcpy(char *dest, char *src);
O ponteiro dest deve ter a memória alocada suficiente para conter a
cadeia src.
 Função que devolve uma cópia da cadeia de caracteres:
char *strdup(char *s);
 Função que compara duas cadeias de caracteres:
int strcmp(char *s1, char *s2);
devolve um inteiro maior, igual ou menor do que 0 consoante a cadeia
s1 for menor, igual ou maior do que s2.

97 AED (IST/DEEC)
Estruturas [1] (omitem-se [2] e [3])
 As tabelas apenas permitem alocar, estática ou dinamicamente,
“caixas” de memória para objectos do mesmo tipo;
 Só inteiros, só reais, só caracteres...
 Para agrupar objectos de tipos diferentes numa mesma
variável existem as estruturas
 Exemplo:

#define SIZE 80
struct {
char nome[SIZE];
int idade;
float altura;
} pessoa;

?????? ... ???? ?? ??


pessoa

98 AED (IST/DEEC)
Estruturas [4]
 Fazer
no * aed;
equivale a fazer
??
aed

 E fazer
aed = (no *) malloc(1*sizeof(no));
equivale a fazer
&aed.numero ?? ?? ??
aed
&aed.numero
numero aluno proximo

99 AED (IST/DEEC)
Estruturas [5]
 E fazer
aed->proximo = (no *) malloc(1*sizeof(no));
equivale a fazer
&#1 ?? ?? &#2 ?? ?? ??
aed

&#1 numero aluno proximo


numero aluno proximo

&#2
 Notar que qualquer campo de uma estrutura que seja um ponteiro apenas
reserva o espaço para o ponteiro aquando da alocação;
 Compete ao programador estar consciente de que tem ponteiros como
campos das estruturas. Tem de reservar posteriormente memória para
guardar os objectos apontados por esse ponteiro, assim como lembrar-se
de libertar essa memória.
100 AED (IST/DEEC)
Estruturas [6]
 Como se liberta a memória dos objectos criados atrás
 Correctamente
Free(aed->proximo);
Free(aed);
??
aed

 Incorrectamente
Free(aed);
?? ?? ?? ??
aed

numero aluno proximo

&#2
101 AED (IST/DEEC)
Tabela de listas
 Como se cria uma tabela de listas?
list **tlst;
tlst = (list **) malloc(4*sizeof(list *));

&tlst[0] ?? ?? ?? ??
tlst
&tlst[0]
&tlst[1]
&tlst[2]
&tlst[3]

 Cada “tlst[i]”, com i = 0, 1, 2, 3, é um ponteiro para uma variável


do tipo “list”.
 Notar que “tlst” é um ponteiro para apontadores de “list”.

102 AED (IST/DEEC)


Lista de listas [1]
 Como se cria uma lista de listas?
typedef struct _l_of_l_
{
list *lista; /* ponteiro para sub-lista */
struct _l_of_l_ *seguinte; /* ponteiro para resto da lista */
} lst_lst;

lst_lst *exemplo;

 A variável “exemplo” é um apontador para um tipo que é uma


estrutura.
 Essa estrutura possui dois campos
 apontador para o próximo elemento da lista de listas.
 apontador para variáveis do tipo list;

103 AED (IST/DEEC)


Lista de listas [2]
 A instrução
lst_lst *exemplo;
produz apenas
??
exemplo

 E as instruções
exemplo = (lst_lst *) malloc(sizeof(lst_lst));
exemplo->seguinte = NULL;
produzem
&#1 ?? 00........00
exemplo
&#1
lista seguinte
104 AED (IST/DEEC)
Lista de listas [3]
 Suponha-se que de seguida se faz
exemplo->seguinte = (lst_lst *)malloc(sizeof(lst_lst));
exemplo->seguinte->seguinte = NULL;

&#1 ?? &#2 ?? 00........00


exemplo
&#1
lista seguinte lista seguinte
&#2

 Para criar o primeiro nó da lista, faz-se


exemplo->lista = (list *)malloc(sizeof(list));
exemplo->lista->next = NULL;

&#1 &#3 &#2 ?? 00........00 ?? 00........00


exemplo
&#3
lista next
105 AED (IST/DEEC)
Lista de tabelas [1]
 Como se cria uma lista de tabelas?
typedef struct _l_tab {
Item * tabela;
struct _l_tab * seguinte;
} ltab;

 As instruções
ltab * xpto;
xpto = (ltab *) malloc(sizeof(ltab));
xpto->seguinte = NULL;

criam
&#1 ?? 00........00
xpto
&#1

106 AED (IST/DEEC)


Lista de tabelas [2]
 Para criar uma tabela no campo “xpto->tabela” basta usar
o procedimento já descrito para a criação de uma tabela
xpto->tabela = (Item *)malloc(N*sizeof(Item));
 Para criar o próximo elemento da lista basta usar o
procedimento já descrito para criação de um novo nó numa
lista
xpto->seguinte = (ltab *) malloc(sizeof(ltab));
xpto->seguinte->seguinte = NULL;

&#1 &#2 &#3 ?????? ... ???? ?? 00........00


xpto
&#1
&#2
&#3

107 AED (IST/DEEC)


Listas duplamente ligadas [1]
(Da aula 1)
 Para criar uma lista com ponteiros para a frente e para trás
(anel)

typedef struct _d_lst_


{
Item objecto;
struct _d_lst_ * anterior;
struct _d_lst_ * posterior;
} dlst;

dlst * frente_verso;
frente_verso = (dlst *) malloc(sizeof(dlst));
frente_verso->anterior = frente_verso->posterior = NULL;

frente_verso
&#1 ?? 00........00 00........00

&#1
anterior
objecto posterior
108 AED (IST/DEEC)
Listas duplamente ligadas [2]
 Para acrescentar um novo elemento há que fazer

frente_verso->posterior = (dlst *) malloc(sizeof(dlst));


frente_verso->posterior->anterior = frente_verso;
frente_verso->posterior->posterior = NULL;

frente_verso
&#1 ?? 00........00 &#2 ?? &#1 00........00

&#1
anterior anterior
objecto posterior objecto posterior

&#2

109 AED (IST/DEEC)


Listas duplamente ligadas [1]
(Da aula 3, omitem-se de [2] a [6])
 As filas duplamente ligadas são usadas quando é preciso
percorrer a lista do início até ao fim e do fim até ao início.
 Interface – dlist.h:

typedef struct dlist Dl;


Dl *NewDList();
Dl *AddAtEnd(Item, Dl*);
Dl *AddAtHead(Item, Dl*);
Dl *Concat(Dl*,Dl*);
Item First(Dl*);
Item Last(Dl*);
Item Next(Dl*);
Item Previous(Dl*);
Item Nth(Dl*, int n);
Item Find(Item, Dl*, int test(Item,Item));
void Apply( void f(Item), Dl*);

110 AED (IST/DEEC)


Listas duplamente ligadas [7]
 O cliente pode usar, por exemplo, listas de alunos
 Primeiro deve-se definir um tipo abstracto para aluno
typedef struct _aluno aluno; /* na interface */

 Implementado por
struct _aluno { char *nome; int num; };

 Para o tipo abstracto aluno define-se a função de igualdade


int AlunoIgual(Item i1, Item i2) {
return (Num((aluno *) i1) == Num((aluno *) i2));}

 Onde Num é definido como


int Num (aluno *a) {return a->num;};

111 AED (IST/DEEC)


Listas duplamente ligadas [8]
 A procura na lista de alunos pode ser feita assim
aluno *Procura(int num, Dl *l_alunos)
{
aluno *a = NovoAluno(“”,num);
return Find(a, l_alunos, AlunoIgual);
}

 Onde NovoAluno é um construtor


aluno *NovoAluno(char * nome, int num)
{
aluno *a = malloc(sizeof(aluno));
a->nome = nome; a->num = num;
return a;
}

112 AED (IST/DEEC)


Listas duplamente ligadas [9]
 Para imprimir no écran todos os alunos contidos na lista

void PrintAlunos(Dl *l_alunos)


{
Apply(PrintAluno, l_alunos);
}

 Onde a função PrintAluno é definida por

void PrintAluno(Item a)
{
printf(“Aluno nº %d, %s\n”, Num((aluno *) a), Nome((aluno *) a));
}

 Não é preciso escrever ciclos

113 AED (IST/DEEC)

Você também pode gostar