37
Índice i Índice Índice Capítulo 2 – Estrutura de Dados sequencial com armazenamento sequencial 1. A Estrutura Abstrata de Dados “Lista” ................................................... 1 1.1. Definição .................................................................................. 1 1.2. Implementação de Listas utilizando armazenamento sequencial ................. 2 1.3. Implementação de Listas utilizando armazenamento não sequencial ............ 3 2. Listas - armazenamento sequencial usando memória estática ...................... 3 2.1. Introdução ................................................................................. 3 2.2. Declaração ................................................................................ 5 2.3. Criar um elemento ....................................................................... 6 2.4. Criar uma lista ............................................................................ 6 2.5. Lista vazia e lista cheia ................................................................. 7 2.6. Inserir um elemento numa lista ........................................................ 8 2.6.1. Inserir no início ..................................................................... 8 2.6.2. Inserir no fim ........................................................................ 9 2.6.3. Inserir por ordem .................................................................. 10 2.7. Remover um elemento duma lista .................................................... 10 2.8. Listar/mostrar um elemento ou os elementos duma lista ........................ 12 2.9. Destruir uma lista ....................................................................... 13

Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

  • Upload
    dolien

  • View
    213

  • Download
    0

Embed Size (px)

Citation preview

Page 1: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Índice i

Índice

Índice

Capítulo 2 – Estrutura de Dados sequencial com

armazenamento sequencial

1. A Estrutura Abstrata de Dados “Lista” ................................................... 1

1.1. Definição .................................................................................. 1

1.2. Implementação de Listas utilizando armazenamento sequencial ................. 2

1.3. Implementação de Listas utilizando armazenamento não sequencial ............ 3

2. Listas - armazenamento sequencial usando memória estática ...................... 3

2.1. Introdução ................................................................................. 3

2.2. Declaração ................................................................................ 5

2.3. Criar um elemento ....................................................................... 6

2.4. Criar uma lista ............................................................................ 6

2.5. Lista vazia e lista cheia ................................................................. 7

2.6. Inserir um elemento numa lista ........................................................ 8

2.6.1. Inserir no início ..................................................................... 8

2.6.2. Inserir no fim ........................................................................ 9

2.6.3. Inserir por ordem .................................................................. 10

2.7. Remover um elemento duma lista .................................................... 10

2.8. Listar/mostrar um elemento ou os elementos duma lista ........................ 12

2.9. Destruir uma lista ....................................................................... 13

Page 2: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

ii Índice

Índice

3. Listas - armazenamento sequencial usando memória dinâmica ................... 14

3.1. Introdução ................................................................................ 14

3.2. Declaração ................................................................................ 16

3.3. Criar um elemento ...................................................................... 16

3.4. Criar uma lista ........................................................................... 17

3.5. Lista vazia e lista cheia ................................................................. 18

3.6. Inserir um elemento numa lista ....................................................... 19

3.6.1. Inserir no início .................................................................... 19

3.6.2. Inserir no fim ....................................................................... 20

3.6.3. Inserir por ordem .................................................................. 20

3.7. Remover um elemento duma lista .................................................... 21

3.8. Listar/mostrar um elemento ou os elementos duma lista ......................... 23

3.9. Destruir uma lista ....................................................................... 24

4. Ordenação duma lista ...................................................................... 25

4.1. Seleção Linear ........................................................................... 25

4.2. Bubblesort ................................................................................ 26

4.3. Quicksort ................................................................................. 28

4.4. Por Inserção .............................................................................. 30

5. Pesquisa dum elemento numa lista ..................................................... 30

5.1. Pesquisa exaustiva ...................................................................... 30

5.2. Pesquisa Sequencial ..................................................................... 31

5.3. Pesquisa Binária ......................................................................... 32

Page 3: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

A Estrutura Abstrata de Dados “Lista” 1

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Capítulo 2 – Estrutura de Dados sequencial com

armazenamento sequencial

1. A Estrutura Abstrata de Dados “Lista”

1.1. Definição

Uma EAD Lista é uma sequência linear de tipos de dados do mesmo tipo com as

seguintes propriedades:

1. Existe um primeiro elemento (a cabeça) com um único sucessor (o próximo);

2. Existe um último elemento (a cauda) sem sucessor;

3. Qualquer outro elemento tem um sucessor (próximo) e um antecessor (anterior);

sobre a qual se realiza as seguintes operações:

1. Criar um elemento

2. Criar uma lista

3. Verificar se uma lista está vazia (não tem elementos)

4. Procurar um elemento

5. Procurar o antecessor de um elemento

6. Remover/eliminar um elemento

7. Inserir um elemento atrás de um elemento específico

8. Inserir um elemento à frente de um elemento específico

9. Recuperar um elemento

10. Atualizar um elemento

11. Ordenar uma lista

12. Imprimir/listar uma lista

13. Determinar a quantidade de elementos da lista (tamanho/comprimento)

14. Eliminar/destruir uma lista

15. Verificar se existe espaço para outro elemento

Page 4: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

2 A Estrutura Abstrata de Dados “Lista”

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Os tipos de dados podem ser considerados muito abstratos. A regra principal na

estrutura da EAD Lista é utilizar ponteiros. Os elementos numa lista estão sempre ligados

pela sua relação sucessor-antecessor.

Uma EAD Lista pode ser implementada num espaço de armazenamento sequencial ou

num espaço de armazenamento não sequencial. No caso da implementação num espaço

sequencial podem-se usar array's, os quais podem ser declarados usando memória estática

ou memória dinâmica. No caso do armazenamento não sequencial será usado ponteiros e

memória dinâmica.

1.2. Implementação de Listas utilizando armazenamento sequencial

Ao implementar uma estrutura de dados num espaço de armazenamento

sequencial/contínuo, todos os dados na estrutura são mantidos num array. Os array's

podem ser declarados com um tamanho que é fixado quando o programa é escrito, não

podendo ser alterado enquanto o programa está a ser executado, ou então usando memória

dinâmica em que o tamanho não é fixado quando o programa é escrito e vai sendo alterado

à medida que o programa vai sendo executado, consoante as necessidades de espaço de

armazenamento.

No primeiro caso, quando se está a escrever um programa, tem que se decidir qual é

o limite máximo de memória que vai ser necessário para os array's e estabelecer este

limite nas declarações. Se o programa for executado com um pequeno conjunto de dados,

então muito deste espaço nunca vai ser utilizado. Se pelo contrário, o programa for

executado com um grande conjunto de dados, então pode-se esgotar o espaço estabelecido

na declaração e encontrar uma situação de overflow. Isto pode ocorrer mesmo no caso da

memória do computador não estar totalmente usada, devido aos limites iniciais do array

serem muito pequenos.

No segundo caso, apenas é necessário declarar um apontador/ponteiro para uma

variável do mesmo tipo dos elementos da lista, sendo que a lista vai crescendo em

tamanho à medida das necessidades de inserção de elementos na lista. Esta estratégica

tem como grande problema a possibilidade de inexistência um bloco de memória contígua

necessário para receber todos os elementos da lista.

As dificuldades anteriores podem ser ultrapassadas usando outra forma de manter as

estruturas de dados na memória sem usar array's, como é o caso das listas ligadas.

Page 5: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 3

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

No tipo de implementação de Listas usando array's, a definição de cada um dos seus

elementos consiste nos seguintes campos:

1. Dados;

2. Chave (opcional) − campo pelo qual a lista está ordenada;

3. Localização (índice) na lista.

Numa lista L:

− o elemento de índice 0 (primeira posição – L[0]) não tem antecessor;

− o elemento de índice N-1 (última posição – L[N-1]) não tem sucessor;

− o elemento da lista com índice k tem:

- o elemento de índice k-1 como seu antecessor,

- o elemento de índice k+1 como seu sucessor.

1.3. Implementação de Listas utilizando armazenamento não sequencial

Uma Lista implementada num espaço de armazenamento não sequencial, a memória

de cada elemento é reservada individualmente, usando ponteiros e alocação de memória, e

