· web viewem algumas linguagens de programação, tal como pascal ou matlab, o índice do...

87
Apostila Análise de Algoritmos e Estruturas de Dados Prof Dr. rer. nat. Daniel D. Abdala

Upload: tranque

Post on 15-Dec-2018

222 views

Category:

Documents


0 download

TRANSCRIPT

Page 1:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Apostila

Análise de Algoritmos e Estruturas de Dados

Prof Dr. rer. nat. Daniel D. Abdala

Florianópolis, janeiro 2012

Page 2:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Prefácio

Esta apostila tem com objetivo sumarizar o conteúdo relativo a duas disciplinas aplicáveis tanto nos cursos de automação de sistemas como ciências da computação.

Ela foi escrita tendo como base a linguagem C por motivos tais e tais

Apresentar as estruturas simples suportadas diretamente pela linguagem CApresentar formas de estruturar informação complexas que requerem a efetiva criação da

estrutura de dadosAbordar a alocação de memória para estruturas de maneira estática e dinâmica

1

Page 3:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Sumário1 Formas Homogêneas para Estruturação de Dados..............................................................6

1.1 Arrays...........................................................................................................................6

1.1.1 Definindo um Array..............................................................................................6

1.1.2 Inicializando os valores de um Array....................................................................7

1.1.3 Acessando os Elementos de um Array..................................................................8

1.1.4 Atenção ao Acessar um Array.............................................................................10

1.2 Matrizes......................................................................................................................11

1.2.1 Manipulando uma Matriz como Array................................................................11

1.2.2 Cuidados ao se Acessar Matrizes........................................................................12

1.3 Enumerações (Enumerated).......................................................................................12

1.4 Exercícios....................................................................................................................12

2 Formas Heterogêneas para Estruturação de Dados...........................................................13

2.1 Estruturas (Structs).....................................................................................................13

2.2 Uniões (Unions)..........................................................................................................13

3 Ponteiros............................................................................................................................14

3.1 Operadores para Ponteiros........................................................................................14

3.2 Aritmética de Ponteiros..............................................................................................14

3.3 Ponteiros e Funções...................................................................................................14

3.4 Ponteiros e Arrays......................................................................................................14

3.5 Ponteiros e Estruturas................................................................................................14

4 Alocação Dinâmica de Memória.........................................................................................15

5 Tipos Abstratos de Dados...................................................................................................16

5.1 Organizando TADs em Módulos.................................................................................16

5.2 Exemplo de TAD.........................................................................................................16

5.3 Exercícios....................................................................................................................18

6 Recursividade.....................................................................................................................19

7 Manipulação de arquivos...................................................................................................20

7.1 Identificador de um Arquivo.......................................................................................20

7.2 Caminho de um Arquivo.............................................................................................20

7.3 Modos de abertura de um arquivo.............................................................................20

7.4 Lendo dados de um Arquivo Binário...........................................................................20

7.5 Lendo dados de um Arquivo Texto.............................................................................20

2

Page 4:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

7.6 Escrevendo dados em um Arquivo Binário.................................................................20

7.7 Escrevendo dados em um Arquivo Texto...................................................................20

8 Listas...................................................................................................................................21

8.1 Listas com e sem cabeçalho........................................................................................22

8.2 Inserção no Início da lista...........................................................................................22

8.3 Inserção no Méio da lista............................................................................................23

8.4 Inserção no Fim da lista..............................................................................................23

8.5 Remoção de Elementos de uma Lista.........................................................................23

8.6 Busca em Listas Encadeadas.......................................................................................24

8.7 Listas Duplamente Encadeadas..................................................................................24

8.8 Listas Circulares..........................................................................................................24

8.9 Exercícios....................................................................................................................25

9 Pilhas..................................................................................................................................27

9.1.1 Operações básicas:.............................................................................................27

9.1.2 Implementando uma pilha usando arrays..........................................................27

9.1.3 Implementando uma pilha usando estruturas dinâmicas...................................29

10 Filas................................................................................................................................31

10.1 Operações básicas:.....................................................................................................31

10.2 Exercícios....................................................................................................................32

11 Tabelas de Espalhamento...............................................................................................34

11.1 Função de espalhamento...........................................................................................36

11.1.1 A função de espalhamento perfeita...................................................................36

11.1.2 Escolhendo boas funções de espalhamento.......................................................36

11.2 Colisões......................................................................................................................37

11.3 Exercícios....................................................................................................................37

12 Grafos.............................................................................................................................39

12.1 Motivação...................................................................................................................39

12.1.1 Definição.............................................................................................................40

12.1.2 Grau de um Vértice.............................................................................................41

12.1.3 Adjacência..........................................................................................................42

12.1.4 Isomorfismos......................................................................................................42

12.2 Caminhos....................................................................................................................42

12.2.1 Caminhos............................................................................................................42

12.2.2 Circuitos..............................................................................................................43

3

Page 5:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.2.3 Caminhos Eulerianos..........................................................................................43

12.2.4 Caminhos Hamiltonianos....................................................................................43

12.3 Representação Computacional de grafos...................................................................44

12.3.1 Representação via Matriz de Adjacência............................................................44

12.3.2 Representação via Listas de Adjacência..............................................................44

12.4 Problema do Menor Caminho....................................................................................44

12.4.1 Problema do Caminho mais Longo.....................................................................45

12.5 Algoritmos de Busca em Grafos..................................................................................45

12.5.1 Busca em Profundidade......................................................................................45

12.5.2 Busca em Largura...............................................................................................46

12.6 Grafos Planares..........................................................................................................47

12.7 Fluxo em Rede............................................................................................................47

12.8 Exercícios....................................................................................................................47

13 Árvores...........................................................................................................................48

13.1 Nomenclatura.............................................................................................................48

13.2 Correlação entre Árvores e Grafos.............................................................................49

13.3 Árvore binária.............................................................................................................50

13.4 Árvore Binárias Quase Completas..............................................................................50

13.5 Numeração de nós de árvores binárias......................................................................51

13.6 Implementando Árvores Binárias Usando Arrays.......................................................51

13.7 Busca em Árvores Binárias.........................................................................................52

13.8 Implementando Árvores Binárias Usando Nós Dinâmicos Ligados.............................52

13.8.1 Interface para Árvores Binárias Dinâmicas.........................................................53

13.9 Exercícios....................................................................................................................53

14 Heaps e Filas de Prioridade............................................................................................55

14.1 Filas de Prioridade......................................................................................................55

14.1.1 Estrutura.............................................................................................................55

14.1.2 Operações...........................................................................................................55

14.1.3 Tópico correlato: Encontrar o maior elemento de uma lista..............................56

14.1.4 Estratégias de Implementação...........................................................................57

14.2 Heap...........................................................................................................................58

14.2.1 Operações...........................................................................................................58

14.2.2 Heap Binária.......................................................................................................59

14.2.3 Operações básicas da heap binária.....................................................................60

4

Page 6:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

14.3 Filas de Prioridade Usando Heaps..............................................................................61

14.4 Exercícios....................................................................................................................61

15 Ordenação e Pesquisa de Dados....................................................................................62

16 Complexidade de algoritmos..........................................................................................63

16.1 Medindo a Complexidade...........................................................................................63

16.2 Pior, Melhor e Caso Médios.......................................................................................63

16.2.1 Pior Caso (Lower Bound)....................................................................................64

16.2.2 Melhor Caso (Upper Bound)...............................................................................64

16.2.3 Caso Médio.........................................................................................................64

16.3 Ordem de Crescimento...............................................................................................64

16.4 Notação Assintótica....................................................................................................64

17 Introdução a Programação Orientada a Objetos............................................................66

18 Algoritmos......................................................................................................................67

18.1 Cálculo dos Números Primos......................................................................................67

19 Anexo 1 – Lista de Provas exemplo................................................................................68

20 Anexo 2 – ASCII...............................................................................................................69

21 Anexo 3 – Teoria dos Grafos...........................................................................................70

22 Referências Bibliográficas...............................................................................................71

5

Page 7:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

1 Formas Homogêneas para Estruturação de Dados

Todas as estruturas de dados que serão abordadas nesta apostila são construídas como base usando arrays ou listas ligadas.

Existem vantagens e desvantagens em se utilizar uma ou outra estrutura baseArrays e listas ligadas são as estruturas de dados base sob as quais todas as outras

estruturas mais complexas são construidas

Estruturas de dados homogêneas são compostas por apenas um tipo atômico de dados. Usualmente chamamos de estruturas homogêneas arrays e enumerações, estruturas das mais simples. Elas estão presentes em virtualmente todas as linguagens de programação e, na maioria dos casos, possuem suporte direto em assembly.

São geralmente referenciados pela palavra do inglês “array” que significa série ou seqüência. Pode também ser referenciada como vetor ou matriz. Para fiz de normalização da nomenclatura, utiliza-se neste texto o termo array para referenciar arranjos de dados unidimensionais e matrizes para arranjos de dados bi, tri, e N–dimensional.

Essencialmente, um array é um arranjo ordenado de números ou elementos quaisquer que seguem um padrão específico. Geralmente se apresentam em linhas, colunas ou matrizes.

Em computação, arrays e matrizes são alocados em memória de maneira seqüencial o que permite um rápido acesso aos seus elementos. No entanto, o número de elementos a ser alocado em um array deve ser definido antes que o mesmo possa ser utilizado (definido em tempo de compilação) o que se apresenta como uma notável desvantagem na realização de muitas soluções computacionais.

1.1 ArraysTal como citado anteriormente, vetores são arranjos unidimensionais de dados. Eles

possuem um índice o que permite o acesso direto de cada um dos elementos (dados) do array. O índice é geralmente um valor seqüencial incremental sem repetições que identifica inequivocamente cada um dos elementos do array. A figura abaixo exemplifica graficamente a estrutura de um array. Note que existem dois campos a serem observados. “Dado” referencia os elementos armazenados em um array e “índice” referencia, tal como o nome sugere, o índice to elemento no arranjo de dados a partir do primeiro elemento1.

1.1.1 Definindo um Array

1 Em algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como C o índice do primeiro elemento é “0”. É importante identificar o primeiro índice válido pois tal fato pode ter um impacto considerável na geração de erros lógicos durante o acesso dos elementos de um array.

6

Tempo de compilação – significa durante a edição do programa.Tempo de execução – significa durante a execução do programa.

Page 8:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Arrays são definidos em C tal qual como qualquer outra variável atômica, no entanto, deve-se adicionalmente especificar o número de elementos que serão indexados. O molde para definição de arrays segue a sintaxe dada abaixo:

<tipo> <nome_array>[<num_elementos>];

De maneira simples, um array é definido especificando-se três componentes:a) o tipo atômico de dados das células que comporão o array;b) um identificador ou nome de variável que será utilizado para referenciar o array;c) o número de células que serão alocadas na memória para o array, a ser

especificado dentro do operador “[]” (colchetes).

Mais adiante será visto que o operador “[]” também é utilizado para acessar as células de um array. É importante diferenciar quando o operador “[]” está sendo utilizado na definição de um array, no acesso do valor contido em uma célula, ou na atribuição de um valor a uma dada célula.

Por exemplo, é possível declarar um array para armazenar os 10 primeiros números primos da seguinte maneira:

int primos[10]; //exemplo de declaração de array

1.1.2 Inicializando os valores de um Array

Ao declararmos um array, não existe a priori nenhuma garantia a respeito de que valores estarão armazenados nas células reservadas para esta estrutura homogênea de dados. Existem algumas linguagens de programação que garantem que todas as células de um array serão inicializadas contendo um valor específico tal como “0” ou NULL. No entanto este não é o caso com a linguagem C. Ao declararmos um array, as células que o comporão conterão exatamente os valores pré-existentes na memória, o que denominamos como valores randômicos ou “lixo”. Desta forma, fica sob responsabilidade do programador definir explicitamente quais serão os valores iniciais de um array. O exemplo a seguir demonstra a declaração de um array chamado “x” de 20 posições. A seguir, o conteúdo de cada uma das 20 células do array é impresso. Execute o programa diversas vezes e note como os valores impressos mudam. Isso se dá porque a posição em que o array é alocado na memória RAM muda de execução para execução. Como não existe nenhuma garantia dos valores previamente existentes em tais posições de memória, seu conteúdo prévio, ou lixo, muda.

