Prévia do material em texto
L N C C Parte 1: Estruturas de dados elementares GA-024 Antônio Tadeu A. Gomes, D.Sc. atagomes@gmail.com http://wiki.martin.lncc.br/atagomes-cursos-lncc-ga024 Sala 2C-01 L N C C Estruturas de dados elementares ! Matrizes ! Listas encadeadas ! Pilhas e filas ! Cadeias de caracteres L N C C Matrizes (I) ! Conjuntos multidimensionais homogêneos ! Alocação estática: - É necessário saber de antemão as dimensões ! Ex. (bidimensional): - Variável que representa matriz é um ponteiro para o primeiro elemento da primeira linha - Há: ! Formas alternativas de inicialização na declaração ! Passagem de matrizes como parâmetros de funções (representação “row major”) L N C C Matrizes (II) ! Alocação dinâmica - C e C++ só permitem alocação dinâmica de conjuntos unidimensionais - É necessário criar abstrações conceituais com vetores para representar matrizes alocadas dinamicamente ! Uso de TAD pode ser conveniente para encapsular abstração float* v; … v = (float*) malloc(n * sizeof(float)); L N C C Matrizes (III) ! TAD “Matrix” bidimensional (elementos ponto-flutuante) - Estrutura: ??? - Operações principais: ! Matrix* matrix_create( int m, int n ): cria matriz de zeros ! Matrix* matrix_create( void ): cria a partir de stdin ! matrix_print( Matrix* m ): imprime matriz em stdout ! matrix_destroy( Matrix* m ) ! float matrix_getelem( Matrix* m, int x, int y ) ! matrix_setelem( Matrix* m, int x, int y, float elem ) ! matrix_{getcolumncount,getlinecount}( Matrix* m ) - Outras operações: ! Matrix* matrix_transpose( Matrix* m ) ! Matrix* matrix_{add,subtract,multiply}( Matrix* m, Matrix* m ) L N C C Matrizes (IV) ! Matriz dinâmica bidimensional representada por um vetor simples: - mat[ i ][ j ] mapeado em v[ k ] onde k = i * n + j - Implementação de matrix_getelem()? Operação matrix_create( int m, int n ): float *mat; /* matriz m x n como um vetor */ ... mat = (float*) malloc(m*n*sizeof(float)); ... L N C C Matrizes (V) ! Matriz dinâmica bidimensional representada por um vetor de ponteiros: - Cada elemento do vetor armazena o endereço do primeiro elemento de cada linha da matriz - Implementação de matrix_getelem()? Operação matrix_create( int m, int n ): int i; float **mat; /* matriz como um vetor de ponteiros */ ... mat = (float**) malloc(m*sizeof(float*)); for (i=0; i<m; i++) mat[i] = (float*) malloc(n*sizeof(float)); ... L N C C Matrizes (VI) ! Matrizes simétricas - mat é uma matriz simétrica se mat[ i ][ j ] = mat[ j ][ i ] - estratégia de armazenamento: ! basta armazenar os elementos da diagonal e metade dos elementos restantes, por exemplo, os elementos abaixo da diagonal, ou seja, mat[ i ][ j ], onde i > j - em lugar de n2 valores, armazena-se n(n+1)/2 valores - Implementação de matrix_getelem()? ! Com vetores simples ! Com vetores de ponteiros L N C C Matrizes (VII) ! Matrizes esparsas - Muitas posições preenchidas por zeros - Ex. de aplicação: solução numérica de eq. diferenciais parciais (EDPs) (métodos diretos ou iterativos) - Alocação ineficiente de memória tanto com vetores simples como com vetores de ponteiros � ⇧⇧⇧⇧⇧⇧⇤ 10 0 0 0 �2 0 3 9 0 0 0 3 0 7 8 7 0 0 3 0 8 7 5 0 0 8 0 9 9 13 0 4 0 0 2 �1 ⇥ ⌃⌃⌃⌃⌃⌃⌅ L N C C Matrizes (VIII) ! Compressed Row Storage (CRS) - Não assume características específicas da matriz row ptr 1 3 6 9 13 17 20 val 10 -2 3 9 3 7 8 7 3 ... 9 13 4 2 -1 col ind 1 5 1 2 6 2 3 4 1 ... 5 6 2 5 6 � ⇧⇧⇧⇧⇧⇧⇤ 10 0 0 0 �2 0 3 9 0 0 0 3 0 7 8 7 0 0 3 0 8 7 5 0 0 8 0 9 9 13 0 4 0 0 2 �1 ⇥ ⌃⌃⌃⌃⌃⌃⌅ L N C C Matrizes (IX) ! Variantes da CRS: - “Column-major” (CCS) - Block-compressed (BCRS, BCCS) ! Útil na discretização de EDPs com vários graus de liberdade (tamanho do bloco determinado pelo número de graus de liberdade) ! Neste caso, vetor val armazena ponteiros para um outro vetor que contém de fato os blocos Tamanho do bloco = 2 L N C C Matrizes (X) ! Outras formas de representação: - Compressed Diagonal Storage (CDS) ! Matrizes de banda fixa � ⇧⇧⇧⇧⇧⇧⇤ 10 �3 0 0 0 0 3 9 6 0 0 0 0 7 8 7 0 0 0 0 8 7 5 0 0 0 0 9 9 13 0 0 0 0 2 �1 ⇥ ⌃⌃⌃⌃⌃⌃⌅ val(:,-1) 0 3 7 8 9 2 val(:, 0) 10 9 8 7 9 -1 val(:,+1) -3 6 7 5 13 0 L N C C Matrizes (XI) ! Outras formas de representação (cont.): - Jagged Diagonal Storage (JDS) ! Mais eficiente em espaço que CDS ! Útil na implementação de métodos iterativos em computadores paralelos/vetoriais - Skyline Storage (SKS) ! Matrizes de banda variável ! Útil na implementação de métodos diretos e na fatoração de matrizes em blocos ! Referência: [Barret et al., 2006, Seção 4.3] L N C C Listas encadeadas (I) ! Vetores e matrizes (alocadas estaticamente ou dinamicamente com vetores simples/de ponteiros) - Ocupam (grupos de) espaços contíguos de memória ! Permitem acesso (quase) direto aos elementos - Devem ser dimensionados com um número máximo de elementos ! Em C, possibilidade de redimensionamento com realloc() L N C C Listas encadeadas (II) ! Lista encadeada: - Sequência de elementos chamados de nós - Um nó de uma lista contém: ! a informação armazenada (homogênea ou não em relação ao resto da lista) ! um ou mais ponteiros que apontam para outros nós da lista - Uma lista é representada por um ponteiro para um nó da lista - Ponteiros em um nó de uma lista podem ser NULL L N C C Listas encadeadas (III) ! TAD “List” em C (elementos inteiros) ! Operações principais: ! List* list_create( void ): cria lista vazia ! void list_destroy( List* l) ! List* list_insert( List* l, int i ): insere nó com informação i no início da lista ! List* list_remove( List* l, int v ): retira nó com informação v ! int list_empty(List* l): retorna 1 se lista vazia ! List* list_get(List* l, int v): retorna ponteiro para nó com informação v ! Outras operações: ! void list_print(List* l): imprime conteúdo da lista em stdout L N C C Listas encadeadas (IV) ! Tipos de listas: - Listas encadeadas simples - Listas circulares - Listas duplamente encadeadas L N C C Listas encadeadas (V) ! Ex.1: lista encadeada simples armazenando inteiros struct list { int info; struct list* next; }; typedef struct list List; /* inserção no início: retorna a lista atualizada */ List* list_insert(List* l, int i) { List* novo = (List*) malloc(sizeof(List)); novo->info = i; novo->next = l; return novo; } /* imprime valores dos elementos em stdout */ void list_print (List* l) { List* p; for (p = l; p != NULL; p = p->next) printf(“%d\n”, p->info); } L N C C Listas encadeadas (VI) ! Ex.1 (cont.) - Remoção ! Caso 1: ! Caso 2: /* retira elemento da lista */ List* list_remove( List* l, int v ) { List* ant = NULL; /* ponteiro para elemento anterior */ List* p = l; /* ponteiro para percorrer a lista */ /* procura elemento na lista, guardando anterior */ while (p != NULL && p->info != v) { ant = p; p = p->next; } /* verifica se achou elemento */ if (p == NULL) return l; /* não achou: retorna lista original */ /* retira elemento */ if (ant == NULL) /* retira elemento do inicio */ l = p->next; else /* retira elemento do meio da lista */ ant->next = p->next; free(p); return l; } L N C C Listas encadeadas (VII) ! Ex.2: lista encadeada simples ordenada /* insere elemento em ordem */ List* list_insert(List* l, int v) { List* novo; List* ant = NULL; /* ponteiro para elemento anterior */ List* p = l; /* ponteiro para percorrer a lista */ /* procura posição de inserção */ while (p != NULL && p->info < v) { ant = p;p = p->next; } /* cria novo elemento */ novo = (List*) malloc(sizeof(List)); novo->info = v; /* encadeia elemento */ if (ant == NULL) { /* insere elemento no início */ novo->next = l; l = novo; } else { /* insere elemento no meio/final da lista */ novo->next = ant->next; ant->next = novo; } return l; } L N C C Listas encadeadas (VIII) ! Ex.3: lista circular - A lista pode ser representada por um ponteiro para um elemento inicial qualquer da lista - E as outras operações? void list_print(List* l) { List* p = l; /* faz p apontar para o nó inicial */ /* testa se lista não é vazia e então percorre com do-while */ if (p) do { printf("%d\n", p->info); /* imprime informação do nó */ p = p->next; /* avança para o próximo nó */ } while (p != l); } L N C C Listas encadeadas (IX) ! Ex.4: lista duplamente encadeada - Cada elemento tem um ponteiro para o próximo elemento e um ponteiro para o elemento anterior - E as outras operações? struct list { int info; struct list* prev; struct list* next; }; /* inserção no início: retorna a lista atualizada */ List* list_insert(List* l, int v) { List* novo = (List*) malloc(sizeof(List)); novo->info = v; novo->next = l; novo->prev = NULL; /* verifica se lista não estava vazia */ if (l != NULL) l->prev = novo; return novo; } L N C C Listas encadeadas (X) ! Nós de uma lista podem armazenar ponteiros para informações, ao invés das informações em si - Permite a implementação de listas genéricas - Demanda generalização também nas operações do TAD ! Neste caso, uso de linguagem orientada o objetos (ex. C++) facilita implementação - Herança e polimorfismo /* Definição de nó genérico */ struct list { int type; void *info; struct list *next; }; L N C C Listas encadeadas (XI) ! Representação de matrizes esparsas usando listas encadeadas - Representações CRS, BCRS, CDS, JDS, SKS etc. dão suporte a operações matemáticas eficientes (adição, multiplicação, produto interno etc.) quando a matriz é um operando, mas não a modificações na matriz (inclusão/ exclusão de item) de forma eficiente ! Problema relacionado à alocação estática de memória - Representações de lista encadeada e tabela de dispersão (hash) oferecem alternativas interessantes L N C C Listas encadeadas (XII) ! Implementação de matrizes esparsas usando listas encadeadas - Cada coluna da matriz é representada por uma lista circular (com um nó cabeça) ! Idem para cada linha - Elementos diferentes de zero na matriz (elementos ponto-flutuante) são representados por nós com a seguinte estrutura: - Nós cabeça possuem valores de line e column iguais a -1 struct node { struct node* right; struct node* below; int line; int column; float info; } typedef struct node Node; L N C C Listas encadeadas (XIII) • Comparação com implementação usando vetores? � ⇧⇧⇤ 50 0 0 0 10 0 20 0 0 0 0 0 �30 0 �60 5 ⇥ ⌃⌃⌅ L N C C Pilhas e filas (I) ! Pilhas - Novo elemento é inserido no topo e acesso é apenas ao topo - O primeiro que sai é o último que entrou (LIFO – last in, first out) - Operações básicas: ! Empilhar (push) um novo elemento, inserindo-o no topo ! Desempilhar (pop) um elemento, removendo-o do topo L N C C Pilhas e filas (II) ! TAD “Stack” em C (elementos ponto-flutuante): - Operações principais: ! Stack* stack_create(void) ! void stack_destroy(Stack* s) ! void stack_push(Stack* s, float v) ! float stack_pop(Stack* s) ! int stack_empty(Stack* s) - Duas implementações principais: ! Como vetor ! Como lista encadeada L N C C Pilhas e filas (III) ! Implementação de pilha com vetor - elementos inseridos ocupam as primeiras posições do vetor - elemento vet[n-1] representa o elemento do topo #define N 50 /* número máximo de elementos */ struct stack { int n; /* vet[n]: primeira posição livre do vetor */ float vet[N]; /* vet[n-1]: topo da pilha */ /* vet[0] a vet[N-1]: posições ocupáveis */ }; typedef struct stack Stack; Stack* stack_create(void) { Stack* s = (Stack*) malloc(sizeof(Stack)); s->n = 0; /* inicia com zero elementos */ return s; } L N C C Pilhas e filas (IV) ! Implementação de pilha com vetor (cont.) void stack_push (Stack* s, float v) { if (s->n == N) { /* capacidade esgotada */ printf("Capacidade da pilha estourou.\n"); exit(1); /* aborta programa */ } /* insere elemento na próxima posição livre */ s->vet[s->n] = v; s->n++; } float stack_pop (Stack* s) { float v; if (stack_empty(s)) { printf("Pilha vazia.\n"); exit(1); /* aborta programa */ } /* retira elemento do topo */ v = s->vet[s->n-1]; s->n--; return v; } L N C C Pilhas e filas (V) ! Ex.: Notações para expressões aritméticas - infixa = (1-2)*(4+5) - pós-fixa = 1 2 – 4 5 + * - pré-fixa = * - 1 2 + 4 5 ! Expressões pós-fixadas podem ser avaliadas em uma pilha - cada operando é empilhado numa pilha de valores - quando se encontra um operador ! desempilha-se o número apropriado de operandos (dois para operadores binários e um para operadores unários) ! realiza-se a operação devida ! empilha-se o resultado L N C C Pilhas e filas (VI) ! Ex. (cont.): avaliação de 1 2 – 4 5 + * L N C C Pilhas e filas (VII) ! Filas - Um novo elemento é inserido ao final da fila - Um elemento é retirado do início da fila - Primeiro que entra é o primeiro que sai (FIFO – first in first out) - Operações básicas: ! Enfileirar (enqueue) um novo elemento ao final da fila ! Retirar (dequeue) um elemento do início da fila enqueue dequeue L N C C Pilhas e filas (VIII) ! TAD “Queue” em C (elementos ponto-flutuante): - Operações principais: ! Queue* queue_create(void) ! void queue_destroy(Queue* q) ! void queue_enqueue(Queue* q, float v) ! float queue_dequeue(Queue* q) ! int queue_empty(Queue* q) - Duas implementações principais: ! Como vetor ! Como lista encadeada L N C C Pilhas e filas (IX) ! Implementação de fila com vetor - Processo de inserção e remoção em extremidades opostas da fila faz com que a fila “ande” no vetor - Incremento das posições do vetor deve ser de forma “circular” #define N 100 /* número máx. de elementos */ struct queue { int n; /* número de elementos na fila */ int ini; /* posição do próximo elemento a ser retirado da fila */ float vet[N]; }; typedef struct queue Queue; Queue* queue_create(void) { Queue* q = (Queue*) malloc(sizeof(Queue)); q->n = 0; /* inicia com zero elementos */ q->ini = 0; /* escolhe posição inicial */ return q; } L N C C Pilhas e filas (X) ! Implementação de fila com vetor (cont.) void queue_enqueue(Queue* q, float v) { int fim; if (q->n == N) { /* capacidade esgotada */ printf("Capacidade da fila estourou.\n"); exit(1); /* aborta programa */ } /* insere elemento na próxima posição livre */ fim = (q->ini + q->n) % N; q->vet[fim] = v; q->n++; } float queue_dequeue(Queue* q) { float v; if (queue_empty(q)) { printf("Fila vazia.\n"); exit(1); /* aborta programa */ } /* retira elemento do início*/ v = q->vet[q->ini]; q->ini = (q->ini + 1)%N; q->n--; return v; } L N C C Pilhas e filas (XI) ! Fila dupla: - Fila na qual é possível: ! inserir novos elementos no início e no fim ! retirar elementos de ambos os extremos - Simula, dentro de uma mesma estrutura, duas filas, com os elementos em ordem inversa uma da outra ! Mudança no TAD, para incluir operações de queue e dequeue no início e no fim da fila - Implementações: ! Com vetor ! Com lista duplamente encadeada L N C C Pilhas e filas (XII)! Exs.: - Filas de pacotes em roteadores - Filas de tarefas aguardando a CPU em sistemas operacionais ! Caso especial de filas: priorização dos itens - Exemplos acima! - Métodos numéricos iterativos (seleção repetida de um item com maior/menor valor) - Sistemas de gerência de memória LRU (Least Recently Used) L N C C Pilhas e filas (XIII) • Fila de prioridades: valor associado a um elemento reflete sua habilidade relativa de abandonar o conjunto de elementos rapidamente • TAD “PriQueue” em C (elementos ponto-flutuante): - Operações principais: ! PriQueue* priqueue_create(void) ! void priqueue_destroy(PriQueue* q) ! void priqueue_enqueue(PriQueue* q, float v) ! float priqueue_dequeue(PriQueue* q): retira maior/menor elemento ! float priqueue_peek(PriQueue* q): obtém maior/menor elem., sem retirá-lo - Implementações principais: ! Como vetor ou lista encadeada (ordenados ou não) ! Como heap (não confundir com o homônimo usado em alocação dinâmica de memória!) L N C C Pilhas e filas (XIV) • Heap: sequência de itens c[1], c[2], …, c[n] tal que c[i] >= c[2i] e c[i] >= c[2i+1] para todo i = 1, 2, ..., n/2 • Definição corresponde a “max heaps” • Implementação usual com vetores • Note que primeiro item tem sempre índice 1!!! • Ex. (max heap com elementos do tipo caracter): 1 2 3 4 5 6 7 S R O E N A D #define N 101 /* num máx de elementos +1 !!*/ struct priqueue { int n; /* número de elementos na fila */ float vet[N]; /* posições ocupáveis: 1 a N-1 */ }; typedef struct priqueue PriQueue; PriQueue* priqueue_create(void) { PriQueue* q = (PriQueue*) malloc(sizeof(PriQueue)); q->n = 0; /* inicia com zero elementos */ return q; } L N C C Pilhas e filas (XV) • Implementação de fila de prioridades com heap: #include <float.h> … void priqueue_enqueue(PriQueue* q, float v) { if (q->n == N) { /* capacidade esgotada */ printf("Capacidade da fila estourou.\n"); exit(1); /* aborta programa */ } q->n++; q->vet[q->n] = -FLT_MAX; /*menor float existente */ increase(q, q->n, v); } float priqueue_dequeue(PriQueue* q) { float v; if (q->n < 1) { printf("Fila vazia.\n"); exit(1); /* aborta programa */ } /* retira elemento do início*/ v = q->vet[1]; q->vet[1] = q->vet[q->n--]; rebuild(q); return v; } float priqueue_peek(PriQueue* q) { if (q->n < 1) { printf("Fila vazia.\n"); exit(1); /* aborta programa */ } return q->vet[1]; } L N C C Pilhas e filas (XVI) • Operações auxiliares para priqueue_enqueue() e priqueue_dequeue(): • static void rebuild(PriQueue *q): reconstrói a heap após retirada • (static) void (priqueue_)increase(PriQueue *q, int i, float v): aumenta valor do elemento na posição i para v static void rebuild(PriQueue* q) { int left = 1; int j = left*2; float v = q->vet[left]; while (j <= q->n) { if ((j < q->n) && (q->vet[j] < q->vet[j+1])) j++; if (v >= q->vet[j]) break; q->vet[left] = q->vet[j]; left = j; j = left*2; } q->vet[left] = v; } static void increase(PriQueue* q, int i, float v) { while ((i > 1) && (v >= q->vet[i/2])) { q->vet[i] = q->vet[i/2]; i /= 2; } q->vet[i] = v; } L N C C Cadeias de caracteres (I) ! Tipo básico caractere: - Em C: char (tamanho = 1 byte = 8 bits) ! Alguns alfabetos precisam de maior representatividade - Tabela de códigos: ! correspondência entre caracteres e códigos numéricos ! Exs.: ASCII (C/C++), Unicode (Java)... L N C C Cadeias de caracteres (II) ! Representação de cadeias de caracteres (strings) em C: - Vetor do tipo char, terminado pelo caractere nulo ('\0') - É necessário reservar uma posição adicional no vetor para o caractere de fim da cadeia ! Inicialização de cadeias de caracteres: - Vetor de caracteres (pouco usual) - Caracteres entre aspas duplas ! caractere nulo é representado implicitamente - Exs: char s0[ ] = {’R’, ’i’, ’o’, ’\0’}; char s1[] = ""; char s2[] = "Rio de Janeiro"; char s3[81]; char s4[81] = "Rio"; L N C C Cadeias de caracteres (III) ! Leitura de caracteres e cadeias de caracteres através da entrada padrão (stdin) - Operação scanf() ! especificadores de formato definem comportamento ! Exs: ! O que acontece com as entradas: - “r”? - “ r”? - “rio”? - “ rio”? - “rio de janeiro\n”? char a; ... scanf("%c", &a); ... char a; ... scanf(" %c", &a); ... char a[81]; ... scanf("%s", a); ... char a[81]; ... scanf(" %[^\n]", a); ... L N C C Cadeias de caracteres (IV) ! Biblioteca de cadeias de caracteres padrão em C: string.h - size_t strlen(const char *s) - char *strcpy(char *s1, const char *s2) - char *strcat(char *s1, const char *s2) - int strcmp(const char *s1, const char *s2) - char *strdup(const char *s1) - char *strstr(const char *s1, const char *s2) - ... ! Para cada uma dessas operações, pode-se imaginar implementações recursivas, se considerarmos que uma cadeia de caracteres é: - Uma cadeia vazia ou - Um caracter seguido de uma (sub)cadeia L N C C Cadeias de caracteres (V) ! Uma nota sobre constantes do tipo cadeia de caracteres: - Avaliação da constante resulta no ponteiro para onde a cadeia de caracteres está armazenada - Cuidado: o que acontece se executarmos ? char cidade[4]; strcpy (cidade, "Rio" ); printf ( "%s \n", cidade ); char *cidade = "Rio"; printf ( "%s \n", cidade ); ou ou char cidade[] = "Rio"; printf ( "%s \n", cidade ); cidade[2] = 'x'; L N C C Cadeias de caracteres (VI) ! Vetores de cadeias: - Alocação estática: ! Matriz de elementos do tipo char - Alocação dinâmica: ! Vetor de ponteiros ! Cada cadeia de caracteres (elemento do vetor) é alocada dinamicamente ! Nota (geral!) sobre alocação dinâmica e papel de clientes e provedores no contrato de uma função: - Idealmente (mas nem sempre!) quem aloca tem que desalocar depois L N C C Cadeias de caracteres (VII) ! Um caso especial em C: função main() ! Quais os valores de argc e argv caso o executável seja chamado como: ? int main (int argc, char** argv) { … } nome_programa param1 param2 L N C C Cadeias de caracteres (VIII) ! Processamento de cadeias - Inúmeras aplicações: ! Processamento de textos ! Recuperação de informação ! Sequenciamento de DNA ! Representação de imagens ! ... - Principais operações: ! Casamento de cadeias (ou casamento de padrões) ! Compressão de cadeias L N C C Cadeias de caracteres (IX) ! Casamento de cadeias - Casamento exato - Casamento aproximado: operações de inserção, substituição e retirada de caracteres do padrão são permitidas ! Conceito de distância de edição ! Ex. (dist. edição = 1) L N C C Cadeias de caracteres (X) ! Casamento exato: - Leitura dos caracteres do texto um a um: ! Algoritmos força bruta, Knuth-Morris-Pratt e Shift-And ! Ex. força bruta: - Códigos C++: [Ziviani, 2007, págs. 531-532] - Uso de uma janela que desliza pelo texto, pesquisando por sufixo da janela que casa com sufixo do padrão, por comparações da direita p/ a esquerda: ! Algoritmos Boyer-Moore-Horspool e BMH-Sunday - Códigos C++: [Ziviani, 2007, págs. 532-533] char *forcaBruta ( char * texto, char *padrao ) { int n = strlen(texto); int m = strlen(padrao); for ( int i = 0; i < (n − m + 1) ; i ++) { int k = i; int j = 0; while ( ( j < m) && (texto[k] == padrao[j] ) ) ) { j ++; k++; } if ( j ==m) return &texto[i]; } return NULL; } L N C C Cadeias de caracteres (XI) ! Compressão de cadeias: - Motivação: explosão de informação textual disponível ! Bibliotecas digitais ! Bancos de dados de documentos ! World-WideWeb - Além da economia de espaço, deve-se considerar: ! Velocidade de compressão e de descompressão ! Possibilidade de realizar casamento de cadeias diretamente no texto comprimido ! Permitir acesso direto a qualquer parte do texto comprimido e iniciar a descompressão a partir da parte acessada - Algumas técnicas: ! Huffman (baseado em caracteres e palavras), Ziv-Lempel (eficiente na compressão, ineficiente no casamento), Moffat e Katajainen (melhorias no Huffman)