Você está na página 1de 7

Listas Lineares

Listas Lineares

Armazena informações referentes a um conjunto de


elementos relacionados de alguma forma (e.g. itens de
Estruturas de Dados I estoque, funcionários de uma empresa, etc.)

Conjunto de n ≥ 0 nós L[1] , L[2] , ... , L[n] tq

L[1] é o primeiro nó ( se n > 0 )


Instituto Multidisciplinar L[k] é precedido por L[k-1], ( para 1 < k ≤ n )
Universidade Federal Rural do Rio de Janeiro

Listas Lineares Listas Lineares

Operações Casos Especiais de Listas Lineares

Busca por um elemento Pilha: inserções e remoções apenas em uma extremidade


Inclusão Fila: inserções em um extremo e remoções em outro
Remoção
Outras Operações
Percurso por todos os nós Tipos de Armazenamento - Lista Linear
Alteração de um elemento
Alocação Sequencial: As posições na memória de dois nós
Combinação de duas ou mais listas
consecutivos na lista são contíguas
Ordenação da lista
Alocação Encadeada: As posições na memória de dois nós
Determinação do primeiro elemento (ou último)
consecutivos na lista não são contíguas
Determinação da cardinalidade

Listas Lineares Listas Lineares

Alocação Sequencial Alocação Sequencial

Forma simples de se manter uma lista linear em memória


Lista Ordenada:
O endereço do ( j + 1)-ésimo nó da lista está b unidades
adiante do endereço correspondente ao j-ésimo nó (b é o Lista na qual seus nós estão ordenados segundo os valores
número de palavras de memória que cada nó ocupa) de suas chaves
Pode ser utilizada na implementação de pilhas e filas
Lista Não Ordenada:
Consideraremos cada nó:
Lista na qual seus nós estão ordenados
- constituído por campos que armazenam
propriedades dos elementos da lista
Obs.: Podemos ordenar uma lista (array) em tempo
- possuindo um identificador chamado chave, O(n log n).
como um dos campos do nó (todas as chaves
sendo distintas) (Merge Sort)

1
Busca Linear Busca Linear

Busca Sequencial
Algoritmo // Busca de um elem na lista L

Apresentaremos um algoritmo de busca de um nó em uma função busca(x)


lista L . i = 1;
A variável x corresponde à chave do nó procurado. b = 0;
enquanto i ≤ n faça
No final da execução do algoritmo, a função busca informa
se x = L[i].chave então
o índice do nó que se deseja buscar.
b = i;
Se o nó não for encontrado, o índice é nulo. i = i + 1;
senão
O algoritmo é como segue. i = i + 1;
busca = b;

Busca Linear Busca Linear

Como um segundo algoritmo, temos A complexidade de pior caso do algoritmo é O(n).


Algoritmo // Busca de um elem na lista L
Para a complexidade de caso médio, considere
função busca(x)
z = probabilidade de sucesso no resultado da busca.
L[n+1].chave = x; // Sentinela
i = 1; Consideraremos idênticas as probabilidades de a chave
enquanto L[i].chave ≠ x faça procurada ser encontrada em posições distintas da lista.
i = i + 1;
O algoritmo só reconhece n + 1 entradas distintas:
se i ≠ n+1 então
busca = i; entradas em que a chave se encontra na k-ésima posição
senão (1 ≤ k ≤ n)
busca = 0; entradas em que a chave não se encontra na lista

Busca Linear Busca Linear

Seja A complexidade média é dada por


Ek = uma entrada em que a chave procurada se encontra na
k-ésima posição (1 ≤ k ≤ n) da lista (n + 1)(2 - z)/2.
E0 = uma entrada tal que a chave não se encontra na lista

As probabilidades das entradas são:


p(Ek) = z , para 1 ≤ k ≤ n Exercício: Demonstre esta expressão para a complexidade
n média.
p(E0) = 1 - z

Observamos que o número de passos dados pelo Algoritmo é


t(Ek) = k , para 1 ≤ k ≤ n ,
t(E0) = n + 1.