void imprimeLixoArray(){ int x[20]; int count;

printf("%d", x[0]); for(count =1; count < 20; count++){ printf(",%d", x[count]); }}

Felizmente, a linguagem C fornece diversas formas de inicializarmos os valores de um array. Os valores de um array podem ser inicializados explicitamente por meio da especificação do valor de cada um de seus elementos logo após a sua declaração. Os valores devem ser apresentados dentro dos operadores de escopo “{” e “}” dados em ordem e separados por vírgulas. A linha de código abaixo apresenta um exemplo da inicialização explicita dos valores de um array que contém os 10 primeiros números primos:

7

Page 9:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

int primos[10] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29};

Pode-se ainda inicializar explicitamente mais que simples arrays de números. O exemplo abaixo demonstra como inicializar os valores de um array contendo todas as letras do alfabeto.

char letras[26] = {‘a’,’b’,’c’,’d’,’e’,’f’,’g’,’h’,’i’,’j’,’k’, ’l’,’m’,’n’,’o’,’p’,’q’,’r’,’s’,’t’,’u’,’v’,’w’,’x’,’y’,’z’};

Pode-se ainda inicializar explicitamente strings representando sentenças textuais que serão utilizadas pelo programa.

char mensagem[19] = “operacao concluida!”

No exemplo acima, especifica-se claramente o número de caracteres que comporão a string. No entanto, tal tipo de declaração explicita impõe um esforço adicional em se identificar o número de caracteres de cada uma das strings do sistema. Alternativamente, é possível declarar uma string seguindo a sintaxe

apresentada abaixo. Neste caso, o número de elementos que comporão a string é omitido. Quando o compilador C compila tal linha de código, ele se responsabilizará em contar o número de caracteres na string e alocará exatamente o número de bytes necessários para armazenar a string em questão.

char mensagem2[ ] = “operacao concluida!”

Em muitos casos, no entanto, os elementos que comporão a string não podem ser inicializados explicitamente a priori. Em tais casos, os valores dos elementos devem ser atribuídos em tempo de compilação pelo programador. Casos tais como este serão discutidos em detalhes mais adiante neste texto.

1.1.3 Acessando os Elementos de um Array

Na memória primária do computador (também chamada memória RAM – Random Access Memory), um array qualquer é alocado tal como mostrado na figura abaixo. Note que dependendo do tipo atômico de dados, mais ou menos bytes serão necessários para representar cada um dos elementos componentes de um array. Por exemplo, no caso abaixo, considera-se uma máquina de 32 bits, onde a palavra mínima endereçável possui 32 bits ou 4 bytes. A declaração de um array como apresentada abaixo

int groupA[7];

terá como resultado a seguinte alocação em memória:

8

String (fita) – é um dos tipos de dados mais comuns existente virtualmente em todas as linguagens de programação. Essencialmente, representa uma seqüência de caracteres. Em C usamos arrays para representar strings.

<var>[indice] – o operador “[]” é usado para acessar uma célula específica de um array.

Page 10:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Figura 1 – representação em memória de um array

Note que cada possível posição endereçável na arquitetura de computador proposta possui quatro bytes, logo, visto que a representação do tipo de dados “int” também quatro bytes a os elementos ou células do array groupA serão alocadas nas posições 00H~06H.

A declaração acima se assemelha a declaração de 7 variáveis do tipo “int”, digamos por exemplo:

int vet1, vet2, vet3, vet4, vet5, vet6, vet7;

Dentre as principais diferenças entre a declaração utilizando arrays e a declaração de variáveis individuais pode-se observar:

A declaração individual requer um identificador único para cada célula; A declaração via arrays permite referenciar múltiplas células utilizando um único

identificador; Arrays são uma forma eficiente de representar conjuntos finitos de elementos; Facilitam o acesso aos diversos elementos de um conjunto, facilitando diversas

soluções algorítmicas;

Devido ao fato de que todas as células de um array são alocadas seqüencialmente na memória RAM, é possível acessá-las de maneira rápida e eficiente, fato largamente explorado por muitos algoritmos. O método de acesso as células individuais de um array é chamado de indexação. Os índices das células são contados a partir da primeira posição alocada para o array. Para acessar as células de um array em C, utiliza-se o operador “[ ]” (colchetes). A sintaxe é simples:

<variável>[índice]

Ela pode aparecer dos dois lados de uma expressão, sendo que se ela ocorrer do lado esquerdo da expressão, o valor representado pelo lado direito será atribuído a célula do array indicada no lado esquerdo, e se ela ocorrer no lado direito da expressão, a valor indexado pela mesma será atribuído a variável que ocorre do lado esquerdo.

Considerando o array groupA definido anteriormente, podemos atribuir um valor a uma célula específica (indexada por um inteiro positivo) da seguinte maneira:

int index = 3; int respostaUniversal = 42;groupA[index] = respostaUniversal;

que efetivamente atribui o numero 42 armazenado na variável respostaUniversal para a quarta célula do array groupA indexada pelo índice 3 armazenado na variável index.

9

Page 11:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Note que não é necessário utilizar variáveis para o valor a ser atribuído à célula ou para indexar a posição desejada do array. Pode-se utilizar diretamente números para tal.

groupA[3] = 42;

Similarmente, pode-se recuperar ou acessar o valor contido em uma célula de um array simplesmente dispondo a expressão variável[índice] do lado direito da expressão:

respostaUniversal = groupA[3];

A seguir, é apresentada uma função exemplo que demonstra a declaração de um array de 10 células e o acesso aos seus elementos. O programa em questão calcula e imprime o quadrado dos números [1..10].