o seu tamanho é atualizado à medida que vão sendo inseridos ou removidos elementos da

Lista. Neste tipo de implementação enquadram-se os conceitos de Lista ligada, usando

ligações simples, duplas ou circulares.

2. Listas - armazenamento sequencial usando memória estática

2.1. Introdução

Na implementação de Listas utilizando armazenamento sequencial em memória

estática, todos os elementos da Lista são mantidos num array (estático), cujo tamanho

máximo é fixado quando o programa é escrito e não pode ser alterado enquanto o

programa está a ser executado. Quando se está a escrever um programa, tem que se

decidir qual é o limite máximo de memória que vai ser necessário para os array's e

estabelecer este limite nas declarações (tamanho máximo), o que implica que o número

de elementos da lista (tamanho efetivo) não pode exceder aquele valor. Se o programa

for executado com um pequeno conjunto de dados (tamanho efetivo muito inferior ao

tamanho máximo), então muito deste espaço nunca vai ser utilizado. Se pelo contrário, o

programa for executado com um grande conjunto de dados, então pode-se esgotar o

espaço estabelecido na declaração (tamanho efetivo superior ao tamanho máximo) e

Page 6: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

4 Listas - armazenamento sequencial usando memória estática

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

encontrar uma situação de overflow. Isto pode ocorrer mesmo no caso da memória do

computador não estar totalmente usada, devido ao limite inicial do array (tamanho

máximo) sere muito baixo.

É importante fazer uma distinção entre estes dois conceitos (tamanho efetivo e

tamanho máximo). Numa lista de N (tamanho efetivo) elementos, pode ser inserido ou

eliminado um elemento, donde, se N = 3 então a lista contém somente 3 elementos

(tamanho efetivo igual a 3), se N = 19 então a lista contém 19 elementos (tamanho efetivo

igual a 3) − o tamanho da lista é variável. De qualquer forma, o valor de N (tamanho

efetivo) não pode ser superior ao valor do tamanho máximo da lista; caso isto não

aconteça, está-se perante uma situação de overflow.

Uma lista é, por definição, uma estrutura dinâmica de dados porque o seu tamanho

pode variar. Um array estático é uma estrutura de dados fixa porque o seu tamanho é

fixado quando o programa é compilado. Os conceitos de lista e array estático estão

relacionados uma vez que uma lista de tamanho variável pode ser implementada usando

um array com tamanho fixo (com algumas posições do array não sendo utilizadas).

Existem diferentes formas de implementar uma lista e não se deve confundir decisões

de implementação com decisões de escolha e especificação da estrutura de dados.

Exemplo: uma lista com no máximo 100 elementos (valores inteiros).

#define TamMax 100;

int Lista[TamMax], N, k;

N = 50;

for (k = 0; k < N; k++)

scanf(“%d”, &Lista[k]);

0 1 2 ... 48 49 50 ... 98 99

9 14 18 ... 27 35 ...

N = 100;

for (k = 50; k < N; k++)

scanf(“%d”, &Lista[k]);

0 1 2 ... 48 49 50 ... 98 99

9 14 18 ... 27 35 51 ... 176 185

N = 102;

for (k = 100; k < N; k++)

scanf(“%d”, &Lista[k]);

// ERRO: Não existem os elementos Lista[100] e Lista[101] (o maior índice é 99) − overflow

Page 7: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 5

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Características fundamentais:

Tamanho máximo: constante (= TamMax) ⇒ ineficiência

Tamanho efetivo: necessário atualizar e testar eventual transbordo (> TamMax)

Acesso: direto

Reordenamento ⇒ trocas (ou vetor auxiliar)

Inserção (e remoção) de elementos ⇒ deslocamento de todos os elementos seguintes

2.2. Declaração

Os elementos de uma lista podem ser declarados (definidos) como um tipo qualquer

simples (por exemplo, inteiro, real ou carácter) ou estruturado (por exemplo, registo).

Como se disse, uma lista pode ser definida como um vetor (array). Portanto, antes de se

definir uma lista tem que se definir os elementos da lista.

Considere-se, por exemplo, as seguintes definições em linguagem C:

#define TamMax 100;

typedef struct {

char Nome[50], Morada[50];

int Idade, BI;

char Sexo[1]; // Masculino(M/m) ou Feminino(F/f)

} ELEMENTO;

ELEMENTO Lista[TamMax];

Nesta lista, cada um dos elementos consiste no seguinte:

Dados − informação associada (Nome, Morada, Idade, BI e Sexo)

Chave − campo que servirá de base para ordenar a lista (por exemplo, o Nome)

Localização − é o índice que lhe está associado (entre 0 e TamMax-1).

Para controlar o tamanho da lista, utiliza-se uma variável global do tipo inteiro.

Considere-se, por exemplo, a seguinte declaração em linguagem C:

int N; { número de elementos (tamanho efetivo) da lista }

Nos capítulos seguintes, sempre que se referir à ordenação da lista, é suposto ser por

ordem crescente do campo Chave, se nada se disser em contrário.

Page 8: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

6 Listas - armazenamento sequencial usando memória estática

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

2.3. Criar um elemento

Quando se pretende inserir um novo elemento numa lista, este deve ser criado antes;

isto é, atribuir valores a todos os campos da estrutura que suporta a informação associada

a cada elemento da lista. Um possível algoritmo para criar um elemento é o seguinte:

Dados de entrada: um elemento E (sem informação)

Dados de saída: o elemento E (preenchido com a informação desejada)

Algoritmo:

Para cada campo do elemento E fazer

Pedir informação

Introduzir informação

Uma função que traduz o algoritmo é a que se segue, a qual recebe um elemento E

sem informação e devolve o elemento E com a informação desejada introduzida.

void CriarElemento (ELEMENTO *E) {

printf(“Insira o nome : \n“);

fflush(stdin); // para esvaziar o buffer do teclado

gets(E->Nome);

printf(“Insira a morada : \n“);

fflush(stdin);

gets(E->Morada);

printf(“Insira a idade : \n“);

scanf(“%d”, &(E->Idade));

printf(“Insira o BI : \n“);

scanf(“%d”, &(E->BI));

printf(“Insira o sexo (M/m; F/f) : \n“);

fflush(stdin);

gets(E->Sexo);

}

Ordem complexidade (pior caso): O(0). Prova: total de operações = 13.

2.4. Criar uma lista

Quando se pretende criar uma lista, isto deve ser realizado com a introdução de um

elemento (que será o primeiro). Um possível algoritmo para criar uma lista é o seguinte:

Page 9: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 7

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Dados de entrada: o tamanho da lista (que é 0)

Dados de saída: a lista L com um só elemento e o seu tamanho (que é 1)

Algoritmo:

Criar um novo elemento

Inserir o novo elemento na posição 1 (índice 0) da lista L (L[0])

Atualizar o tamanho da lista, atribuindo o valor 1 a N

Uma função que traduz o algoritmo é a que se segue, a qual recebe o tamanho da

lista (N = 0) e devolve a lista L com um elemento e o seu tamanho a valer 1.

void CriarLista (ELEMENTO L[], int *N) {

ELEMENTO E;

CriarElemento(&E);

L[0] = E;

*N = 1;

}

Ordem complexidade (pior caso): O(0). Prova: total de operações = 13 (CriarElemento) + 2.

2.5. Lista vazia e lista cheia

Uma lista está vazia quando não tem qualquer elemento e, está cheia quando não

pode receber mais elementos. Para se verificar se a lista está vazia ou cheia, basta analisar

o valor da variável associada ao tamanho da Lista (TAM), da seguinte forma:

N = 0 ⇒ lista vazia

N = TamMax ⇒ lista cheia

Um possível algoritmo para verificar se uma lista está vazia é o seguinte:

Dados de entrada: uma lista L e o tamanho da lista (N)

Dados de saída: 1 (se a lista L está vazia) e 0 (se a lista L não está vazia)

Algoritmo:

Se N = 0 Então