2
Busca Linear Busca Linear

Busca Ordenada Busca ordenada


Algoritmo // Busca de um elem na lista L
Consideraremos uma lista ordenada. função buscaord(x)
L[n+1].chave = x; // Sentinela
Se a chave não se encontra na lista, podemos ter esta
informação sem ser necessário percorrê-la até o fim. i = 1;
enquanto L[i].chave < x faça
De forma análoga ao algoritmo anterior, atribuiremos o valor i = i + 1;
procurado ao último elemento da lista. se i = n+1 ou L[i].chave ≠ x então
buscaord = 0;
O algoritmo é como segue. senão
buscaord = i;

Busca Linear Busca Linear

A complexidade de pior caso do algoritmo é O(n). O número t de passos dados pelo algoritmo em uma busca
sem sucesso é variável.
Para a complexidade de caso médio, considere Consideraremos idênticas as probabilidades de ocorrência
z = probabilidade de sucesso no resultado da busca. dos diferentes valores de t .
Consideraremos idênticas as probabilidades de a chave
procurada ser encontrada em posições distintas da lista. A expressão da complexidade média do algoritmo é dada por

O algoritmo reconhece 2n + 1 entradas distintas: (n - z + 2)/2.


entradas em que a chave se encontra na k-ésima posição
Exercício: Demonstre esta expressão para a complexidade
(1 ≤ k ≤ n)
média.
entradas em que a chave não se encontra na lista (n + 1 pos.)

Busca Binária Busca Binária

Busca Binária Busca Binária


Algoritmo
Consideraremos uma lista ordenada. função buscabinaria(x)
inf = 1; sup = n;
Inicialmente, pesquisamos o valor de x no nó do meio da
lista. enquanto inf ≤ sup faça
meio = (inf + sup)/2;
Se os valores não coincidem, então metade da lista pode ser se L[meio].chave = x então
desconsiderada nos passos seguintes. O valor se encontra buscabinaria = meio;
ou na metade inferior ou na metade superior.
inf = sup + 1;
O procedimento é aplicado sucessivamente. senão se L[meio].chave < x então
inf = meio + 1;
O algoritmo é como segue.
senão sup = meio - 1;

3
Busca Binária Busca Binária

O algoritmo examina as posições L[inf] ... L[sup]. O número de iterações é, no máximo, 1 +  log n .

A cada iteração do laço, o algoritmo elimina metade das Cada iteração do laço pode ser executada em tempo
posições que ainda faltam. constante.
No pior caso, o tamanho da lista (tabela) é dado por: Desta forma, a complexidade da Busca Binária é dada por
O(log n).
Iteração 1 : tamanho n n
Iteração 2 : tamanho n/2 ~n/2
Iteração 3 : tamanho (n/2)/2 ~n/4
...
Iteração m : tamanho 1 ~n/2m

Listas Lineares - Inserção e Remoção Listas Lineares - Inserção e Remoção

Inserção e Remoção Inserção

Utilizamos um algoritmo de busca em tais operações quando: Consideramos o espaço de memória disponível de B.
Não se conhece a posição do elemento a ser removido Algoritmo
A lista não pode conter elementos repetidos se n < B então
Se temos uma lista (array) ordenada e desejamos preservar se busca(x) = 0 então
tal ordem, devemos deslocar elementos da lista para a L[n+1] = novovalor;
criação ou remoção de um elemento. n = n+1;
senão imprimir(“elemento já existe”);
Se a lista é não ordenada, temos:
senão imprimir(“erro”);
Inserção: Adição do elemento no final do array
Remoção: Desloc. do último elemento para a posição removida

Listas Lineares - Inserção e Remoção Listas Lineares - Inserção e Remoção