void acesandoCelulasArray(){ //variaveis locais unsigned int quadrados[10]; int c;

for(c = 0; c < 10; c++){ //atribui um valor a celula do array quadrados[c] = (c+1) * (c+1); } for(c = 0; c < 10; c++){ //acessa o valor contido na celula de um array printf("\n%d",quadrados[c]); }}

Tabela 1 – tipos de dados em C, número de bytes e seus valores mínimos e máximos

Tipo Bytes Bits Range short int 2 16 -32,768 -> +32,767 unsigned short int 2 16 0 -> +65,535 unsigned int 4 16 0 -> +4,294,967,295 int 4 32 -2,147,483,648 -> +2,147,483,647 long int 4 32 -2,147,483,648 -> +2,147,483,647 signed char 1 8 -128 -> +127 unsigned char 1 8 0 -> +255 float 4 32 double 8 64 long double 12 96

1.1.4 Atenção ao Acessar um Array

10

Page 12:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

É importante notar que diferentemente de outras linguagens de programação tais como Pascal e Basic, onde os arrays possuem um delimitador de fim de array, em C isto não ocorre. Desta forma o seguinte trecho de código não gerará nenhum erro de compilação ou execução

1.2 Matrizes

unsigned char imagemGS[100][100];

unsigned char imagemRGB[100][100][3];

1.2.1 Manipulando uma Matriz como Array

11

unsigned char – é um tipo de dados que armazena valores na faixa [0,255].Note que o tipo atômico de dados é char, e o modificador de tipo unsigned simplesmente informa o compilador de que este tipo de dados não aceita valores negativos.

Page 13:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

1.2.2 Cuidados ao se Acessar Matrizes

É muito importante notar que em C não existe nada que limite o tamanho de arrays.

1.3 Enumerações (Enumerated)

Forma de definir constrantes Valores inteiros correspondentes a cada identificador iniciam-se em 0 a menos que

definidos diferentemente Valores ocorrem seqüencialmente se não definidos Um identificador posterior deve sempre possuir um valor maior que um

identificador anterior

Sintaxe:

enum <nome_enum>{ <id> <=> <valor_int>, <id> <=> <valor_int>, <id> <=> <valor_int>, ...};

Exemplo:

enum cores{ Branco, Amarelo, Verde, Cinza, Preto};

1.4 Exercícios

12

Page 14:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

2 Formas Heterogêneas para Estruturação de Dados

Diferentemente das estruturas homogêneas de dados tais como arrays e enumerações, as estruturas heterogêneas são compostas potencialmente por tipos de dados diferentes. Serão apresentados dois tipos básicos de estruturas heterogêneas suportados diretamente por C, as estruturas (structs) e uniões (unions). As estruturas são uma forma de agrupar em uma única entidade, variáveis de tipos diferentes, que possuem algum tipo de relacionamento lógico entre si. Elas são uma ótima forma de passar várias variáveis correlatas para funções e permitem uma maior organização do modelo de dados de uma aplicação, facilitando a depuração de erros e manutenção do sistema. As uniões são uma forma de utilizar a mesma região de memória para acomodar variáveis de tipos diferentes. Elas são particularmente úteis em situações em que o tipo de dados a ser usado é definido durante a execução do programa.

2.1 Estruturas (Structs)

struct <nome_estrutura>{ <tipo> nome_variavel; <tipo> nome_variavel; ... ...};

2.2 Uniões (Unions)

13

Page 15:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

3 Ponteiros

O bom entendimento das listas ligadas, a segunda estrutura de dados base utilizada nesta apostila, faz-se necessário um bom conhecimento a respeito de ponteiros e de sua aritmética.

3.1 Operadores para Ponteiros

3.2 Aritmética de Ponteiros

3.3 Ponteiros e Funções

3.4 Ponteiros e Arrays

3.5 Ponteiros e Estruturas

14

Page 16:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

4 Alocação Dinâmica de Memória

15

Page 17:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

5 Tipos Abstratos de Dados

Tipo abstrato de dado – TAD – é uma especificação de um conjunto de dados e operações que podem ser executadas sobre esses dados. Além disso, é uma metodologia de programação que tem como proposta reduzir a informação necessária para a criação/programação de um algoritmo através da abstração das variáveis envolvidas em uma única entidade fechada com operações próprias à sua natureza.

A idéia que permeia os tipos abstratos de dados é a de que os tipos simples ou atômicos suportados diretamente pela maioria das linguagens de programação nem sempre são suficientemente expressivos para representar e manipular tipos de dados mais complexos recorrentemente requeridos por programas de computador. Desta forma, novos tipos de dados devem ser definidos. As operações lógico/matemáticas a serem executadas sobre tais tipos de dados definidos não são diretamente suportadas pelas linguagens de programação, desta forma, funções devem ser definidas para a correta e efetiva manipulação de tais tipos de dados. A agregação dos tipos de dados definidos acrescida de suas funções relacionadas especificamente desenvolvidas para manipular tais estruturas é chamada de tipo abstrato de dados.

TADs permitem o desenvolvimento modular de aplicações, fato que propicia uma maneira organizada e eficiente para a criação de programas.

//TAD: <Nome_da_TAD>

//Tipo ou estrutura de dados

//funções que manipulam a estrutura de dados acima definida

O tipo ou estrutura de dados deve ser definido no início do arquivo (biblioteca) que define a TAD de modo que todo o restante do módulo conheça a estrutura a ser manipulada.

Podemos utilizar a palavra reservada typedef para definir um tipo de dados customizado para a TAD sendo desenvolvida.typedef <tipo> nome;

Figura 2 forma geral da definição typedef

As funções que compõem a TAD e manipulam a estrutura de dados especifica devem possuir a seguinte forma: <tipo_de_retorno> <nome_da_funcao>(<estrutura_de_dados >, <outros argumentos>)

Figura 3 forma geral da definição typedef

5.1 Organizando TADs em Módulos

TADs normalmente são organizados em arquivos separados do programa principal, também conhecidos como módulos. Pode-se criar um arquivo “.h” contendo todas as funções e tipos de dados necessários para a implementação da TAD ou um arquivo “.h” contendo apenas os protótipos das funções e os tipos de dados a serem manipulados

5.2 Exemplo de TAD

/////////////////////////////////////////////// fracao.h//

16

Page 18:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

//Desc: TAD implementando o tipo de // dados fracao ////DDA 14/09/11/////////////////////////////////////////////

//Definicao da informacao a ser manipuladatypedef int fracao[2];

//Definicao das funcoes que manipulam essa//estrutura de dados

double retornaValorReal(fracao f){ return f[0] / f[1];}

fracao somaFracao(fracao f1, f2){ fracao f; f[1] = mmc(f1[1], f2[1]); f[0] = f1[0] + f2[0]; return f;}

fracao multiplicaFracao(fracao f1, f2){ fracao f; f[1] = f1[1] * f2[1]; f[0] = f1[0] * f2[0]; return f;}

int mmc(int n1, int n2){ //codigo para o calculo do mmc}

Figura 4 – definição e implementação de uma TAD em um único arquivo “.h”

/* TAD: Ponto (x,y) *//* Tipo exportado */typedef struct ponto Ponto;/* Funções exportadas *//* Função cria** Aloca e retorna um ponto com coordenadas (x,y)*/Ponto* cria (float x, float y);/* Função libera** Libera a memória de um ponto previamente criado.*/void libera (Ponto* p);/* Função acessa** Devolve os valores das coordenadas de um ponto*/void acessa (Ponto* p, float* x, float* y);/* Função atribui** Atribui novos valores às coordenadas de um ponto*/void atribui (Ponto* p, float x, float y);/* Função distancia** Retorna a distância entre dois pontos*/float distancia (Ponto* p1, Ponto* p2);

Figura 5 – TAD ponto “cabeçalho”

#include <stdlib.h> /* malloc, free, exit */#include <stdio.h> /* printf */#include <math.h> /* sqrt */#include "ponto.h"

17

Page 19:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

struct ponto { float x; float y;};

Ponto* cria (float x, float y) { Ponto* p = (Ponto*) malloc(sizeof(Ponto)); if (p == NULL) { printf("Memória insuficiente!\n"); exit(1); } p->x = x; p->y = y; return p;}void libera (Ponto* p) { free(p);}

void acessa (Ponto* p, float* x, float* y) { *x = p->x; *y = p->y;}void atribui (Ponto* p, float x, float y) { p->x = x; p->y = y;}

float distancia (Ponto* p1, Ponto* p2) { float dx = p2->x – p1->x; float dy = p2->y – p1->y; return sqrt(dx*dx + dy*dy);}

Figura 6 – TAD Ponto “implementação”

5.3 Exercícios

1. Quais são as duas partes constituintes necessárias para a definição de uma TAD;2. Toda função que compõe uma TAD deve receber necessariamente pelo menos um

atributo. Qual é este atributo, justifique sua resposta;3. Escreva um programa que faça uso do TAD Ponto definida anteriormente;4. Acrescente novas operações ao TAD Ponto, tais como soma e subtração de pontos;5. Crie uma TAD para a manipulação de vetores;6. Crie uma TAD para manipulação de strings;7. Crie uma TAD para manipulação de números complexos;

Extra: Como deve ser feita a alocação de memória de uma TAD? Variáveis devem ser declaradas em tempo de compilação ou em tempo de execução via alocação dinâmica de memória? Justifique sua resposta e dê exemplos.

18

Page 20:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

6 Recursividade

19

Page 21:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

7 Manipulação de arquivos

7.1 Identificador de um Arquivo

7.2 Caminho de um Arquivo

7.3 Modos de abertura de um arquivo

7.4 Lendo dados de um Arquivo Binário

7.5 Lendo dados de um Arquivo Texto

7.6 Escrevendo dados em um Arquivo Binário

7.7 Escrevendo dados em um Arquivo Texto

20

Page 22:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

8 Listas

As listas ou listas encadeadas são a estrutura de dados mais simples concebível excetuando-se naturalmente os arrays. Listas encadeadas nada mais são que uma seqüência de células ligadas ou encadeadas umas as outras. As células de uma lista encadeada são compostas de dois elementos cada. O primeiro elemento é o dado efetivo a ser armazenado e o segundo compraz uma referência para o próximo elemento da lista. A figura abaixo apresenta um modelo esquemático da célula de uma lista encadeada assim como a sua estrutura de dados.

Quando lidamos com arrays estáticos, devemos saber de antemão ou em tempo de compilação qual o tamanho ou quantidade de bytes a ser alocados para o array. Como foi visto em sala de aulas via exemplos, existem muitas situações em programação onde a quantidade exata de memória a ser alocada não é conhecida na criação do programa devendo ser definida durante sua execução. Para estes casos pode-se alocar a memória necessária via alocação dinâmica de memória. No entanto ainda existem outros casos em que mais memória deve ser alocada para armazenar os dados do programa a medida que o programa vai sendo utilizado. Este é um dos casos em que a utilização de listas ligadas é mais indicada do que a utilização de arrays estáticos ou dinâmicos. Outra vantagem das listas ligadas é que não existe nenhuma restrição indicando que as células que as compõem devem estar alocadas seqüencialmente na memória tal como ocorre com arrays ou memória alocada dinamicamente via malloc. Em verdade, cada célula pode existir em diferentes regiões a memória.

Figura 7 – representação gráfica de uma lista ligada

A lista ligada é composta, como foi dito anteriormente, por células de informação. Cada célula contém um item de dado e um ponteiro para a próxima célula da lista. O final da lista é marcado pela última célula da lista apontando para o ponteiro nulo (NULL).

Uma lista ligada vazia nada mais é que um ponteiro apontando para NULL.Por fim, pode-se imaginar que o requisito imposto de um ponteiro apontando para o

próximo elemento de cada elemento da lista seja um uso desnecessário de memória. No entanto vale lembrar que em geral os itens armazenados em uma lista encadeada são muito maiores que os tipos atômicos de dados, tornando assim a proporção entre memória de dados e ponteiros relativamente negligenciável.

21

Page 23:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

8.1 Listas com e sem cabeçalho

Listas ligadas podem ser organizadas de duas maneiras distintas.

Lista com cabeçalho: O conteúdo da primeira célula não importa, marca apenas o início da lista. A primeira célula está sempre no mesmo lugar na memória, mesmo que a lista fique vazia.Digamos que LISTA é o endereço da primeira célula. Então LISTA->prox == NULL se e somente se a lista está vazia. Para criar uma lista vazia, basta dizer celula c,*LISTA; c.prox = NULL; LISTA = &c;

ou celula *LISTA; LISTA = malloc (sizeof (celula)); LISTA->prox = NULL;

Lista sem cabeçalho: O conteúdo da primeira célula é tão relevante quanto o das demais. Nesse caso, a lista está vazia se o endereço de sua primeira célula é NULL. Para criar uma lista vazia basta fazer

celula *LISTA; LISTA = NULL;

Itens podem ser inseridos em qualquer ponto da lista ligada: Início; Meio; Fim;

8.2 Inserção no Início da lista

Na inserção de elementos no início da lista deve-se observar se a mesma possui ou não um cabeçalho. Para o caso em que a lista possua cabeçalho, deve-se proceder da seguinte forma:

1. Criar a nova célula a ser inserida;2. Apontar o ponteiro “prox” da célula sendo inserida para a primeira célula da lista

original (NEW_CEL->prox = HEAD->prox);3. Apontar o ponteiro “prox” do cabeçalho para a célula a ser inserida HEAD->prox =

NEW_CEL->prox.

Exercício: Como deve se proceder à inserção de células no início de listas sem cabeçalho?

Figura 8 Inserção no início de uma lista sem cabeçalho

void insereInicio (celula *head, celula *cel){

22

Page 24:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

cel->prox = head->prox; head->prox = cel->prox;}

8.3 Inserção no Meio da lista A inserção de um elemento no meio da lista ou em uma posição arbitrária independe do

fato da lista possuir ou não cabeçalho. A idéia geral é encontrar a posição na qual a célula corrente deve ser inserida e então identificar as células anterior e posterior. Uma vez identificadas tais células basta que os ponteiros sejam ajustados.

Figura 9 Inserção no meio de uma lista sem cabeçalho

void inserePosicao (celula *head, celula *cel, int pos){ //itera até encontrar a posição certa //testa se a posição indicada é valida //EXERCICIO: escrever a atribuição dos ponteiros prox da célula // anterior e próxima a posição em que a célula deve ser inserida }

8.4 Inserção no Fim da lista

Tal inserção ocorre de maneira muito similar ao caso anterior. A única diferença é que célula->prox deve apontar para NULL.

Figura 10 Inserção no final de uma lista sem cabeçalho

8.5 Remoção de Elementos de uma ListaOs elementos de uma lista podem ser removidos, a semelhança das regras de inserção,

de três maneiras distintas: a) Remoção do primeiro elemento da lista;b) Remoção do último elemento da lista;c) Remoção de um elemento em uma posição arbitrária.

As funções de remoção são muito similares as de inserção e, portanto ficam como exercício.

23

Page 25:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Compare a remoção de um item de uma lista encadeada com a remoção de um item de um array!

8.6 Busca em Listas EncadeadasEmbora existam vantagens obvias relativas à utilização de listas encadeadas para

gerência de memória alocada e inserção e remoção de itens, listas encadeadas não podem ser indexadas tal como um array. Existem todavia diversas formas de acessar os elementos de uma lista encadeada. As duas mais relevantes são:

a) Busca por um dado elemento – em que um elemento de dado “elem” é especificado. A lista deve ser percorrida iterativamente até que o elemento seja encontrado;

b) Recuperação do elemento N a partir do início da lista – A lista é percorrida a partir do início até que o elemento N é encontrado.

celula *buscaElemento (int elem, celula *HEAD){ celula *p; p = HEAD->prox; while (p != NULL && p->conteudo != elem) p = p->prox; return p; }

EXERCÍCIO: implemente a recuperação do elemento N a partir do início da lista.

A partir da idéia geral de listas encadeadas, estruturas de dados variantes podem ser concebidas. Em especial duas estruturas são de especial interesse. As listas duplamente encadeadas e as listas circulares.

8.7 Listas Duplamente Encadeadas

Uma lista duplamente encadeada é assim denominada pela forma como as células que compõem a lista são ligadas. Ao contrário das listas simples, existem dois caminhos possíveis para se percorrer a lista. Pode-se pesquisar a lista duplamente encadeada em sentido normal e em sentido inverso. Isso se da pelo fato de que os elementos da lista possuem dois ponteiros, um que aponta para a próxima célula da posta e outro que aponta para a célula anterior. Adicionalmente, a primeira célula terá seu ponteiro “ant” apontando para NULL e a última célula terá seu ponteiro “prox” apontando igualmente para NULL.

8.8 Listas CircularesListas circulares são listas encadeadas simples com a diferença de que elas não possuem

um começo e um fim. Um ponteiro externo aponta para uma célula qualquer da lista. Itens são inseridos ou removidos da lista a partir da posição atual do ponteiro externo.

24

Page 26:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

8.9 Exercícios

1. Crie uma função para imprimir todos os elementos de uma lista encadeada com e sem cabeçalho;

2. Escreva uma função que receba uma lista encadeada e devolva o endereço de um nó que esteja o mais próximo possível do meio da lista. Faça isso sem contar explicitamente o número de nós da lista;

3. Compile e execute o seguinte programa: typedef struc cel celula; struct cel { int conteudo; celula *prox; }; int main (void) { printf ("sizeof (celula) = %d\n", sizeof (celula)); return 0; }

4. Por que a seguinte versão de insere não funciona?

void insere (int x, celula *p) { celula nova; nova.conteudo = x; nova.prox = p->prox; p->prox = &nova; }

5. Escreva uma função que insira um novo elemento em uma lista encadeada sem cabeça. Será preciso tomar algumas decisões de projeto antes de começar a programar.

6. Critique a seguinte versão da função remove:

void remove (celula *p, celula *ini) { celula *morta; morta = p->prox; if (morta->prox == NULL) p->prox = NULL; else p->prox = morta->prox; free (morta); }

7. Invente um jeito de remover uma célula de uma lista encadeada sem cabeça. (Será preciso tomar algumas decisões de projeto antes de começar a programar.)

8. Escreva uma função que copie um vetor para uma lista encadeada. Faça duas versões: uma iterativa e uma recursiva.

25

Page 27:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

9. Escreva uma função que copie uma lista encadeada para um vetor. Faça duas versões: uma iterativa e uma recursiva. 

10. Escreva uma função que faça uma cópia de uma lista dada. 11. Escreva uma função que concatena duas listas encadeadas (isto é, "amarra" a

segunda no fim da primeira). 12. Escreva uma função que conta o número de células de uma lista encadeada. 13. Escreva uma função que remove a k-ésima célula de uma lista encadeada sem

cabeça. Escreva uma função que insere na lista uma nova célula com conteúdo x entre a k-ésima e a k+1-ésima células.