Devolver 1 (a lista L está vazia)

Senão

Devolver 0 (a lista L não está vazia)

Page 10: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

8 Listas - armazenamento sequencial usando memória estática

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Uma função que traduz o algoritmo é a que se segue, a qual recebe o tamanho de

uma lista (N) e, devolve 0 ou 1 consoante a lista L está vazia ou não.

int ListaVazia (int N) {

if (N == 0)

return 1; // a lista está vazia

return 0; // a lista não está vazia

}

Ordem complexidade (pior caso): O(0). Prova: C = 0 e A = 1.

Um possível algoritmo para verificar se uma lista está cheia é o seguinte:

Dados de entrada: uma lista L e o tamanho da lista (N)

Dados de saída: 1 (se a lista L está cheia) e 0 (se a lista L não está cheia)

Algoritmo:

Se N = TamMax

Então devolver 1 (a lista L está cheia)

Senão devolver 0 (a lista L não está cheia)

Uma função que traduz o algoritmo é a que se segue, a qual recebe uma lista (L) e o

seu tamanho (N) e, devolve 0 ou 1 consoante a lista L está cheia ou não.

int ListaCheia (ELEMENTO L[], int N) {

if (N == TamMax)

return 1; // a lista L está cheia

return 0; // a lista L não está cheia }

Ordem complexidade (pior caso): O(0). Prova: C = 0 e A = 1.

2.6. Inserir um elemento numa lista

Na resolução deste problema tem de ser analisadas três situações distintas, consoante

a forma como se pretende inserir o elemento: no início, no fim ou com a lista ordenada. No

entanto, esta operação só deve ser realizada caso a lista não esteja cheia.

2.6.1. Inserir no início

Neste caso tem que se fazer avançar uma posição a todos os elementos para que a

posição 1 (índice 0) fique liberta, para que esta possa receber o novo elemento. Um

possível algoritmo é o que se segue.

Page 11: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 9

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Avançar uma posição todos os elementos da lista L

Inserir X na posição 1 (índice 0)

Atualizar o tamanho da lista, incrementando um valor a N

Uma função que traduz o algoritmo é a que se segue, que recebe o elemento a inserir

(X), uma lista (L) e o tamanho da lista (*N) e, devolve a lista e o seu tamanho atualizados.

void InserirInicio (ELEMENTO X, ELEMENTO L[], int *N) {

int k;

*N = *N + 1; // atualizar o tamanho da lista, incrementando um valor for (k = *N-1; k > 0; k--)

L[k] = L[k-1]; // avançar todos os elementos da lista uma posição

L[0] = X; // colocar o elemento X na 1ª posição (índice 0) }

Ordem complexidade (pior caso): O(N). Prova: C = 0 e A = (N−1) + 2 = N + 1; C + A = N + 1.

2.6.2. Inserir no fim

Aqui apenas é necessário introduzir o novo elemento na posição seguinte à do último

elemento da lista, passando a ser este novo elemento o último da lista. Um possível

algoritmo é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Inserir X na posição N+1 (uma posição à frente do último elemento da lista)

Atualizar o tamanho da lista, incrementando um valor a N

Uma função que traduz o algoritmo é a que se segue, que recebe o elemento a inserir

(X), uma lista (L) e o seu tamanho (*N) e, devolve a lista L e o seu tamanho atualizados.

void InserirFim (ELEMENTO X, ELEMENTO L[], int *N) {

*N = *N + 1; // atualizar o tamanho da lista, incrementando um valor

L[*N-1] = X; // colocar o elemento X na última posição (*N-1) }

Ordem complexidade (pior caso): O(0). Prova: C = 0 e A = 2.

Page 12: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

10 Listas - armazenamento sequencial usando memória estática

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

2.6.3. Inserir por ordem

Este é o caso mais utilizado, começando-se por pesquisar a posição onde inserir o

elemento, para depois se fazer avançar uma posição todos os elementos que se encontram

depois da posição pesquisada, para que esta posição fique liberta para receber o novo

valor. Um possível algoritmo é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho atualizados

Algoritmo: (lista ordenada crescentemente)

Determinar a posição k onde inserir o elemento X

Avançar uma posição todos os elementos da lista L que estão depois da posição k

Inserir X na posição k

Atualizar o tamanho da lista, incrementando N em um valor

A função que traduz o algoritmo é a que se segue, a qual recebe o elemento X a

inserir, a lista L e o tamanho da lista (*N), e devolve a lista e o seu tamanho atualizados.

void InserirOrdem (ELEMENTO X, ELEMENTO L[], int *N) {

int k, pos = 0;

while (pos < *N && L[pos].BI < X.BI)

pos = pos + 1; // o elemento X deve ser inserido na posição pos da lista L

*N = *N + 1; // atualizar o tamanho da lista, incrementando um valor

for (k = *N-1; k > pos; k--)

L[k] = L[k-1]; // avançar uma posição os elementos de L depois de pos

L[pos] = X; // colocar o elemento X na posição pos }

Ordem complexidade (pior caso): O(N). Prova: C = N e A = 1 + k + (N−k) + 2 = 3 + N;

C + A = N + 3 + N = 3 + 2 x N.

2.7. Remover um elemento duma lista

Este problema consiste em remover (eliminar) um dado elemento de uma lista, a qual

pode ou não estar ordenada. No entanto, apesar destas duas situações serem diferentes,

em termos algorítmicos essa diferença encontra-se apenas na forma de pesquisar a posição

do elemento a remover. Para tal, basta apenas aplicar um dos métodos existentes para o

efeito (ver Pesquisa exaustiva − pág. 30), o que mais se adequar à lista: ordenada ou não

Page 13: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 11

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

ordenada. No entanto, esta operação só deve ser realizada caso a lista não esteja vazia.

Desta forma, um possível algoritmo é o que se segue.

Dados de entrada: o elemento X, uma lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

k ← posição do elemento X na lista L (utilizar um dos métodos de pesquisa)

Recuar uma posição todos os elementos posicionados depois da posição k

Atualizar o tamanho da lista, decrementando N em um valor

Uma função que traduz o algoritmo é a que se segue, a qual recebe o elemento a

remover (X), uma lista ordenada (L) e o tamanho da lista (*N) e, devolve a lista e o seu

tamanho atualizados.

int Remover (ELEMENTO X, ELEMENTO L[], int *N) {

int k, pos;

pos = PesquisaExaustiva(X, L, *N);

if (pos == -1) // o elemento a remover, X, não pertence à lista L

return 0; // para indicar que o elemento X não foi removido

for (k = pos; k < *N-1; k++)

L[k] = L[k+1];

*N = *N – 1; // atualizar o tamanho da lista, decrementando um valor

return 1; // para indicar que o elemento X foi removido }

No entanto, a forma mais comum de tratar este problema não é fornecer o elemento

a remover, mas sim apenas a posição daquele elemento, sendo o trabalho de pesquisa da

posição do referido elemento, assim como a verificação se aquele elemento pertence ou

não à lista, feito antes da remoção. Isto é, antes de se remover um elemento duma lista,

verifica-se se aquele elemento pertence à lista e, caso pertença, em que posição se

encontra, o que se pode fazer recorrendo a um dos métodos de pesquisa existentes (ver

Pesquisa dum elemento numa lista, pág. 30). Um possível algoritmo é o que se segue.

Dados de entrada: a posição (Pos) do elemento a remover, a lista L e o seu tamanho (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Recuar uma posição todos os elementos que se encontram em posição após Pos

Atualizar o tamanho da lista, decrementando N em um valor

Page 14: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

12 Listas - armazenamento sequencial usando memória estática

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Uma função que traduz o algoritmo é a que se segue, a qual recebe a posição do

elemento a remover (Pos), uma lista (L) e o tamanho da lista (N) e, devolve a lista L e o

seu tamanho atualizados.

void Remover (int Pos, ELEMENTO L[], int *N) {

int k;

for (k = Pos; k < *N-1; k++)

L[k] = L[k+1]; // recuar uma posição os elementos desde k

*N = *N – 1; // atualizar o tamanho da lista, decrementando a *N um valor }

Ordem complexidade (pior caso): O(N). Prova: C = 0 e A = (N - 1) + 1 = N; C + A = N.

2.8. Listar/mostrar um elemento ou os elementos duma lista

Uma das operações muito úteis é aquela que permite mostrar um ou todos os

elementos de uma lista. Para mostrar apenas um elemento, um possível algoritmo é o

seguinte:

Dados de entrada: um elemento E

Dados de saída: nada (apenas mostra no écran os dados do elemento dado)

Algoritmo:

Para cada campo do elemento E fazer

Mostrar informação

Uma função que traduz o algoritmo é a que se segue, a qual recebe um elemento E e

mostra no écran a informação que lhe está associada.

void MostrarElemento (ELEMENTO E) {

printf(“Nome: “);

puts(E.Nome);

printf(“Morada: “);

puts(E.Morada);

printf(“Idade: %d\n”, E.Idade);

printf(“BI: %d\n”, E.BI);

printf(“Sexo: “);

puts(E.Sexo);

printf(“\n”);

}

Page 15: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória estática 13

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Para mostrar todos os elementos de uma lista, um possível algoritmo é o seguinte:

Dados de entrada: uma lista L e o seu tamanho N

Dados de saída: nada (apenas mostra no écran os dados dos elementos da lista L)

Algoritmo:

Para cada elemento k da lista L fazer

Mostrar o elemento da posição/índice k

Uma função que traduz o algoritmo é a que se segue, a qual recebe uma lista L e o

seu tamanho N e mostra no écran a informação que está associada a cada elemento de L.

void MostrarLista (ELEMENTO L[], int N) {

int k;

for (k = 0; k < N; k++) {

printf(“L[%d]: \n”, k);

MostrarElemento(L[k]);

printf(“\n”);

}

}

2.9. Destruir uma lista

Quando se pretende destruir uma lista, basta atribuir ao seu tamanho o valor nulo

(N = 0). Um possível algoritmo para criar uma lista é o seguinte:

Dados de entrada: o tamanho da lista (N)

Dados de saída: o valor do tamanho da lista a nulo (N = 0)

Algoritmo:

Atualizar ao tamanho da lista o valor 0 (N = 0)

Uma função que traduz o algoritmo é a que se segue, a qual recebe o tamanho da

lista (N = 0) e devolve a lista L com um elemento e o seu tamanho a valer 1.

void DestruirLista (int *N) {

*N = 0;

}

Ordem complexidade (pior caso): O(0).

Page 16: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

14 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

3. Listas - armazenamento sequencial usando memória dinâmica

3.1. Introdução

No caso da utilização de armazenamento sequencial com memória dinâmica, todos os

elementos da Lista são mantidos num array (array dinâmico), cujo tamanho não é fixado

quando o programa é escrito e vai sendo alterado à medida que o programa vai sendo

executado, consoante as alterações que sofrerá. Quando se está a escrever um programa,

apenas terá que se definir uma variável do tipo apontador para um elemento do tipo de

dados da Lista, assim como uma variável para guardar o seu tamanho. Ou seja, no início

apenas se declara um ponteiro que receberá o endereço do primeiro elemento do array (ou

Lista), mas que no início não tem elementos (tamanho nulo). Desta forma, a memória

necessária para armazenar cada elemento do array será reservada à medida que um novo

elemento é inserido na Lista e libertada à medida que os elementos são removidos, sendo

que os elementos são inseridos sequencialmente na memória. No entanto, e no que

respeita às restantes operações a efetuar sobre um array dinâmico (ou Lista), estas são

efetuadas exatamente da mesma forma como é feita sobre um array estático.

Numa lista cada um dos elementos consiste no seguinte:

1. Dados;

2. Chave (opcional) − campo pelo qual a lista está ordenada;

3. Localização (índice) na lista.

Numa lista L:

− O elemento de índice 0 (primeira posição – L[0]) não tem antecessor;

− O elemento de índice N-1 (última posição – L[N-1]) não tem sucessor;

− O elemento da lista com índice k tem:

- o elemento de índice k-1 como seu antecessor,

- o elemento de índice k+1 como seu sucessor.

Numa lista de N elementos, pode ser inserido ou eliminado um elemento, donde, se

N = 3 então a lista contém somente 3 elementos, se N = 19 então a lista contém 19

elementos − o tamanho da lista é variável.

Uma lista é, por definição, uma estrutura dinâmica de dados porque o seu tamanho

pode variar. Um array dinâmico é também uma estrutura de dados variável porque o seu

tamanho é atualizado à medida que o programa vai sendo executado.

Page 17: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória dinâmica 15

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Os conceitos de lista e array dinâmico estão relacionados uma vez que uma lista de

tamanho variável pode ser implementada usando um array com tamanho variável,

aumentando ou diminuindo de tamanho à medida que novos elementos são inseridos ou

elementos são removidos.

Existem diferentes formas de implementar uma lista e não se deve confundir decisões

de implementação com decisões de escolha e especificação da estrutura de dados.

Exemplo: uma lista com 50 elementos (valores inteiros) numa primeira fase, que

passa para 100 elementos numa segunda fase.

int *Lista, N, k;

N = 50;

Lista = (int *) malloc (N*sizeof(int));

for (k = 0; k < N; k++)

scanf(“%d”, &Lista[k]);

0 1 2 ... 48 49

9 14 18 ... 27 35

N = 100;

Lista = (int *) realloc (Lista, N*sizeof(int));

for (k = 50; k < N; k++)

scanf(“%d”, &Lista[k]);

0 1 2 ... 48 49 50 ... 98 99

9 14 18 ... 27 35 51 ... 176 185

N = 102;

Lista = (int *) realloc (Lista, N*sizeof(int));

for (k = 100; k < N; k++)

scanf(“%d”, &Lista[k]);

0 1 2 ... 48 49 50 ... 98 99 100 101

9 14 18 ... 27 35 51 ... 176 185 245 267

Características fundamentais:

Tamanho máximo: variável ⇒ eficiência

Tamanho atual: necessário atualizar (testar disponibilidade de memória antes de

aumentar o tamanho − para inserir novos elementos).

Acesso: direto

Reordenamento ⇒ trocas (ou vetor auxiliar)

Inserção/Remoção de elementos ⇒ deslocamento de todos os elementos seguintes

Page 18: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

16 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

3.2. Declaração

Os elementos de uma lista podem ser definidos como um tipo qualquer simples (por

exemplo, inteiro, real ou carácter) ou estruturado (por exemplo, registo). Como se disse,

uma lista pode ser definida como um vetor (array). Portanto, antes de se definir uma lista

tem que se definir os elementos da lista.

Considere-se, por exemplo, as seguintes definições em linguagem C:

typedef struct {

char Nome[50], Morada[50];

int Idade, BI;

char Sexo[1]; // Masculino(M/m) ou Feminino(F/f)

} ELEMENTO;

ELEMENTO *Lista;

int N;

Nesta lista, cada um dos elementos consiste no seguinte:

1. Dados − informação associada (Nome, Morada, Idade, BI e Sexo)

2. Chave − campo que servirá de base para ordenar a lista (por exemplo, o Nome)

3. N − número de elementos (tamanho) da Lista

4. Localização − é o índice que lhe está associado (entre 0 e N-1).

Nos capítulos seguintes, sempre que se referir à ordenação da lista, é suposto ser por

ordem crescente do campo Chave, se nada se disser em contrário.

3.3. Criar um elemento

Quando se pretende inserir um novo elemento numa lista, este deve ser criado antes;

isto é, atribuir valores a todos os campos da estrutura que suporta a informação associada

a cada elemento da lista. Um possível algoritmo para criar um elemento é o seguinte:

Dados de entrada: nada

Dados de saída: retorna o endereço de um elemento E (criado e preenchido com a

informação desejada localmente, usando alocação de memória)

Algoritmo:

Para cada campo do elemento E fazer

Pedir informação

Introduzir informação

Page 19: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória dinâmica 17

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Uma função que traduz o algoritmo é a que se segue (a função apresentada no

capítulo anterior para o mesmo efeito também serve), a qual recebe um elemento E sem

informação e devolve o elemento E com a informação desejada introduzida.

ELEMENTO *CriarElemento {

ELEMENTO *E;

E = (ELEMENTO *) malloc(sizeof(ELEMENTO));

if (E == NULL)

return NULL;

printf(“Insira o nome : \n“);

fflush(stdin);

gets(E->Nome);

printf(“Insira a morada : \n“);

fflush(stdin);

gets(E->Morada);

printf(“Insira a idade : \n“);

scanf(“%d”, &(E->Idade));

printf(“Insira o BI : \n“);

scanf(“%d”, &(E->BI));

printf(“Insira o sexo (M/m; F/f) : \n“);

fflush(stdin);

gets(E->Sexo);

return E;

}

Ordem complexidade (pior caso): O(0).

3.4. Criar uma lista

Quando se pretende criar uma lista, isto deve ser realizado com a introdução de um

elemento (que será o primeiro). Um possível algoritmo para criar uma lista é o seguinte:

Dados de entrada: o tamanho da lista (que é o)

Dados de saída: a lista L com um só elemento e o seu tamanho (que é 1)

Page 20: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

18 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Algoritmo:

Criar um novo elemento

Inserir o novo elemento na posição 1 (índice 0) da lista L (L[0])

Atualizar o tamanho da lista, atribuindo o valor 1 a N

Uma função que traduz o algoritmo é a que se segue, a qual recebe o tamanho da

lista (N = 0) e devolve a lista L com um elemento e o seu tamanho a valer 1.

ELEMENTO *CriarLista (int *N) {

ELEMENTO *L, *E;

E = CriarElemento();

L = (ELEMENTO *) malloc(sizeof(ELEMENTO));

if (L == NULL)

return NULL;

L[0] = *E;

*N = 1;

free(E);

return L;

}

Ordem complexidade (pior caso): O(0).

3.5. Lista vazia e lista cheia

Uma lista está vazia quando não tem qualquer elemento e, está cheia quando não

existe memória que permita acrescentar mais elementos à lista L.

Para se verificar se uma lista está vazia, basta analisar o valor da variável N (se é

nulo) ou o endereço contido em L (se for NULL, L está vazia), da seguinte forma:

if (N == 0) ou if (L == NULL)

Uma lista está “cheia” se a alocação de memória para mais um elemento não for

conseguida; ou seja:

V = (ELEMENTO *) realloc ((N+1) * sizeof(ELEMENTO))

if (V == NULL)

return 1; // realocação de memória não conseguida (lista cheia)

Uma função para verificar se uma lista está vazia, pode ser um dos dois que se

seguem: o primeiro recebe uma lista (L) e o segundo recebe o tamanho de uma lista (N), e

ambos devolvem o valor 1 (lista vazia) ou 0 (lista não vazia).

Page 21: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória dinâmica 19

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

int ListaVazia1 (ELEMENTO L[]) {

if (L == NULL)

return 1; // lista vazia

return 0; // lista não vazia

}

int ListaVazia2 (int N) {

if (N == 0)

return 1; // lista vazia

return 0; // lista não vazia }

3.6. Inserir um elemento numa lista

Na resolução deste problema tem de ser analisadas três situações distintas, consoante

a forma como se pretende inserir o elemento: no início, no fim ou com a lista ordenada.

3.6.1. Inserir no início

Começa-se por avançar uma posição a todos os elementos da lista, para que a 1ª

posição fique livre para receber o novo elemento. Um possível algoritmo é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Realocar memória para mais um elemento

Avançar uma posição todos os elementos da lista L

Inserir X na posição 1 (índice 0)

Atualizar o tamanho da lista, incrementando um valor a N

Uma função que traduz o algoritmo é a que se segue, que recebe o elemento a inserir

(X), uma lista (L) e o seu tamanho (*N) e, devolve a lista L e o seu tamanho atualizados.

ELEMENTO *InserirInicio (ELEMENTO X, ELEMENTO L[], int *N) {

int k;

L = (ELEMENTO *) realloc(L, (*N+1) * sizeof(ELEMENTO));

if (L == NULL)

return NULL; // impossível alocar memória para um novo elemento

Page 22: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

20 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

*N = *N+1; // atualizar o tamanho da lista, incrementando um valor

for (k = *N-1; k > 0; k--)

L[k] = L[k-1]; // avançar todos os elementos da lista uma posição

L[0] = X; // colocar o elemento X na 1ª posição (índice 0)

return L; // operação com êxito – novo elemento inserido }

Ordem complexidade (pior caso): O(N). Prova: C = 0 e A = (N−1) + 2 = N + 1; C + A = N + 1.

3.6.2. Inserir no fim

Neste caso, apenas é necessário introduzir o novo elemento na última posição da lista

(nova posição). Um possível algoritmo é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Inserir X na posição N+1 (uma posição à frente do último elemento da lista)

Atualizar o tamanho da lista, incrementando-o num valor

Uma função que traduz o algoritmo é a que se segue, que recebe o elemento a inserir

(X), uma lista (L) e o seu tamanho (*N) e, devolve a lista L e o seu tamanho atualizados.

ELEMENTO *InserirFim (ELEMENTO X, ELEMENTO L[], int *N) {

L = (ELEMENTO *) realloc(L, (*N+1) * sizeof(ELEMENTO));

if (L == NULL)

return NULL; // impossível alocar memória para um elemento

*N = *N + 1; // atualizar o tamanho da lista, incrementando um valor

L[*N-1] = X; // colocar o elemento X na última posição (*N-1)

return L; // operação com êxito – novo elemento inserido }

Ordem complexidade (pior caso): O(0). Prova: C = 0 e A = 2.

3.6.3. Inserir por ordem

É o caso mais utilizado. Começa por pesquisar a posição pos onde inserir o novo

elemento; depois, todos os elementos que se posicionem depois de pos avançam uma

posição, ficando a posição pos livre para receber o novo valor. Um possível algoritmo é o

que se segue (considerando a ordenação pelo campo BI).

Page 23: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória dinâmica 21

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Dados de entrada: o elemento X, a lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho atualizados

Algoritmo: (lista ordenada crescentemente)

Determinar a posição k onde inserir o elemento X

Avançar uma posição todos os elementos da lista L que estão depois da posição k

Inserir X na posição k

Atualizar o tamanho da lista, incrementando-o num valor

A função que traduz o algoritmo é a que se segue, a qual recebe o elemento X a

inserir, a lista L e o tamanho da lista (*N), e devolve a lista e o seu tamanho atualizados.

ELEMENTO *InserirOrdem (ELEMENTO X, ELEMENTO L[], int *N) {

int k, pos;

L = (ELEMENTO *) realloc(L, (*N+1) * sizeof(ELEMENTO));

if (L == NULL)

return NULL; // operação sem êxito – impossível inserir novo elemento

pos = 0;

while ((pos < *N) && (L[pos].BI > X.BI))

pos = pos + 1; // o elemento X deve ser inserido na posição pos da lista L

*N = *N + 1; // atualizar o tamanho da lista, incrementando um valor

for (k = *N-1; k > pos; k--)

L[k] = L[k-1]; // avançar uma posição os elementos de L depois de pos

L[pos] = X; // colocar o elemento X na posição pos

return L; // operação com êxito – novo elemento inserido

}

Ordem complexidade (pior caso): O(N). Prova: C = N e A = 1 + k + (N−k) + 2 = 3 + N;

C + A = N + 3 + N = 3 + 2 x N.

3.7. Remover um elemento duma lista

Este problema consiste em remover (eliminar) um dado elemento de uma lista, a qual

pode ou não estar ordenada. No entanto, apesar destas duas situações serem diferentes,

em termos algorítmicos essa diferença encontra-se apenas na forma de pesquisar a posição

do elemento a remover. Para tal, basta apenas aplicar um dos métodos existentes para o

efeito (ver Pesquisa exaustiva − pág. 30), o que mais se adequar à lista: ordenada ou não

ordenada. Desta forma, um possível algoritmo é o que se segue.

Page 24: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

22 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Dados de entrada: o elemento X, uma lista L e o tamanho da lista (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

k ← posição do elemento X na lista L (utilizar um dos métodos de pesquisa)

Recuar uma posição todos os elementos que estão depois do elemento da posição k

Atualizar o tamanho da lista, decrementando N em um valor

Uma função que traduz o algoritmo é a que se segue, a qual recebe o elemento a

remover (X), uma lista ordenada (L) e o tamanho da lista (*N) e, devolve a lista e o seu

tamanho atualizados.

ELEMENTO *Remover (ELEMENTO X, ELEMENTO L[], int *N) {

int k, pos, *aux;

pos = PesquisaExaustiva(X, L, *N);

if (pos != -1) { // o elemento a remover, X, pertence à lista L

for (k = pos; k < *N-1; k++)

L[k] = L[k+1];

*N = *N – 1; // atualizar o tamanho da lista, decrementando um valor

L = (ELEMENTO *) realloc(L, (*N) * sizeof(ELEMENTO)); //atualizar lista

}

return L;

}

No entanto, a forma mais comum de tratar este problema não é fornecer o elemento

a remover, mas sim apenas a posição daquele elemento, sendo o trabalho de pesquisa da

posição do referido elemento, assim como a verificação se aquele elemento pertence ou

não à lista, feito antes da remoção. Isto é, antes de se remover um elemento duma lista,

verifica-se se aquele elemento pertence à lista e, caso pertença, em que posição se

encontra, o que se pode fazer recorrendo a um dos métodos de pesquisa existentes (ver

Ordenação duma lista, pág. 25). Assim sendo, um possível algoritmo é o que se segue.

Dados de entrada: a posição (Pos) do elemento a remover, a lista L e o seu tamanho (N)

Dados de saída: a lista L e o seu tamanho (N) atualizados

Algoritmo:

Recuar uma posição todos os elementos que se encontram em posição após Pos

Atualizar o tamanho da lista, decrementando N em um valor

Page 25: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Listas - armazenamento sequencial usando memória dinâmica 23

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Uma função que traduz o algoritmo é a que se segue, a qual recebe a posição do

elemento a remover (Pos), uma lista (L) e o tamanho da lista (N) e, devolve atualizados a

lista L e o seu tamanho.

ELEMENTO *Remover (int Pos, ELEMENTO L[], int *N) {

int k;

for (k = Pos; k < *N-1; k++)

L[k] = L[k+1]; // recuar uma posição os elementos desde k

*N = *N – 1; // atualizar o tamanho da lista, decrementando a N um valor

L = (ELEMENTO *) realloc(L, (*N) * sizeof(ELEMENTO)); // atualizar lista

return L;

}

Ordem complexidade (pior caso): O(N). Prova: C = 0 e A = (N - 1) + 1 = N; C + A = N.

3.8. Listar/mostrar um elemento ou os elementos duma lista

Uma das operações muito úteis é aquela que permite mostrar um ou todos os

elementos de uma lista.

Para mostrar apenas um elemento, um possível algoritmo é o seguinte:

Dados de entrada: um elemento E

Dados de saída: nada (apenas mostra no écran os dados do elemento dado)

Algoritmo:

Para cada campo do elemento E fazer

Mostrar informação

Uma função que traduz o algoritmo é a que se segue, a qual recebe um elemento E e

mostra no écran a informação que lhe está associada.

void MostrarElemento (ELEMENTO E) {

printf(“Nome: “);

puts(E.Nome);

printf(“Morada: “);

puts(E.Morada);

printf(“Idade: %d\n”, E.Idade);

printf(“BI: %d\n”, E.BI);

printf(“Sexo: “);

puts(E.Sexo);

Page 26: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

24 Listas - armazenamento sequencial usando memória dinâmica

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

printf(“\n”);

}

Ordem complexidade (pior caso): O(0). Prova: total de operações = 9.

Para mostrar todos os elementos de uma lista, um possível algoritmo é o seguinte:

Dados de entrada: uma lista L e o seu tamanho N

Dados de saída: nada (apenas mostra no écran os dados dos elementos da lista L)

Algoritmo:

Para cada elemento k da lista L fazer

Mostrar o elemento da posição/índice k

Uma função que traduz o algoritmo é a que se segue, a qual recebe uma lista L e o

seu tamanho N e mostra no écran a informação que está associada a cada elemento de L.

void MostrarLista (ELEMENTO L[], int N) {

int k;

for (k = 0; k < N; k++) {

printf(“L[%d]: \n”, k);

MostrarElemento(L[k]);

printf(“\n”);

}

}

Ordem complexidade (pior caso): O(N). Prova: total de operações = N x (1 + 9 + 1) = 11xN.

3.9. Destruir uma lista

Quando se pretende destruir uma lista, basta realocar memória para 0 elementos e

atribuir ao seu tamanho o valor nulo (N = 0). Um possível algoritmo para criar uma lista é o

seguinte:

Dados de entrada: a lista L e o seu tamanho (N)

Dados de saída: a lista L sem elementos e o seu tamanho a valer 0 (N = 0)

Algoritmo:

Realocar memória para 0 elementos

Atualizar ao tamanho da lista o valor 0 (N = 0)

Uma função que traduz o algoritmo é a que se segue, a qual recebe a lista L a destruir

e o seu tamanho (N), devolve a lista L vazia e o seu tamanho a valer 0.

Page 27: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Ordenação duma lista 25

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

ELEMENTO *DestruirLista (ELEMENTO *L, int *N) {

L = (ELEMENTO *) realloc(0*sizeof(ELEMENTO));

*N = 0;

L = NULL;

return L;

}

Ordem complexidade (pior caso): O(0).

4. Ordenação duma lista

Este problema consiste em ordenar uma lista que se encontra desordenada. Existem

vários métodos para conseguir atingir tal objetivo, dos quais se destacam os seguintes:

Seleção Linear, Bubblesort, Quicksort e Inserção. Os algoritmos associados aos dois

primeiros são versões iterativas e os associados aos dois restantes são versões recursivas.

No estudo destes métodos vai-se admitir, tal como nos anteriores, que se pretende ordenar

a lista crescentemente, relativamente ao campo Chave (que pode ser, por exemplo, o

campo BI).

4.1. Seleção Linear

Este método consiste em determinar o menor elemento e colocá-lo na posição 1

(índice 0), depois determinar o segundo menor elemento e colocá-lo na posição 2 (índice

1), e assim sucessivamente até colocar o maior elemento na posição N (índice N-1). Um

possível algoritmo é o que se segue.

Dados de entrada: uma lista L e o tamanho da lista (N)

Dados de saída: a lista L ordenada (alguns elementos estão em posições diferentes)

Algoritmo:

Para cada posição da lista L (k = 0, …, N-1) Fazer

menor ← posição do k-ésimo menor elemento da lista L

Trocar o elemento da posição menor com o da posição k

Uma função que traduz o algoritmo é a que se segue, que recebe uma lista não

ordenada (L) e o tamanho da lista (N) e, devolve a lista L (apesar do tamanho ser o mesmo,

alguns dos elementos mudaram de posição) atualizada.

Page 28: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

26 Ordenação duma lista

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

void OrdenarSeleccaoLinear (ELEMENTO L[], int N) {

int k, j, menor;

ELEMENTO aux;

for (k = 0; k < N; k++) {

menor = k; // inicialmente o menor elemento está na posição i

for (j = k+1; j < N; j++) // percorrer os elementos desde a posição i+1

if (L[menor].BI > L[k].BI)

menor = j; // atualizar menor, pois encontrou-se um elemento menor

aux = L[menor]; // trocar os elementos das posições menor com o de i

L[menor] = L[k];

L[k] = aux; } }

Ordem de complexidade (pior caso): O(N2).

Prova:

k = 0; j = 1,… , N-1 ⇒ N-1 comparações

k = 1; j = 2,… , N-1 ⇒ N-2 comparações

...

k = N-2; j = N-1,… , N-1 ⇒ N-(N-1) = 1 comparação

Logo, o número de comparações é o seguinte:

C = (N-1) + (N-2) + … + 1 = )1N(2

1)1N(−⋅

+− =

2

)1N(N −⋅ =

2NN2

Por outro lado, o número de atribuições é o seguinte:

A = 4.(N-1) + C { for k + for j }

Concluindo, temos que o número total de operações é o seguinte:

C + A = 2

NN2−

+ 4(N − 1) + 2

NN2−

= 22

NN2−

+ 4.N - 4 = N2 + 3.N - 4

Ou seja, o algoritmo é da ordem de complexidade de N2.

4.2. Bubblesort

Este método consiste em fazer trocas sucessivas entre dois elementos seguidos

(vizinhos), de forma que o maior fique depois do menor. Este processo (iteração) é

repetido percorrendo todos os elementos da lista as vezes que for necessário, até que não

se faça qualquer troca, o que acontece quando a lista está ordenada. Note-se que após a

Page 29: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Ordenação duma lista 27

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

iteração k se concluir, os k-últimos elementos ficam ordenados (após a conclusão da 1ª

iteração, o maior elemento fica na N-ésima (última) posição, e assim sucessivamente);

portanto, cada iteração apenas vai analisar até uma determinada posição. Um possível

algoritmo é o que se segue.

Dados de entrada: uma lista L e o tamanho da lista (N)

Dados de saída: a lista L modificada (apesar do tamanho e dos elementos se manter,

alguns elementos estão em posições diferentes)

Algoritmo:

limite ← N // inicialmente vai-se analisar até à posição N

Fazer

trocas ← 0

Para cada posição da lista L (k = 1, …, limite−1) Fazer

Se L[k+1].Chave < L[k].Chave Então

Trocar o elemento da posição k+1 com o da posição k

trocas ← trocas + 1

limite ← limite – 1 // o elemento que está na posição limite está ordenado

Enquanto (trocas > 0) // termina quando o teste “Se…Então…” for sempre falso

Uma função que traduz o algoritmo é a que se segue, a qual recebe uma lista não

ordenada (L) e o tamanho da lista (N) e, devolve atualizado a lista L (apesar do tamanho

ser o mesmo, alguns dos elementos mudaram de posição).

void OrdenarBubblesort (ELEMENTO L[], int N) {

int k, limite = N-1, trocas; // inicialmente vai-se analisar até à posição N-1

ELEMENTO aux;

do {

trocas = 0;

for (k = 0; k < limite; k++)

if (L [k+1].BI < L[k].BI) {

aux = L[k+1];

L[k+1] = L[k];

L[k] = aux;

trocas = trocas + 1; }

limite = limite − 1; // o elemento da posição limite está na posição correta

} while (trocas = 0);

}

Page 30: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

28 Ordenação duma lista

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Ordem complexidade (pior caso − lista ordenada pela ordem inversa): O(N2).

Prova:

1ª iteração: k = 0,…, N-2 ⇒ N-1 comparações

2ª iteração: k = 0,…, N-3 ⇒ N-2 comparações

...

k-ésima iteração: k = 0,…, N-k ⇒ k comparações ...

N-ésima iteração (última): k = 0,…, 0 ⇒ 1 comparação

Logo, o número de comparações é o seguinte:

C = (N-1) + (N-2) + … + 1 = )1N(2

1)1N(−⋅

+− =

2

)1N(N −⋅ =

2NN2

Por outro lado, o número de atribuições é o seguinte:

A = (N-1) + 4.C { do…while + for k }

Concluindo, temos que o número total de operações é o seguinte:

C + A = 2

NN2− + (N-1) + 4.

2NN2

− = 5.2

NN2− + N-1 =

22N3N5 2

−−

Ou seja, o algoritmo é da ordem de complexidade de N2.

4.3. Quicksort

Este método consiste em considerar um elemento qualquer da lista, o pivot, que

poderá ser o primeiro da lista, e deslocá-lo para a sua posição na lista ordenada, colocando

todos os elementos menores do que o pivot à sua esquerda e os maiores à sua direita (se

ordenação por ordem crescente). Este processo é aplicado recursivamente às sublistas que

ainda não estejam ordenadas. Um possível algoritmo é o que se segue.

Dados de entrada: uma lista L e as posições do início e do fim da lista

Dados de saída: a lista L ordenada

Algoritmo:

pivot ← elemento que está na posição inicio da lista

Fazer:

Procurar um elemento maior do que o pivot (posição k)

Procurar um elemento à direita de k que seja menor do que o pivot

Trocar os dois elementos encontrados antes, entre si

Enquanto for possível fazer alguma troca

Trocar o pivot com o elemento menor do que ele que se encontra mais afastado

Page 31: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Ordenação duma lista 29

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Se existe alguma sublista à esquerda do pivot que não está ordenada Então

Aplicar recursivamente o método àquela sublista

Se existe alguma sublista à direita do pivot que não está ordenada Então

Aplicar recursivamente o método àquela sublista

Uma função que traduz o algoritmo é a que se segue, que recebe uma lista não

ordenada (L) e o tamanho da lista (N) e, devolve atualizado a lista L (apesar do tamanho

ser o mesmo, alguns dos elementos mudaram de posição).

void Trocar (int *a, int *b) {

int aux;

aux = *a;

*a = *b;

*b = aux;

}

int DeterminarPivot (int V[], int inicio, int fim) {

int i, k = inicio; // k = posição do pivot V[inicio]

for (i = inicio+1; i <= fim; i++)

if (V[i].BI < V[inicio].BI) {

k++;

Trocar(&V[i], &V[k]);

}

Trocar(&V[inicio], &V[k]);

return k;

}

void OrdenarQuicksort (int V[], int inicio, int fim) {

int k; // k = posição do pivot V[inicio]

if (inicio < fim) {

k = DeterminarPivot(V, inicio, fim);

OrdenarQuicksort(V, inicio, k-1);

OrdenarQuicksort(V, k+1, fim);

} }

Page 32: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

30 Pesquisa dum elemento numa lista

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

4.4. Por Inserção

Este método consiste em tomar um dos elementos da lista, ordenar a sublista

composta pelos restantes elementos e depois inserir o elemento tomada antes na sublista

ordenada. Este processo é executado recursivamente e utilizando o algoritmo para inserir

um elemento numa lista ordenada estudado antes (ver Inserir um elemento numa lista −

pág. 8). Um possível algoritmo é o que se segue.

Dados de entrada: uma lista L e o tamanho da lista (N)

Dados de saída: a lista L ordenada (alguns elementos estão em posições diferentes)

Algoritmo:

Se N > 1 Então

Ordenar a sublista de L com tamanho N-1 (do índice 0 ao índice N-2)

Inserir o elemento no índice N-1, L[N-1], na sublista ordenada no passo anterior

Uma função que traduza o algoritmo é a que se segue, que recebe uma lista não

ordenada (L) e o tamanho da lista (N) e, devolve atualizado a lista L (apesar do tamanho

ser o mesmo, alguns dos elementos mudaram de posição).

void OrdenarPorInsercao (ELEMENTO L[], int N) {

int k;

if (N > 1) {

k = N-1;

OrdenarPorInsercao(L, k);

InserirOrdem(L[N-1], L, k);

} }

5. Pesquisa dum elemento numa lista

Dada uma lista, pretende-se determinar se um dado elemento pertença à lista; se

sim, deve-se indicar a posição na lista. A lista pode estar desordenada ou ordenada.

5.1. Pesquisa exaustiva

Este método aplica-se quando a lista não se encontra ordenada, pois a pesquisa do

elemento terá que ser feita exaustivamente (percorrendo e analisando todos os elementos

da lista). Um possível algoritmo para este caso é o que se segue.

Page 33: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Pesquisa dum elemento numa lista 31

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Dados de entrada: o elemento X, a lista L não ordenada e o tamanho da lista (N)

Dados de saída: a posição de X na lista L (se X ∈ L)

Algoritmo:

Para cada elemento de L, L[k] com k = 0, …, N-1, Fazer

Se X.Chave = L[k].Chave Então

Terminar pesquisa (L[k] tem como chave o mesmo valor de X)

Senão

Avançar mais uma posição da lista L

Se X ∈ L Então

Devolver k (posição do elemento de L com a mesma Chave de X)

Senão

Devolver -1 (não foi encontrado qualquer elemento com a mesma Chave de X)

Uma função que traduz o algoritmo é a que se segue, a qual devolve a posição do

elemento X (se X ∈ L) ou -1 (se X ∉ L).

int PesquisaExaustiva (ELEMENTO X, ELEMENTO L[], int N) {

int k = 0;

while ((k < N) && (L[k].BI != X.BI))

k = k + 1;

if (k < N)

return k;

return -1; }

Ordem complexidade (pior caso): O(N).

5.2. Pesquisa Sequencial

Este método aplica-se a listas ordenadas, em que a pesquisa do elemento é feita a

partir do início da lista e sequencialmente, terminando quando o elemento for encontrado

ou quando o valor da Chave do elemento a pesquisar for superior ao da Chave do elemento

a analisar. Um possível algoritmo é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista L (N)

Dados de saída: a posição de X na lista L (se X ∈ L) ou zero (se X ∉ L)

Page 34: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

32 Pesquisa dum elemento numa lista

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Algoritmo:

Para cada elemento de L, L[k] com k = 0, …, N-1, Fazer

Se X.Chave = L[k].Chave Então

Terminar pesquisa (L[k] tem como chave o valor X)

Senão

Se X.Chave > L[k].Chave Então

Terminar pesquisa (nenhum elemento da lista L tem a Chave de X)

Senão

Avançar mais uma posição da lista L

Se X ∈ L Então

Devolver k (posição do elemento de L com a mesma Chave de X)

Senão

Devolver -1 (não foi encontrado qualquer elemento com a mesma Chave de X)

Uma função que traduz o algoritmo é a que se segue, a qual devolve a posição do

elemento X (se X ∈ L) ou -1 (se X ∉ L).

int PesquisaSequencial (ELEMENTO X, ELEMENTO L[], int N) {

int k = 0;

while ((k < N) && (L[k].BI <= X.BI))

k = k + 1;

if (k < N && (L[k].BI == X.BI)

return k;

return -1;

}

Ordem complexidade (pior caso): O(N). Prova: C1 = N, C2 = N e A = 1; C + A = 2 N + 1

5.3. Pesquisa Binária

Neste método a lista deverá estar ordenada, sendo que a pesquisa do elemento na

lista é feita através da análise de sublistas. Isto é, pega-se no elemento que se encontra ao

meio da lista e verifica-se se o elemento a pesquisar está para a direita ou para a esquerda

deste elemento; se está à esquerda, vai-se pesquisar apenas a sublista à esquerda deste

elemento, caso contrário, pesquisa-se apenas a sublista à direita. O processo termina

quando o elemento for encontrado ou quando a sublista onde se vai pesquisar o elemento

não tem elementos, o que significa que o elemento a pesquisar não pertence à lista.

Page 35: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Pesquisa dum elemento numa lista 33

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

A implementação deste método pode ser feita de duas formas distintas ou versões:

iterativa e recursiva. Um possível algoritmo para a versão iterativa é o que se segue.

Dados de entrada: o elemento X, a lista L e o tamanho da lista L (N)

Dados de saída: a posição de X na lista L (se X ∈ L) ou -1 (se X ∉ L)

Algoritmo:

esq ← 1

dir ← N

Enquanto (esq ≤ dir) Fazer { esq ≤ dir ⇒ L ≠ ∅ }

meio ← posição do elemento que está ao meio da lista − (esq + dir) / 2

Se X.Chave = L[meio].Chave Então

X encontra-se na posição meio

Terminar pesquisa

Senão

Se X.Chave < L[meio].Chave Então

dir ← meio - 1

Senão

esq ← meio + 1

Se (esq > dir) Então

X ∉ L (nenhum elemento de L tem a mesma Chave de X)

Uma função que traduz o algoritmo é a que se segue, a qual devolve a posição do

elemento X (se X ∈ L) ou -1 (se X ∉ L).

int PesquisaBinaria (ELEMENTO X, ELEMENTO L[], int N) {

int esq = 0, dir = N-1, meio;

while (esq <= dir) {

meio = (esq + dir) / 2;

if (X.BI == L[meio].BI)

return meio; //elemento na posição meio de L com mesmo Nome de X

else

if (X.BI < L[meio].BI)

dir = meio − 1;

else

esq = meio + 1; }

return -1; // nenhum elemento da lista L tem a mesma Chave de X }

Page 36: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

34 Pesquisa dum elemento numa lista

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Ordem complexidade (pior caso − X ∉ L e X < L[1]): O(log2 N).

Prova:

1ª comparação: [1..N] = [1..N/20]

2ª comparação: [1..N/2] = [1..N/21]

3ª Comparação: [1..(N/2)/2] = [1..N/4] = [1..N/22]

...

k-ésima comparação: [1..N/2k−1]

Na última comparação: [1..1]. Ou seja, N/2k−1 = 1.

Logo, 2k−1 = N ⇒ k - 1 = log2 N ⇔ k = log2 N + 1

Assim sendo, o número de comparações é o seguinte: C = log2 N + 1.

O número de atribuições está associado ao número de comparações: por cada

comparação são efetuadas 2 atribuições (meio e dir/esq). Logo, A = 2.(log2 N + 1).

Concluindo,

C + A = (log2 N + 1) + (2.(log2 N + 1)) = 3.log2 N + 3.

Ou seja, o algoritmo é da ordem de complexidade de log2 N.

Um possível algoritmo para a versão recursiva é o que se segue.

Dados de entrada: o elemento X, a lista L e as posições dos elementos mais à esquerda

(esq) e mais à direita (dir) na lista L

Dados de saída: a posição de X na lista L (se X ∈ L) ou zero (se X ∉ L)

Algoritmo:

Se esq > dir Então { esq > dir ⇒ L = ∅ }

X ∉ L pesquisa de X numa lista vazia

STOP terminar pesquisa

meio ← posição do elemento que está ao meio da lista − (esq + dir) / 2

Se X.Chave = L[meio].Chave Então

X encontra-se na posição meio

Senão

Se X.Chave < L[meio].Chave Então

Pesquisar X na lista L entre as posições esq e meio−1

Senão

Pesquisar X na lista L entre as posições meio+1 e dir

Page 37: Capitulo 2 - Listas com armazenamento sequencialcbarrico/Disciplinas/ProgramacaoAlgoritmos... · 2015-02-15 · pela sua relação sucessor-antecessor. ... 98 99 9 14 18 ... 27 35

Pesquisa dum elemento numa lista 35

Cap. 2 - Estrutura de Dados sequencial com armazenamento sequencial

Uma função que traduz o algoritmo é a que se segue, a qual devolve a posição do

elemento X (se X ∈ L) ou -1 (se X ∉ L).

int PesquisaBinariaRec (ELEMENTO X, ELEMENTO L[], int esq, dir) {

int i, meio;

if (esq > dir)

return -1; // pesquisar X numa lista vazia ⇒ X ∉ L

meio = (esq + dir) / 2;

if (X.BI == L[meio].BI)

return meio;

else

if (X.BI < L[meio].BI)

return PesquisaBinariaRec(X, L, esq, meio-1);

else

return PesquisaBinariaRec(X, L, meio+1, dir);

}

Ordem complexidade (pior caso): O(log2 N) (tal como na versão iterativa, fazendo a mesma

análise).