Remoção Remoção
Os algoritmos possuem complexidade de pior caso O(n).
Algoritmo Algoritmo
Temos:
se n ≠ 0 então se n ≠ 0 então
indice = busca(x); Vetor Ordenado
indice = busca(x); Vetor Não Ordenado
se indice ≠ 0 então se ≠ 0 então
indice (Binária)
Busca O(log n) Sequencial O(n)
valorrecuperado = L[indice]; valorrecuperado
Inserção O(n) = L[indice]; O(1)
para indice ≤ i ≤ n-1 faça para indice O(n)
Remoção ≤ i ≤ n-1 faça O(1)
L[i] = L[i+1]; L[i] = L[i+1];
n = n - 1; n = n - 1;
senão imprimir(“elemento inexistente”); senão imprimir(“elemento inexistente”);
senão imprimir(“erro”); senão imprimir(“erro”);

4
Algoritmos de Ordenação Algoritmos de Ordenação

Algoritmos de ordenação de um array: Os subproblemas são resolvidos recursivamente e, em


seguida, as soluções encontradas são combinadas para
Merge Sort obter-se uma solução para o problema original.
Quicksort

Esta abordagem envolve três passos em cada nível da


recursão:
Divisão e Conquista
Divisão do problema em um determinado número de
Abordagem utilizada no projeto de algoritmos. subproblemas.
Conquista dos subproblemas de forma recursiva. Se os
Nesta abordagem temos o desmembramento do problema subproblemas forem de um tamanho pequeno o suficiente,
em vários subproblemas semelhantes ao problema original, poderão ser resolvidos de forma direta.
porém menores em tamanho. Combinação das soluções encontradas para a obtenção da
solução para o problema original.

Algoritmos de Ordenação Algoritmos de Ordenação

Merge-Sort Merge

Algoritmo de ordenação de um array, com abordagem de Procedimento auxiliar do Merge-Sort.


divisão e conquista: A = array
Divisão do array original de tamanho n em 2 subarrays de p, q, r = indices do array tais que p ≤ q < r
tamanho n/2 cada.
Conquista , envolvendo a ordenação dos 2 subarrays
Considerando os dois sub-arrays A[p...q] e A[q+1 ... r]
recursivamente, utilizando Merge-Sort. ordenados, o procedimento os intercala para formar um
Combinação dos 2 subarrays ordenados para a obtenção do sub-array ordenado a ser armazenado em A[p ... r].
array de tamanho n ordenado.
O procedimento requer tempo Θ(n), onde n = r - p + 1.
Obs.: Não chamamos a recursão quando o array a ser ordenado
possuir tamanho 1.

Algoritmos de Ordenação Algoritmos de Ordenação

Merge Merge-Sort
MergeSort(Array A, int p , int r) {
Merge(Array A, int p , int q, int r) { if(p < r){
n1 = q - p + 1; n2 = r - q; q = (p + r)/2;
criar_array(L); MergeSort(A, p, q);
criar_array(R); MergeSort(A, q+1, r);
for(i=1;i<=n1;i++) L[i] = A[p + i - 1]; Merge(A, p, q, r);
for(j=1;j<=n2;j++) R[j] = A[q + j]; }
L[n1 + 1] = ∞; }
R[n2 + 1] = ∞;
i = j = 1; 3 5 6 7 8 9 10 16
Exemplo:
for(k=p;k<=r;k++){ Entrada: 5 7 9 16 3 6 8 10
if(L[i]<=R[j]) { A[k] = L[i]; i++; } [ 16, 9, 7, 5, 10, 8, 3, 6 ]
else { A[k] = R[j]; j++; } Saída: 9 16 5 7 8 10 3 6
} [ 3, 5, 6, 7, 8, 9, 10, 16 ]
}
16 9 7 5 10 8 3 6

5
Algoritmos de Ordenação Algoritmos de Ordenação
Merge-Sort - Análise Considerando
Teorema 1 (Master Theorem): Sejam a ≥ 1 e b > 1 constantes, T (n ) = 2T (n / 2) + Θ( n),
seja f (n) uma função, e seja T (n) definida sobre os inteiros não
negativos pela recorrência
temos a = 2 , b = 2 e f(n) = Θ(n).
T (n) = aT (n / b) + f (n) Assim, com a aplicação do Teorema 1, temos
onde interpretamos n / b como ou n / b  ou n / b  . Então T (n) pode
T (n ) = Θ ( n log n)
ser limitada assintoticamente como segue.
1. Se f ( n) = O (n log a −ε ) para alguma constante ε > 0 , então
b