14. Escreva uma função que verifica se duas listas dadas são iguais, ou melhor, se têm o mesmo conteúdo. Faça duas versões: uma iterativa e uma recursiva.

15. Escreva uma função que desaloca (função free) todos os nós de uma lista encadeada. Estamos supondo, é claro, que cada nó da lista foi originalmente alocado por malloc. 

16. Escreva uma função que inverte a ordem das células de uma lista encadeada (a primeira passa a ser a última, a segunda passa a ser a penúltima etc.). Faça isso sem usar espaço auxiliar; apenas altere os ponteiros. Dê duas soluções: uma iterativa e uma recursiva. 

17. Projeto de Programação.  Digamos que um texto é um vetor de caracteres contendo apenas letras, espaços e sinais de pontuação. Digamos que uma palavra é um segmento maximal de texto que consiste apenas de letras. Escreva uma função que recebe um texto e imprime uma relação de todas as palavras que ocorrem no texto juntamente com o número de ocorrências de cada palavra.

18. Descreva, em linguagem C, a estrutura de uma das célula de uma lista duplamente encadeada.

19. Escreva uma função que remove de uma lista duplamente encadeada a célula apontada por p. (Que dados sua função recebe? Que coisa devolve?)

20. Escreva uma função que insira em uma lista duplamente encadeada, logo após a célula apontada por p, uma nova célula com conteúdo y. (Que dados sua função recebe? Que coisa devolve?)

21. Problema de Josephus.  Imagine que temos n pessoas dispostas em círculo. Suponha que as pessoas estão numeradas 1 a n no sentido horário. Começando com a pessoa de número 1, percorra o círculo no sentido horário e elimine cada m-ésima pessoa enquanto o círculo tiver duas ou mais pessoas. Qual o número do sobrevivente? 

26

Page 28:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

9 Pilhas

A estrutura de dados Pilha emula a forma de organização de objetos intuitiva que é utilizada diariamente nos mais diversos contextos da vida humana. Containeres são empilhados e desempilhados diariamente em todos os portos do mundo, processos nas mesas de advogados, produtos em supermercados são apenas alguns dos milhares de exemplos da utilização de pilhas.

Essencialmente, Pilha é uma estrutura de dados amplamente utilizada em diversos escopos computacionais. Por exemplo, toda função em um programa de computador possui uma pilha onde são alocadas as variáveis estáticas pertinentes ao escopo da função e endereço de retorno para o ponto de invocação da função, dentre outras informações.

O funcionamento de uma pilha é simples e intuitivo: Itens são empilhados um a um, e sempre o último elemento sobre todos os que já se encontram na pilha. Apenas o último elemento empilhado pode ser removido da pilha, pois se tentarmos remover um elemento, digamos, que se encontra no meio da pilha, corremos o risco de desestruturar toda a estrutura (imagine remover uma latinha de atum que se encontra debaixo de outras 200!). A figura abaixo exemplifica uma pilha de diversos tamanhos. Ela serve para nos informar de duas características muito importantes a respeito de pilhas: a) seu topo; e b) seu tamanho.

Em um ambiente computacional utilizamos pilhas para agrupar dados que devem se comportar (armazenados e acessados) tal como lidamos com pilhas no mundo real. Para tal, as seguintes operações básicas são previstas:

9.1.1 Operações básicas: criar uma pilha vazia; inserir um novo elemento; retirar o elemento do topo da pilha; verificar qual é o elemento no topo da pilha; verificar se a pilha está vazia.

Existem diversas formas de se programar a estrutura de dados Pilha em um programa de computador. Dentre elas as mais utilizadas são: a)implementação via um array estático; e b) implementação dinâmica utilizando listas encadeadas.

9.1.2 Implementando uma pilha usando arrays

A idéia que permeia a programação de pilhas utilizando arrays (método estático) é criar um array estático de N posições e uma variável topo que aponta para o topo da pilha. Alternativamente pode-se criar uma estrutura de dados para acomodar ambas as variáveis. Elementos devem ser alocados (empilhados) a partir da posição inicial (vet[0]) até potencialmente que o tamanho máximo do array seja atingido. Elementos podem ser

27

Page 29:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

removidos (desempilhados) desde que a pilha não esteja vazia por meio da remoção do ultimo elemento alocado (elemento que se encontra na posição topo). Durante todas as interações com a pilha é imprescindível que a variável topo seja mantida atualizada.

Abaixo é apresentada a implementação da pilha descrita acima para acomodar variáveis do tipo float.#define N 50

struct pilha { int topo; float vet[N];};

typedef struct pilha Pilha;

Pilha* pilha_cria (void) { Pilha* p = (Pilha*)malloc(sizeof(Pilha)); p->topo = 0; //inicializa com zero elementos return p;}

void pilha_insere (Pilha* p, float v){ //verifica se topo alcancado if (p->topo == N) { printf (“topo da pilha alcancado!\n”); exit (1); }

p->vet[p->topo] = v; p->topo++;}

float pilha_remove (Pilha* p){ float v; if (pilha_vazia (p)) { printf (“Pilha está vazia!\n”); exit(1); }

//remove o elemento do topo da pilha

v = p->vet[p->(topo-1)]; p->topo--; return v;}

int pilha_vazia (Pilha* p){ return (p->topo == 0);}

void pilha_libera (Pilha* p)

28

Page 30:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

{ free(p);}

Como pode-se imaginar, a implementação estática de pilhas impõe várias restrições no que se refere a capacidade de crescimento da estrutura de dados e com relação ao tipo de dados a ser alocado. Adicionalmente, a implementação de pilhas utilizando arrays impõe uma restrição adicional referente ao fato de que toda a estrutura deve ser alocada seqüencialmente na memória. Uma forma de lidar com tais limitações, ou seja, flexibilizar esta estrutura de dados é utilizar listas encadeadas em sua implementação.

9.1.3 Implementando uma pilha usando estruturas dinâmicas

Essencialmente a implementação dinâmica de pilhas pode ser feita de diversas maneiras. Neste contexto, estudaremos a implementação dinâmica usando listas encadeadas. Note que, essencialmente, o funcionamento e os mesmos métodos relacionados as pilhas implementadas via arrays serão exatamente os mesmos a serem implementados em pilhas dinâmicas. O único ponto de divergência entre tais implementações reside no fato de que os elementos da pilha serão alocados e gerenciados de maneiras diversas. Em suma, a interface da estrutura de dados, independentemente de como a mesma é implementada, permanece a mesma.

Na implementação dinâmica, duas estruturas de dados são necessárias. Uma para representar as células individuais que comporão a pilha (muito semelhante as células usadas nas listas encadeadas) e uma estrutura de cabeçalho que conterá a referência para o primeiro elemento da pilha, referência para seu topo e facultativamente o número de elementos existentes na pilha.struct celula{ int dado; struct celula ant;};typedef struct celula Celula;

struct pilha { int tamanho; Celula *topo; Celula *ant;};typedef struct pilha Pilha;

A regra de funcionamento da pilha é clara, elementos só podem ser inseridos (empilhados) sobre o último elemento já existente na pilha. Isso é feito da seguinte maneira:

1. Novo_elemento->ant = topo;2. Topo -> Novo_elemento;3. Pilha->tamanho = tamanho + 1;

A remoção de elementos da pilha é feita da seguinte maneira:

29

Page 31:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

1. retorna topo;2. topo = topo->ant;3. Pilha->tamanho = tamanho – 1;

Para verificar qual o último elemento empilhado, basta que retornemos uma referência para topo.

Para checarmos o tamanho da pilha basta acessarmos o campo Pilha->tamanho

30

Page 32:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

10 Filas

A Fila é uma estrutura de dados que modela as filas que existem comumente nas mais diversas situações do mundo real. Essencialmente em uma fila o elementos são inseridos em série um atrás do outro. Um dado elemento sempre será inserido a atrás dos elementos já existentes na fila, e quando a remoção de um elemento da fila é requerida, o elemento mais antigo inserido, ou seja, o primeiro elemento da fila é removido.

10.1 Operações básicas: criar uma fila vazia; inserir um novo elemento; retirar o elemento da cabeça da fila; verificar qual é o elemento na cabeça da fila; verificar se a fila está vazia; verificar o número de elementos na fila.

Uma fila nada mais é do que uma lista encadeada onde as regras de inserção e remoção são bem estipuladas. Elementos são sempre inseridos no início da lista e removidos do final da lista. struct celula{ int dado; struct celula prox;};typedef struct celula Celula;

struct fila { int tamanho; Celula *início; Celula *prox; Celula *cabeça;};typedef struct pilha Pilha;

É possível implementarmos uma fila usando listas encadeadas simples. Ao definir esta estrutura como base para a implementação de filas, a inserção de novos elementos é simples:

1. Novo_elemento->prox = inicio;2. inicio = Novo_elemento;3. fila->tamanho++.

A remoção de um elemento da fila no então é bem problemática. Devemos percorrer todos os elementos da lista até encontrarmos a partir de seu início até encontrarmos o elemento que aponta para NULL, ou seja o último elemento da lista. Ainda, devemos manter uma referência para o elemento anterior durante a busca pelo último elemento. Assim que o último elemento é encontrado, devemos fazer com que cabeça aponte para o elemento anterior, fazer com que o elemento anterior aponte para null e retornar o elemento previamente apontado por cabeça. Como podemos ver essa é uma implementação bem pouco eficaz. Imagine o caso em que existam centenas de milhares de elementos em uma lista!

31

Page 33:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Também é possível implementar filas usando listas duplamente encadeadas. Tente identificar a vantagem em se utilizar listas duplamente encadeadas ao invés de listas encadeadas simples.

Da mesma forma que podemos implementar uma pilha usando arrays, podemos também implementar filas. No entanto a implementação pode ser pouco eficiente. Tente implementar uma fila em arrays e liste os problemas encontrados.

10.2 Exercícios

22. Crie uma função que recebe como argumento uma string contendo parênteses encadeados:

Ex: “(((()))(((((()))))((((()))))))”Utilize uma pilha para decidir se o número de parênteses encadeados está certo ou

errado. A função dever retornar 1 em caso afirmativo, e 0 em caso negativo. A função deve ignorar qualquer outro caractere existente na string que não seja “(” ou “)”. Um parêntese encadeado significa que para cada “(“ deve existir um “)”;

23. Com relação a pilhas estáticas (que utilizam um array) o que poderíamos fazer para remediar uma situação em que durante a execução do programa descobrimos que precisamos de uma pilha maior do que a que foi alocada inicialmente (estaticamente)?

24. Qual a importância do campo topo, na estrutura pilha estática apresentada?;25. Crie uma implementação estática da pilha, tal como a vista anteriormente, para

acomodar variáveis do tipo int;26. Considere a representação pictográfica de uma estrutura de pilha dinâmica dada

abaixo:

32

Page 34:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Explique o que há de errado com esta estrutura. Porque ela não funcionaria para a implementação de uma pilha?

27. Considere a representação pictográfica de uma estrutura de fila dinâmica dada abaixo:

Explique o problema em se utilizar esta estrutura. É possível implementar uma fila usando este modelo? Justifique.

28. Proponha uma maneira mais eficiente de implementar filas usando listas encadeadas simples (dica, use um ponteiro adicional)

33

Page 35:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

11 Tabelas de Espalhamento

Algumas das estruturas de dados vistas anteriormente requerem que seus elementos (células dinâmicas) sejam inspecionados seqüencialmente até que a desejada seja encontrada. Em alguns casos, todos os N elementos devem ser inspecionados, em outros apenas uma parcela M / ≤ N são visitados e finalmente, existem casos em que o elementos detentor da chave que deseja-se acesso pode ser encontrado com apenas 1 operação (O(1)).

Ex:1. Encontrar a maior chave (prioridade) em uma lista encadeada não ordenada – todos os

N elementos da lista devem ser inspecionados;2. Encontrar o elemento X da lista cujo dado de X = elemento – melhor caso O(1) pior

caso O(N);3. Remover o elemento que possui a maior chave em uma Heap – O(1).

No caso ideal, as chaves da estrutura devem ser acessadas em uma única instrução. Insto pode ser alcançado via a utilização de um array (estático/seqüencial)

No entanto, como visto anteriormente, a utilização de arrays apresenta algumas desvantagens se comparada a uma lista dinâmica.

Ainda, a utilização do índice dos arrays como chave de pesquisa pode ser problemática como mostra o exemplo a seguir:

Ex: Um programa para gerência e arquivamente de e-books desenvolvido para tablets utiliza uma chave seqüencial (1 para o primeiro livro adquirido, 2 para o seguindo e assim por diante) para indexar todos os livros gerenciados pelo sistema.

Desta forma, pode-se estruturar a informação do sistema (estrutura de dados do sistema) usando um simples array estático, digamos de N=10000 posições.

No entanto, a única pesquisa direta possível de ser feita em O(1) é pela ordem de compra dos e-books (chave) o que não é muito útil.

34

Page 36:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Caso desejemos pesquisar a lista de livros pelo nome do autor ou pelo título, devemos estruturar a informação de maneira diferente.

Existem vários problemas com esta forma de estruturar a informação:

1. Muitas posições (de fato, a maioria) do array nunca indexarão um autor válido (p. ex. quem chamaria “aaaaaaaaaa”?);

2. Existe uma possibilidade de mesmo uma chave que faça sentido, p. ex. “Chang” não seja utilizada;

3. Requer uma quantidade massiva de memória para acomodar o array.

Ainda, como mapear o nome p. ex. “aaaaaaaaad“ para o índice 4 do array?Para tal é necessária uma função de, dada uma string de 10 caracteres (chave), retorne

um inteiro que é o índice do array. Esta função é chamada: Função de mapeamento; Função de espelhamento; Função de hash.

Para que o esquema de armazenamento acima fosse útil, seria necessário um método de conversão da chave (um nome que faça sentido) para um inteiro (índice do array) dentro de uma faixa limitada.

Quaisquer duas chaves não devem ser convertidas/mapeadas para o mesmo índice.

Podemos limitar o tamanho do array de indexação para uma faixa mais gerenciável se limitarmos o tamanho da chave a ser utilizada. Por exemplo, utilizando apenas dois caracteres teríamos:

35

Page 37:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

O problema com tal limitação ocorre quando desejamos mapear duas chaves que começam com as mesmas letras. Por exemplo:

11.1 Função de espalhamento

A função de espalhamento (também chamada de hash function) é o conceito central que fundamenta as tabelas de espalhamento.

int mapH(char nome[2]){ int id1 = retornaIndiceNoAlfabeto(nome[0]); int id2 = retornaIndiceNoAlfabeto(nome[1]); return (26*(id1-1))+id2;}

11.1.1 A função de espalhamento perfeita

Uma função de espalhamento perfeita mapeia todos os elementos de um conjunto de entradas C (chaves) para um conjunto de inteiros. Em essência, chamamos a função de espalhamento de perfeita se ela é uma função bijetora.

Se todas as chaves são conhecidas a priori, uma função de espalhamento perfeita pode ser usada para criar uma tabela de espalhamento perfeita que não apresentará nenhuma colisão.

A grande vantagem de espalhamento perfeito é o fato de que pesquisas em tempo constante serem possíveis. Tal fato contrasta consideravelmente com os modelos de endereçamento aberto ou encadeamento, onde o tempo de pesquisa é em geral baixo mas que pode vir a ser muito longo para alguns casos.

11.1.2 Escolhendo boas funções de espalhamento

36

Page 38:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Uma boa função de espalhamento é essencial para garantir boa performance em tabelas de espalhamento. Uma função de espalhamento não adequada para as chaves em questão tende a degradar o desempenho geral da tabela de espalhamento. No entanto, é importante ter em mente que a função de mapeamento compraz apenas uma pequena parte do todo a ser computado em uma tabela de espalhamento.

Um requisito básico para garantir o bom desempenho da função de espalhamento é que ela deve prover uma distribuição uniforme dos valores de espalhamento. Uma distribuição não uniforme tende a aumentar o numero de colisões assim como o custo associado para resolvê-las. Uniformidade na distribuição é em geral difícil de ser obtida, no entanto ela pode ser avaliada empiricamente via testes estatísticos. Note que a distribuição precisa ser uniforme apenas para tabelas de tamanho N que ocorrem na aplicação. Uma boa prática, no caso em que o array de índices seja atualizado dinamicamente é atualizar o tamanho do array para sempre exatamente o dobro ou metade do tamanho do array. Desta forma, é mais fácil garantir a uniformidade da função de mapeamento, bastando que a mesma seja atualizada em potência de 2.

11.2 Colisões

Uma colisão é definida como sendo o cálculo de um mesmo índice para duas chaves distintas.

Como visto anteriormente, a única forma de evitar que colisões ocorram no momento em que a função de espalhamento calcula o endereço de uma dada chave é sabendo a priori todas as possíveis chaves, o que leva a criação de uma função de espalhamento perfeita.

No entanto para a maioria das aplicações práticas, trabalha-se com funções de espalhamento que invariavelmente induzirão que eventualmente colisões ocorram. Existem diversas maneiras de se lidar com colisões. As duas maneiras mais usadas são:

A) Endereçamento Aberto: No endereçamento aberto elementos são inseridos nas posições calculadas pela função de espalhamento diretamente. Quando uma colisão ocorre, o registro que colide será inserido na próxima posição livre da tabela de espalhamento;

