Estrutura de Dados
_______________________________________
Algoritmos de Busca Busca linear (listas)
o Examina cada elemento da estrutura seqüencialmente
o Complexidade: O(n)
o Pode ser usado diretamente em uma lista não-processada (desordenada)
o Muito lento para grandes quantidades de dados, mas aceitável para listas pequenas e que mudam constantemente
Implementação (iterativa):
private int linearSearch(int a[], int valueToFind) {
for (int i=0; i<a.length; i++) {
if (valueToFind == a[i]) { return i; } } return -1; }
Busca binária (listas)
o Realiza sucessivas divisões do espaço de busca, comparando o elemento buscado com o elemento no meio da subdivisão (divisão e conquista)
o Complexidade: O(log n)
o Parte do pressuposto que a lista é de acesso aleatório e está ordenada o Ótimo desempenho comparado à busca linear para grandes quantidades de
Implementação (recursiva):
BinarySearch(A[0..N-1], value, low, high) { if (high < low)
return -1 // not found mid = (low + high) / 2 if (A[mid] > value)
return BinarySearch(A, value, low, mid-1) else if (A[mid] < value)
return BinarySearch(A, value, mid+1, high) else
return mid // found }
Busca em árvores
o Depth-first (busca em profundidade)
Vai até uma determinada folha da árvore e faz backtracking o Breadth-First (busca em largura)
Faz a procura por níveis. Começa na raiz e pesquisa todos os nós adjacentes no mesmo nível e, se não achar o elemento procurado, desce para o próximo nível e repete o processo
Algoritmos de Ordenação Simples – O(n2)
Bubble Sort
o A idéia é percorrer a lista diversas vezes, fazendo o maior elemento
deslocar-se para o final da mesma após sucessivas comparações
o Compara v[i] com v[i+1]. Se estiverem desordenados, há um swap (troca). Isso é repetido para todos os elementos da lista dois-a-dois
for(i = 0; i < v.length-1; i++){
Selection Sort
o O algoritmo funciona da seguinte forma: Ache o valor mínimo da lista
Troque-o pelo elemento na primeira posição
Repita esses passos para o restante da lista (começando de índice+1)
public void SelectionSort(int[] v) { int index_min;
int aux;
for (int i=0; i<v.length; i++) { index_min = i;
for (int j=i+1; j<v.length; j++) { if (v[j]<v[index_min]) { index_min=j; } if (index_min!=i) { aux = v[index_min]; v[index_min] = v[i]; v[i] = aux; } } } } Insertion Sort
o Em termos gerais, percorre um vetor de elementos da esquerda para a
direita e à medida que avança vai deixando os elementos mais à esquerda ordenados.
public static void insertionSort(int[] vetor) { for (int i = 0; i < vetor.length; i++) { int aux = vetor[i];
int j = i;
j = j - 1; }
vetor[j] = aux; }
Algoritmos de Ordenação Avançados – O(n log n)
Mergesort
o Conceitualmente o mergesort funciona da seguinte forma:
Divida a lista não-ordenada em duas sublistas de aproximadamente
metade do tamanho da original
Divida cada uma das sublistas recursivamente até termos listas de
tamanho 1, onde, então, elas são retornadas
Junte (merge) as duas sublistas em uma lista ordenada
function mergesort(m)
var list left, right, result if length(m) ≤ 1
return m else
var middle = length(m) / 2 for each x in m up to middle add x to left
for each x in m after middle add x to right
left = mergesort(left) right = mergesort(right) result = merge(left, right) return result
Quicksort
o Os passos são:
Rearranje a lista de forma que todos os elementos anteriores ao pivô sejam menores ou iguais a ele, e todos os elementos posteriores ao pivô sejam maiores ou iguais a ele. Ao fim do processo o pivô estará em sua posição final. Essa operação é denominada partição
Recursivamente ordene a sublista dos elementos menores e a sublista
dos elementos maiores
o Complexidade: O (n log n) , mas , no pior caso, pode chegar a O(n2)
o Tipicamente, o quicksort é bem mais rápido do que os outros algoritmos que rodam a O(n log n), pois seu loop interno pode ser otimizado na maioria das arquiteturas
function quicksort(array)
var list less, equal, greater if length(array) ≤ 1
return array
select a pivot value pivot from array for each x in array
if x < pivot then append x to less if x = pivot then append x to equal
if x > pivot then append x to greater
return concatenate(quicksort(less), equal, quicksort(greater))
Estrutura de dados: Array
Estrutura de dados linear, homogênea (contém elementos do mesmo tipo) e armazenada contiguamente
Elementos são acessados diretamente (acesso aleatório) através de um índice. Acesso rápido
Têm tamanho fixo , não podem ser incrementados ou diminuídos sem implicar complexos processos de cópia
Estrutura de dados: Stack (Pilha)
Uma pilha é uma lista linear na qual o primeiro elemento a entrar é o último elemento a sair. Ela possui apenas uma entrada, chamada de topo, a partir da qual os dados entram e saem dela
É muito usada em compiladores para parsing e como estrutura para armazenar variáveis locais a um bloco de função
Operações: push (empilhar) , pop (desempilhar) e peek (olhar o conteúdo do topo)
Os itens em uma pilha podem ser empilhados e desempilhados em tempo constante de O(1), sendo desnecessário qualquer tipo de comparação Estrutura de dados: Queue (Fila)
Estrutura do tipo First in , First Out (FIFO)
Semelhante à pilha, só que neste caso só são permitidas inserções no final da fila e remoções no começo.
Esse tipo específico de estrutura é muito usado em aplicações que requerem que os itens sejam acessados na ordem em que foram adicionados à lista
Outros tipos de filas:
o Deque (double-ended queue): permite remover/inserir elementos em
qualquer lado
o Priority Queue: mantém os elementos ordenados de acordo com algum
critério de prioridade
Estrutura de dados: Linked List (Lista Ligada) Estrutura de dados do tipo linear e dinâmica
É composta por células que apontam para o próximo elemento da lista Uma lista ligada deve guardar as referências para o seu primeiro e último
elementos (este, por sua vez, tem seu apontador voltado para uma referência nula) Vantagem: A inserção de um elemento no meio da lista não implica mover todos os elementos
Desvantagem: o acesso sequencial, para eliminação ou inserção de um elemento no meio da lista
o Cada nó tem referência para ambos o anterior e o próximo elemento
o Melhora o desempenho de algumas operações sobre a lista, como remover um elemento do final ou mostrar os elementos na ordem inversa
Estrutura de dados: Binary Trees (Árvores Binárias) Uma árvore binária é uma estrutura caracterizada por:
o Ou não tem elemento algum (árvore vazia)
o Ou tem um elemento distinto, denominado raiz, com dois apontadores para duas estruturas diferentes, denominadas sub-árvore esquerda e sub-árvore direita
Cada elemento pode ter até, no máximo, dois filhos
Em uma árvore binária de busca, todos os nós que são descendentes à esquerda de um nó A têm valores menores que A. Todos os nós que são descendentes à direita de A têm valores maiores que A
Árvores binárias fazem buscas, inserções e deleções em O(log n)
Percorrer uma árvore significa visitar todos os seus nós em uma determinada ordem.
Pode-se percorrer de 3 formas: o Pré ordem
Visite a raiz
Percorra a sub-árvore à esquerda
Percorra a sub-árvore à direita o Em ordem
Percorra a sub-árvore à esquerda Visite a raiz
Percorra a sub-árvore à direita
o Pós ordem
Percorra a sub-árvore à direita Visite a raiz
Uma árvore binária balanceada é aquela em que a diferença de profundidade (nível) entre todas as folhas (elementos sem filhos) é de, no máximo, 1.
O número máximo de folhas por nível é dado por 2i, onde i = nível
Estrutura de dados: Hashtable (Tabela hash)
É uma estrutura de dados especial, que associa chaves de pesquisa (hash) a valores Seu objetivo é, a partir de uma chave simples, fazer uma busca rápida e obter o valor desejado
É baseada em arrays
Função de espalhamento: responsável por gerar um índice a partir de uma determinada chave. Idealmente, este índice deve ser único para evitar colisões Para resolver colisões pode-se usar:
o Algoritmo de rehash, que calcula um novo hash baseado em uma
determinada função, caso haja colisão
o Encadeamento, onde encontra-se uma posição disponível na tabela e