log n
T (n) = Θ(n log a ) .
b

2. Se f ( n) = Θ( n log a ) , então T (n) = Θ(n log a log n) .


b b

3. Se f ( n) = Ω( n log a +ε ) para alguma constante ε > 0 , e se


b
Merge Sort n
af ( n / b) ≤ cf ( n ) para alguma constante c < 1 e todo n
suficientemente grande, então T (n) = Θ( f ( n)) . (Cormen et al. [ 1].) Obs.: Esta expressão pode também ser obtida pelo método da iteração.

Algoritmos de Ordenação Algoritmos de Ordenação

Quicksort Quicksort

Para a ordenação de um sub-array A [ p ... r ] , a divisão e


Algoritmo de ordenação de n números com tempo de conquista se dá em três passos como a seguir.
execução Θ(n2) no pior caso. Divisão do array A [ p ... r ] em dois sub-arrays (possivelmente
Contudo, seu tempo de execução esperado é Θ(n log n). vazios) A [ p ... q-1 ] e A [ q+1 ... r ] de forma que cada elemento
de A [ p ... q-1 ] é menor ou igual a A [ q ] e cada elemento de
Possui a vantagem de ordenação local. A [ q+1 ... r ] é maior ou igual a A [ q ] (i.e. A [ p ... q-1 ] ≤ A [ q ] ≤ A [ q+1 ... r ]).
Conquista envolvendo a ordenação dos 2 sub-arrays
Utiliza divisão e conquista. A [ p ... q-1 ] e A [ q+1 ... r ] recursivamente, utilizando
Quicksort.
Combinação obtida sem tarefa adicional, visto que os sub-
arrays são ordenados localmente.

Algoritmos de Ordenação Algoritmos de Ordenação

Quicksort Quicksort

Partition(A, p, r){
QuickSort(A, p, r) { x = A[r];
if(p < r){ i = p - 1:
q = Partition(A, p, r); for(j=p;j<r;j++){
QuickSort(A, p, q-1); if(A[j]<=x){
QuickSort(A, q+1, r); i++;
} trocar(A[i],A[j]);
} }
}
trocar(A[i+1],A[r]);
O algoritmo faz uso do procedimento Partition, como return i+1;
segue. }

6
Algoritmos de Ordenação Algoritmos de Ordenação

Quicksort - Exemplo Quicksort - Exemplo


QuickSort(A, p, r) { Quicksort(A,1,8) QuickSort(A, p, r) { Quicksort(A,1,3) Quicksort(A,5,8)
if(p < r){ if(p < r){ q=4
q = Partition(A, p, r); q = Partition(A, p, r);
QuickSort(A, p, q-1); 2 8 7 1 3 5 6 4 QuickSort(A, p, q-1); 2 1 3 4 7 5 6 8
QuickSort(A, q+1, r); QuickSort(A, q+1, r);
} } i=3 j=7
} }

Partition(A, p, r){ Partition(A, p, r){


x = A[r]; x = A[r];
i = p - 1: i = p - 1:
for(j=p;j<r;j++){ for(j=p;j<r;j++){
if(A[j]<=x){ if(A[j]<=x){
i++; i++;
trocar(A[i],A[j]); trocar(A[i],A[j]);
} }
} }
trocar(A[i+1],A[r]); trocar(A[i+1],A[r]);
return i+1; return i+1;
} }

Algoritmos de Ordenação

Referências

[1] T.H. Cormen, C.E. Leiserson, R.L. Rivest, C. Stein.


Algoritmos - Teoria e Prática. Ed. Campus, 2002.
[2] J.L. Szwarcfiter, L. Markenzon. Estruturas de Dados e
seus Algoritmos. LTC, 1994.

Você também pode gostar