B) Encadeamento Separado: Ao invés de indexar um registro diretamente para o índice calculado com base em sua chave pela função de espalhamento, o registro é indexado em uma estrutura de dados suplementar tal como uma lista encadeada. Quando uma colisão ocorre, o registro que colide é inserido na lista encadeada.

11.3 Exercícios

1. Calcule quantas posições seriam necessárias em um array para que o mesmo pudesse acomodar todas as possíveis chaves de “aaaaaaaaaaaa” a “zzzzzzzzzz”. Considere o alfabeto de 26 letras.

2. Quais são os dois tipos de colisões listados neste resumo? Descreva-os dando exemplos. (utilize diagramas para clarificar a explicação)

3. Quais são as duas condições necessárias para que uma função de espalhamento seja perfeita?

4. Explique a diferença entre índice e chave.5. Site uma situação em que uma lista encadeada é mais indicada como estrutura de

dados a ser escolhida e outra em que uma tabela de espalhamento é a melhor opção.6. Caso as chaves utilizadas em uma tabela de espalhamento sejam strings, quais são as

vantagens e desvantagens de se utilizar mais ou menos caracteres na função de espalhamento para o cálculo do índice no array de espalhamento?

37

Page 39:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

7. Dê um exemplo em que a chave a ser utilizada não é uma string mas sim um valor numérico. Como seria a função de espalhamento neste caso?

38

Page 40:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12 Grafos

A teoria dos grafos é um ramo da matemática que estuda as relações entre objetos de um determinado conjunto. Ela possui entreita relação com os campos da teoria dos conjuntos e da topologia matemática. Este campo de estudo já é investigado desde o século XVIII (Euler, L., 1736) quando L. Euler provou que o famoso problema das pontes de Königsberg não possuia solução por meio da representação deste sob a forma de um grafo, fundou efetivamente o campo.

Uma das grandes vantágens da representação de problemas e criação de modelos utilizando para tal grafos é devido a simplicidade notação associada a uma sólida fundamentação matemática que habilita a solução de problemas reais.

12.1 Motivação

Considere o seguinte problema: Deseja-se desenhar a figura de uma casa sem remover o lapis do papel e sem desenhar a mesma linha mais de uma vez.

Fig. 1 – desenho da figura de uma casa sem a remoção do lális do papel

Fig. 2 – representação via grafos do problema do desenho da casa

Dependendo do ponto de partida do qual a figura da casa comece a ser desenhada, a resposta para problema será positiva. No entanto dependendo do ponto selecionado para iniciar o desenho, não existirá uma sequência que permitirá o desenho da casa sem passar duas vezes por pelo menos uma das linhas. Agora inicie o desenho a partir dos pontos A, B ou E e tente encontrar uma solução para o problema.

O exemplo anterior demonstra um dos muitos problemas que podem ser resolvidos utilizando a teoria dos grafos. No entanto, este é um problema que a primeira vista não possui uma aplicação prática, sendo facilmente classificado como uma curiosidade matemática.

Na realidade, existem muitos problemas reais que possuem os mesmos princípios básicos apresentados pelo problema descrito acima. Por exemplo, considere que o departamento de coleta de lixo de uma cidade deseja otimizar o processo de coleta. Para tal, o caminhão de lixo deverá passar apenas uma única vez por cada rua, de modo a minimizar o tempo de coleta de lixo, gasto de combustível, etc. Considere ainda que um país que possui uma extensa malha ferroviária. Para ir de uma cidade A para uma cidade B, o viajante passará possivelmente por diversas outras cidades, e potencialmente deverá trocar de trens para chegar ao seu objetivo. Como encontrar a melhor configuração de por quais cidades passar e quais trens tomar de modo a minimizar o tempo de viajem, custo, número de trens a serem utilizados, etc? O mesmo problema pode ser mapeado para rotas rodoviarias, conecções de voo, planejamento de linhas de onibus municipais, etc. De fato, esta conjunto de problemas define uma classe chamada problemas de caminho. Tais problemas são de especial interesse dentro do contexto da teoria dos grafos.

39

Page 41:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Considere agora o seguinte problema: Em uma rua existem três casas (casa 1 – C1, casa 2 – C2 e casa 3 – C3). Estas casas acabaram de ser construidas e precisas que os serviços de utilidade pública (Telefone – T, Luz – L e Gás - G)sejam instalados. Todos os serviços de utilizade pública chegar a rua, mas devem ser instalados nas cadas de modo que nenhuma das linhas de ligação se cruzem. Existe uma forma de efetuar tal conecção?

Fig. 3 – problema da ligação de utilizades públicas

Mais a diante neste capítulo, será demonstrado que não existe uma forma de efetuar tais ligações sem que pelo menos uma das linhas ligando as utilidades públicas se cruzem. Novamente, a primeira vista este parece ser um problema sem aplicações reais, afinal de contas, o que justificaria o fato da proibição do cruzamento das ligações de utilidades públicas? Existe várias aplicações práticas para o problema do não cruzamento de linhas. Por exemplo no planejamento de layouts de placas de circuitos impressos (layout routing). Esta classe de problemas é conhecida como problemas de planaridade.

Estes são alguns dos exemplos que serão estudados no contexto da teoria dos grafos. Especial atenção também será dada para como representar grafos em um computador, para os principais algoritmos associados a caminhos e buscas, e finalmente a complexidade dos algoritmos.

12.1.1 DefiniçãoPara qualquer conjunto V , denotamos V (2 ) o conjunto de todos os pares não-ordenados

de elementos de V . Se V tem n elementos V (2 ) tem (n2)=n (n−1)2 elementos. Os elementos

de V (2 ) são identificados como os subconjuntos de V que têm cardinalidade 2. Assim, cada elemento de V (2 ) tem a forma {u , v }, sendo u e v dois elementos distintos de V .

Um grafo G é um par ordenado G(V , A ) em que V é um conjunto arbitrário e A é um subconjunto de V (2 ). Os elementos de V são chamados de vértices e os elementos de A são chamados de arestas.

Figura 11

40

1

2

4

3

6

5

vértices arestas

Page 42:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Figura 12

Figura 13

Figura 14

12.1.2 Grau de um Vértice

41

1

2

4

3

6

5

1

2

4

3

6

5

1

2

4

3

6

5

4

1

13

4

2

912

1

2

4

3

6

5

21

2

4

3

6

5

2

23

3

4

Page 43:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.1.3 Adjacência

12.1.4 Isomorfismos

12.2 Caminhos

Fig. 4 – esquema das pontes de Königsberg Fig. 5 – representação via grafos

12.2.1 Caminhos

42

1

2

4

3

6

5

1

1 1

1

2

21

1

11

2

2

1

2

4

3

6

5

Adj(1,2) Adj(2,6)

Page 44:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.2.2 Circuitos

12.2.3 Caminhos Eulerianos

12.2.4 Caminhos Hamiltonianos

43

1

2

4

3

6

5

1

2

4

3

6

5

AC

BD

abc

de

g

f

A

C

B

DE

A

C

B

DE

A

C

B

DE F

Page 45:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.3 Representação Computacional de grafos

12.3.1 Representação via Matriz de Adjacência

12.3.2 Representação via Listas de Adjacência

12.4 Problema do Menor Caminho

44

C

A

D

B4

3 7

291

C

A

D

B

7ACBDA 3+1+7+9=20

913

C

A

D

B4

91CBADC1+4+9+2=16

0 1 0 1 0 01 2 3 4 5 6

1 0 1 0 0 10 1 0 1 1 11 0 1 0 1 00 0 1 1 0 00 1 1 0 0 0

123456

0 1 0 1 0 01 2 3 4 5 6

1 0 1 0 0 10 1 0 1 1 11 0 1 0 1 00 0 1 1 0 00 1 1 0 0 0

123456

1 2 →42 1 →3 →63 2 →4 →5

→64 1 →3 →55 3 →46 2 →3

L =

Page 46:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.4.1 Problema do Caminho mais Longo

12.5 Algoritmos de Busca em Grafos

12.5.1 Busca em ProfundidadePara cada vértice, escolher um dos adjacentes ainda não visitados e continuar

recursivamente até que não existam mais vértices não visitados. Quando isto ocorre, retornar recursivamente até um estado onde ainda existam vértices não visitados.

Algoritmo DFS(G,v):Entrada: G = (V; E), um grafo não dirigido, e v é um vértice de V.Saída: depende da aplicação.

45

J

C

E

P

1 9

3 514

J – Casa do JoãozinhoC – Campinho de Futebol

P – PracinhaE – Escola

J

C

EP

1

31403

14

1

J

C

EP

1 9

3

menor custo

0

1

310

menor custo

C

EP

1

30

1

38

5

C

EP

1

30

1

38

5JJ

Page 47:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

início marque v; execute prework; para todas as arestas (v; w) faça se w não estiver marcado então DFS(G,w); execute postwork;fim

12.5.2 Busca em Largura

A partir do vértice inicial, visitar todos os vértices adjacentes. Quando estes se exaurirem, visitar os vértices adjacentes destes que acabaram de ser visitados e assim por diante até que não existam mais vértices não visitados.

Algoritmo BFS(G,v):Entrada: G = (V; E), um grafo não dirigido, e v é um vértice de V.Saída: depende da aplicação.início marque v; coloque v em uma fila; enquanto a fila não estiver vazia faça remova o primeiro vértice w da fila; execute prework para todas as arestas (w; x) tais que x não está marcado faça marque x; inclua (w; x) na árvore T; coloque x na fila;fim

46

v c

ab

de

v c

ab

de

(v,a)(v,b)(v,c)

v c

ab

de

(a,v)(a,b)(a,d)

v c

ab

de

(b,a)(b,v)(b,c)v c

ab

de

(c,v)(c,b)backtracking

v c

ab

de

(d,a)(d,e)

v c

ab

de

Page 48:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

12.6 Grafos Planares

12.7 Fluxo em Rede

12.8 Exercícios

47

v c

ab

de

v c

ab

de

v c

ab

de

v c

ab

de

v c

ab

de

v c

ab

de

Page 49:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

13 Árvores

Existe uma ampla variedade de dados que são comumente organizados sob a forma de árvores hierárquicas utilizadas recorrentemente em nosso dia a dia. Exemplos são a organização administrativa de empresas (Diretor, Gerentes, Chefes de Departamento, etc), a tabela de jogos de uma disputa esportiva (rodada classificatória, oitavas de final, quartas de final, etc) taxonomias (botânica, biologia, etc) dentre muitos outros.

É lógico concluir que a criação de uma estrutura de dados computacional para acomodação de tais tipos de informação se faz necessária. Neste capítulo apresentamos o conceito de árvores. Inicialmente será apresentada a nomenclatura que permeia esta estrutura de dados, seguida de uma comparação com o conceito de grafos no qual será demonstrado que árvores nada mais são do que um caso particular de grafos. Em seguida o estudo de árvores se concentrará em um tipo específico chamado Árvores Binárias.

13.1 Nomenclatura

A figura abaixo sumariza todos os principais conceitos relativos a árvores. Toda árvore é composta por elementos chamados nós. Tais nós são organizados de uma maneira hierárquica, ou seja, ligados logicamente uns aos outros de forma a mimetizar ramificações de uma árvore. Embora na natureza árvores ocorram naturalmente de forma que a raiz seja localizada na parte inferior (próxima ao chão) e seus galhos se ramifiquem para cima, em computação, árvores são representadas na ordem inversa, ou seja, raiz no topo e ramificações se expandindo para baixo.

O nó localizado mais acima é chamado de nó raiz. Ele deve ser único para cada árvore servindo alternativamente como um cabeçalho da estrutura de dados. Outro fator que

48

Page 50:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

diferencia o nó raiz dos demais nós é o fato de que ele não possuir nós ancestrais. Tal fato traz a nossa atenção outro conceito importante, o fato de que as relações entre nós em uma árvore são nomeadas. Um nó A é dito ancestral ou pai de outro nó B se B é expandido a partir de A. Ainda B é dito filho de A. Os nós que não possuem nenhum filho são chamados de nós folha ou nós terminais e todos os demais nós que não são nem o nó raiz nem folhas são chamados de nós internos. Cada nível de ancestralidade, ou seja, a cada vez que uma nova linha de expansão ocorre em uma árvore dizemos que um novo nível de ramificação é criado. Os níveis são numerados a partir da raiz (nível 0) até o último nível de expansão (nível N) que conterá apenas nós folha. Por fim, o número total de níveis de uma árvore é chamado de altura da arvore.

13.2 Correlação entre Árvores e Grafos

Árvores nada mais são do que um tipo especial de grafos. A figura abaixo apresenta um diagrama de Venn que exemplifica tal fato.

Para que um grafo seja uma árvore o mesmo deve ser: Conectado, acíclico, Não orientado.

Analise os três exemplos abaixo:

Da esquerda para a direita vemos que o primeiro grafo é uma árvore porque o mesmo apresenta as três características requeridas. O segundo exemplo não é uma árvore porque existe um circuito A,B,F,C,A. O terceiro exemplo não é uma árvore porque todos os nós não estão conectados. De fato neste exemplo existem duas árvores. Neste caso chamamos o grafo de uma arborescência ou conjunto de árvores.

Exemplo: O grafo abaixo é ou não uma árvore? Justifique

49

Page 51:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

13.3 Árvore bináriaComo exemplificado no diagrama de Venn acima, árvores binárias são um tipo especial

de árvores nas quais todo nó possui exatamente dois nós filhos exceto os nos folha, que devem possuir exatamente 0 filhos. Elas são muito útil para modelar situações em que precisam ser tomadas decisões bidirecionais em cada ponto de um processo

Dizemos que uma árvore binária é completa de nível d se todas as folhas da árvore encontram-se no nível d.

Note que se a árvore contiver m nós no nível l, ela conterá no máximo 2m nós no nível l+1

Uma árvore binária completa de nível d contém exatamente 2l nós em cada lível l entre 0 e d (profundidade d com exatamente 2d nós no nível d.

Nível (d)

no

max. nósPo

tência

0 1 20

1 2 21

2 4 22

3 8 23

4 16 24

5 32 25

13.4 Árvore Binárias Quase Completas

Uma árvore binária é dita quase completa se todas as folhas da árvore devem estar localizadas no nível d ou no nível d-1. Para cada nó nd na árvore com um descendente direto no nível d, todos os descendentes esquerdos de nd que forem folhas estiverem também no nível d.

50

Page 52:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Uma forma mais simples de verificar se uma árvore binária é quase completa é checar se todos os nós folha encontram-se nos níveis d e d-1, e se todos os nós folhas estão „identados para a esquerda“.

O interesse em árvores binárias quase completas reside no fato de que se uma dada árvore é quase completa, sua implementação computacional fica consideravelmente simplificada como veremos mais adiante.

13.5 Numeração de nós de árvores binárias

Os nós de uma árvore binária quase completa podem ser numerados da seguinte forma: 1 para a raiz Filho esquerdo = dobro do n. do pai Filho direito = dobro + 1 do n. do pai

A numeração dos nós ajuda na implementação (implementação por vetores) e facilita a pesquisa de itens na árvore. A figura abaixo exemplifica a numeração de uma árvore completa de altura 3 assim como sua codificação em um array.

13.6 Implementando Árvores Binárias Usando Arrays

Existem diversas formas de se implementar árvores binárias. Neste capítulo estudaremos a implementação via arrays estáticos e via estruturas dinâmicas ligadas.

A implementação via arrays requer que a árvore binária a ser codificada seja uma árvore binária completa ou quase completa. Tal fato impõe a restrição de que toda a estrutura da árvore deve ser conhecida antes de se criar a estrutura de dados para acomodá-la.

Para a criação do espaço de armazenamento basta que se aloque (estaticamente via declaração do tamanho do array no programa ou de maneira dinâmica via malloc).

51

Page 53:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

A construção da árvore se dá via a inserção dos elementos da árvore no array seguindo o modelo de numeração de nós visto no tópico anterior.

A implementação de árvores binárias via array serve para resolver problemas os quais todos os elementos da árvore são conhecidos a priori. O ponto chave na solução de tais problemas recai na busca de itens dentro da estrutura da árvore, que em muitos casos pode ser consideravelmente mais rápido do que a busca linear ou seqüencial.

13.7 Busca em Árvores Binárias

Não existe uma ordem “natural” para se percorrer uma árvore binária. No entanto três formas básicas de busca são definidas:

Ordem Anterior (percurso em profundidade) Ordem (ou em ordem simétrica) Ordem Posterior

As formas de busca aqui descritas possuem implementação intuitiva via algoritmos recursivos.

Em ordem anterior, também chamada de busca PreOrdem ou busca em profundidade, os elementos da árvore são inspecionados a partir da raiz e da esquerda para a direita. Cada nó visitado tem seu valor inspecionado A busca continua até que um nó folha da esquerda é encontrado. Neste momento a busca passa a acessar os elementos da direita.

Na busca emOrdem, os elementos da árvore são inspecionados a partir da raiz e da esquerda para a direita até que um nó folha seja encontrado (nó sem filhos). A partir deste momento o nó é acessado a busca então analisa o no da direita. O processo continua recursivamente percorrendo toda a árvore.

Na busca em ordem posterior ou pós ordem a idéia é construir uma árvore de chamadas de função recursiva na pilha em que todos os elementos da esquerda não percorridos seguidos dos elementos da direita. A diferença reside no fato de que os elementos só serão acessados a partir do momento em que a recursão atingir a condição de parada. Desta forma os elementos serão acessados na ordem inversa a qual eles foram percorridos.

13.8 Implementando Árvores Binárias Usando Nós Dinâmicos Ligados

52

ordemAnterior(no)

IMPRIMA no.valor

SE no.esq ≠ null ENTAO

ordemAnterior(no esq)

SE no.dir ≠ null ENTAO

ordemAnterior(no.dir)

emOrdem(no) SE no.esq ≠ null

ENTAO emOrdem(no.esq) IMPRIMA no.valor SE no.dir ≠ null

ENTAO emOrdem(no.dir)ordemPosterior(no)

SE no.esq ≠ null ENTAO

emOrdem(no.esq) SE no.dir ≠ null

ENTAO emOrdem(no.dir) IMPRIMA no.valor

Page 54:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Para implementar uma árvore binária baseada em nós dinâmicos faz-se necessário: Definir uma estrutura de dados capaz de acomodar os dados a serem organizados sob

a forma de uma árvore binária; Definir nesta estrutura ponteiros para as sub-árvores da esquerda e da direita; Definir a interface ou lista de métodos que manipularão tal estrutura.

struct node{ int dado; int level; struct node *esq; struct node *dir;};typedef struct node Node;

A estrutura acima define um nó para uma árvore binária. Note que com a inclusão do campo int level (nível) permite a identificação de em que nível da árvore o nó se encontra.

13.8.1 Interface para Árvores Binárias Dinâmicasvoid bitree_init(BiTree *tree);void bitree_destroy(BiTree *tree)void bitree_ins_left(BiTree *tree, Node *n);void bitree_ins_right(BiTree *tree, Node *n);Node *bitree_rem_left(BiTree *tree);Node *bitree_rem_right(BiTree *tree,BiTree *bitree_merge(BiTree *left, BiTree *right);int bitree_size(BiTree *tree);Node *bitree_root(BiTree *tree);int bitree_is_eob(Node *n);int bitree_is_leaf(Node *n);int bitree_data(Node *n);BiTree *bitree_left(BiTree *tree);BiTree *bitree_right(BiTree *tree);

13.9 Exercícios

29. Para que tipo de problemas árvores são a estrutura de dados indicada? De exemplos;30. Desenhe uma árvore qualquer e identifique na mesma os vários termos relativos á

árvores tal como nó pai, nó filho, folha, etc....31. Implemente uma estrutura de dados para acomodar árvores binárias completas e

quase completas. Use um array para armazenamento dos dados da árvore; (para este exercício algumas decisões de codificação deverão ser tomadas, por exemplo o número N de elementos no array)

a. Escreva uma função que crie uma árvore binária;b. Escreva uma função que insira um novo elemento nesta árvore;c. Crie uma função que construa uma árvore binária a partir de uma lista de

números. Esta função deve retornar todos os elementos repetidos na lista;d. Discuta o problema de não balanceamento decorrente da ordem em que os

elementos são inseridos na árvore binária do exercício anterior. Utilize um exemplo para exemplificar o problema;

32. Delineie uma função que teste se uma árvore binária é completa ou quase completa;33. Porque desejamos que uma árvore binária seja quase completa? (dica, tem a ver com

facilidade de implementação) Descreva como podemos calcular o índice dos elementos de uma árvore se a mesma for quase completa e representada por meio de um array;

34. Implemente a função para busca em árvores binárias entitulada PreOrdem;35. Implemente a função para busca em árvores binárias entitulada emOrdem;

53

Page 55:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

36. Implemente a função para busca em árvores binárias entitulada PosOrdem;37. Implemente uma função que receba duas arvores binárias e retorne uma árvore

binária composta pelas duas fornecidas. (uma arvore será a sub-árvore direita e a outra a esquerda)

54

Page 56:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

14 Heaps e Filas de Prioridade

14.1 Filas de Prioridade

A fila de prioridade nada mais é que uma fila comum que permite que elementos sejam adicionados associados com uma prioridade. Cada elemento na fila deve possuir um dado adicional que representa sua prioridade de atendimento. Uma regra explicita define que o elemento de maior prioridade (o que tem o maior número associado) deve ser o primeiro a ser removido da fila, quando uma remoção é requerida.

As filas de prioridade são utilizadas em diversas situações. Por exemplo nas filas do banco existe um esquema de prioridade em que clientes preferenciais, idosos ou mulheres grávidas possuem uma mais alta prioridade de atendimento se comparados aos demais clientes. Em filas de atendimento para análise de sinais em sistemas de controle, sensores prioritários são adicionados a fila de atendimento com uma maior prioridade que sensores secundários, o que garante o atendimento de sinais prioritários a frente dos demais.

14.1.1 Estrutura

Como discutido anteriormente, os elementos ou células de uma fila de prioridade struct cel{ struct cel *ant; struct cel *prox; int dado; int prioridade; };typedef struct cel celula;

As células em uma fila podem ser encadeadas de maneira simples ou dupla tal como os diagramas apresentados abaixo. A escolha do método de encadeamento deve ser dirigida pela necessidade imposta pela aplicação a qual a estrutura de dados se presta assim como por outros fatores tal como o fato dos dados serem inseridos de maneira ordenada ou não;

Fila de prioridade usando uma lista encadeada simples

Fila de prioridade usando uma lista duplamente encadeada

55

Page 57:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

14.1.2 Operações

As filas de prioridade prevêem duas operações básicas que são na realidade uma extensão das operações básicas de uma fila comum. São elas:

Inserir com prioridade Remover elemento de mais alta prioridade

Adicionalmente, versões mais avançadas de filas de prioridade podem requerer as seguintes operações:

Alterar prioridade de um dado elemento; Número de elementos na fila; Testar a existência de elementos de mesma prioridade;

14.1.3 Tópico correlato: Encontrar o maior elemento de uma lista

Antes de começarmos a estudar como implementar filas de prioridade é necessário desenvolver uma maneira de encontrar o maior elemento dentre os existentes em uma dada lista de dados.

Existem várias formas de encontrar o maior elemento de uma lista de números, de fato, este será um tópico abordado em detalhes mais adiante neste curso quando estudarmos ordenação de listas. No entanto, para fins de discussão, consideremos a versão “ingênua” abaixo:

Entrada: lista L de númerosSaída: m / m > e ∀ e L∊

m encontrarMaior(L) m ← L(1) t ← |L| PARA c = 2 ATE t SE L(c) > m m ← L(c) FIMFIM

Encontrar o maior elemento desta lista sempre demorará O(n) para cada pesquisa. Embora O(n) seja uma boa complexidade computacional, as três regras de análise de algoritmos nos impõem a seguinte pergunta:

“Podemos fazer melhor?”

Analisemos o algoritmo acima mais de perto. Adicionar um novo elemento na lista pode ser feito muito rapidamente, de fato em O(1). No entanto, se mudarmos a forma de inserir os novos elementos da lista, podemos manter o maior elemento sempre em uma posição

56

Page 58:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

acessível, tornando a resolução do problema de encontrar o maior elemento é checar até o último elemento da lista.

Se mudarmos a forma de inserir os novos elementos da lista podemos manter o maior elemento sempre em uma posição acessível, tornando a resolução do problema de encontrar o maior elemento factível em O(1). Note que, no entanto, a inserção dos novos elementos não terá mais complexidade O(1) mas sim, possivelmente O(n) ou ainda complexidades de mais alta ordem.

“Precisamos de um algoritmo de inserção que tenha complexidade O(n) no pior caso”

“Precisamos de uma forma de organizar os dados, uma nova ESTRUTURA DE DADOS”

14.1.4 Estratégias de Implementação

Filas de prioridade podem ser implementadas de diversas maneiras. Usando listas encadeadas como estrutura de dados base;

o Listas encadeadas simples;o Listas duplamente encadeadas;

As listas encadeadas ainda podem seguir uma estratégia em que os elementos são inseridos de maneira ordenada ou não:

Não ordenada – inserção tal qual em uma fila comum (sempre no final da fila), estratégia esta chamada de filas não ordenadas;

Ordenada – Antes de um elementos ser inserido a fila, a posição correta (de acordo com a sua prioridade) deve ser identificada.

Na implementação não ordenada, utiliza-se uma lista encadeada como estrutura de dados base. A inserção de elementos acontece tal como na fila normal, no entanto, a remoção de itens se dá por meio da seleção do elemento de maior prioridade da fila. Para tal, toda a lista deve ser percorrida de modo a identificar o elemento com maior prioridade.

O método Inserir_com_prioridade(Fila, prioridade) deve ser composto dos seguintes passos:

Inserir elemento no final da fila Atualizar a prioridade do elemento Ajustar ponteiros para manter a estrutura de dados consistente

O método para remoção do elemento de mais alta prioridade deve ser composto dos seguintes passos:

Percorrer todos os elementos da lista para identificar o de maior prioridade Remover tal elemento da lista Ajustas ponteiros para manter a estrutura de dados consistente Retornar o elemento identificado

Uma outra possibilidade é manter a fila organizada de alguma forma para que o elemento demais alta prioridade fique sempre acessível e identificável rapidamente, fazendo com que o acesso a tal elemento seja imediato e que a sua remoção da lista tenha uma complexidade computacional baixa (idealmente O(1)).

A primeira maneira que veremos para produzir uma fila de prioridade com tais características será via a utilização de uma fila ordenada pela ordem de prioridade de seus elementos.

57

Page 59:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Inserção O(n) Remoção O(1)

No final das contas, a diferença das implementações ordenada e não ordenada de filas de prioridade usando filas comuns apenas difere de em qual operação possuirá maior complexidade, se na inserção ou remoção.

Ambas as implementação solucionam o problema, no entanto, a pergunta persiste:

“podemos fazer melhor?”

A resposta é sim, Existe uma forma de implementar filas de prioridade com complexidade O(1) para acesso do elemento de maior prioridade e O(nlogn) para inserção e remoção de elementos. No entanto, devemos estudar primeiro, outra estrutura de dados chamada Heap.

14.2 Heap

A Heap é uma estrutura especializada baseada em árvores que satisfaz a seguinte propriedade:

Propriedade 1: Se B é um nó filho de A, então chave(A) ≥ chave(B).

A propriedade acima implica em que o nó raiz seja sempre o elemento com maior chave na estrutura. A estrutura acima é conhecida como max_heap. A propriedade 1 pode ser invertida para chave(A) ≤ chave(B) o que implica em que o nó raiz sempre terá o elemento com a menor chave. Neste caso, a estrutura será chamada de mim_heap.

Os principais usos da heap são na implementação eficiente de filas de prioridade, em alguns algoritmos como o Algoritmo de Djikstra, e em algoritmos de ordenação de dados tal como o algoritmo heapsort.

CuriosidadeA estrutura de dados heap não deve ser confundida com a

heap que é comumente referenciada como a região de memória usada para a alocação dinâmica de memória. O termo foi originalmente usado para a estrutura de dados. No entanto, algumas linguagens de programação tal como Lisp implementavam a alocação dinâmica de memória usando esta estrutura de dados o que veio a conferir o nome a esta área de memória o nome.

A heap é geralmente implementada em um array e não requer ponteiros entre os elementos.

14.2.1 OperaçõesAs operações mais comuns associadas com heaps são:

create_heap - cria uma heap vazia;

58

Page 60:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

find_max - função que dada uma heap, encontra e retorna uma copia de seu maior elemento (Note que tal definição pressupõe uma max_heap, no caso das min_heaps a função seria melhor nomeada find_min);

delete_max - remove o maior elemento da heap increase_key - incrementa a prioridade de um dado elemento. A heap deve ser re-

balanceada após tal incremento; decrease_key - decrementa a prioridade de um dado elemento. A heap deve ser re-

balanceada após tal decremento; insert - insere um novo elemento na heap. A heap deve ser re-balanceada

após tal decremento; merge - compõe uma nova heap dadas duas heaps como entrada. A heap

deve ser re-balanceada;

14.2.2 Heap Binária

A heap binária é um caso especial (na realidade o mais largamente utilizado) de heaps que usa como estrutura de dados base uma árvore binária. Ela possui duas propriedades:

Propriedade da Forma – A árvore binária deve ser completa ou quase completa. Ou seja, todos os níveis exceto possivelmente o último estão completamente preenchidos. Se o último nível não estiver completo, os nós folha deste nível estão completamente preenchidos da esquerda para a direita;

Propriedade heap – A ≥ B (no caso da max_heap) ou A≤ B (no caso da min_heap)

A primeira propriedade (propriedade da forma) impõe uma restrição quanto à forma da árvore. Tal restrição tem como razão o fato de que árvores binárias completas ou quase completas podem ser numeradas da esquerda para a direita e de cima para baixo (tal como visto no tópico de árvores binárias). Devido a tal facilidade de numeração, árvores desta forma podem ser implementadas eficientemente usando arrays como estrutura de dados base. O acesso a elementos antecessores e predecessores (pais e filhos) pode ser feito através das duas regras simples:

Número do filho esquerdo = Número do pai x 2 Número do filho direito = Número do pai x 2 + 1

A segunda propriedade impõe a restrição de como os elementos da árvore devem se relacionar. Tal restrição na realidade força a organização dos elementos da lista de forma que nós pais sempre possuirão chave de mais alta ordem que os filhos (o número da chave pai será sempre maior que o número da chave filho).

O que diferencia uma heap de uma árvore binária qualquer é a forma pelas quais os elementos são inseridos e removidos da heap. A idéia é inserir os elementos de tal maneira que o elemento de mais alta prioridade esteja sempre localizado na raiz, e a remoção do

59

Page 61:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

elemento de mais alta ordem pode ser feita via a remoção da raiz. No entanto, tais regras de inserção e remoção demandam um trabalho adicional de modo a manter a estrutura da heap consistente.

14.2.3 Operações básicas da heap binária

Inserção

Inserir um elemento na heap comparado com uma árvore binária requer cuidado adicional para que as duas propriedades que definem a heap sejam obedecidas. O pseudo-algoritmo de inserção é no entanto, simples:

1. Adicionar o elemento a ser inserido na próxima posição livre da heap;2. Comparar o elemento inserido com seu pai, se eles forem iguais ou respeitarem as

propriedades heap, pare;3. Se não, troque o elemento com seu pai e retorne ao passo 2

Ex:

A figura acima exemplifica o processo de inserção em uma heap binária. Na primeira figura (esquerda para a direita) temos uma heap binária quase completa que respeita as duas propriedades de forma e heap. Na segunda figura inserimos o elemento 18 na primeira posição livre. Note que 18 > 16 violando a propriedade heap. Neste caso devemos trocar os elementos 16 e 18 de posição, o que pode ser visto na figura 3. Ainda na figura 3 vemos que o elemento 18 > 17 o que novamente, viola a propriedade heap. Trocamos novamente estes elementos e vemos na figura 4 que a heap agora se encontra consistente, respeitando ambas as propriedades de forma e heap.

RemoçãoIdentificar o elemento a ser removido em uma heap é simples. Basta removermos o nó

raiz da árvore binária que implementa a heap. No entanto, após tal remoção a árvore binária ficará sem um nó raiz, de modo que um dos nós ainda existentes na árvore deve ser eleito como o novo nó raiz. O pseudo-algoritmo para a remoção do nó de maior chave na heap é dado a seguir:

1. Remover a raiz da heap e substituí-la com o último elemento do último nível;2. Comparar a nova raiz com seus filhos, se eles respeitam a propriedade heap, parar;3. Se não, trocar o nó raiz com um de seus filhos e retornar ao passo anterior; (menor

filho na min_heap e maior filho na max_heap) Ex:

A figura acima exemplifica o processo de remoção. A primeira figura da esquerda para a direita mostra o elemento 11 a ser removido e o elemento 4 (o último elemento alocado na

60

Page 62:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

heap) que deve tomar o seu lugar. No passo seguinte comparamos o elemento 4 ao 5 e 8 e notamos que o elemento 4 deve trocar de lugar com o elemento 8. A última figura mostra a nova heap que obedece ambas as propriedades de forma e heap.

14.3 Filas de Prioridade Usando Heaps

Como discutido anteriormente, filas de prioridade podem ser implementadas de maneira ordenada ou não ordenada usando filas simples. Existe uma complexidade adicional em ordenar os elementos da fila à medida que eles são inseridos (caso ordenado) ou procurar o elemento de maior ordem no momento em que a remoção do elemento de mais alta ordem é requerida. Em ambos os casos vimos que as operações inserção ou remoção requererão O(n) e a outra O(1).

Outra forma de implementar a fila de prioridade é por meio da utilização de heaps binárias. Note que as operações de inserção e remoção ambas levam no máximo O(nlogn) o que é bom se comparado a O(n).

14.4 Exercícios

8. Implemente uma fila de prioridade não ordenada usando elementos dinâmicos (células). Proponha as estruturas de dados necessárias e implemente as funções para inserção de um novo elemento na fila e a remoção do elemento de mais alta prioridade. Utilize desenhos esquemáticos para exemplificar o funcionamento da estrutura de dados;

9. Qual a diferença entre uma fila de prioridade implementada usando listas encadeadas onde os elementos são ou não ordenados?

10. Escreva um algoritmo para a inserção de um elemento em uma heap implementada usando arrays. Proponha a estrutura de dados necessária.

11. Escreva um algoritmo para a inserção de um elemento em uma heap implementada dinamicamente. Proponha a estrutura de dados necessária.

12. Compare a dificuldade em se implementar os exercícios 3 e 4. Quais são as limitações da versão obtida no exercício 3 e as do exercício 4?

13. Repita os exercícios 3-5 para a remoção do elemento de maior prioridade.14. Quais são as duas propriedades que uma max_heap deve apresentar? E quais são as

duas da min_heap?15. Com relação às operações increase_key(elemento) e decrease_key(elemento). Quais

serão os passos necessários para garantir que a heap esteja consistente (obedecendo as duas propriedades) após a execução de cada uma destas funções. Sinta-se livre para implementar as duas funções.

16. Escreva a função heap *merge(heap *h1, heap *h2) que recebe como argumento duas heaps e retorna uma heap composta por h1 e h2. Delineie o algoritmo antes de implementá-lo.

61

Page 63:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

15 Ordenação e Pesquisa de Dados

Nome Pior Médio Melhor MemóriaQuickSort n2 nlogn nlogn lognMergeSort nlogn nlogn nlogn nHeapSort nlogn nlogn nlogn 1InsertionSort n2 n2 n❑ 1SelectionSort n2 n2 n2 1TimSort nlogn nlogn n nShellSortBubbleSortBinaryTreeSort

15.1 Ordenação por TrocaEsta classe de algoritmos de ordenação alcança a permutação desejada por meio da

troca das chaves dos elementos sendo ordenados.

15.2 Ordenação por Bolha (BubbleSort)

O funcionamento da ordenação por bolha se baseia na idéia de percorrer a lista de chaves a serem ordenadas diversas vezes de maneira sequencial. Em cada passada, o elemento selecionado é comparado com o seu sucessor, e se eles estiverem em ordem inversa (A comparação A>B ou A<B depende da ordenação estar sendo feita em ordem crescente ou ordem decrescente), ambos são trocados de lugar (o elemento A é colocado no lugar de B e vice versa).

15.3 Ordenação Rápida (QuickSort)

15.4 Ordenação por Seleção (SelectionSort)

62

Page 64:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

15.5 Ordenação por Árvore Binária

15.6 Ordenação usando Heaps (HeapSort)

15.7 Ordenação por Inserção (InsertionSort)

15.8 Ordenação de Shell (ShellSort)

15.9 Ordenação por Intercalação de Raiz

15.10 Busca Seqüencial

15.11 Busca Seqüencial Indexada

15.12 Busca Binária

15.13 Busca por Interpolação

15.14 Busca em Árvores

63

Page 65:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

16 Complexidade de algoritmos

Análise de algoritmos é uma disciplina da computação que se preocupa em medir e analisar os recursos necessários por algoritmos para levar a termo sua execução. Adicionalmente é de sua competência o estudo da viabilidade de um dado algoritmo, fornecendo meios para se avaliar se o mesmo é aplicável ao contexto a que ele se propõe.

Estuda-se análise de algoritmos porque muitas vezes nos deparamos com situações em que devemos medir se o algoritmo proposto para uma dada solução é capaz de resolver o problema em um tempo aceitável. Adicionalmente existe também a preocupação no que se refere à quantidade de memória, banda de transferência de dados em rede, etc.

Identificar o mais eficiente, dentre todas as possíveis implementações de um mesmo algoritmo.

Desenvolver novos algoritmos para problemas que já tem solução (mais eficientes) Executar uma análise de escalabilidade dos algoritmos Permite uma análise de viabilidade dos algoritmos propostos

16.1 Medindo a Complexidade

Espacial -> Mede a quantidade de memória que o algoritmo requer para sua execuçãoTemporal -> mede o tempo, dada uma entrada de dados, que o algoritmo requer para

produzir uma resposta (mais usado)As medidas de análise utilizadas devem conter as seguintes características:

Ser independentes da tecnologia empregada (hardware e software); Modelo matemático simplificado que representa os fatores mais relevantes;

Temporal -> função que relaciona o tamanho da entrada com o tempo de execução:t = f(n)

Espacial -> Função que relaciona o tamanho da entrada com o espaço de armazenamento requerido

e = g(n)Considere a seguinte situação. O problema apresentado na figura XXX foi resolvido de

cinco maneiras diferentes, resultando em cinco algoritmos (A1 ... A5). Tais algoritmos foram implementados utilizando diferentes níveis de complexidade computacional. Supondo que uma operação leva 1ms para ser executado, e dado Tk(n) sendo a complexidade, ou seja, o número de operações que o algoritmo efetua para N entradas.

N A1T1(n) = n

A2T2(n) = n log n

A3T3(n)=n2

A4T4(n)= n3

A5T(n)=2n

1 0.001s 0.016 0.064s 0.256s 4s16 0.016s 0.064s 0.256s 4s 1m4s32 0.032s 0.16s 1.0s 33s 46 dias512 0.512s 9.0s 4m22s 1dia13h 10137 séculos

16.2 Pior, Melhor e Caso Médios

A análise de complexidade de pior, melhor e caso médios fornece uma medida de esforço necessário para a execução de um programa em diferentes situações.

64

Page 66:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

16.2.1 Pior Caso (Lower Bound)

Normalmente a análise de complexidade de algoritmos concentra-se primordialmente na identificação do pior caso

1. Pior caso é uma garantia de que o algoritmo nunca demorará mais do que esta estimativa;

2. Em alguns casos, o pior caso acontece muito freqüentemente. Por exemplo em procuras em banco de dados;

3. Em geral o caso médio tende a ser tão ruim como o pior caso.

16.2.2 Melhor Caso (Upper Bound)

16.2.3 Caso MédioNo caso em que o algoritmo possua partes que podem ou não vir a ser executadas

dependendo da entrada, define-se uma nova variável de tempo de execução auxiliar e utiliza-se esta variável na computação do tempo final.

16.3 Ordem de CrescimentoNa análise de melhor, pior e caso médios são abstraídos o custo (tempo) de cada

instrução atribuindo uma constante ci para representá-los. Tal se deve ao fato de que instruções executadas em diferentes computadores fatalmente demorarão diferentes parcelas de tempo.

A análise de algoritmos é a medição da complexidade ou a quantidade de trabalho necessária para sua execução.

Pode-se expressar a quantidade de trabalho por meio de operações fundamentais, as quais variam de acordo com o algoritmo, e em função do volume de dados.

As operações fundamentais ou primitivas são as instruções que o processador irá executar e que duram um período de tempo finito e fixo.

o Atribuição de valores a variáveiso Chamadas de funçõeso Operações aritméticaso Comparação de números e variáveis o Acesso a elementos de um arrayo Retorno de funções

Volume de dados refere-se a quantidade de dados a ser processada pelo algoritmo. Usualmente representada em análise de complexidade algorítmica por N.

16.4 Notação Assintótica

Também conhecida com notação O (diz-se big O)Considere uma função f(n) não negativa para todos n>=0. Diz-se que f(n) é O(g(n)) e

escrevemos f(n) = O(g(n)), se existem um inteiro n0 e uma constante c>0, tais que para todo inteiro m>=n0, f(n) < c g(n).

Designação FunçãoConstante c

65

Page 67:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

Logarítmica log NLogarítmica quadrática log2NLinear NN log N N log NQuadrática N2

Cúbica N3

Exponencial 2N

r=∑i=1

N

i

Alg1 Alg2 Alg3r=0for i=1 to N r=r+iend

r = 0for i=1 to N for j=1 to i r = r+1 endend

r = (N*(N+1))/2

ope

raçõ

es

N+1N

1+(N(N+1))/2(N(N+1))/2

4

total

2N+1 N2+N+1 4

int maxElemArray(int A[],int n){ tmpMax = A[0]; for(i =1; i<n;i++) { if(tmpMax < A[i]) { tmpMax = A[i]; } } return tmpMax;}

21+n

2(n-1)

1

Figura 15 –

66

Page 68:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

17 Introdução a Programação Orientada a Objetos

67

Page 69:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

18 Algoritmos

18.1 Cálculo dos Números Primos

The first algorithm we shall consider will begin with the integer 2 and proceed to select each successive integer as a potential prime, (pp), checking for primacy by testing to see if it can be factored by any previously identified primes, then storing each newly verified prime in a prime set (ps) array.

void calcPrimesBruteForce(int upTo){ #define MAX 100000 int pp = 2, primecount=1, prime[MAX], c; bool test; prime[0] = 2;

while(pp < upTo){ pp = pp + 1; test = True; for(c = 0; c < pp - 1; c++){ if( (pp % prime[c]) == 0){ test = False; break; } } if (test){ prime[primecount] = pp; primecount++; } }}

68

Definição – Um número primo é um número natural que tem exatamente dois divisores positivos (distintos). Um número que não é primo é chamado de composto.

“0” e “1” – por definição, o zero e o um não são considerados nem primos nem compostos.

Page 70:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

19 Anexo 1 – Lista de Provas exemplo

69

Page 71:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

20 Anexo 2 – ASCII

70

Page 72:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

21 Anexo 3 – Teoria dos Grafos

71

Page 73:  · Web viewEm algumas linguagens de programação, tal como Pascal ou Matlab, o índice do primeiro elemento de um array é definido como “1”, em outras linguagens tais como

Daniel D. Abdala

22 Referências Bibliográficas

72