Dados_C

195
1 Universidade Católica de Pelotas Centro Politécnico Ciência da Computação Estruturas de Dados em C por Prof. Dr. Paulo Roberto Gomes Luzzardi [email protected] [email protected] http://infovis.ucpel.tche.br/luzzardi http://graphs.ucpel.tche.br/luzzardi http://gcg.ucpel.tche.br Versão 2.02 Referências Bibliográficas CORMEN, et al. Algoritmos - Teoria e Prática. Rio de Janeiro: Campus, 2002. VELOSO, Paulo e SANTOS, Clésio - Estruturas de Dados - Editora Campus, 4 ed., Rio de Janeiro, 1986. WIRTH, Niklaus. Algoritmos e Estruturas de Dados. Rio de Janeiro: Prentice-Hall do Brasil, 1989. PINTO, Wilson - Introdução ao Desenvolvimento de Algoritmos e Estrutura de Dados, Editora Érica, 1994. Pelotas, 25 de fevereiro de 2009

Transcript of Dados_C

Page 1: Dados_C

1

Universidade Católica de Pelotas Centro Politécnico

Ciência da Computação

Estruturas de Dados em C

por

Prof. Dr. Paulo Roberto Gomes Luzzardi [email protected]

[email protected]

http://infovis.ucpel.tche.br/luzzardi http://graphs.ucpel.tche.br/luzzardi

http://gcg.ucpel.tche.br

Versão 2.02

Referências Bibliográficas CORMEN, et al. Algoritmos - Teoria e Prática. Rio de Janeiro: Campus, 2002. VELOSO, Paulo e SANTOS, Clésio - Estruturas de Dados - Editora Campus, 4 ed., Rio de Janeiro, 1986. WIRTH, Niklaus. Algoritmos e Estruturas de Dados. Rio de Janeiro: Prentice-Hall do Brasil, 1989. PINTO, Wilson - Introdução ao Desenvolvimento de Algoritmos e Estrutura de Dados, Editora Érica, 1994.

Pelotas, 25 de fevereiro de 2009

Page 2: Dados_C

2

Sumário

1. Tipos de Dados 1.1 Conceitos Básicos 1.2 Tipos Primitivos 1.3 Construção de Tipos (Estruturados ou Complexos) 1.3.1 Strings 1.3.2 Vetor (Agregados Homogêneos) 1.3.3 Struct (Estrutura) 1.3.4 Ponteiros (Apontadores) 1.4 Operadores (Aritméticos, Relacionais e Lógicos) 1.4.1 Aritméticos 1.4.2 Relacionais 1.4.3 Lógicos 2. Vetores e Matrizes 2.1 Conceitos Básicos 3. Listas Lineares 3.1 Listas Genéricas 3.2 Tipos de Representações 3.2.1 Lista Representada por Contigüidade Física 3.2.2 Lista Representada por Encadeamento 3.2.3 Lista Encadeada com Descritor 3.2.4 Lista Duplamente Encadeada 3.2.5 Listas com disciplinas de Acesso 3.2.5.1 Filas 3.2.5.1.1 Fila com Vetor

3.2.5.1.2 Fila Circular 3.2.5.1.3 Fila com Alocação Dinâmica

3.2.5.2 Pilhas 3.2.5.2.1 Pilha com Vetor 3.2.5.2.2 Pilha com Alocação Dinâmica 3.2.5.2.3 Analisador de Expressões usando Pilha 3.2.5.3 Deques 3.3 Representação por Contigüidade Física 3.4 Representação por Encadeamento

Page 3: Dados_C

3

4. Arquivos 4.1 Sistema de Arquivo Bufferizado 4.2 Argumentos argc e argv 5. Pesquisa de Dados 5.1 Pesquisa Seqüencial 5.2 Pesquisa Binária 5.3 Cálculo de Endereço (Hashing) 6. Classificação de Dados (Ordenação) 6.1 Classificação por Força Bruta 6.2 Vetor Indireto de Ordenação (Tabela de Índices) 6.3 Classificação por Encadeamento 6.4 Métodos de Classificação Interna

6.4.1 Método por Inserção Direta 6.4.2 Método por Troca 6.4.2.1 Método da Bolha (Bubble Sort) 6.4.3 Método por Seleção 6.4.3.1 Método por Seleção Direta 7. Árvores 7.1 Conceitos Básicos

7.2 Árvores Binárias 7.3 Representações 7.3.1 Representação por Contigüidade Física 7.3.2 Representação por Encadeamento 7.4 Caminhamentos em Árvores 7.4.1 Caminhamento Pré-Fixado (Pré-Ordem)

7.4.2 Caminhamento In-Fixado (Central) 7.4.3 Caminhamento Pós-Fixado 7.4.4 Algoritmos recursivos para percorrer Árvores Binárias

7.4.4.1 Caminhamento Pré-Fixado (Pré-Ordem) 7.4.4.2 Caminhamento In-Fixado (Central) 7.4.4.3 Caminhamento Pós-Fixado

7.5 Árvore de Busca Binária 7.6 Árvores AVL

7.6.1 Inserção em uma árvore AVL 7.6.2 Remoção em uma árvore AVL

Page 4: Dados_C

4

8. Grafos

8.1 Conceitos 8.2 Representação por Lista e Matriz de Adjacências 8.2.1 Lista de Adjacências 8.2.2 Matriz de Adjacências 8.3 Percurso em Amplitude e Percurso em Profundidade 8.4 Determinação do Caminho Mínimo

Page 5: Dados_C

5

1. Tipos de Dados 1.1 Conceitos Básicos Estruturas de Dados Estuda as principais técnicas de representação e manipulação de dados na memória principal (Memória de Acesso Randômico, RAM – Random Access Memory). Organização de Arquivos Estuda as principais técnicas de representação e manipulação de dados na memória secundária (Disco). Conceito

Dados São as informações a serem representadas, armazenadas ou manipuladas. Tipos de Dados É o conjunto de valores que uma constante, ou variável, ou expressão pode assumir, ou então a um conjunto de valores que possam ser gerados por uma função. Na definição de uma variável, constante, expressão ou função deve-se definir o Tipo de Dado, por algumas razões: 1) Representar um tipo abstrato de dado (Realidade); 2) Delimitar a faixa de abrangência (Limites); 3) Definir a quantidade de bytes para armazenamento;

Page 6: Dados_C

6

4) E as operações que podem ser efetuadas. Os tipos de dados podem ser: Primitivos ou Estruturados, sendo que os estruturados, são chamados de Complexos. 1.2 Tipos Primitivos São os tipos de dados que, além de depender das características do sistema, dependem do processador e do co-processador. Tipos primitivos da Linguagem de Programação C: CARACTER ( char ch; ) INTEIRO ( int i; ) REAL ( float f; ou double d;)

Tipo Bytes Bits Faixa de valores char 1 8 -128 à 127 Int 2 16 -32768 à 32767

float 4 32 -3.4E-38 à 3.4E+38 double 8 64 -1.7E-308 à 1.7E+308 void 0 0 Sem tipo

1.3 Construção de Tipos (Estruturados ou Complexos) Tipos obtidos através de tipos primitivos, podem ser: STRING (Cadeia de Caracteres) ( char *s; ou char s[11]; ) VETOR (Agregados Homogêneos) ( int v[10]; ) ESTRUTURA (Agregados Heterogêneos) ( struct ) PONTEIRO (Apontadores) ( int *p; ) 1.3.1 String (Cadeia de Caracteres) Tipo de dado que permite que uma variável possua vários caracteres. Exemplo: char nome[30]=”UCPel”; ou char *s=”UCPel\n”;

Page 7: Dados_C

7

1.3.2 Vetor (Agregados Homogêneos) Tipo de dado que permite que uma variável possua vários elementos, todos do mesmo tipo. Exemplo: #define MAX 10 float vetor[MAX]; ............. vetor[0] até vetor[9] 1.3.3 Struct (Estrutura) Tipo de dado que permite que uma variável possua vários campos. Os campos podem ser de tipos de dados distintos. Exemplos: #define MAX 50 struct ALUNO { int matricula; char nome[30]; char endereco[40]; } struct ALUNO turma[MAX]; struct DATA { int dia;

int mes; int ano;

} data; Acesso aos elementos: data.dia, data.mes ou data.ano struct TEMPO { int horas;

int minutos; int segundos;

} *tempo;

Page 8: Dados_C

8

Acesso aos elementos tempo->horas, tempo->minutos ou tempo->segundos ou

(*tempo).horas, (*tempo).minutos ou (*tempo).segundos

1.3.4 Ponteiros (Apontadores) É um tipo de dado, onde a variável contém o endereço de outra variável, ou um endereço de memória. Permite ainda, alocação dinâmica de Memória, ou seja, alocação de memória em tempo de execução do programa. Exemplo: int *p;

Exemplos: #include <stdio.h> int main(void) { int *p; int n; n = 65; p = &n; printf(“Conteúdo: %d\n”,*p); getchar(); } &n ....... Endereço da variável “n” na memória principal (RAM), sendo que o endereço é formado de Segmento:OffSet (segmento e deslocamento). #include <stdio.h> #include <stdlib.h>

Page 9: Dados_C

9

int main(void) { int *p; int i,n; printf("Quantos valores: "); scanf("%d",&n); p = (int *) malloc(sizeof(int)*n); if (p == NULL) printf("ERRO FATAL: Falta de Memória"); else { for (i = 0;i < n;i++) p[i] = i; for (i = 0;i < n;i++) printf("Conteúdo: %d\n",p[i]); free(p); } getchar(); } Exemplo: #include <stdio.h> #include <stdlib.h> int main(void) { int *p; int i,n; printf("Quantos valores: "); scanf("%d",&n); p = (int *) malloc(sizeof(int)*n); if (p == NULL) printf("ERRO FATAL: Falta de Memória"); else { for (i = 1;i <= n;i++) { *p = i; p++; } for (i = 1;i <= n;i++) { p--; printf("Conteúdo: %d\n",*p); } free(p); } getchar();

Page 10: Dados_C

10

}

Definições malloc(p) ....................... Aloca dinamicamente memória para o ponteiro "p" free(p) .......................... Desaloca memória ocupada pela variável "p" NULL ............................. Palavra reservada para ponteiro NULO p ................................... Endereço da memória RAM *p ................................. Conteúdo do ponteiro &p ................................. Endereço do ponteiro 1.4 Operadores (Aritméticos, Relacionais e Lógicos) 1.4.1 Aritméticos

+ Adição - Subtração * Multiplicação / Divisão % Resto Inteiro da Divisão ++ Incremento -- Decremento

1.4.2 Relacionais

> Maior < Menor

>= Maior ou Igual <= Menor ou Igual == Igual != Diferente

1.4.3 Lógicos

&& e || ou ! não

Page 11: Dados_C

11

2. Vetores e Matrizes Permitem armazenamento de vários dados na memória RAM ao mesmo instante de tempo e com contigüidade física, ou seja, uma variável com possui vários elementos, igualmente distanciados, ou seja, um ao lado do outro. 2.1 Conceitos Básicos

Vetor: (É uma matriz unidimensional) #define MAX 7 int vetor[MAX]; Matriz: (Possui mais de uma dimensão) #define M 3 #define N 4 float matriz[M][N]; Índice: Constante numérica inteira que referencia cada elemento Exemplos: Dada a definição acima: vetor[0]...................... primeiro elemento vetor[MAX-1].............. último elemento m[0][0]...................... primeiro elemento m[M-1][N-1]............... último elemento Entrada de um Vetor Entrada de uma Matriz Bidimensional for (i = 0;i < MAX;i++) for (i = 0;i < M;i++) scanf(“%d”, &vetor[i]); for (j = 0;j < N;j++) scanf(“%f”, &matriz[i][j]);

Page 12: Dados_C

12

Exercícios: 1) Escreva um programa em C que lê uma matriz A (6x6) e cria 2 vetores SL e SC de 6 elementos que contenham respectivamente a soma das linhas (SL) e a soma das colunas (SC). Imprimir os vetores SL e SC. 2) Escreva um programa em C que lê uma matriz A (12x13) e divide todos os elementos de cada uma das 12 linhas de A pelo valor do maior elemento daquela linha. Imprimir a matriz A modificada. Observação: Considere que a matriz armazena apenas elementos inteiros 3) Escreva um programa em C que insere números inteiros (máximo 10 elementos) em um vetor mantendo-o ordenado. Quando o usuário digitar um ZERO o programa deve imprimir na tela o vetor ordenado. Operações sobre os Dados • Criação dos Dados • Manutenção dos Dados

• Inserção de um Componente • Remoção de um Componente • Alteração de um Componente

• Consulta aos Dados • Destruição dos Dados • Pesquisa e Classificação

Alocação de Memória (RAM - Random Access Memory) Alocação Estática de Memória É a forma mais simples de alocação, na qual cada dado tem sua área reservada, não variando em tamanho ou localização ao longo da execução do programa. float f; // a variável “f” ocupa 4 bytes durante toda a execução do programa

Page 13: Dados_C

13

Alocação Dinâmica de Memória Nesta forma de alocação, são feitas requisições e liberações de porções da memória ao longo da execução do programa. Para isto, são usadas variáveis do tipo Ponteiro. int *p; // a variável “p” poderá ocupar “n” bytes a qualquer momento

Célula, Nodo ou Nó Espaço reservado (alocado) na memória RAM para uma variável (tipo primitivo ou complexo), ou seja, número de bytes “gastos” para o armazenamento de um dado. Campo

É uma subdivisão de uma célula, ou seja, cada elemento de uma estrutura (struct). No exemplo abaixo, tempo é uma célula e horas, minutos e segundos são os campos. struct TEMPO { int horas;

int minutos; int segundos;

} *tempo;

3. Listas Lineares 3.1 Listas Genéricas Conceito Conjunto de dados que mantém a relação de ordem Linear entre os componentes. É composta de elementos (componentes ou nós), os quais podem conter um dado primitivo ou estruturado. Lista Linear É uma estrutura que permite representar um conjunto de dados de forma a preservar a relação de ordem entre eles.

Page 14: Dados_C

14

Uma lista linear X é um conjunto de nodos (nós) X1, X2, ... Xn, Tais que: 1) Existem “n” nodos na lista (n >= 0) 2) X1 é o primeiro nodo da lista 3) Xn é o último nodo da lista 4) Para todo i,j entre 1 e n, se i<j, então o elemento Xi antecede o elemento Xj 5) Caso i = j-1, Xi é o antecessor de Xj e Xj é o sucessor de Xi

Observação: Quando n=0, diz-se que a Lista é Vazia Exemplos de Listas • Lista de clientes de um Banco • Lista de Chamada • Fichário Operações sobre Listas 1) Percurso Permite utilizar cada um dos elementos de uma lista, de tal forma que: • 0 primeiro nodo utilizado é o primeiro da lista; • Para utilizar o nodo Xj, todos os nodos de X1 até X(j-1) já foram

utilizados; • último nodo utilizado é o último nodo da lista.

Page 15: Dados_C

15

2) Busca Procura um nodo específico da lista linear, de tal forma que: • nodo é identificado por sua posição na lista; • nodo é identificado pelo seu conteúdo. 3) Inserção Acrescenta um nodo X a uma lista linear, de tal forma que: • nodo X terá um sucessor e/ou um antecessor; • Após inserir o nodo X na posição i (i >= 1 e i <= n+1), ele passará a

ser i-ésimo nodo da lista; • número de elementos (n) é acrescido de uma unidade.

4) Retirada (Exclusão) Retira um nodo X da lista, de tal forma que: • Se Xi é o elemento retirado, o seu sucessor passa a ser o sucessor de

seu antecessor. X(i+1) passa a ser o sucessor de X(i-1). Se Xi é o primeiro nodo, o seu sucessor passa a ser o primeiro, se Xi é o último, o seu antecessor passa a ser o último;

• número de elementos (n) é decrescido de uma unidade. Operações Válidas sobre Listas • Acessar um elemento qualquer da lista; • Inserir um novo elemento à lista; • Concatenar duas listas; • Determinar o número de elementos da lista; • Localizar um elemento da lista com um determinado valor; • Excluir um elemento da lista; • Alterar um elemento da lista; • Criar uma lista;

Page 16: Dados_C

16

• Destruir a lista.

3.2 Tipos de Representações 3.2.1 Lista Representada por Contigüidade Física Os nodos são armazenados em endereços contíguos, ou igualmente distanciados um do outro. Os elementos são armazenados na memória um ao lado do outro, levando-se em consideração o tipo de dado, ou seja, a quantidade de bytes. Se o endereço do nodo Xi é conhecido, então o endereço do nodo X(i+1) pode ser determinado.

• Os relacionamentos são representados pela disposição física dos

componentes na memória; • A posição na estrutura lógica determina a posição na estrutura física. Observação: Uma lista pode ser implementada através de um vetor de “m” elementos. Atenção: Se “n’ = “m” a Lista é chamada Cheia Observação: Como o número de nodos armazenados na lista pode ser modificado durante a execução do programa, deve-se representar como parte de um vetor de “m” elementos com “n <= m”.

Page 17: Dados_C

17

Representação: A lista X está representada por um vetor V de “m” elementos. Componentes de uma Lista • Número de nodos da lista (n); • Vetor de nodos (v); • Tamanho total da lista (m).

#define m 7 typedef float TDADOS; typedef struct { int n; TDADOS v[m]; } TLISTA; TLISTA l;

Page 18: Dados_C

18

Observação: Considera-se que o primeiro nodo da lista será armazenado na primeira posição do vetor. Exemplo: typedef int TDADOS; #define SUCESSO 1 #define LISTA_CHEIA 2 // ------------------------------------ Cria_Lista void Cria_Lista(TLISTA *x) { x->n = 0; } // ------------------------------------ Inclui_Fim int Inclui_Fim(TLISTA *x, TDADOS no) { if (x->n == m) return(LISTA_CHEIA); else { x->v[x->n] = no; x->n = x->n + 1; return(SUCESSO); } } // ------------------------------------ Inclui_Inicio int Inclui_Inicio(TLISTA *x, TDADOS no) { int i; if (x->n == m) return(LISTA_CHEIA); else { for (i = x->n-1;i >= 0;i--) x->v[i+1] = x->v[i]; x->v[0] = no; x->n = x->n + 1; return(SUCESSO); } }

Page 19: Dados_C

19

4) Incluir dados em uma lista de números inteiros (máximo 10 elementos), mantendo-a ordenada. Solução do problema proposto (4): // Inserir Lista mantendo Ordenada #include <stdio.h> // ---------------------------------------------- Definições #define m 5 #define SUCESSO 0 #define LISTA_CHEIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int n; TDADOS v[m]; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *x); int Inclui_Fim(TLISTA *x, TDADOS dado); int Inclui_Inicio(TLISTA *x, TDADOS dado); int Inclui_Posicao(TLISTA *x, TDADOS dado, int pos); int Verifica_Posicao(TLISTA *x, TDADOS dado); void Exibe_Lista(TLISTA x); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) { TLISTA l; TDADOS valor; int erro; Cria_Lista(&l); printf("Valor: "); scanf("%d", &valor); if (valor != 0) { Inclui_Fim(&l,valor);

Page 20: Dados_C

20

do { Exibe_Lista(l); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { erro = Verifica_Posicao(&l,valor); if (erro) Imprime_Erro(erro); } } while (valor != 0 && erro != LISTA_CHEIA); } Exibe_Lista(l); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista(TLISTA *x) { x->n = 0; } // ------------------------------------ Inclui_Fim int Inclui_Fim(TLISTA *x, TDADOS dado) { if (x->n == m) return(LISTA_CHEIA); else { x->v[x->n] = dado; x->n = x->n + 1; return(SUCESSO); } } // ------------------------------------ Inclui_Inicio int Inclui_Inicio(TLISTA *x, TDADOS dado) { int i; if (x->n == m) return(LISTA_CHEIA); else { for (i = x->n-1;i >= 0;i--) x->v[i+1] = x->v[i]; x->v[0] = dado; x->n = x->n + 1;

Page 21: Dados_C

21

return(SUCESSO); } } // ------------------------------------ Inclui_Posicao int Inclui_Posicao(TLISTA *x, TDADOS dado, int pos) { int i; if (x->n == m) return(LISTA_CHEIA); else { for (i = x->n-1;i >= pos;i--) x->v[i+1] = x->v[i]; x->v[pos] = dado; x->n = x->n + 1; return(SUCESSO); } } // ------------------------------------ Verifica_Posicao int Verifica_Posicao(TLISTA *x, TDADOS dado) { int i=0; do { if (dado < x->v[i]) return(Inclui_Posicao(x,dado,i)); i++; } while (i < x->n); return(Inclui_Fim(x,dado)); } // ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case LISTA_CHEIA: printf("ERRO: Lista Cheia\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; } } // ------------------------------------ Exibe_Lista

Page 22: Dados_C

22

void Exibe_Lista(TLISTA x) { int i; printf("Lista Ordenada: "); for (i = 0;i < x.n;i++) printf("%02d ",x.v[i]); } 5) Incluir dados em uma lista linear de números inteiros (máximo 50) sem repetição. O programa termina quando o dado lido for zero, então o programa deve imprimir a lista na tela sem repetição. Solução do problema proposto (5): // Inserir Lista sem Repeticao #include <stdio.h> // ---------------------------------------------- Definições #define m 5 #define TRUE !0 #define FALSE 0 #define SUCESSO 0 #define LISTA_CHEIA 1 #define LISTA_VAZIA 2 #define DADO_REPETIDO 3 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int n; TDADOS v[m]; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *x); int Inclui_Fim(TLISTA *x, TDADOS dado); int Inclui_Inicio(TLISTA *x, TDADOS dado); int Insere_Sem_Repeticao(TLISTA *x, TDADOS dado); void Imprime_Erro(int erro); void Exibe_Lista(TLISTA x); // ------------------------------------ Programa Principal

Page 23: Dados_C

23

int main(void) { TLISTA l; TDADOS valor; int erro; Cria_Lista(&l); printf("Valor: "); scanf("%d", &valor); if (valor != 0) { Inclui_Fim(&l,valor); do { Exibe_Lista(l); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { erro = Insere_Sem_Repeticao(&l,valor); if (erro) Imprime_Erro(erro); } } while (valor != 0 && erro != LISTA_CHEIA); } Exibe_Lista(l); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista(TLISTA *x) { x->n = 0; } // ------------------------------------ Inclui_Fim int Inclui_Fim(TLISTA *x, TDADOS dado) { if (x->n == m) return(LISTA_CHEIA); else { x->v[x->n] = dado; x->n = x->n + 1; return(SUCESSO); } } // ------------------------------------ Inclui_Inicio

Page 24: Dados_C

24

int Inclui_Inicio(TLISTA *x, int dado) { int i; if (x->n == m) return(LISTA_CHEIA); else { for (i = x->n-1;i >= 0;i--) x->v[i+1] = x->v[i]; x->v[0] = dado; x->n = x->n + 1; return(SUCESSO); } } // ------------------------------------ Insere_Sem_Repeticao int Insere_Sem_Repeticao(TLISTA *x, TDADOS dado) { int i,achei = FALSE; if (x->n == m) return(LISTA_CHEIA); else { for (i = 0;i < x->n;i++) if (x->v[i] == dado) { achei = TRUE; return(DADO_REPETIDO); } if (!achei) return(Inclui_Fim(x,dado)); } return(SUCESSO); } // ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case LISTA_CHEIA: printf("ERRO: Lista Cheia\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; case DADO_REPETIDO: printf("ERRO: Dado Repetido\n"); break;

Page 25: Dados_C

25

} } // ------------------------------------ Exibe_Lista void Exibe_Lista(TLISTA x) { int i; printf("Lista sem Repetição: "); if (x.n != 0) for (i = 0;i < x.n;i++) printf("%02d ",x.v[i]); } Contigüidade Física Uma alternativa para representação por contigüidade física é não iniciar no início do vetor, isto facilita as inserções.

Observação: As operações de inclusão e exclusão de nodos podem optar pela extremidade da lista que irá diminuir (no caso de exclusão) ou aumentar (no caso de inserção) de comprimento. “A escolha deverá considerar o caso que produz menor movimentação de elementos”. typedef float TDADOS; typedef struct { int inicio;

int fim; TDADOS v[m]; } TLISTA; TLISTA l; Lista vazia

Page 26: Dados_C

26

início = -1 fim = -1 Lista cheia início = 0 fim = m-1 6) Escreva um programa em C que permite a inclusão de números inteiros no início ou no fim da lista linear (máximo 7 elementos). Solução do problema proposto (6): // Lista Linear #include <stdio.h> #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define m 7 #define SUCESSO 0 #define LISTA_CHEIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int inicio; int fim; TDADOS v[m]; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *x); int Inclui_Inicio(TLISTA *x, TDADOS dado); int Inclui_Fim(TLISTA *x, TDADOS dado); void Exibe_Lista(TLISTA x); void Imprime_Erro(int erro); // ---------------------------------------------- Programa Principal int main(void)

Page 27: Dados_C

27

{ TLISTA l; TDADOS valor; int erro; char ch; Cria_Lista(&l); do { Exibe_Lista(l); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { printf("[I]nício ou [F]im ?"); do { ch = toupper(getchar()); } while (!strchr("IF",ch)); switch (ch) { case 'I': erro = Inclui_Inicio(&l,valor); break; case 'F': erro = Inclui_Fim(&l,valor); break; } if (erro) Imprime_Erro(erro); } } while (valor != 0 && erro != LISTA_CHEIA); Exibe_Lista(l); getchar(); } // ---------------------------------------------- Cria_Lista void Cria_Lista(TLISTA *x) { x->inicio = -1; x->fim = -1; } // ---------------------------------------------- Inclui_Inicio int Inclui_Inicio(TLISTA *x, TDADOS dado) { if (x->inicio == -1) { x->inicio = m / 2; x->fim = x->inicio; x->v[x->inicio] = dado; return(SUCESSO); }

Page 28: Dados_C

28

else if (x->inicio == 0) return(LISTA_CHEIA); else { x->inicio = x->inicio - 1; x->v[x->inicio] = dado; return(SUCESSO); } } // ---------------------------------------------- Inclui_Fim int Inclui_Fim(TLISTA *x, TDADOS dado) { if (x->fim == -1) { x->inicio = m / 2; x->fim = x->inicio; x->v[x->fim] = dado; return(SUCESSO); } else if (x->fim == m-1) return(LISTA_CHEIA); else { x->fim = x->fim + 1; x->v[x->fim] = dado; return(SUCESSO); } } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case LISTA_VAZIA: printf("\nERRO: Lista Vazia"); break; case LISTA_CHEIA: printf("\nERRO: Lista Cheia"); break; } } // ---------------------------------------------- Exibe_Lista void Exibe_Lista(TLISTA x) { int i;

Page 29: Dados_C

29

printf("\nLista: "); if (x.inicio != -1) for (i = x.inicio;i <= x.fim;i++) printf("%02d ",x.v[i]); else printf("VAZIA"); } 7) Escreva um programa em C que permite a inclusão de números inteiros no início ou no fim da lista linear (máximo 7 elementos) avisando qual lado está cheio. Observação: Note que a lista pode estar cheia num lado e não estar cheia no outro lado. A próxima solução (7) avisa ao usuário qual lado da lista linear está cheio. Solução do problema proposto (7): // Lista Linear no meio do Vetor #include <stdio.h> #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define m 7 #define SUCESSO 0 #define LISTA_CHEIA_ESQUERDA 1 #define LISTA_CHEIA_DIREITA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int inicio; int fim; TDADOS v[m]; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *x); int Inclui_Inicio(TLISTA *x, TDADOS dado); int Inclui_Fim(TLISTA *x, TDADOS dado); void Imprime_Erro(int erro);

Page 30: Dados_C

30

void Exibe_Lista(TLISTA x); // ---------------------------------------------- Programa Principal int main(void) { TLISTA l; TDADOS valor; int erro; char ch; Cria_Lista(&l); do { Exibe_Lista(l); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { printf("[I]nício ou [F]im ?"); do { ch = toupper(getchar()); } while (!strchr("IF",ch)); switch (ch) { case 'I': erro = Inclui_Inicio(&l,valor); break; case 'F': erro = Inclui_Fim(&l,valor); break; } if (erro) Imprime_Erro(erro); } } while (valor != 0); Exibe_Lista(l); getchar(); } // ---------------------------------------------- Cria_Lista void Cria_Lista(TLISTA *x) { x->inicio = -1; x->fim = -1; } // ---------------------------------------------- Inclui_Inicio int Inclui_Inicio(TLISTA *x, TDADOS dado) { if (x->inicio == -1) {

Page 31: Dados_C

31

x->inicio = m / 2; x->fim = x->inicio; x->v[x->inicio] = dado; return(SUCESSO); } else if (x->inicio == 0) return(LISTA_CHEIA_ESQUERDA); else { x->inicio = x->inicio - 1; x->v[x->inicio] = dado; return(SUCESSO); } } // ---------------------------------------------- Inclui_Fim int Inclui_Fim (TLISTA *x, TDADOS dado) { if (x->fim == -1) { x->inicio = m / 2; x->fim = x->inicio; x->v[x->fim] = dado; return(SUCESSO); } else if (x->fim == m-1) return(LISTA_CHEIA_DIREITA); else { x->fim = x->fim + 1; x->v[x->fim] = dado; return(SUCESSO); } } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case LISTA_CHEIA_ESQUERDA: printf("\nERRO: Lista Cheia na ESQUERDA"); break; case LISTA_CHEIA_DIREITA: printf("\nERRO: Lista Cheia na DIREITA"); break; } }

Page 32: Dados_C

32

// ---------------------------------------------- Exibe_Lista void Exibe_Lista(TLISTA x) { int i; printf("\nLista: "); if (x.inicio == -1) printf("VAZIA"); else for (i = x.inicio;i <= x.fim;i++) printf("%02d ",x.v[i]); } 8) Escreva um programa em C que permite a inclusão de números inteiros em uma lista linear no início, fim e na posição escolhida pelo usuário Solução do problema proposto (8): // Lista Linear meio do vetor #include <stdio.h> #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define m 7 #define SUCESSO 0 #define LISTA_CHEIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int inicio; int fim; TDADOS v[m]; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *x); int Inclui_Inicio(TLISTA *x, TDADOS dado); int Inclui_Fim(TLISTA *x, TDADOS dado);

Page 33: Dados_C

33

int Inclui_Posicao(TLISTA *x, TDADOS dado, int pos); void Exibe_Lista(TLISTA x); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) { TLISTA l; TDADOS valor; int erro,pos,inic,fim; char ch; Cria_Lista(&l); do { Exibe_Lista(l); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { printf("[I]nício, [P]osição ou [F]im ?"); do { ch = toupper(getchar()); } while (!strchr("IFP",ch)); switch (ch) { case 'I': erro = Inclui_Inicio(&l,valor); break; case 'F': erro = Inclui_Fim(&l,valor); break; case 'P': if (l.inicio == -1) inic = 1; else if (l.inicio == 0) inic = 1; else inic = l.inicio +1; if (l.fim == -1) fim = m; else fim = l.fim + 1; printf("\n"); do { printf("Posição [%d..%d]: ",inic,fim); scanf("%d",&pos); } while (!(pos >= inic && pos <= fim)); erro = Inclui_Posicao(&l,valor,pos-1); break; } } if (erro)

Page 34: Dados_C

34

Imprime_Erro(erro); } while (valor != 0); Exibe_Lista(l); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista(TLISTA *x) { x->inicio = -1; x->fim = -1; } // ------------------------------------ Inclui_Inicio int Inclui_Inicio(TLISTA *x, TDADOS dado) { if (x->inicio == -1) { x->inicio = m / 2; x->fim = x->inicio; x->v[x->inicio] = dado; return(SUCESSO); } else if (x->inicio == 0) return(LISTA_CHEIA); else { x->inicio = x->inicio - 1; x->v[x->inicio] = dado; return(SUCESSO); } } // ------------------------------------ Inclui_Fim int Inclui_Fim(TLISTA *x, TDADOS dado) { if (x->fim == -1) { x->fim = m / 2; x->inicio = x->fim; x->v[x->fim] = dado; return(SUCESSO); } else if (x->fim == m-1) return(LISTA_CHEIA); else

Page 35: Dados_C

35

{ x->fim = x->fim + 1; x->v[x->fim] = dado; return(SUCESSO); } } // ----------------------------------- Inclui_Posicao int Inclui_Posicao(TLISTA *x, TDADOS dado, int pos) { int i,erro; if (x->inicio == -1) { x->inicio = pos; x->fim = pos; x->v[x->inicio] = dado; return(SUCESSO); } else if (x->inicio == 0 && x->fim == m-1) return(LISTA_CHEIA); else if (pos == x->inicio-1) { erro = Inclui_Inicio(x,dado); return(erro); } else if (pos == x->fim+1) { erro = Inclui_Fim(x,dado); return(erro); } else { for (i = x->fim;i >= pos;i--) x->v[i+1] = x->v[i]; x->v[pos] = dado; x->fim = x->fim + 1; return(SUCESSO); } } // ----------------------------------- Exibe_Lista void Exibe_Lista(TLISTA x) { int i;

Page 36: Dados_C

36

printf("\nLista: "); if (x.inicio == -1) printf("VAZIA"); else for (i = x.inicio;i <= x.fim;i++) printf("%02d ",x.v[i]); } // ----------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case LISTA_CHEIA: printf("ERRO: Lista Cheia\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; } } Vantagens e Desvantagens da Representação por Contigüidade Física Vantagens

• A consulta pode ser calculada (acesso randômico aos dados); • Facilita a transferência de dados (área de memória contígua); • Adequada para o armazenamento de estruturas simples.

Desvantagens

• O tamanho máximo da lista precisa ser conhecido e alocado antecipadamente, pois a lista é alocada estaticamente na memória;

• Inserções e remoções podem exigir considerável movimentação de dados;

• Inadequada para o armazenamento de estruturas complexas; • Mantém um espaço de memória ocioso (não ocupado); • Como a lista é limitada, devem ser testados os limites.

3.2.2 Lista representada por Encadeamento Permite Alocação Dinâmica de Memória, ou seja, a lista cresce com a execução do programa. Operações como inserção e remoção são mais simples. Isto é feito através de variáveis do tipo ponteiro, ou seja,

Page 37: Dados_C

37

um elemento aponta (possui o endereço, posição de memória do próximo elemento) para o próximo.

9) Escreva um programa em C que permite incluir números inteiros em uma lista encadeada. Solução do problema proposto (9): // Lista Encadeada #include <stdio.h> #include <stdlib.h> // ------------------------------------ Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS dado; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; } TLISTA; // ------------------------------------ Prototypes void Cria_Lista (TLISTA *l); int Inclui_Lista (TLISTA *l, TDADOS d); int Remove_Primeiro(TLISTA *l); int Conta_Elementos_Lista(TLISTA l); int Remove_Ultimo(TLISTA *l); void Imprime_Erro(int erro);

Page 38: Dados_C

38

void destroi_Lista(TLISTA *l); // ------------------------------------ Programa Principal int main(void) { TLISTA l; TDADOS d; int n,erro; Cria_Lista(&l); do { printf("Valor: "); scanf("%d",&d); if (d != 0) { erro = Inclui_Lista(&l,d); if (erro) { Imprime_Erro(erro); break; } } } while (d != 0); n = Conta_Elementos_Lista(l); printf("Número de Elementos: %d\n",n); Destroi_Lista(&l); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista (TLISTA *l) { l->primeiro = NULL; } // ------------------------------------ Inclui_Lista int Inclui_Lista (TLISTA *l, TDADOS d) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { if (l->primeiro == NULL) { l->primeiro = p;

Page 39: Dados_C

39

p->dado = d; p->elo = NULL; } else { p->dado = d; p->elo = l->primeiro; l->primeiro = p; } return(SUCESSO); } } // ------------------------------------ Remove_Primeiro int Remove_Primeiro(TLISTA *l) { TNODO *p; if (l->primeiro == NULL) return(LISTA_VAZIA); else { p = l->primeiro; l->primeiro = p->elo; free(p); return(SUCESSO); } } // ------------------------------------ Conta_elementos int Conta_Elementos_Lista(TLISTA l) { TNODO *p; int n = 0; if (l.primeiro == NULL) return(n); else { p = l.primeiro; while (p != NULL) { n++; p = p->elo; } return(n); } }

Page 40: Dados_C

40

// ------------------------------------ Remove_Ultimo int Remove_Ultimo(TLISTA *l) { TNODO *p,*q; if (l->primeiro == NULL) return(LISTA_VAZIA); else { q = l->primeiro; p = l->primeiro; while (p->elo != NULL) { q = p; p = p->elo; } if (l->primeiro == q) l->primeiro = NULL; else q->elo = NULL; free(p); return(SUCESSO); } } // ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: Falta de Memória\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; } getchar(); } // ------------------------------------------ Destroi_Lista void Destroi_Lista(TLISTA *l) { TNODO *p; if (l->primeiro != NULL) { p = l->primeiro; while (p != NULL) {

Page 41: Dados_C

41

l->primeiro = p->elo; free(p); p = l->primeiro; } } } 10) Escrever um programa em C que insere dados em uma lista encadeada, permitindo obter o conteúdo do último elemento, imprimindo também, toda a lista. Solução do problema proposto (10): // Lista Encadeada #include <stdio.h> #include <stdlib.h> // ------------------------------------ Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS dado; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; } TLISTA; // ------------------------------------ Prototypes void Cria_Lista (TLISTA *l); int Inclui_Lista (TLISTA *l, TDADOS dado); int Consulta_Ultimo(TLISTA l,TDADOS *dado); int Imprime_Lista(TLISTA l); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) {

Page 42: Dados_C

42

TLISTA l; TDADOS valor; int erro; Cria_Lista(&l); do { printf("Valor: "); scanf("%d",&valor); if (valor != 0) { erro = Inclui_Lista(&l,valor); if (erro) { Imprime_Erro(erro); break; } } } while (valor != 0); erro = Consulta_Ultimo(l,&valor); if (erro == SUCESSO) { printf("Último Elemento: %d\n",valor); Imprime_Lista(l); } else Imprime_Erro(erro); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista (TLISTA *l) { l->primeiro = NULL; } // ------------------------------------ Inclui_Lista int Inclui_Lista (TLISTA *l, TDADOS d) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { if (l->primeiro == NULL) { l->primeiro = p; p->dado = d;

Page 43: Dados_C

43

p->elo = NULL; } else { p->dado = d; p->elo = l->primeiro; l->primeiro = p; } return(SUCESSO); } } // ------------------------------------ Consulta_Ultimo int Consulta_Ultimo(TLISTA l,TDADOS *dado) { TNODO *p; if (l.primeiro == NULL) return(LISTA_VAZIA); else { p = l.ultimo; *dado = p->dado; return(SUCESSO); } } // ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: Falta de Memória\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; } } // ------------------------------------ Imprime_Lista int Imprime_Lista(TLISTA l) { TNODO *p; if (l.primeiro == NULL) return(LISTA_VAZIA); else {

Page 44: Dados_C

44

printf("Lista Encadeada: "); p = l.primeiro; while (p != NULL) { printf("%02d ",p->dado); p = p->elo; } return(SUCESSO); } } 11) Escrever um programa em C que permite incluir, excluir e consultar (no início ou fim) dados inteiros em uma lista encadeada. Em cada operação imprimir a lista. Solução do problema proposto (11): // Lista Encadeada #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> // ---------------------------------------------- Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 // ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS dado; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *l); int Inclui_Lista(TLISTA *l, TDADOS valor); int Exclui_Lista(TLISTA *l); int Consulta_Lista(TLISTA l, TDADOS *valor);

Page 45: Dados_C

45

void Imprime_Erro(int erro); void Destroi_Lista(TLISTA *l); void Exibe_Primeiro(TLISTA l); void Exibe_Lista(TLISTA l); // ------------------------------- Programa Principal int main(void) { TLISTA l; TDADOS valor; int erro; char tecla; Cria_Lista(&l); do { Exibe_Primeiro(l); Exibe_Lista(l); printf("[I]ncluir, [E]xcluir, [C]onsultar ou [F]inalizar: "); do { tecla = toupper(getchar()); } while (!strchr("IECF",tecla)); switch (tecla) { case 'I': printf("Valor: "); scanf("%d",&valor); erro = Inclui_Lista(&l,valor); break; case 'E': erro = Exclui_Lista(&l); if (!erro) { printf("Ok, Elemento Excluído"); printf(", tecle <enter> para continuar"); } break; case 'C': erro = Consulta_Lista(l,&valor); if (!erro) printf("Valor Consultado: %d",valor); � break; } if (erro && tecla != 'F') Imprime_Erro(erro); } while (tecla != 'F'); Destroi_Lista(&l); } // ------------------------------- Cria_Lista void Cria_Lista(TLISTA *l) { l->primeiro = NULL;

Page 46: Dados_C

46

} // ------------------------------- Inclui_Lista int Inclui_Lista(TLISTA *l, TDADOS dado) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; if (l->primeiro == NULL) { l->primeiro = p; p->elo = NULL; } else { p->elo = l->primeiro; l->primeiro = p; } return(SUCESSO); } } // ------------------------------- Exclui_Lista int Exclui_Lista(TLISTA *l) { TNODO *p; if (l->primeiro == NULL) return(LISTA_VAZIA); else { p = l->primeiro; l->primeiro = p->elo; free(p); return(SUCESSO); } } // ------------------------------- Consulta_Lista int Consulta_Lista(TLISTA l, TDADOS *dado) { TNODO *p;

Page 47: Dados_C

47

if (l.primeiro == NULL) return(LISTA_VAZIA); else { p = l.primeiro; *dado = p->dado; return(SUCESSO); } } // ------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: FALTA DE MEMÓRIA"); break; case LISTA_VAZIA: printf("ERRO: LISTA VAZIA"); break; } printf(", tecle <enter> para continuar"); } // ----------------------------------------- Destroi_Lista void Destroi_Lista(TLISTA *l) { TNODO *p,*q; if (l->primeiro != NULL) { p = l->primeiro; while (p != NULL) { q = p->elo; free(p); p = q; } } Cria_Lista(l); } // ----------------------------------------- Exibe_Primeiro void Exibe_Primeiro(TLISTA l) { if (l.primeiro != NULL) printf("Primeiro: | %p |",l.primeiro); else printf("Primeiro: | NULL |");

Page 48: Dados_C

48

} // ----------------------------------------- Exibe_Lista void Exibe_Lista(TLISTA l) { TNODO *p; printf("Lista Encadeada: "); if (l.primeiro == NULL) printf("LISTA VAZIA"); else { p = l.primeiro; while (p != NULL) { printf("| %02d ",p->dado); p = p->elo; } } } Vantagens das Listas representadas por Encadeamento

• Lista cresce indeterminadamente, enquanto houver memória livre (Alocação Dinâmica de Memória);

• As operações de insersão e remoção de elementos não exige a movimentação dos demais elementos.

Desvantagens das Listas representadas por Encadeamento

• Determinar o número de elementos da lista, pois para tanto, deve-se percorrer toda a lista;

• Acessar diretamente um determinado elemento pela sua posição, pois só é conhecido o primeiro elemento da lista;

• Acessar o último elemento da lista, pois para acessá-lo, deve-se “visitar” todos os intermediários.

3.2.3 Lista Encadeada com Descritor Como foi visto anteriormente, as dificuldades da lista encadeada, é descobrir o número de elementos e ainda, acessar o último elemento. Estas dificuldades podem ser resolvidas utilizando-se um descritor, da seguinte forma:

Page 49: Dados_C

49

12) Escrever o mesmo programa em C que insere dados em uma lista encadeada com descritor, permitindo obter o conteúdo do último elemento diretamente, imprimindo também, toda a lista. Solução do problema proposto (12): // Lista Encadeada com Descritor #include <stdio.h> #include <stdlib.h> // ------------------------------------ Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 // ------------------------------------ Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS dado; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; int n; TNODO *ultimo; } TDESCRITOR;

Page 50: Dados_C

50

// ------------------------------------ Prototypes void Cria_Lista (TDESCRITOR *d); int Inclui_Esquerda (TDESCRITOR *d, TDADOS dado); int Inclui_Direita (TDESCRITOR *d, TDADOS dado); int Consulta_Ultimo(TDESCRITOR d,TDADOS *dado); void Imprime_Lista(TDESCRITOR d); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) { TDESCRITOR d; TDADOS dado; char tecla; int erro; Cria_Lista(&d); do { Imprime_Lista(d); printf("\nValor: "); scanf("%d",&dado); if (dado != 0) { printf("[E]squerda ou [D]ireita: "); do { tecla = toupper(getchar()); } while (!strchr("ED",tecla)); switch (tecla) { case 'E': erro = Inclui_Esquerda(&d,dado); break; case 'D': erro = Inclui_Direita(&d,dado); break; } if (erro) { Imprime_Erro(erro); break; } } } while (dado != 0); erro = Consulta_Ultimo(d,&dado); if (erro == SUCESSO) printf("Último Elemento: %d\n",dado); else Imprime_Erro(erro); getchar(); }

Page 51: Dados_C

51

// ------------------------------------ Cria_Lista void Cria_Lista (TDESCRITOR *l) { l->primeiro = NULL; l->n = 0; l->ultimo = NULL; } // ------------------------------------ Inclui_Esquerda int Inclui_Esquerda (TDESCRITOR *d, TDADOS dado) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; p->elo = d->primeiro; d->primeiro = p; if (d->n == 0) d->ultimo = p; (d->n)++; return(SUCESSO); } } // ------------------------------------ Inclui_Direita int Inclui_Direita (TDESCRITOR *d, TDADOS dado) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; p->elo = NULL; if (d->n == 0) d->primeiro = p; else d->ultimo->elo = p; d->ultimo = p; (d->n)++; return(SUCESSO); }

Page 52: Dados_C

52

} // ------------------------------------ Consulta_Ultimo int Consulta_Ultimo(TDESCRITOR d,TDADOS *dado) { TNODO *p; if (d.primeiro == NULL) return(LISTA_VAZIA); else { p = d.primeiro; while (p->elo != NULL) p = p->elo; *dado = p->dado; return(SUCESSO); } } // ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: Falta de Memória\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; } } // ------------------------------------ Imprime_Lista void Imprime_Lista(TDESCRITOR l) { TNODO *p; printf("\nLista Encadeada: "); if (l.primeiro == NULL) printf("VAZIA"); else { p = l.primeiro; while (p != NULL) { printf("%02d ",p->dado); p = p->elo; } }

Page 53: Dados_C

53

} 13) Escrever um programa em C que permite incluir, excluir e consultar (no início ou fim) dados inteiros em uma lista encadeada. Em cada operação imprimir a lista. Solução do problema proposto (13): // Lista Encadeada com Descritor #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> // ----------------------------------------- Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 // ----------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS dado; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; int n; TNODO *ultimo; } TLISTA; // ---------------------------------------------- Prototypes void Cria_Lista(TLISTA *l); int Inclui_Inicio(TLISTA *l, TDADOS valor); int Inclui_Fim(TLISTA *l, TDADOS valor); int Exclui_Inicio(TLISTA *l); int Exclui_Fim(TLISTA *l); int Consulta_Inicio(TLISTA l, TDADOS *valor); int Consulta_Fim(TLISTA l, TDADOS *valor); void Imprime_Erro(int erro); void Destroi_Lista(TLISTA *l); void Exibe_Descritor(TLISTA l); void Exibe_Lista(TLISTA l);

Page 54: Dados_C

54

// ------------------------------- Programa Principal int main(void) { TLISTA l; TDADOS valor; int erro; char tecla1,tecla2; Cria_Lista(&l); do { Exibe_Descritor(l); Exibe_Lista(l); printf("[I]ncluir, [E]xcluir, [C]onsultar ou [F]inalizar: "); do { tecla1 = toupper(getchar()); } while (!strchr("IECF",tecla1)); if (tecla1 != 'F') { printf("[I]nicio ou [F]im: "); do { tecla2 = toupper(getchar()); } while (!strchr("IF",tecla2)); switch (tecla1) { case 'I': printf("Valor: "); scanf("%d",&valor); if (tecla2 == 'I') erro = Inclui_Inicio(&l,valor); else erro = Inclui_Fim(&l,valor); break; case 'E': if (tecla2 == 'I') erro = Exclui_Inicio(&l); else erro = Exclui_Fim(&l); if (!erro) printf("Ok, Elemento Excluído"); break; case 'C': if (tecla2 == 'I') erro = Consulta_Inicio(l,&valor); else erro = Consulta_Fim(l,&valor); if (!erro) printf("Valor Consultado: %d",valor); break; } if (erro) Imprime_Erro(erro); }

Page 55: Dados_C

55

} while (tecla1 != 'F'); Destroi_Lista(&l); } // ------------------------------- Cria_Lista void Cria_Lista(TLISTA *l) { l->primeiro = NULL; l->n = 0; l->ultimo = NULL; } // ------------------------------- Inclui_Inicio int Inclui_Inicio(TLISTA *l, TDADOS dado) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; if (l->n == 0) { l->primeiro = p; l->n = 1; l->ultimo = p; p->elo = NULL; } else { p->elo = l->primeiro; l->primeiro = p; l->n = l->n + 1; } return(SUCESSO); } } // ------------------------------- Inclui_Fim int Inclui_Fim (TLISTA *l, TDADOS dado) { TNODO *p,*q; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA);

Page 56: Dados_C

56

else { p->dado = dado; p->elo = NULL; if (l->n == 0) { l->primeiro = p; l->n = 1; l->ultimo = p; } else { q = l->ultimo; l->ultimo = p; q->elo = p; (l->n)++; } return(SUCESSO); } } // ------------------------------- Exclui_Inicio int Exclui_Inicio(TLISTA *l) { TNODO *p; if (l->n == 0) return(LISTA_VAZIA); else { p = l->primeiro; l->primeiro = p->elo; l->n = l->n - 1; if (l->n == 0) { l->primeiro = NULL; l->ultimo = NULL; } free(p); return(SUCESSO); } } // ------------------------------- Exclui_Fim int Exclui_Fim(TLISTA *l) { TNODO *p,*q;

Page 57: Dados_C

57

if (l->n == 0) return(LISTA_VAZIA); else { p = l->primeiro; while (p->elo != NULL) { q = p; p = p->elo; } l->ultimo = q; q->elo = NULL; l->n = l->n - 1; if (l->n == 0) { l->primeiro = NULL; l->ultimo = NULL; } free(p); return(SUCESSO); } } // ------------------------------- Consulta_Inicio int Consulta_Inicio(TLISTA l, TDADOS *dado) { TNODO *p; if (l.n == 0) return(LISTA_VAZIA); else { p = l.primeiro; *dado = p->dado; return(SUCESSO); } } // ------------------------------- Consulta_Fim int Consulta_Fim(TLISTA l, TDADOS *dado) { TNODO *p; if (l.n == 0) return(LISTA_VAZIA); else { p = l.ultimo; *dado = p->dado;

Page 58: Dados_C

58

return(SUCESSO); } } // ------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: FALTA DE MEMÓRIA"); break; case LISTA_VAZIA: printf("ERRO: LISTA VAZIA"); break; } } // ----------------------------------------- Destroi_Lista void Destroi_Lista(TLISTA *l) { TNODO *p,*q; if (l->n > 0) { p = l->primeiro; while (p != NULL) { q = p->elo; free(p); p = q; } } Cria_Lista(l); } // ----------------------------------------- Exibe_Descritor void Exibe_Descritor(TLISTA l) { if (l.primeiro != NULL && l.ultimo != NULL) printf("Descritor: | %p | %d | %p |",l.primeiro,l.n,l.ultimo); else printf("Descritor: | NULL | 0 | NULL |"); } // ----------------------------------------- Exibe_Lista void Exibe_Lista(TLISTA l) { TNODO *p;

Page 59: Dados_C

59

printf("Lista Encadeada: "); if (l.n == 0) printf("LISTA VAZIA"); else { p = l.primeiro; while (p != NULL) { printf("| %02d ",p->dado); p = p->elo; } } } 3.2.4 Lista Duplamente Encadeada Na lista duplamente encadeada, cada elemento possui um elo para o anterior e o posterior, sendo que a lista pode ter ou não descritor. 14) Escrever um programa em C que insere dados em uma lista duplamente encadeada com descritor. Solução do problema proposto (14): // Lista Duplamente Encadeada com Descritor #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> // --------------------------------------- Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 #define POSICAO_INVALIDA 3 // --------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { struct nodo *anterior; TDADOS dado; struct nodo *posterior; } TNODO;

Page 60: Dados_C

60

typedef struct { TNODO *primeiro; int n; TNODO *ultimo; } TDESCRITOR; // --------------------------------------------- Prototypes void Inicializa_Descritor(TDESCRITOR *d); int Insere_Direita(TDESCRITOR *d, TDADOS dado); int Insere_Esquerda(TDESCRITOR *d, TDADOS dado); void Imprime_Lista_Esquerda(TDESCRITOR d); void Imprime_Lista_Direita(TDESCRITOR d); void Imprime_Erro(int erro); void Exibe_Descritor(TDESCRITOR d); int Insere_Posicao(TDESCRITOR *d, int pos, TDADOS dado); // ------------------------------- Programa Principal int main(void) { TDESCRITOR d; TDADOS valor; int erro,pos; char tecla; Inicializa_Descritor(&d); do { Exibe_Descritor(d); Imprime_Lista_Esquerda(d); Imprime_Lista_Direita(d); printf("\nValor: "); scanf("%d",&valor); if (valor != 0) { printf("Inserir na [E]squerda, [D]ireita ou [P]osição: "); do { tecla = toupper(getchar()); } while (!strchr("EDP",tecla)); switch (tecla) { case 'E': erro = Insere_Esquerda(&d,valor); break; case 'D': erro = Insere_Direita(&d,valor); break; case 'P': printf("\nPosição: "); scanf("%d",&pos); erro = Insere_Posicao(&d,pos,valor); break; }

Page 61: Dados_C

61

if (erro) Imprime_Erro(erro); } } while (valor != 0); } // ------------------------------- Inicializa_Descritor void Inicializa_Descritor(TDESCRITOR *d) { d->primeiro = NULL; d->n = 0; d->ultimo = NULL; } // ------------------------------- Insere_Direita int Insere_Direita (TDESCRITOR *d, TDADOS dado) { TNODO *p,*q; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; p->posterior = NULL; if (d->n == 0) { d->primeiro = p; d->n = 1; d->ultimo = p; p->anterior = NULL; } else { q = d->ultimo; d->ultimo = p; q->posterior = p; p->anterior = q; (d->n)++; } return(SUCESSO); } } // ------------------------------- Insere_Esquerda int Insere_Esquerda(TDESCRITOR *d, TDADOS dado) {

Page 62: Dados_C

62

TNODO *p,*q; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->dado = dado; p->anterior = NULL; if (d->n == 0) { d->primeiro = p; d->n = 1; d->ultimo = p; p->posterior = NULL; } else { q = d->primeiro; d->primeiro = p; q->anterior = p; p->posterior = q; (d->n)++; } return(SUCESSO); } } // ------------------------------- Imprime_Lista_Direita void Imprime_Lista_Direita(TDESCRITOR d) { TNODO *p; printf("\nLista pela Direita: "); p = d.ultimo; while (p != NULL) { printf("%02d ",p->dado); p = p->anterior; } } // ------------------------------- Imprime_Lista_Esquerda void Imprime_Lista_Esquerda(TDESCRITOR d) { TNODO *p; printf("\nLista pela Esquerda: ");

Page 63: Dados_C

63

p = d.primeiro; while (p != NULL) { printf("%02d ",p->dado); p = p->posterior; } } // ------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: Falta de Memória\n"); break; case LISTA_VAZIA: printf("ERRO: Lista Vazia\n"); break; case POSICAO_INVALIDA: printf("ERRO: Posição Inválida\n"); break; } } // ------------------------------- Exibe_Descritor void Exibe_Descritor(TDESCRITOR d) { printf("\nDescritor: %p | %d | %p |",d.primeiro,d.n,d.ultimo); } // ------------------------------- Insere_Posicao int Insere_Posicao(TDESCRITOR *d, int pos, TDADOS dado) { TNODO *p,*q; int i; if (pos < 1 || pos > d->n+1) return(POSICAO_INVALIDA); else if (pos == 1) return(Insere_Esquerda(d,dado)); else if (pos == d->n+1) return(Insere_Direita(d,dado)); else { p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else

Page 64: Dados_C

64

{ p->dado = dado; q = d->primeiro; i = 1; while (i < pos-1) { q = q->posterior; i++; } p->anterior = q; p->posterior = q->posterior; q->posterior = p; p->posterior->anterior = p; (d->n)++; return(SUCESSO); } } }

Page 65: Dados_C

65

a) Preencha os campos de elo com os valores adequados para que seja representada a seqüência (A, B, C, D): (sem descritor)

Preencha os campos de elo com os valores adequados para que seja representada a seqüência (A, B, C, D, E, F): (com descritor)

Page 66: Dados_C

66

Page 67: Dados_C

67

Vantagens e Desvantagens das Listas Duplamente Encadeadas Vantagens

• Inserção e remoção de componentes sem movimentar os demais; • Pode-se ter qualquer quantidade de elementos, limitado pela

memória livre, pois cada elemento é alocado dinamicamente. Desvantagens

• Gerência de memória mais onerosa, alocação / desalocação para cada elemento;

• Procedimentos mais complexos; • Processamento serial (Acesso Seqüencial).

3.2.5 Listas Lineares com Disciplina de Acesso São tipos especiais de Listas Lineares, onde inserção, consulta e exclusão são feitas somente nos extremos. Estas listas lineares com disciplina de acesso são: Filas, Pilhas e Deques. 3.2.5.1 Filas É uma Lista Linear na qual as inserções são feitas no fim e as exclusões e consultas no início da fila.

Critério de Utilização FIFO - "First In First Out" (primeiro a entrar é o primeiro a sair) Operações sobre Filas Cria_Fila(f); Cria FILA Vazia Destroi_Fila(f); Desfaz a FILA erro = Insere_ Fila(f, i); Insere o dado "i" no fim da FILA erro = Exclui_ Fila (f); Retira da FILA o primeiro elemento erro = Consulta_ Fila (f, j); Copia em "j" o primeiro elemento da FILA

Page 68: Dados_C

68

Erros nas operações sobre Filas: FILA CHEIA ou FILA VAZIA

15) Escrever um programa em C que insere, exclui e consulta dados (números inteiros) em uma fila. Solução do problema proposto (15): 3.2.5.1.1 Fila com Vetor // Fila em Vetor #include <stdio.h> #include <string.h> #include <ctype.h> // -------------------------------- Definições #define m 9 #define SUCESSO 0 #define FILA_CHEIA 1 #define FILA_VAZIA 2 // -------------------------------- Tipos de Dados

Page 69: Dados_C

69

typedef int TDADOS; typedef struct { int primeiro; int ultimo; TDADOS elem[m]; } FILA; // -------------------------------- Prototypes void Cria_Fila(FILA *f); int Insere_Fila(FILA *f, TDADOS dado); int Exclui_Fila(FILA *f); int Consulta_Fila(FILA f, TDADOS *dado); void Imprime_Erro(int erro); // ----------------------------------- Programa Principal int main(void) { FILA f; TDADOS valor; int erro; char ch; Cria_Fila(&f); do { printf("[I]ncluir, [E]xcluir, [C]onsultar ou [F]im?"); do { ch = toupper(getchar()); } while (!strchr("IECF",ch)); switch (ch) { case 'I': printf("\nValor: "); scanf("%d",&valor); erro = Insere_Fila(&f,valor); break; case 'E': erro = Exclui_Fila(&f); break; case 'C': erro = Consulta_Fila(f,&valor); if (erro == SUCESSO) printf("\nPrimeiro da Fila: %d\n",valor); break; } if (erro && ch != 'F') Imprime_Erro(erro); } while (ch != 'F'); } // ------------------------------------ Cria_Fila

Page 70: Dados_C

70

void Cria_Fila(FILA *f) { f->primeiro = 0; f->ultimo = -1; } // ------------------------------------ Insere_Fila int Insere_Fila(FILA *f, TDADOS dado) { if (f->ultimo == m-1) return(FILA_CHEIA); else { (f->ultimo)++; f->elem[f->ultimo] = dado; return(SUCESSO); } } // -------------------------------- Exclui_Fila int Exclui_Fila(FILA *f) { if (f->ultimo == -1) return(FILA_VAZIA); else { printf("\nExcluido\n"); (f->primeiro)++; if (f->primeiro > f->ultimo) { f->primeiro = 0; f->ultimo = -1; } return(SUCESSO); } } // ------------------------------- Consulta_Fila int Consulta_Fila(FILA f, TDADOS *dado) { if (f.ultimo == -1) return(FILA_VAZIA); else { *dado = f.elem[f.primeiro]; return(SUCESSO); } }

Page 71: Dados_C

71

// ------------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FILA_CHEIA: printf("\nERRO: Lista Cheia\n"); break; case FILA_VAZIA: printf("\nERRO: Lista Vazia\n"); break; } } Exemplo: FILA VAZIA

Inclusão de: 3, 5, 4, 7, 8 e 6

Exclusão dos três primeiros elementos: 3, 5 e 4

Problema com as Filas A reutilização da fila depois que alguns elementos foram extraídos. Solução deste Problema

Page 72: Dados_C

72

Para reutilizar “as vagas” dos elementos já extraídos, deve-se usar uma Fila Circular. 3.2.5.1.2 Fila Circular

16) Escrever um programa em C que insere, exclui e consulta dados (números inteiros) em uma fila circular.

Solução do problema proposto (16): // Fila Circular #include <stdio.h> #include <string.h> #include <ctype.h> // -------------------------------- Definições #define m 9 #define SUCESSO 0 #define FILA_CHEIA 1 #define FILA_VAZIA 2 // -------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int primeiro; int ultimo; int tamanho; TDADOS elem[m]; } FILA; // -------------------------------- Prototypes

Page 73: Dados_C

73

void Cria_Fila_Circular(FILA *f); int Insere_Fila_Circular(FILA *f, TDADOS dado); int Exclui_Fila_Circular(FILA *f); int Consulta_Fila_Circular(FILA f, TDADOS *dado); void Imprime_Erro(int erro); // ----------------------------------- Programa Principal int main(void) { FILA f; TDADOS valor; int erro; char ch; Cria_Fila_Circular(&f); do { printf("[I]ncluir, [E]xcluir, [C]onsultar ou [F]im?"); do { ch = toupper(getchar()); } while (!strchr("IECF",ch)); switch (ch) { case 'I': printf("\nValor: "); scanf("%d",&valor); erro = Insere_Fila_Circular(&f,valor); break; case 'E': erro = Exclui_Fila_Circular(&f); break; case 'C': erro = Consulta_Fila_Circular(f,&valor); if (erro == SUCESSO) printf("\nPrimeiro da Fila: %d\n",valor); break; } if (erro && ch != 'F') Imprime_Erro(erro); } while (ch != 'F'); } // ------------------------------------ Cria_Fila_Circular void Cria_Fila_Circular(FILA *f) { f->primeiro = 0; f->ultimo = -1; f->tamanho = 0; } // ------------------------------------ Insere_Fila_Circular

Page 74: Dados_C

74

int Insere_Fila_Circular(FILA *f, TDADOS dado) { if (f->tamanho == m) return(FILA_CHEIA); else { (f->tamanho)++; f->ultimo = (f->ultimo + 1) % m; f->elem[f->ultimo] = dado; return(SUCESSO); } } // -------------------------------- Exclui_Fila_Circular int Exclui_Fila_Circular(FILA *f) { if (f->tamanho == 0) return(FILA_VAZIA); else { printf("\nExcluido\n"); (f->tamanho)--; f->primeiro = (f->primeiro + 1) % m; return(SUCESSO); } } // ------------------------------- Consulta_Fila_Circular int Consulta_Fila_Circular(FILA f, TDADOS *dado) { if (f.tamanho == 0) return(FILA_VAZIA); else { *dado = f.elem[f.primeiro]; return(SUCESSO); } } // ------------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FILA_CHEIA: printf("\nERRO: Lista Cheia\n"); break; case FILA_VAZIA: printf("\nERRO: Lista Vazia\n"); break;

Page 75: Dados_C

75

} } Como calcular a nova posição:

3.2.5.1.3 Fila com Alocação Dinâmica 17) Escrever um programa em C que insere, exclui e consulta dados (números inteiros) em uma fila alocada dinamicamente.

Solução do problema proposto (17): // Fila Dinamica #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> // -------------------------------- Definições #define SUCESSO 0 #define FILA_VAZIA 1 #define FALTA_DE_MEMORIA 2 // -------------------------------- Tipos de Dados

Page 76: Dados_C

76

typedef int TDADOS; typedef struct nodo { TDADOS info; struct nodo *seg; } TNODO; typedef struct { TNODO *primeiro; int tamanho; TNODO *ultimo; } TFILA; // ------------------------------------ Prototypes void Cria_Fila(TFILA *f); int Inserir_Fila(TFILA *f, TDADOS dado); int Excluir_Fila (TFILA *f); int Consultar_Fila (TFILA f, TDADOS *dado); void Destroi_Fila(TFILA *f); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) { TFILA f; TDADOS valor; int erro; char tecla; Cria_Fila(&f); do { printf("[I]ncluir, [E]xcluir, [C]onsultar ou [F]im?"); do { tecla = toupper(getchar()); } while (!strchr("IECF",tecla)); switch (tecla) { case 'I': printf("\nValor: "); scanf("%d",&valor); erro = Inserir_Fila(&f,valor); break; case 'E': erro = Excluir_Fila(&f); if (erro) Imprime_Erro(erro); else printf("\nElemento Excluido\n"); break; case 'C': erro = Consultar_Fila(f,&valor); if (erro)

Page 77: Dados_C

77

Imprime_Erro(erro); else printf("\nValor: %d\n",valor); break; } } while (tecla != 'F'); Destroi_Fila(&f); } // ---------------------------------------------- Cria_Fila void Cria_Fila(TFILA *f) { f->primeiro = NULL; f->tamanho = 0; f->ultimo = NULL; } // ---------------------------------------------- Inserir_Fila int Inserir_Fila(TFILA *f, TDADOS dado) { TNODO *t; t = (TNODO *) malloc(sizeof(TNODO)); if (t == NULL) return(FALTA_DE_MEMORIA); else { t->info = dado; t->seg = NULL; f->tamanho = f->tamanho + 1; if (f->ultimo != NULL) f->ultimo->seg = t; f->ultimo = t; if (f->primeiro == NULL) f->primeiro = t; return(SUCESSO); } } // ---------------------------------------------- Excluir_Fila int Excluir_Fila(TFILA *f) { TNODO *t; if (f->primeiro == NULL) return(FILA_VAZIA); else {

Page 78: Dados_C

78

t = f->primeiro; f->tamanho = f->tamanho - 1; f->primeiro = t->seg; free(t); if (f->primeiro == NULL) { f->ultimo = NULL; return(FILA_VAZIA); } } return(SUCESSO); } // ---------------------------------------------- Consultar_Fila int Consultar_Fila (TFILA f, TDADOS *dado) { if (f.primeiro == NULL) return(FILA_VAZIA); else *dado = f.primeiro->info; return(SUCESSO); } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FILA_VAZIA: printf("\nERRO: Fila Vazia\n"); break; case FALTA_DE_MEMORIA: printf("\nERRO: Falta de Memória\n"); break; } } // ---------------------------------------------- Destroi_Fila void Destroi_Fila(TFILA *f) { while (f->tamanho != 0) Excluir_Fila(f); }

Page 79: Dados_C

79

3.2.5.2 Pilhas É uma Lista Linear na qual as inserções, exclusões e consultas são feitas em um mesmo extremo (TOPO).

Critério de Utilização LIFO - "Last In First Out" (último a entrar é o primeiro a sair) Operações sobre Pilhas Cria_Pilha(p); Cria pilha Vazia Destroi_Pilha(p); Desfaz a pilha erro = Push(p, i); Empilha o dado "i" no fim da PILHA erro = Pop(p, i); Desempilha o primeiro elemento erro = Consulta_Pilha(p,j); Copia em "j" o primeiro elemento Identificadores da Pilha B(p) Base da PILHA T(p) Topo da PILHA L(p) Limite da PILHA 3.2.5.2.1 Pilha com Vetor

18) Escreva um programa em C que insere (Push), exclui (Pop) e consulta dados (números inteiros) em uma Pilha alocada estaticamente.

Page 80: Dados_C

80

Solução do problema proposto (18): // Pilha com Vetor #include <stdio.h> #include <string.h> #include <ctype.h> // -------------------------------- Definições #define m 7 #define SUCESSO 0 #define PILHA_CHEIA 1 #define PILHA_VAZIA 2 // -------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int topo; TDADOS elem[m]; } TPILHA; // -------------------------------- Prototypes void Cria_Pilha(TPILHA *p); int Push(TPILHA *p, TDADOS dado); int Pop(TPILHA *p, TDADOS *dado); int Consulta_Pilha(TPILHA p, TDADOS *dado); void Imprime_Erro(int erro); // -------------------------------- Programa Principal int main(void) { TPILHA p; char tecla; TDADOS valor; int erro; Cria_Pilha(&p); do { printf("[P]ush, p[O]p, [C]onsultar ou [F]im? "); do { tecla = toupper(getchar()); } while (!strchr("POCF",tecla)); switch (tecla) { case 'P': printf("\nValor: ");

Page 81: Dados_C

81

scanf("%d",&valor); erro = Push(&p,valor); if (erro) Imprime_Erro(erro); break; case 'O': erro = Pop(&p,&valor); if (erro) Imprime_Erro(erro); else printf("\nValor: %d\n",valor); break; case 'C': erro = Consulta_Pilha(p,&valor); if (erro) Imprime_Erro(erro); else printf("\nValor: %d\n",valor); break; } } while (tecla != 'F'); } // ---------------------------------------------- Cria_Pilha void Cria_Pilha(TPILHA *p) { p->topo = -1; } // ---------------------------------------------- Push int Push(TPILHA *p, TDADOS dado) { if (p->topo == m-1) return(PILHA_CHEIA); else { p->topo = p->topo + 1; p->elem[p->topo] = dado; return(SUCESSO); } } // ---------------------------------------------- Pop int Pop(TPILHA *p, TDADOS *dado) { if (p->topo == -1) return(PILHA_VAZIA); else { *dado = p->elem[p->topo];

Page 82: Dados_C

82

p->topo = p->topo - 1; return(SUCESSO); } } // ---------------------------------------------- Consulta_Pilha int Consulta_Pilha(TPILHA p, TDADOS *dado) { if (p.topo == -1) return(PILHA_VAZIA); else { *dado = p.elem[p.topo]; return(SUCESSO); } } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case PILHA_CHEIA: printf("\nERRO: Pilha Cheia\n"); break; case PILHA_VAZIA: printf("\nERRO: Pilha Vazia\n"); break; } } 19) Escreva um programa em C que simula uma Torre de Hanoi.

Solução do problema proposto (19): // Torre de Hanoi #include <stdio.h> #include <conio.h> // Turbo C++ 1.01 ou Turbo C 2.01 #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define m 3 #define SUCESSO 0 #define PILHA_CHEIA 1 #define PILHA_VAZIA 2 #define MOVIMENTO_INVALIDO 3

Page 83: Dados_C

83

// ---------------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int topo; TDADOS elem[m]; } TPILHA; // ---------------------------------------------- Prototypes void Cria_Pilha(TPILHA *p); int Push(TPILHA *p, TDADOS dado); int Pop(TPILHA *p, TDADOS *dado); int Consulta_Pilha(TPILHA p, TDADOS *dado); void Lista_Pilhas(TPILHA p1, TPILHA p2, TPILHA p3); void Lista_Pilha(TPILHA p1, int col); void Imprime_Erro(int erro); // ---------------------------------------------- Programa Principal int main(void) { TPILHA p1,p2,p3; TDADOS valor,v2; int erro,pilha; char tecla1,tecla2; clrscr(); Cria_Pilha(&p1); Cria_Pilha(&p2); Cria_Pilha(&p3); Push(&p1,30); Push(&p1,20); Push(&p1,10); Lista_Pilhas(p1,p2,p3); do { gotoxy(1,6); printf("\n De: p[1], p[2], p[3] ou [F]im? "); do { tecla1 = toupper(getchar()); } while (!strchr("123F",tecla1)); if (tecla1 != 'F') { gotoxy(1,7); printf("\nPara: p[1], p[2], p[3] ou [F]im? "); do { tecla2 = toupper(getchar()); } while (!strchr("123F",tecla2)); switch (tecla1)

Page 84: Dados_C

84

{ case '1': erro = Pop(&p1,&valor); pilha=1; break; case '2': erro = Pop(&p2,&valor); pilha=2; break; case '3': erro = Pop(&p3,&valor); pilha=3; break; } switch (tecla2) { case '1': if (!erro) { erro = Consulta_Pilha(p1,&v2); if (!erro && valor > v2) Imprime_Erro(MOVIMENTO_INVALIDO); if (erro || valor < v2) Push(&p1,valor); else switch (pilha) { case 1: Push(&p1,valor); break; case 2: Push(&p2,valor); break; case 3: Push(&p3,valor); break; } } break; case '2': if (!erro) { erro = Consulta_Pilha(p2,&v2); if (!erro && valor > v2) Imprime_Erro(MOVIMENTO_INVALIDO); if (erro || valor < v2) Push(&p2,valor); else switch (pilha) { case 1: Push(&p1,valor); break; case 2: Push(&p2,valor); break; case 3: Push(&p3,valor); break; } } break;

Page 85: Dados_C

85

case '3': if (!erro) { erro = Consulta_Pilha(p3,&v2); if (!erro && valor > v2) Imprime_Erro(MOVIMENTO_INVALIDO); if (erro || valor < v2) Push(&p3,valor); else switch (pilha) { case 1: Push(&p1,valor); break; case 2: Push(&p2,valor); break; case 3: Push(&p3,valor); break; } } break; } Lista_Pilhas(p1,p2,p3); if (p2.topo == 2 || p3.topo == 2) { gotoxy(1,10); textcolor(LIGHTRED); printf("Game Over, Congratulations ..."); getchar(); break; } } } while (tecla1 != 'F' && tecla2 != 'F'); } // ---------------------------------------------- Cria_Pilha void Cria_Pilha(TPILHA *p) { p->topo = -1; } // ---------------------------------------------- Push int Push(TPILHA *p, TDADOS dado) { if (p->topo == m-1) return(PILHA_CHEIA); else { p->topo = p->topo + 1; p->elem[p->topo] = dado; return(SUCESSO);

Page 86: Dados_C

86

} } // ---------------------------------------------- Pop int Pop(TPILHA *p, TDADOS *dado) { if (p->topo == -1) return(PILHA_VAZIA); else { *dado = p->elem[p->topo]; p->topo = p->topo - 1; return(SUCESSO); } } // ---------------------------------------------- Consulta_Pilha int Consulta_Pilha(TPILHA p, TDADOS *dado) { if (p.topo == -1) return(PILHA_VAZIA); else { *dado = p.elem[p.topo]; return(SUCESSO); } } // --------------------------------- Lista_Pilhas void Lista_Pilhas(TPILHA p1, TPILHA p2, TPILHA p3) { gotoxy(1,1); printf(" -- -- --"); gotoxy(1,2); printf(" -- -- --"); gotoxy(1,3); printf(" -- -- --"); gotoxy(1,4); printf("-------------"); gotoxy(1,5); printf(" p1 p2 p3"); Lista_Pilha(p1,2); Lista_Pilha(p2,6); Lista_Pilha(p3,10); } // --------------------------------- Lista_Pilha

Page 87: Dados_C

87

void Lista_Pilha(TPILHA p, int col) { int i,l=3; if (p.topo != -1) for (i = 0;i <= p.topo;i++) { gotoxy(col,l); printf("%02d",p.elem[i]); l--; } } // --------------------------------- Imprime_Erro void Imprime_Erro(int erro) { gotoxy(1,10); switch (erro) { case PILHA_CHEIA: printf("Erro: PILHA CHEIA"); break; case PILHA_VAZIA: printf("Erro: PILHA VAZIA"); break; case MOVIMENTO_INVALIDO: printf("Erro: MOVIMENTO INVµLIDO"); break; } getchar(); gotoxy(1,10); delline(); } 3.2.5.2.2 Pilha com Alocação Dinâmica

20) Escreva um programa em C que inclui, exclui e consulta dados em uma Pilha Encadeada.

Page 88: Dados_C

88

Solução do problema proposto (20): // --------------------------------------- Pilha Encadeada #include <stdio.h> #include <string.h> #include <ctype.h> #include <stdlib.h> // ------------------------------------- Defini‡äes #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define PILHA_VAZIA 2 // -------------------------------------- Tipos de Dados typedef int TDADOS; typedef struct nodo { TDADOS info; struct nodo *seg; } TNODO; typedef struct { TNODO *topo; } TPILHA; // -------------------------------------------- Prototypes void Cria_Pilha(TPILHA *p); int Push(TPILHA *p, TDADOS dado); int Pop(TPILHA *p, TDADOS *dado); int Consulta_Pilha(TPILHA p, TDADOS *dado); void Imprime_Erro(int erro); void Exibe_Pilha(TPILHA p); // -------------------------------------------- Programa Principal int main(void) { TPILHA p; TDADOS valor; char tecla; int erro; Cria_Pilha(&p); do { Exibe_Pilha(p);

Page 89: Dados_C

89

printf("[P]ush, p[O]p, [C]onsultar ou [F]im?"); do { tecla = toupper(getchar()); } while (!strchr("POCF",tecla)); switch (tecla) { case 'P': printf("Valor a ser Empilhado: "); scanf("%d",&valor); erro = Push(&p,valor); if (erro) Imprime_Erro(erro); break; case 'O': erro = Pop(&p,&valor); if (erro) Imprime_Erro(erro); else printf("Valor Desempilhado: %d, tecle algo",valor); break; case 'C': erro = Consulta_Pilha(p,&valor); if (erro) Imprime_Erro(erro); else printf("Topo: %d, tecle algo",valor); break; } } while (tecla != 'F'); } // ---------------------------------------------- Cria_Pilha void Cria_Pilha(TPILHA *p) { p->topo = NULL; } // ---------------------------------------------- Push int Push(TPILHA *p, TDADOS dado) { TNODO *t; t = (TNODO *) malloc(sizeof(TNODO)); if (t == NULL) return(FALTA_DE_MEMORIA); else { t->info = dado; t->seg = p->topo; p->topo = t; return(SUCESSO); }

Page 90: Dados_C

90

} // ---------------------------------------------- Pop int Pop(TPILHA *p, TDADOS *dado) { TNODO *t; if (p->topo == NULL) return(PILHA_VAZIA); else { t = p->topo; *dado = t->info; p->topo = t->seg; free(t); return(SUCESSO); } } // ---------------------------------------------- Consulta_Pilha int Consulta_Pilha(TPILHA p, TDADOS *dado) { TNODO *t; if (p.topo == NULL) return(PILHA_VAZIA); else { t = p.topo; *dado = t->info; return(SUCESSO); } } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case FALTA_DE_MEMORIA: printf("ERRO: Falta de Memória"); break; case PILHA_VAZIA: printf("ERRO: Pilha Vazia"); break; } } // ------------------------------------------------- Exibe_Pilha

Page 91: Dados_C

91

void Exibe_Pilha(TPILHA p) { TNODO *t; printf("Pilha: "); if (p.topo == NULL) printf("VAZIA"); else { t = p.topo; while (t != NULL) { printf("%02d ",t->info); t = t->seg; } } } 3.2.5.2.2 Analisador de Expressões usando Pilhas 21) Criar um Analisador de Expressões utilizando Pilhas. Exemplo: (3 * (4 + 5))

Dicas: 1. Crie duas PILHAS (números e operandos) 2. "(" não faça nada 3. Número (Push pilha 2) empilhe na pilha de números 4. Operando (Push pilha 1) empilhe na pilha de operandos 5. ")" (Pop pilha 2, Pop pilha 2 e Pop pilha 1) execute a operação 6. Até que i = e[0] Valores Válidos: Números: 0 1 2 3 4 5 6 7 8 9 (Números Inteiros Positivos) Operandos: + - * /

Page 92: Dados_C

92

Solução do Trabalho Proposto (21): // -------------------------------- Analisador de Expressões #include <stdio.h> #include <string.h> #include <ctype.h> // -------------------------------- Definições #define m 50 #define SUCESSO 0 #define PILHA_CHEIA 1 #define PILHA_VAZIA 2 #define TRUE !0 #define FALSE 0 // -------------------------------- Tipos de Dados typedef int TDADOS; typedef struct { int topo; TDADOS elem[m]; } TPILHA; // -------------------------------- Prototypes

Page 93: Dados_C

93

void Cria_Pilha(TPILHA *p); int Push(TPILHA *p, TDADOS dado); int Pop(TPILHA *p, TDADOS *dado); int Consulta_Pilha(TPILHA p, TDADOS *dado); void Imprime_Erro(int erro); int Codifica(char ch, TDADOS *valor,TDADOS *op); int Testa_Expressao(char *s); int Calcula(int v1, int v2, char op); // -------------------------------- Programa Principal int main(void) { TPILHA p1,p2; char s[256]; int n,i,tipo,erro; TDADOS valor,v1,v2,operador,resposta; printf("Analisador de Expressões\n"); printf("Expressão: "); gets(s); n = strlen(s); if (n > m) printf("ERRO: Expressão muito Longa"); else if (Testa_Expressao(s)) { Cria_Pilha(&p1); Cria_Pilha(&p2); for (i = 0;i < n;i++) { tipo = Codifica(s[i],&valor,&operador); switch (tipo) { case 1: erro = Push(&p1,valor); break; case 2: erro = Push(&p2,operador); break; case 3: erro = Pop(&p1,&v2); erro = Pop(&p1,&v1); erro = Pop(&p2,&operador); resposta = Calcula(v1,v2,operador); erro = Push(&p1,resposta); break; } } erro = Pop(&p1,&resposta); if (erro) Imprime_Erro(erro); else printf("Resposta: %d",resposta);

Page 94: Dados_C

94

} else { printf("Erro: Expressão Inválida"); } getchar(); } // ---------------------------------------------- Codifica int Codifica(char ch, TDADOS *valor, TDADOS *op) { int codifica = 4; if (ch >= '0' && ch <= '9') { codifica = 1; *valor = ch - 48; } if (strchr("+- ",ch)) { codifica = 2; *op = ch; } if (ch == ')') codifica = 3; return(codifica); } // -------------------------------------------- Testa_Expressao int Testa_Expressao(char *s) { int i,n,abre=0,fecha=0; n = strlen(s); for (i = 0;i < n;i++) if (s[i] == '(') abre++; else if (s[i] == ')') fecha++; if (abre == fecha) return(TRUE); else return(FALSE); } // ------------------------------------------ Calcula int Calcula(int v1, int v2, char op)

Page 95: Dados_C

95

{ switch (op) { case '+': return(v1 + v2); case '-': return(v1 - v2); case '*': return(v1 * v2); case '/': return(v1 / v2); } return(0); } // ---------------------------------------------- Cria_Pilha void Cria_Pilha(TPILHA *p) { p->topo = -1; } // ---------------------------------------------- Push int Push(TPILHA *p, TDADOS dado) { if (p->topo == m-1) return(PILHA_CHEIA); else { p->topo = p->topo + 1; p->elem[p->topo] = dado; return(SUCESSO); } } // ---------------------------------------------- Pop int Pop(TPILHA *p, TDADOS *dado) { if (p->topo == -1) return(PILHA_VAZIA); else { *dado = p->elem[p->topo]; p->topo = p->topo - 1; return(SUCESSO); } } // ---------------------------------------------- Consulta_Pilha int Consulta_Pilha(TPILHA p, TDADOS *dado) { if (p.topo == -1)

Page 96: Dados_C

96

return(PILHA_VAZIA); else { *dado = p.elem[p.topo]; return(SUCESSO); } } // ---------------------------------------------- Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case PILHA_CHEIA: printf("\nERRO: Pilha Cheia\n"); break; case PILHA_VAZIA: printf("\nERRO: Pilha Vazia\n"); break; } }

3.2.5.3 Deque (“Double-Ended QUEue”) É uma fila de duas extremidades. As inserções, consultas e retiradas são permitidas nas duas extremidades.

Deque de Entrada Restrita A inserção só pode ser efetuada ou no início ou no final da lista. Deque de Saída Restrita A retirada só pode ser efetuada ou no início ou no final da lista. Condição Inicial: Esquerda (Esq) e Direita (Dir) no meio do vetor Esq = (m DIV 2 + 1 Dir = (m DIV 2) + 1

Page 97: Dados_C

97

Deque Vazio

Esq = -1 Dir = -1

Deque Cheio

Esq = 0 Dir = m-1 Cálculo do Número de Elementos do Deque número_de_elementos = Dir - Esq + 1; 22) Escreva um programa em C que inclui, exclui e consulta dados em um Deque.

Solução do problema proposto (22): // -------------------------------------------------- Deque #include <stdio.h> #include <string.h> #include <ctype.h> // -------------------------------------------------- Definições #define m 9 #define SUCESSO 0 #define DEQUE_ESQUERDO_CHEIO 1 #define DEQUE_DIREITO_CHEIO 2 #define DEQUE_VAZIO 3 // ------------------------------------------------- Tipos de Dados

Page 98: Dados_C

98

typedef int TDADOS; typedef struct { int esq; int dir; TDADOS v[m]; } TDEQUE; // ------------------------------------ Prototypes void Cria_Deque(TDEQUE *d); int Inclui_Esquerda(TDEQUE *d, TDADOS dado); int Inclui_Direita(TDEQUE *d, TDADOS dado); int Exclui_Esquerda(TDEQUE *d, TDADOS *dado); int Exclui_Direita(TDEQUE *d, TDADOS *dado); void Exibe_Deque(TDEQUE d); void Imprime_Erro(int erro); // ------------------------------------ Programa Principal int main(void) { TDEQUE d; TDADOS valor; char ch,op; int erro; Cria_Deque(&d); do { Exibe_Deque(d); printf("[I]nclui, [E]xclui, [C]onsulta ou [F]im: "); do { op = toupper(getchar()); } while (!strchr("IECF",op)); if (strchr("'IEC",op)) { printf("[E]squerda ou [D]ireita: "); do { ch = toupper(getchar()); } while (!strchr("ED",ch)); switch (op) { case 'I': printf("Valor: "); scanf("%d",&valor); if (valor != 0) switch (ch) { case 'E': erro = Inclui_Esquerda(&d,valor); break; case 'D': erro = Inclui_Direita(&d,valor); break;

Page 99: Dados_C

99

} break; case 'E': switch (ch) { case 'E': erro = Exclui_Esquerda(&d,&valor); break; case 'D': erro = Exclui_Direita(&d,&valor); break; } if (!erro) printf("Valor Excluído: %d",valor); break; case 'C': switch (ch) { case 'E': erro = Consulta_Esquerda(d,&valor); break; case 'D': erro = Consulta_Direita(d,&valor); break; } if (!erro) { printf("Valor Consultado: %d",valor); printf(", tecle <ENTER> para continuar"); getchar(); } break; } if (erro) Imprime_Erro(erro); } } while (op != 'F'); } // ------------------------------------ Cria_Deque void Cria_Deque(TDEQUE *d) { d->esq = -1; d->dir = -1; } // ------------------------------------ Inclui_Esquerda int Inclui_Esquerda(TDEQUE *d, TDADOS dado) { if (d->esq == 0) return(DEQUE_ESQUERDO_CHEIO); else { if (d->esq == -1) {

Page 100: Dados_C

100

d->esq = m / 2; d->dir = d->esq; } else d->esq = d->esq - 1; d->v[d->esq] = dado; return(SUCESSO); } } // ------------------------------------ Inclui_Direita int Inclui_Direita(TDEQUE *d, TDADOS dado) { if (d->dir == m-1) return(DEQUE_DIREITO_CHEIO); else { if (d->dir == -1) { d->dir = m / 2; d->esq = d->dir; } else d->dir = d->dir + 1; d->v[d->dir] = dado; return(SUCESSO); } } // ------------------------------------ Exclui_Esquerda int Exclui_Esquerda(TDEQUE *d, TDADOS *dado) { if (d->esq == -1) return(DEQUE_VAZIO); else { *dado = d->v[d->esq]; d->esq = d->esq + 1; if (d->esq > d->dir) Cria_Deque(d); return(SUCESSO); } } // ------------------------------------ Exclui_Direita int Exclui_Direita(TDEQUE *d, TDADOS *dado) { if (d->dir == -1)

Page 101: Dados_C

101

return(DEQUE_VAZIO); else { *dado = d->v[d->dir]; d->dir = d->dir - 1; if (d->dir < d->esq) Cria_Deque(d); return(SUCESSO); } } // ------------------------------------ Consulta_Esquerda int Consulta_Esquerda(TDEQUE d, TDADOS *dado) { if (d.esq == -1) return(DEQUE_VAZIO); else { *dado = d.v[d.esq]; return(SUCESSO); } } // ------------------------------------ Consulta_Direita int Consulta_Direita(TDEQUE d, TDADOS *dado) { if (d.dir == -1) return(DEQUE_VAZIO); else { *dado = d.v[d.dir]; return(SUCESSO); } } // ------------------------------------ Exibe_Deque void Exibe_Deque(TDEQUE d) { int i; printf("Deque: "); if (d.esq == -1) printf("VAZIO"); else for (i = d.esq;i <= d.dir;i++) printf("%02d ",d.v[i]); }

Page 102: Dados_C

102

// ------------------------------------ Imprime_Erro void Imprime_Erro(int erro) { switch (erro) { case DEQUE_ESQUERDO_CHEIO: printf("ERRO: Deque Cheio à Esquerda"); break; case DEQUE_DIREITO_CHEIO: printf("ERRO: Deque Cheio à Direita"); break; case DEQUE_VAZIO: printf("ERRO: Deque Vazio"); break; } printf(", tecle <ENTER> para continuar"); getchar(); }

Page 103: Dados_C

103

4. Arquivos Os arquivos permitem armazenar dados em disco, ou seja, na memória secundária, permitindo desta forma, que as informações não sejam perdidas quando o computador é desligado. Os arquivos são formados por registros. Cada registro possui uma chave de acesso, ou seja, um índice que diferencia um registro do outro e que permite localizá-lo. Os registros são compostos de campos. Os campos podem ser de qualquer tipo, permitindo que dados e estruturas de dados sejam armazenados. Tais dados podem ser: inteiros, reais, caracteres, strings, estruturas, etc. Os arquivos podem ser de dois tipos básicos: texto ou binário. Os arquivos tipo texto são formados por linhas de caracteres finalizados pelo caracter ‘\n’. Os arquivos binários permitem armazenamento de qualquer tipo de dado. Em C, existe um tipo de dado pré-definido chamado FILE (definido em stdio.h) que permite definir um ponteiro que aponta para um arquivo, ou seja, aponta para uma fila de bytes.

Exemplo: #include <stdio.h> FILE *fp; // onde fp significa File Pointer Observação: Note que o nome do ponteiro do arquivo pode ter qualquer nome (identificador) válido em C, normalmente ele é chamado fp (File Pointer).

Page 104: Dados_C

104

Em C existem dois tipos de sistemas de arquivos: bufferizado e não-bufferizado. 4.1 Sistema de Arquivo Bufferizado A ligação existente entre o sistema de entrada e saída bufferizado e um arquivo em C é um ponteiro que aponta para o arquivo. O ponteiro do arquivo identifica um determinado arquivo em disco e é utilizado pela fila associada a ele para direcionar cada uma das funções de entrada e saída bufferizada para o lugar em que elas operam. Um ponteiro de arquivo é uma variável ponteiro do tipo FILE *. O tipo de dado FILE é pré-definido em “stdio.h”. Exemplo: #include <stdio.h> int main(void) { FILE *fp; Função: fopen A função fopen (file open) permite abrir (criar ou anexar) um arquivo ligando-o a uma fila de bytes. Sintaxe: FILE *fopen (char *nome_arquivo, char *modo); Prototype: stdio.h nome_arquivo: Deve ser uma string que contenha: "drive:\path\nome_do_arquivo" modo: É uma string que contém as características desejadas (veja tabela abaixo). Observação: t (arquivo texto) e b (arquivo binário)

Modo de abertura de arquivos Modo Significado

“r” Abre um arquivo-texto para leitura “w” Cria um arquivo-texto para gravação “a” Anexa a um arquivo-texto “rb” Abre um arquivo binário para leitura

“”wb” Cria um arquivo binário para gravação

Page 105: Dados_C

105

“ab” Anexa a um arquivo binário “r+” Abre um arquivo-texto para leitura/gravação “w+” Cria um arquivo-texto para leitura/gravação “a+” Abre ou cria um arquivo-texto para leitura/gravação “r+b” Abre um arquivo binário para leitura/gravação Modo Significado “w+b” Cria um arquivo binário para leitura/gravação “a+b” Abre um arquivo binário para leitura/gravação “rt” Abre um arquivo texto para leitura “wt” Cria um arquivo texto para gravação “at” Anexa a um arquivo texto “r+t” Abre um arquivo-texto para leitura/gravação “w+t” Cria um arquivo-texto para leitura/gravação “a+t” Abre ou cria um arquivo-texto para leitura/gravação

Observação: Se ocorrer erro na abertura de um arquivo, fopen devolverá um ponteiro nulo, ou seja, se (fp == NULL) erro. Exemplos: #include <stdio.h> int main (void) { FILE *fp; if ((fp = fopen("teste.dat","r")) == NULL) printf("Erro Fatal: Impossível Abrir o Arquivo\n"); else { printf("Ok, arquivo aberto\n"); ... ou #include <stdio.h> int main (void) { FILE *fp; fp = fopen("a:\fontes\teste.dat","w"); if (fp == NULL) printf("Erro Fatal: Impossível Criar o Arquivo\n”); else

Page 106: Dados_C

106

{ printf("Ok, arquivo criado com sucesso\n"); ... Função: putc A função putc é utilizada para “gravar” (write) caracteres em um arquivo. Sintaxe: int putc (int ch, FILE *fp); Prototype: stdio.h ch é o caracter a ser gravado fp é o ponteiro do arquivo aberto pela função fopen() Observação: Se uma gravação for bem sucedida putc() devolverá o caracter gravado, caso contrário devolverá um EOF. EOF - End of File (Fim de Arquivo) Função: getc A função getc é utilizada para “ler” (read) caracteres de um arquivo. Sintaxe: int getc (FILE *fp); Prototype: stdio.h fp é o ponteiro do arquivo aberto pela função fopen() Função: feof A função feof (file end of file) determina se o fim de arquivo foi encontrado. Devolve 0 se não chegou ao fim do arquivo. Sintaxe: int feof (FILE *fp); Prototype: stdio.h Função: fclose

Page 107: Dados_C

107

A função fclose (file close) é utilizada para fechar um arquivo aberto. Antes de fechar o arquivo os dados (que ainda estavam no buffer) são gravados. Sintaxe: int fclose (FILE *fp); Prototype: stdio.h Observação: Retorna 0 se a operação foi bem sucedida. 4.2 Argumentos argc e argv Quando o programador que implementar um programa que recebe parâmetros pela linha de comandos é necessário definir dois argumentos (parâmetros) na função main. argc - Identifica o número de parâmetros presente na linha de comandos. argv - É um vetor de strings que possui todos os parâmetros da linha de comandos. Como definir os argumentos: int main(int argc, char *argv[]) Exemplo: (execução do programa “lista.exe” pela linha de comandos) C:\>lista lista.c <enter>

0 1 2 3 4 5 6 7 8 9 10 11 12 0 ‘C’ ‘:’ ‘\’ ‘l’ ‘i’ ‘s’ ‘t’ ‘a’ ‘.’ ‘e’ ‘x’ ‘e’ NULL

1 ‘l’ ‘i’ ‘s’ ‘t’ ‘a’ ‘.’ ‘c’ NULL argv[0] “c:\lista.exe’ argv[1] “lista.c” 23) Escrever um programa em C que lista na tela um arquivo texto.

Solução do problema proposto (23): Observação: Este programa deve ser compilado e então executado por linha de comando da seguinte forma:

Page 108: Dados_C

108

C:\>lista lista.c <enter> // lista.c #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; char ch; if (argc != 2) printf("Sintaxe: LISTA <Nome_Arquivo_Texto>\n"); else { fp = fopen(argv[1],"rt"); if (fp == NULL) printf("ERRO FATAL: Arquivo [%s] Inexistente\n",argv[1]); else { ch = getc(fp); while (!feof(fp)) { printf("%c",ch); ch = getc(fp); } fclose(fp); } } } 24) Escrever um programa em C que lista na tela um arquivo texto, imprimindo ainda o número de caracteres e o número de linhas.

Solução do problema proposto (24): // bytes.c #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; char ch; long unsigned int n = 0; unsigned int l = 1; if (argc != 2) printf("Sintaxe: LISTA <Nome_Arquivo_Texto>\n"); else

Page 109: Dados_C

109

{ fp = fopen(argv[1],"rt"); if (fp == NULL) printf("ERRO FATAL: Arquivo [%s] Inexistente\n",argv[1]); else { ch = getc(fp); while (!feof(fp)) { if (ch == '\n') l++; n++; printf("%c",ch); ch = getc(fp); } fclose(fp); printf("\nNúmero de Caracteres: %d",n); printf("\nNúmero de Linhas: %d",l); } } } 25) Escreva um programa em C que lê strings via teclado e grava-as (caracter por caracter) em um arquivo texto.

Solução do problema proposto (25): // grava.c #include <stdio.h> #include <string.h> int main(void) { FILE *fp; char nome[256],linha[256]; char ch; int i,n; printf("Nome do Arquivo Texto: "); scanf("%s",nome); fp = fopen(nome,"rt"); if (fp != NULL) printf("ERRO FATAL: Arquivo [%s] Existe\n",nome); else { fp = fopen(nome,"wt"); if (fp == NULL) printf("ERRO FATAL: Problema na Criação do Arquivo [%s]\n",nome); else

Page 110: Dados_C

110

{ do { gets(linha); if (strcmp(linha,"") != 0) { n = strlen(linha); for (i = 0;i < n;i++) putc(linha[i],fp); putc('\n',fp); } } while (strcmp(linha,"") != 0); putc(EOF,fp); fclose(fp); } } } 26) Escrever um programa em C que lê strings via teclado e grava-as (toda de uma vez) em um arquivo texto. Solução do problema proposto (26): // grava.c #include <stdio.h> #include <string.h> int main(void) { FILE *fp; char nome[256],linha[256]; char ch; printf("Nome do Arquivo Texto: "); scanf("%s",nome); fp = fopen(nome,"rt"); if (fp != NULL) printf("ERRO FATAL: Arquivo [%s] Existe\n",nome); else { fp = fopen(nome,"wt"); if (fp == NULL) printf("ERRO FATAL: Problema na Criação do Arquivo [%s]\n",nome); else { do { gets(linha); if (strcmp(linha,"") != 0) { fprintf(fp,"%s",linha);

Page 111: Dados_C

111

putc('\n',fp); } } while (strcmp(linha,"") != 0); putc(EOF,fp); fclose(fp); } } } Função: rewind A função rewind estabelece o localizador de posição do arquivo para o início do arquivo especificado, ou seja, faz com que o ponteiro do arquivo (fp) aponte para o byte zero. Sintaxe: void rewind (FILE *fp); Prototype: stdio.h Funções: getw e putw As funções getw e putw são utilizadas para ler e gravar, respectivamente, inteiros em um arquivo. Função: Leitura de inteiros (getw) Sintaxe: int getw (FILE *fp); Prototype: stdio.h Função: Gravação de inteiros (putw) Sintaxe: int putw (int x, FILE *fp); Prototype: stdio.h Funções: fgets e fputs As funções fgets e fputs são utilizadas para ler e gravar strings. Função: Leitura de strings (fgets) Sintaxe: char *fgets (char *str, int comprimento, FILE *fp); Prototype: stdio.h Função: Gravação de strings (fputs) Sintaxe: char *fputs (char *str, FILE *fp); Prototype: stdio.h

Page 112: Dados_C

112

Observação: A função fgets lê uma string do arquivo especificado até que leia ou um '\n' ou (comprimento - 1) caracteres. Funções: fread e fwrite As funções fread e fwrite são utilizadas para ler e gravar blocos de dados, normalmente uma struct. Função: Leitura de blocos (fread) Sintaxe: int fread (void *buffer, int num_bytes, int cont, FILE *fp); Prototype: stdio.h Função: Gravação de blocos (fwrite) Sintaxe: int fwrite (void *buffer, int num_bytes, int cont, FILE *fp); Prototype: stdio.h buffer: É um ponteiro para a região da memória ou o endereço de uma variável que receberá os dados lidos do arquivo pela função fread ou que será gravada no arquivo pela função fwrite. num_bytes: Especifica a quantidade de bytes a serem lidos ou gravados. cont: Determina quantos blocos (cada um com comprimento de num_bytes) serão lidos ou gravados. fp: É o ponteiro para o arquivo. Função: fseek A função fseek é utilizada para ajustar o localizador de posição do arquivo, ou seja, permite selecionar a posição para efetuar operações de leitura e gravação aleatórias. Sintaxe: int fseek (FILE *fp, long int num_bytes, int origem); Prototype: stdio.h num_bytes: É o número de bytes desde origem até chegar a posição desejada.

Origem em arquivos

Origem Identificador Início do arquivo SEEK_SET Posição corrente SEEK_CUR

Page 113: Dados_C

113

Fim do arquivo SEEK_END Funções: fprintf e fscanf As funções fprintf e fscanf se comportam exatamente como printf e scanf, exceto pelo fato de que elas operam com arquivos em disco. Função: Gravação de dados formatados (fprintf) Sintaxe: int fprintf (FILE *fp, char *formato, lista argumentos); Prototype: stdio.h Função: Leitura de dados formatados (fscanf) Sintaxe: int fscanf (FILE *fp, char *formato, lista argumentos); Prototype: stdio.h Função: remove A função remove apaga do disco o arquivo especificado. Sintaxe: int remove (char *nome_arquivo); Prototype: stdio.h Exemplos: Abaixo, são listados três programas: cria.c, lista.c, consulta.c, os quais possuem como registro, uma string com no máximo 80 caracteres. 27) Escreva um programa em C que lê nomes via teclado (máximo 80 caracteres) e grava-os em um arquivo binário qualquer (o nome deve ser informado pelo usuário). O programa termina quando o usuário digitar apenas um enter. Solução do problema proposto (27): // write.c #include <stdio.h> #include <string.h> int main(void) { FILE *fp;

Page 114: Dados_C

114

char nome[80],arquivo[256]; int n = 0; printf("Nome do Arquivo: "); scanf("%s",arquivo); fp = fopen(arquivo,"rb"); if (fp != NULL) printf("ERRO FATAL: Arquivo [%s] Existe\n",arquivo); else { fp = fopen(arquivo,"wb"); if (fp == NULL) printf("ERRO FATAL: Problema na Criação do Arquivo [%s]\n",arquivo); else { do { printf("Nome: "); gets(nome); if (strcmp(nome,"") != 0) { fwrite(nome,sizeof(nome),1,fp); n++; } } while (strcmp(nome,"") != 0); printf("%d Nomes Gravados\n",n); fclose(fp); } } } 28) Escreva um programa em C que permite listar (na tela) os nomes contidos em um arquivo binário qualquer (criado no programa acima). Solução do problema proposto (28): // read.c #include <stdio.h> #include <string.h> int main(void) { FILE *fp; char nome[80],arquivo[256]; int n = 0; printf("Nome do Arquivo: "); scanf("%s",arquivo); fp = fopen(arquivo,"rb"); if (fp == NULL)

Page 115: Dados_C

115

printf("ERRO FATAL: Arquivo [%s] Inexistente\n",arquivo); else { fread(nome,sizeof(nome),1,fp); while (!feof(fp)) { printf("Nome: %s\n",nome);

fread(nome,sizeof(nome),1,fp); n++; } printf("%d Nomes Lidos\n",n); fclose(fp); getchar(); } } 29) Escreva um programa em C que permite consultar o arquivo de nomes. Para tanto é solicitado, ao usuário, o número do registro para ser calculado a posição deste registro no arquivo. Logo após o registro é exibido na tela. Solução do problema proposto (29): // consulta.c #include <stdio.h> #include <string.h> int main (void) { FILE *fp; char nome[80]; char arquivo[256]; unsigned int n, ok; long int posicao; char ch; printf("Nome do Arquivo: "); scanf(“%s”, arquivo); if ((fp = fopen(arquivo,"rb")) == NULL) { printf("ERRO: Arquivo não EXISTE\n"); getchar(); } else { do { printf("Número do Registro: "); scanf("%d", &n);

Page 116: Dados_C

116

posicao = n * sizeof(nome); fseek(fp, posicao, SEEK_SET); ok = fread(nome, sizeof(nome), 1, fp); if (ok) printf("%d: Nome: %s\n", n, nome); else printf("ERRO: Registro NÃO existe\n"); printf("Continua [S/N] ? "); do { ch = getchar(); } while (!strchr("SsNn",ch)); } while (strchr("Ss",ch)); fclose(fp); } } 30) Escreva um programa em C que permite ordenar o arquivo de nomes gerado pelo programa “write.c”. Para tanto deve ser criada uma Lista Encadeada. Utilizar um método de classificação qualquer. Após a classificação os nomes devem ser gravados no arquivo original de nomes. Solução do problema proposto (30): // sort.c #include <stdio.h> #include <stdlib.h> // ------------------------------------ Definições #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define LISTA_VAZIA 2 #define TRUE !0 #define FALSE 0 typedef char TDADOS; typedef struct nodo { TDADOS dado[80]; struct nodo *elo; } TNODO; typedef struct { TNODO *primeiro; } TLISTA;

Page 117: Dados_C

117

// ------------------------------------ Prototypes void Cria_Lista (TLISTA *l); int Inclui_Lista (TLISTA *l, TDADOS *d); int Exclui_Lista(TLISTA *l, TDADOS *nome); void Destroi_Lista(TLISTA *l); void Exibe_Lista(TLISTA l); void Ordena_Lista(TLISTA *l); // ------------------------------------ Programa Principal int main(void) { FILE *fp; char arquivo[256]; TDADOS nome[80]; TLISTA l; int erro; Cria_Lista(&l); printf("Nome do Arquivo: "); scanf("%s",arquivo); fp = fopen(arquivo,"r+b"); if (fp == NULL) printf("ERRO FATAL: Arquivo [%s] Inexistente\n",arquivo); else { fread(nome,sizeof(nome),1,fp); while (!feof(fp)) { erro = Inclui_Lista(&l,nome); if (erro) { printf("ERRO FATAL: Falta de Memória\n"); getchar(); exit(1); } fread(nome,sizeof(nome),1,fp); } printf("Lista de Entrada"); Exibe_Lista(l); Ordena_Lista(&l); printf("\nLista Ordenada"); Exibe_Lista(l); rewind(fp); erro = Exclui_Lista(&l,nome); while (!erro) { fwrite(nome,sizeof(nome),1,fp); erro = Exclui_Lista(&l,nome); }

Page 118: Dados_C

118

fclose(fp); } Destroi_Lista(&l); getchar(); } // ------------------------------------ Cria_Lista void Cria_Lista (TLISTA *l) { l->primeiro = NULL; } // ------------------------------------ Inclui_Lista int Inclui_Lista (TLISTA *l, TDADOS *d) { TNODO *p; p = (TNODO *) malloc(sizeof(TNODO)); if (p == NULL) return(FALTA_DE_MEMORIA); else { if (l->primeiro == NULL) { l->primeiro = p; strcpy(p->dado,d); p->elo = NULL; } else { strcpy(p->dado,d); p->elo = l->primeiro; l->primeiro = p; } return(SUCESSO); } } // ------------------------------- Exclui_Lista int Exclui_Lista(TLISTA *l, TDADOS *nome) { TNODO *p; if (l->primeiro == NULL) return(LISTA_VAZIA); else { p = l->primeiro;

Page 119: Dados_C

119

strcpy(nome,p->dado); l->primeiro = p->elo; free(p); return(SUCESSO); } } // ----------------------------------------- Destroi_Lista void Destroi_Lista(TLISTA *l) { TNODO *p,*q; if (l->primeiro != NULL) { p = l->primeiro; while (p != NULL) { q = p->elo; free(p); p = q; } } Cria_Lista(l); } // ------------------------------------------ Exibe_Lista void Exibe_Lista(TLISTA l) { TNODO *p; if (l.primeiro == NULL) printf("\nVAZIA"); else { p = l.primeiro; while (p != NULL) { printf("\n%s",p->dado); p = p->elo; } } } // ------------------------------------ Ordena_Lista void Ordena_Lista(TLISTA *l) { TNODO *p,*q; TDADOS temp[80];

Page 120: Dados_C

120

int trocou; if (l->primeiro != NULL) do { trocou = FALSE; p = l->primeiro; q = p->elo; while (q != NULL) { if (strcmp(p->dado,q->dado) > 0) { strcpy(temp,p->dado); strcpy(p->dado,q->dado); strcpy(q->dado,temp); trocou = TRUE; } p = q; q = p->elo; } } while (trocou); }

Page 121: Dados_C

121

5. Pesquisa de Dados Uma operação complexa e trabalhosa é a consulta em tabelas. Normalmente uma aplicação envolve grande quantidade de dados que são armazenadas em Tabelas. As tabelas são compostas de registros (normalmente possui uma chave), e os registros de campos.

5.1 Pesquisa Seqüencial Método mais simples de pesquisa em tabela, consiste em uma varredura seqüencial, sendo que cada campo é comparado com o valor que está sendo procurado. Esta pesquisa termina quando for achado o valor desejado ou quando chegar o final da tabela.

23) Escreva um programa em C que faz uma Busca Seqüencial em uma estrutura que possui os campos: chave, nome, altura e peso.

Page 122: Dados_C

122

Solução do problema proposto (23): // ---------------------------------------------- Pesquisa Sequencial #include <stdio.h> #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define MAX 10 typedef struct { int chave; char nome[21]; float peso; float altura; } TABELA; // ---------------------------------------------- Prototypes int Pesquisa_Sequencial(TABELA t[], int valor,int n); // ---------------------------------------------- Programa Principal int main(void) { TABELA t[MAX]; int n = 0; int ch,chave; char tecla; do { printf("Chave: "); scanf("%d",&t[n].chave); printf("Nome: "); gets(t[n].nome); printf("Peso: "); scanf("%f",&t[n].peso); printf("Altura: "); scanf("%f",&t[n].altura); printf("Continua [S/N] ?"); do { tecla = tolower(getchar()); } while (!strchr("sn",tecla)); n++; } while (tecla == 's' && n < MAX); do { printf("Chave para consulta [0 - Sair]: "); scanf("%d",&ch);

Page 123: Dados_C

123

chave = Pesquisa_Sequencial(t,ch,n); if (chave != -1) { printf("Chave: %d\n",t[chave].chave); printf("Nome: %s\n",t[chave].nome); printf("Peso: %.1f\n",t[chave].peso); printf("Altura: %.1f\n",t[chave].altura); } else printf("Erro: Chave Inexistente\n"); } while (ch != 0); } // ---------------------------------------------- Pesquisa_Sequencial int Pesquisa_Sequencial(TABELA t[], int ch,int n) { int i,chave = -1; for (i = 0;i < n;i++) if (t[i].chave == ch) { chave = i; break; } return(chave); } Observação: A Pesquisa Sequencial apresenta desempenho melhor se a tabela estiver ordenada pela chave de acesso: 24) Escreva um programa em C que faz uma Busca Seqüencial, em uma estrutura ordenada por chave, que possui os campos: chave, nome, altura e peso.

Solução do problema proposto (24): // ---------------------------------------------- Pesquisa Ordenada #include <stdio.h> #include <string.h> #include <ctype.h> // ---------------------------------------------- Definições #define MAX 10 typedef struct { int chave;

Page 124: Dados_C

124

char nome[21]; float peso; float altura; } TABELA; // ---------------------------------------------- Prototypes int Pesquisa_Ordenada(TABELA t[], int ch, int n); void Ordena_Tabela(TABELA t[], int n); // ---------------------------------------------- Programa Principal int main(void) { TABELA t[MAX]; int n = 0; int ch,chave; char tecla; do { printf("Chave: "); scanf("%d",&t[n].chave); printf("Nome: "); gets(t[n].nome); printf("Peso: "); scanf("%f",&t[n].peso); printf("Altura: "); scanf("%f",&t[n].altura); printf("Continua [S/N] ?"); do { tecla = tolower(getchar()); } while (!strchr("sn",tecla)); n++; } while (tecla == 's' && n < MAX); Ordena_Tabela(t,n); do { printf("Chave para consulta [0 - Sair]: "); scanf("%d",&ch); chave = Pesquisa_Ordenada(t,ch,n); if (chave != -1) { printf("Chave: %d\n",t[chave].chave); printf("Nome: %s\n",t[chave].nome); printf("Peso: %.1f\n",t[chave].peso); printf("Altura: %.1f\n",t[chave].altura); } else printf("Erro: Chave Inexistente\n"); } while (ch != 0); }

Page 125: Dados_C

125

// ---------------------------------------------- Ordena_Tabela void Ordena_Tabela(TABELA t[], int n) { int i,j; TABELA temp; for (i = 0;i < n-1;i++) for (j = i+1;j < n;j++) if (t[i].chave > t[j].chave) { temp = t[i]; t[i] = t[j]; t[j] = temp; } } // ---------------------------------------------- Pesquisa_Ordenada int Pesquisa_Ordenada(TABELA t[], int ch, int n) { int i,chave = -1; for (i = 0; i < n;i++) if (t[i].chave >= ch) if (t[i].chave == ch) { chave = i; break; } return(chave); } 5.2 Pesquisa Binária Método de Pesquisa que só pode ser aplicada em tabelas ordenadas.

Page 126: Dados_C

126

O método consiste na comparação do "valor" com a chave localizada na metade da tabela, pode ocorrer: valor = chave............chave localizada valor < chave............chave está na primeira metade (esquerda) valor > chave............chave está na segunda metade (direita)

A cada comparação, a área de pesquisa é reduzida a metade do número de elementos. O número máximo de comparações será:

25) Escreva um programa em C que faz uma Pesquisa Binária em uma tabela de números inteiros.

Solução do problema proposto (25): // --------------------------------------- Pesquisa Binária #include <stdio.h> #define MAX 10 // --------------------------------------- Prototypes void Ordena_Tabela(int t[], int n); int Pesquisa_Binaria(int t[], int n, int valor); void Exibe_Tabela(int t[], int n); // --------------------------------------- Programa Principal int main(void) { int t[MAX]; int indice,n = -1; int valor; do { n++;

Page 127: Dados_C

127

printf("Número: "); scanf("%d",&t[n]); } while (t[n] != 0 && n < MAX); Exibe_Tabela(t,n); Ordena_Tabela(t,n); Exibe_Tabela(t,n); do { printf("Valor: "); scanf("%d",&valor); if (valor != 0) { indice = Pesquisa_Binaria(t,n,valor); if (indice == -1) printf("ERRO: Valor não está na tabela\n"); else printf("Indice: %d\n",indice); } } while (valor != 0); } // ---------------------------------------------- Ordena_Tabela void Ordena_Tabela(int t[], int n) { int i,j,temp; for (i = 0;i < n-1;i++) for (j = i+1;j < n;j++) if (t[i] > t[j]) { temp = t[i]; t[i] = t[j]; t[j] = temp; } } // ---------------------------------------------- Pesquisa_Binaria int Pesquisa_Binaria(int t[], int n, int valor) { int i,j,indice; int inic,fim,metade; inic = 0; fim = n-1; metade = n / 2; indice = -1; do { if (valor == t[metade]) { indice = metade;

Page 128: Dados_C

128

break; } else if (valor < t[metade]) { fim = metade - 1; metade = (fim + inic) / 2; } else { inic = metade + 1; metade = (fim + inic) / 2; } } while (indice == -1 && inic <= fim); return(indice); } // ------------------------------------------------- Exibe_Tabela void Exibe_Tabela(int t[], int n) { int i; printf("Tabela: "); for (i = 0;i < n;i++) printf("%02d ",t[i]); printf("\n"); }

5.3 Cálculo de Endereço (Hashing) Além de um método de pesquisa, este método é também um método de organização física de tabelas (Classificação). Onde cada dado de entrada é armazenado em um endereço previamente calculado (através de uma função), desta forma, o processo de busca é igual ao processo de entrada, ou seja, eficiente. Um dos problemas é definir bem o tipo de função a ser usada, pois normalmente as funções geram endereços repetidos. A eficiência deste método depende do tipo de função. Numa tabela com n elementos com valores na faixa de [0..MAX] pode ser utilizada a seguinte a função:

Onde: n é o número de elementos.

Page 129: Dados_C

129

Exemplo: n = 53 entrada: [0..1000]

Note que no cálculo dos endereços houve repetições, tais como: 2, 33, 23, 10 e 50, por causa disto, é necessário verificar se o endereço está ocupado ou não. Finalmente os endereços calculados são:

26) Escreva um programa em C que faz um Cálculo de Endereço (Hashing) em uma tabela de números inteiros.

Solução do problema proposto (26): // --------------------- Hashing.c #include <stdio.h> #include <string.h> #define k 19 #define TRUE !0 #define FALSE 0 typedef struct { int situacao; int valor; } TABELA; // ---------------------------------------------- Inicializa_Tabela void Inicializa_Tabela(TABELA t[]) { int i;

Page 130: Dados_C

130

for (i = 0;i < k;i++) { t[i].situacao = FALSE; t[i].valor = 0; } } // ---------------------------------------------- Insere_Tabela void Insere_Tabela(TABELA t[], int entrada) { int endereco; endereco = entrada % k; while (t[endereco].situacao) endereco++; t[endereco].valor = entrada; t[endereco].situacao = TRUE; } // ---------------------------------------------- Hashing int Hashing(TABELA t[], int entrada) { int i, endereco; endereco = entrada % k; while (t[endereco].valor != entrada && endereco != k) endereco++; if (endereco != k) return(endereco); else return(0); } // ---------------------------------------------- Exibe_Tabela void Exibe_Tabela(TABELA t[]) { int i; for (i = 0;i < k;i++) printf("%3d ",i); for (i = 0;i < k;i++) printf("%03d ",t[i].valor); } // ---------------------------------------------- PROGRAMA PRINCIPAL

Page 131: Dados_C

131

int main(void) { TABELA t[k]; int n = 0, entrada, endereco; char tecla; Inicializa_Tabela(t); do { Exibe_Tabela(t); n++; printf("Número: "); scanf("%d",&entrada); Insere_Tabela(t,entrada); printf("Continua [S/N]? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && n <= k); do { printf("\nValor a ser CONSULTADO: "); scanf("%d",&entrada); endereco = Hashing(t,entrada); if (endereco == 0) printf("ERRO: Valor Inválido\n"); else printf("Endereco: %d\n",endereco); printf("Continua [S/N]? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla)); } 6. Classificação de Dados (Ordenação) É o processo pelo qual é determinada a ordem em que devem ser apresentados os elementos de uma tabela de modo a obedecer à seqüência de um ou mais campos (chaves de classificação). Classificação Interna...................... Memória Principal Classificação Externa...................... Memória Secundária 6.1 Classificação por Força Bruta

Page 132: Dados_C

132

Logo, os elementos são fisicamente reorganizados. 27) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros.

Solução do problema proposto (27): // Sort.c #include <stdio.h> #include <string.h> #define QUANT 10 void Sort(int v[], int n) { int i,j,temp; for (i = 0;i < n-1;i++) for (j = i+1;j < n;j++) if (v[i] > v[j]) { temp = v[i]; v[i] = v[j]; v[j] = temp; } } void Exibe(int v[], int n) { int i; printf("\nLista: "); for (i = 0;i <= n;i++) printf("%2d ",v[i]);

Page 133: Dados_C

133

} int main(void) { int v[QUANT]; int n = -1; char tecla; do { n++; printf("\nValor: "); scanf("%d",&v[n]); printf("Continua [S/N] ?"); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); Exibe(v,n); } while (strchr("Ss",tecla) && n < QUANT); Sort(v,n); Exibe(v,n); printf("\nTecle qualquer tecla ..."); getchar(); } 6.2 Vetor Indireto de Ordenação (Tabela de Índices) Os elementos não são reorganizados fisicamente, apenas é criado um outro vetor (tabela de índices) que controla a ordem do primeiro. Exemplo:

28) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando um Vetor Indireto de Ordenação (VIO).

Solução do problema proposto (28):

Page 134: Dados_C

134

// Vio_1.c (Vetor Indireto de Ordenação) #include <stdio.h> #include <string.h> #define TRUE !0 #define FALSE 0 #define QUANT 10 // ---------------------------------------------- Verifica int Verifica(int vio[], int i, int k) { int j, ok = TRUE; for (j = 0;j <= k;j++) if (vio[j] == i) ok = FALSE; return(ok); } // ---------------------------------------------- PROGRAMA PRINCIPAL int main(void) { char v[QUANT][20]; int vio[QUANT]; int i,j,k,l,u = -1; char tecla; int troca,ok; do { u++; printf("\nNome: "); gets(v[u]); printf("Continua [S/N] ?"); vio[u] = FALSE; do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && u <= QUANT); k = 0; j = 1; do { troca = TRUE; for (i = 0;i <= u;i++) { ok = Verifica(vio,i,k); if (ok)

Page 135: Dados_C

135

if (strcmp(v[i],v[j]) > 0) { j = i; troca = FALSE; } } if (!troca) { vio[k] = j; k++; j = 1; } if (troca) { ok = Verifica(vio,j,k); if (ok) { k++; vio[k] = j; } if (j < u) j++; else j--; } } while (k != u); printf("\nLista de Nomes Ordenados\n"); for (i = 0;i <= u;i++) printf("Nome: %s\n",v[vio[i]]); getchar(); } 29) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando um Vetor Indireto de Ordenação (VIO).

Solução do problema proposto (29): // Vio.c (Vetor Indireto de Ordenação) #include <stdio.h> #include <string.h> #define QUANT 10 int main(void) { char v[QUANT][30]; int vio[QUANT];

Page 136: Dados_C

136

int i, j, u = -1, temp; char tecla; do { u++; printf("\nNome: "); gets(v[u]); printf("Continua [S/N] ?"); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && u < QUANT); for (i = 0;i <= u;i++) vio[i] = i; for (i = 0;i < u;i++) for (j = i+1;j <= u;j++) if (strcmp(v[vio[i]],v[vio[j]]) > 0) { temp = vio[i]; vio[i] = vio[j]; vio[j] = temp; } printf("\nLista de Nomes Ordenados\n"); for (i = 0; i <= u;i++) printf("Nome: %s\n",v[vio[i]]); getchar(); } 6.3 Classificação por Encadeamento Os elementos permanecem em seus lugares. É criado então uma lista encadeada ordenada. Esta lista possui um Header (Cabeça) o qual indica o primeiro elemento da lista. Exemplo:

Page 137: Dados_C

137

30) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando encadeamento.

Solução do problema proposto (30): // Encadea.c (Ordenação por Encadeamento) #include <stdio.h> #include <string.h> #define TRUE !0 #define FALSE 0 #define QUANT 10 typedef struct { char nome[20]; int prox; } TABELA; // ---------------------------------------------- Verifica void Verifica (TABELA t[], TABELA ta[], int *j) { int i = 0,sai; do { sai = FALSE; if (strcmp(t[i].nome,ta[*j].nome) == 0) { *j = i; sai = TRUE; } i++; } while (!sai); } // ---------------------------------------------- Copia void Copia (TABELA t[], TABELA ta[], int *m, int n) { int i; *m = -1; for (i = 0;i <= n;i++) if (t[i].prox == -1) { (*m)++; strcpy(ta[*m].nome,t[i].nome);

Page 138: Dados_C

138

ta[*m].prox = -1; } } // ---------------------------------------------- PROGRAMA PRINCIPAL int main(void) { TABELA t[QUANT],ta[QUANT]; int i,j,k,m,u = -1; int anterior,primeiro,sai; char tecla; do { u++; printf("\nNome: "); gets(t[u].nome); t[u].prox = -1; printf("Continua [S/N]? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && u < QUANT); primeiro = 0; for (i = 1;i <= u;i++) if (strcmp(t[i].nome,t[primeiro].nome) < 0) primeiro = i; t[primeiro].prox = 0; anterior = primeiro; do { Copia(t,ta,&m,u); if (m != 0) { if (m >= 1) { i = 1; j = 0; do { if (strcmp(ta[i].nome,ta[j].nome) < 0) j = i; i++; } while (i <= m); } } else j = 0; Verifica(t,ta,&j); t[anterior].prox = j; t[j].prox = 0; anterior = j; } while (m != 0);

Page 139: Dados_C

139

j = primeiro; printf("\nLista de Nomes Ordenados por Encadeamento\n"); for (i = 0;i <= u;i++) { printf("%s\n",t[j].nome); j = t[j].prox; } getchar(); } 6.4 Métodos de Classificação Interna Os métodos de Classificação Interna podem ser:

• Por Inserção • Por Troca • Por Seleção

Observação: Para os seguintes métodos considere que as entradas são feitas no vetor v, logo após é criado o vetor c (chaves) e o vetor e (endereços). A ordenação é feita no vetor c e o vetor e é o vetor indireto de ordenação, ou seja, será mantido o vetor de entrada intacto.

6.4.1 Método por Inserção Direta Neste método ocorre a inserção de cada elemento em outro vetor ordenado.

Page 140: Dados_C

140

Utilização: Pequena quantidade de dados, pois é pouco eficiente. O vetor é dividido em dois segmentos. Inicialmente: c[0] e c[1], c[2], ... c[n] A classificação acontece por meio de interações, cada elemento do segundo segmento é inserido ordenadamente no primeiro até que o segundo segmento acabe. Por exemplo:

31) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando Método por Inserção Direta.

Solução do problema proposto (31):

Page 141: Dados_C

141

// MID.c (Método de Inserção Direta) #include <stdio.h> #define QUANT 10 int main(void) { int v[QUANT],c[QUANT],e[QUANT]; int i,j,k,u = -1; int chave,endereco; char tecla; do { u++; printf("\nNúmero: "); scanf("%d",&v[u]); printf("Continua [S/N] ? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && u < QUANT); for (i = 0;i <= u;i++) { c[i] = v[i]; e[i] = i; } for (i = 1;i <= u;i++) { k = 0; j = i - 1; chave = c[i]; endereco = e[i]; while (j >= 0 && k == 0) if (chave < c[j]) { c[j+1] = c[j]; e[j+1] = e[j]; j--; } else k = j + 1; c[k] = chave; e[k] = endereco; } printf("\n Valores: "); for (i = 0;i <= u;i++) printf("%2d ",c[i]); printf("\nEndereços: "); for (i = 0;i <= u;i++)

Page 142: Dados_C

142

printf("%2d ",e[i]); getchar(); } 6.4.2 Método por Troca Neste método, compara-se pares de elementos, trocando-os de posição caso estejam desordenados. 6.4.2.1 Método da Bolha (Bubble Sort) Cada elemento do vetor é testado com o seguinte, se estiverem fora de ordem ocorre a troca, isto é repetido até não ocorrer mais trocas. Por exemplo:

Observação: Na primeira passagem completa o último elemento esta ordenado, logo na segunda passagem não é necessário ir até o fim. 32) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando o Método da Bolha.

Solução do problema proposto (32): // Bolha.c #include <stdio.h> #include <string.h>

Page 143: Dados_C

143

#define QUANT 10 #define TRUE !0 #define FALSE 0 int main(void) { int v[QUANT], c[QUANT], e[QUANT]; int i, j, n = -1, m; int chave, endereco, troca; char tecla; do { n++; printf("\nNúmero: "); scanf("%d",&v[n]); printf("Continua [S/N] ? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && n < QUANT); for (i = 0;i <= n;i++) { c[i] = v[i]; e[i] = i; } m = n - 1; do { troca = TRUE; for (i = 0;i <= m;i++) if (c[i] > c[i+1]) { chave = c[i]; c[i] = c[i+1]; c[i+1] = chave; endereco = e[i]; e[i] = e[i+1]; e[i+1] = endereco; j = i; troca = FALSE; } m = j; } while (!troca); printf("\nLista Ordenada\n"); for (i = 0;i <= n;i++) printf("%d\n",c[i]); getchar(); } 6.4.3 Método por Seleção

Page 144: Dados_C

144

Seleção sucessiva do menor valor da tabela. A cada passo o menor elemento é colocado em sua posição definitiva. 6.4.3.1 Método por Seleção Direta A cada passo do método é feita uma varredura do segmento que corresponde os elementos, ainda não selecionados, e determinado o menor elemento o qual é colocado na primeira posição do elemento por troca. Por exemplo:

33) Escreva um programa em C que ordena (classifica em ordem crescente) um vetor de números inteiros utilizando o Método por Seleção Direta.

Solução do problema proposto (33): // Direta.c #include <stdio.h> #include <string.h> #define QUANT 10 #define TRUE !0 #define FALSE 0 int main(void) { int v[QUANT], c[QUANT], e[QUANT]; int i, j, n = -1, min;

Page 145: Dados_C

145

int chave, endereco, troca; char tecla; do { n++; printf("\nNúmero: "); scanf("%d",&v[n]); printf("Continua [S/N] ? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla) && n < QUANT); for (i = 0;i <= n;i++) { c[i] = v[i]; e[i] = i; } for (i = 0;i <= n-1;i++) { min = i; for (j = i+1;j <= n;j++) if (c[j] < c[min]) min = j; chave = c[i]; c[i] = c[min]; c[min] = chave; endereco = e[i]; e[i] = e[min]; e[min] = endereco; } printf("\nLista Ordenada\n"); for (i = 0;i <= n;i++) printf("%d\n",c[i]); getchar(); }

Page 146: Dados_C

146

7. Árvores São estruturas de dados (não-lineares) que caracterizam uma relação entre os dados, à relação existente entre os dados é uma relação de hierarquia ou de composição (um conjunto é subordinado a outro). 7.1 Conceitos Básicos Definição É um conjunto finito T de um ou mais nós, tais que: a) Existe um nó principal chamado raiz (root); b) Os demais nós formam n >= 0 conjuntos disjuntos T1, T2, ... Tn, onde cada um destes subconjuntos é uma árvore. As árvores Ti (i >= 1 e i <= n) recebem a denominação de sub-árvores.

Terminologia Grau Indica o número de sub-árvores de um nó.

Page 147: Dados_C

147

Observação: Se um nodo não possuir nenhuma sub-árvore é chamado de nó terminal ou folha.

Page 148: Dados_C

148

Nível É o comprimento, ou seja, o número de linhas do caminho da raiz até o nó.

Observação: Raiz é nível zero (0) Exemplo:

Altura É o nível mais alto da árvore. Na árvore acima, a altura é igual a 2. Floresta É um conjunto de zero ou mais árvores disjuntas, ou seja, se for eliminado o nó raiz da árvore, as sub-árvores que restarem chamam-se de florestas.

Page 149: Dados_C

149

Formas de Representação de Árvores

α) Representação Hierárquica

β) Representação por Conjunto (Diagrama de Inclusão ou Composição)

χ) Representação por Expressão Parentetizada (Parênteses Aninhados)

( A ( B ( ) C ( D ( G ( ) H ( ) ) E ( ) F ( I ( ) ) ) ) )

δ) Representação por Expressão não Parentetizada

AA 2 B 0 C 3 D 2 G 0 H 0 E 0 F 1 I 0

ε) Representação por Endentação (Digrama de Barras)

Page 150: Dados_C

150

Page 151: Dados_C

151

7.2 Árvores Binárias Uma árvore binária (T) é um conjunto finito de nós que pode ser vazio ou pode ser dividida em três sub-conjuntos disjuntos: raiz, sub-árvore esquerda (Te) e sub-árvore direita (Td). São estruturas onde o grau de cada nó é menor ou igual a dois, ou seja, no máximo grau 2. O número máximo de nós no nível i é 2i.

São árvores onde cada nó tem no máximo dois filhos (grau máximo 2), desta forma, obtém-se uma estrutura apropriada para busca binária, pois sabe-se que existe, para cada nodo, duas sub-árvores (Te e Td).

Para cada nó da árvore associa-se uma chave (dado, valor ou

informação) que permite a realização de uma classificação. A Construção da árvore deve ser de forma que na sub-árvore à esquerda (Te) da raiz só existam nós com chaves menores que a chave da raiz. E a sub-árvore à direita (Td) só pode conter nós com valores maiores que a raiz. Com esta reestruturação da árvore, a busca de um determinado nó torna-se trivial. O acesso aos dados pode ser feito então através de funções recursivas.

Propriedades 1) O número máximo de nodos no k-ésimo nível de uma árvore binária é 2k; 2) O número máximo de nodos em uma árvore binária com altura k é 2k+1-1, para k >= 0; 3) Árvore completa: árvore de altura k com 2k+1-1 nodos.

Page 152: Dados_C

152

Exemplo de uma aplicação de Árvore Binária (Analisador de Expressão):

Expressão: (3 + 6) * (4 – 1) + 5

Conversão de Árvore Genérica em Árvore Binária

• Ligar os nós irmãos; • Remover a ligação entre o nó pai e seus filhos, exceto as do

primeiro filho.

Page 153: Dados_C

153

Nota: O nó da sub-árvore à esquerda é filho e o nó da sub-árvore à direita é irmão

Page 154: Dados_C

154

7.3 Representações 7.3.1 Representação por Contigüidade Física (Adjacência) Os nodos são representados seqüencialmente na memória. Devem ser tomados os seguintes cuidados:

• Ser alocado espaço suficiente para armazenar a estrutura completa; • Os nodos devem ser armazenados em uma lista, onde cada nodo i da

árvore ocupa o i-ésimo nodo da lista. Exemplo:

Sendo i a posição de um nodo e n o número máximo de nodos da árvore. Observações: 1) O pai de i está em i / 2 sendo i <= n. Se i = 1, i é a raiz e não possui pai. 2) O filho à esquerda de i está em 2i se 2i <= n. Se 2i > n, então tem filho à esquerda. 3) O filho à direita de i está em 2i+1. Se 2i+1 <= n. Se 2i+1 > n, então tem filho à direita. Observação: Representação por Contigüidade Física não é um modo conveniente para representar árvores, na maioria dos casos. Vantagens

• Adequado para árvores binárias completas. • Útil para o armazenamento em disco ou fita (seqüencial).

Page 155: Dados_C

155

Desvantagem

• A estrutura pode ter muitos espaços sem uso. • Estrutura possui limite finito no número de nodos.

7.3.2 Representação por Encadeamento

esq: endereço do nodo filho à esquerda info: contém a informação do nodo dir: endereço do nodo filho à direita

typedef char TDados; typedef struct TNodo { struct TNodo *esq; TDados info; struct TNodo *dir; }; typedef struct { TNodo *raiz; } TArvore; TArvore *a;

Page 156: Dados_C

156

7.4 Caminhamento em Árvores Consiste em processar de forma sistemática e ordenada cada nó da árvore apenas uma vez, obtendo assim uma seqüência linear de nós. Abaixo são mostrados os três tipos de caminhamentos: 7.4.1 Caminhamento Pré-Fixado (Pré-Ordem) 1) Visitar a raiz; 2) Caminhar na sub-árvore da esquerda; 3) Caminhar na sub-árvore a direita. Observação: “Visitar” significa qualquer operação em relação à informação (info) do nodo. Exemplo:

Caminhamento: ABDECFG

7.4.2 Caminhamento In-Fixado (Central) 1) Caminhar na sub-árvore da esquerda; 2) Visitar a raiz; 3) Caminhar na sub-árvore da direita. Exemplo: Conforme exemplo acima, o caminhamento In-Fixado é:

Caminhamento: DBEACGF

Page 157: Dados_C

157

7.4.3 Caminhamento Pós-Fixado 1) Caminhar na subárvore da esquerda; 2) Caminhar na subárvore da direita; 3) Visitar a raiz. Exemplo: Conforme exemplo acima, o caminhamento Pós-Fixado é:

Caminhamento: DEBGFCA

7.4.4 Algoritmos recursivos para percorrer Árvores Binárias

Algoritmos de busca de dados em estruturas hierárquicas, caminhamentos em árvores, por exemplo, utilizam, normalmente, recursividade.

Recursividade é uma técnica utilizada em programação quando se

deseja que uma função faça uma chamada a si própria. Este mecanismo utiliza uma estrutura de pilha para fazer o controle do retorno de todas as chamadas realizadas.

Como vantagens das funções recursivas tem-se:

a) clareza na interpretação do código (funções pequenas); b) “simplicidade” e elegância na implementação.

Como desvantagens tem-se:

a) dificuldade para encontrar erros (debug); b) dificuldade de encontrar o critério de parada da função; c) em alguns casos podem ser ineficientes devido a quantidade de chamadas recursivas.

Page 158: Dados_C

158

A chamada de uma função recursiva requer espaço para os parâmetros, variáveis locais e endereço de retorno. Todas estas informações são armazenadas em uma pilha e depois desalocadas, ou seja, a quantidade de informações é proporcional ao número de chamadas. Todas as operações envolvidas na recursividade contribuem para um gasto maior de tempo, pois a alocação e liberação de memória consomem tempo. 7.4.4.1 Caminhamento Pré-Fixado (Pré-Ordem) void Caminhamento_Pre_Ordem(TArvore *a) { if (!Vazia(a)) { printf("%c ", a->info); // mostra raiz Caminhamento_Pre_Ordem(a->esq); // mostra sub_esq Caminhamento_Pre_Ordem(a->dir); // mostra sub_dir } } 7.4.4.2 Caminhamento In-Fixado (Central) void Caminhamento_In_Fixado(TArvore *a) { if (!Vazia(a)) { Caminhamento_In_Fixado(a->esq); // mostra sub_esq printf("%c ", a->info); // mostra raiz Caminhamento_In_Fixado(a->dir); // mostra sub_dir } } 7.4.4.3 Caminhamento Pós-Fixado void Caminhamento_Pos_Fixado(TArvore *a) { if (!Vazia(a)) { Caminhamento_Pos_Fixado(a->esq); // mostra sub_esq Caminhamento_Pos_Fixado(a->dir); // mostra sub_dir printf("%c ", a->info); // mostra raiz } } 34) Escreva um programa em C que cria a árvore da página 137. O programa deve exibir na tela os três tipos de caminhamentos. Solução do problema proposto (34):

Page 159: Dados_C

159

// tree.c // Compilador: Dev-C++ 4.9.9.2 #include <stdio.h> typedef char TDados; typedef struct Nodo { struct Nodo *esq; TDados info; struct Nodo *dir; } TNodo; typedef TNodo TArvore; // --------------------------------------------- Cria TArvore *Cria(TArvore *esq, TDados info, TArvore* dir) { TArvore *p; p = (TArvore*) malloc(sizeof(TArvore)); if (p == NULL) { printf("ERRO FATAL: Falta de Memória\n"); getchar(); exit(0); } else { p->info = info; p->esq = esq; p->dir = dir; } return p; } // --------------------------------------------- Vazia int Vazia(TArvore *a) { if (a == NULL) return(1); else return(0); } // --------------------------------------------- Caminhamento_Pre_Ordem

Page 160: Dados_C

160

void Caminhamento_Pre_Ordem(TArvore *a) { if (!Vazia(a)) { printf("%c ", a->info); // mostra raiz Caminhamento_Pre_Ordem(a->esq); // mostra sub_esq Caminhamento_Pre_Ordem(a->dir); // mostra sub_dir } } // --------------------------------------------- Caminhamento_In_Fixado void Caminhamento_In_Fixado(TArvore *a) { if (!Vazia(a)) { Caminhamento_In_Fixado(a->esq); // mostra sub_esq printf("%c ", a->info); // mostra raiz Caminhamento_In_Fixado(a->dir); // mostra sub_dir } } // --------------------------------------------- Caminhamento_Pos_Fixado void Caminhamento_Pos_Fixado(TArvore *a) { if (!Vazia(a)) { Caminhamento_Pos_Fixado(a->esq); // mostra sub_esq Caminhamento_Pos_Fixado(a->dir); // mostra sub_dir printf("%c ", a->info); // mostra raiz } } // --------------------------------------------- Destroi TArvore *Destroi(TArvore *a) { if (!Vazia(a)) { Destroi(a->esq); // libera sub_esq Destroi(a->dir); // libera sub_dir free(a); // libera raiz } return(NULL); } // --------------------------------------------- Programa Principal int main(void) {

Page 161: Dados_C

161

TArvore *a,*a1,*a2,*a3,*a4,*a5,*a6; system(“cls”); a1 = Cria(NULL,'d',NULL); a2 = Cria(NULL,'e',NULL); a3 = Cria(a1,'b',a2); a4 = Cria(NULL,'g',NULL); a5 = Cria(a4,'f',NULL); a6 = Cria(NULL,'c',a5); a = Cria(a3,'a',a6); printf("Caminhamentos na Árvore\n\n Pré-Ordem: "); Caminhamento_Pre_Ordem(a); printf("\n In-Fixado: "); Caminhamento_In_Fixado(a); printf("\nPós-Fixado: "); Caminhamento_Pos_Fixado(a); system(“pause”); return(0); }

Resultado do Programa: Caminhamentos na Árvore Pré-Ordem: a b d e c f g In-Fixado: d b e a c g f Pós-Fixado: d e b g f c a 35) Escreva um programa em C que Insere (ordenado), Exclui e exibe na tela os três tipos de caminhamentos na árvore criada. Solução do problema proposto (35): // arvore.c // Autor: Ricardo Andrade Cava // Adaptação: Paulo Roberto Gomes Luzzardi // Data: 16/09/2005 // Compilador: Dev-C++ 4.9.9.2 #include <stdio.h>

Page 162: Dados_C

162

#include <string.h> #include <stdlib.h> // ------------------------------------ defines #define SUCESSO 0 #define FALTA_DE_MEMORIA 1 #define INFO_NAO_EXISTE 2 // ------------------------------------ Definição de Tipos typedef int TDados; typedef struct Nodo { struct Nodo *ptesq; TDados info; struct Nodo *ptdir; } TNodo; typedef struct { TNodo *raiz; } TArvore; // ------------------------------------ Prototypes int Inclui(TArvore *a,TDados info); int Inclui_Recursivo(TNodo **ptnodo,TDados info); int Exclui(TArvore *a,int info); int Exclui_Recursivo(TNodo **ptnodo,int info); TNodo **Procura_Maior(TNodo **ptnodo); void Cria_Arvore(TArvore *a); void Pre_Ordem(TArvore a); void Pre_Ordem_Recursivo (TNodo *ptnodo); void Em_Ordem(TArvore a); void Em_Ordem_Recursivo (TNodo *ptnodo); void Pos_Ordem(TArvore a); void Pos_Ordem_Recursivo (TNodo *ptnodo); // ------------------------------------ Programa Principal int main(void) { TArvore a; TDados info; char tecla,op; system(“cls”); Cria_Arvore(&a); do { printf("[I]ncluir\n"); printf("[E]xcluir\n");

Page 163: Dados_C

163

printf("[C]aminha\n"); printf("[F]im\n"); printf("\nQual a sua Opção? "); do { tecla = getchar(); } while (!strchr("IiEeCcFf",tecla)); printf("%c\n",tecla); switch (tecla) { case 'I': case 'i': printf("\nInformação: "); scanf("%d",&info); Inclui (&a,info); break; case 'E': case 'e': printf("\nInformação: "); scanf("%d",&info); if (Exclui (&a,info)== INFO_NAO_EXISTE) printf("ERRO: Informação Inexistente\n"); break; case 'C': case 'c': printf("[1] Pré-fixado\n"); printf("[2] In-fixado\n"); printf("[3] Pós-fixado\n"); printf("\nQual o Caminhamento? "); do { op = getchar(); } while (!strchr("123",op)); printf("%c\n",tecla); switch (op) { case '1': printf("\nCaminhamento Pré-Ordem: "); Pre_Ordem(a); printf("\n\n"); break; case '2': printf("\nCaminhamento Em-Ordem: "); Em_Ordem(a); printf("\n\n"); break; case '3': printf("\nCaminhamento Pós-Ordem: "); Pos_Ordem(a); printf("\n\n"); break; } break; } } while (!strchr("Ff",tecla)); } // ------------------------------------ Cria_Arvore

Page 164: Dados_C

164

void Cria_Arvore (TArvore *a) { a->raiz = NULL; } // ------------------------------------ Inclui int Inclui(TArvore *a, TDados info) { return(Inclui_Recursivo(&(a->raiz),info)); } // ------------------------------------ Inclui_Recursivo int Inclui_Recursivo (TNodo **ptnodo, TDados info) { TNodo *p; if (*ptnodo == NULL) { p = (TNodo *) malloc (sizeof(TNodo)); if (p == NULL) return(FALTA_DE_MEMORIA); else { p->ptesq = NULL; p->ptdir = NULL; p->info = info; *ptnodo = p; return SUCESSO; } } else if (info < (*ptnodo)->info) return(Inclui_Recursivo(&((*ptnodo)->ptesq),info)); else return(Inclui_Recursivo(&((*ptnodo)->ptdir),info)); } // ------------------------------------ Exclui int Exclui(TArvore *a, int info) { return(Exclui_Recursivo( &(a->raiz),info)); } // ------------------------------------ Exclui_Recursivo int Exclui_Recursivo (TNodo **ptnodo, TDados info) { TNodo *p,**aux;

Page 165: Dados_C

165

if (*ptnodo == NULL) return(INFO_NAO_EXISTE); else if ( info < (*ptnodo)->info) return(Exclui_Recursivo (&((*ptnodo)->ptesq),info)); else if (info > (*ptnodo)->info) return(Exclui_Recursivo (&((*ptnodo)->ptdir),info)); else { if ((*ptnodo)->ptesq == NULL) if ((*ptnodo)->ptdir == NULL) { free ( *ptnodo); *ptnodo = NULL; } else { p = *ptnodo; *ptnodo = (*ptnodo)->ptdir; free(p); } else if ((*ptnodo)->ptdir == NULL) { p = *ptnodo; *ptnodo = (*ptnodo)->ptesq; free(p); } else { aux = Procura_Maior( &(*ptnodo)->ptesq); (*ptnodo)->info = (*aux)->info; return(Exclui_Recursivo(aux,(*aux)->info)); } return SUCESSO; } } // ------------------------------------ Procura_Maior TNodo **Procura_Maior(TNodo **ptnodo) { if ((*ptnodo)->ptdir == NULL) return(ptnodo); else return(Procura_Maior(&(*ptnodo)->ptdir)); } // ------------------------------------ Pre_Ordem

Page 166: Dados_C

166

void Pre_Ordem(TArvore a) { Pre_Ordem_Recursivo(a.raiz); } // ------------------------------------ Pre_Ordem_Recursivo void Pre_Ordem_Recursivo (TNodo *ptnodo) { if (ptnodo != NULL) { printf("%d ",ptnodo->info); Pre_Ordem_Recursivo(ptnodo->ptesq); Pre_Ordem_Recursivo(ptnodo->ptdir); } } // ------------------------------------ Em_Ordem void Em_Ordem(TArvore a) { Em_Ordem_Recursivo(a.raiz); } // ------------------------------------ Em_Ordem_Recursivo void Em_Ordem_Recursivo(TNodo *ptnodo) { if (ptnodo != NULL) { Em_Ordem_Recursivo(ptnodo->ptesq); printf("%d ",ptnodo->info); Em_Ordem_Recursivo(ptnodo->ptdir); } } // ------------------------------------ Pos_Ordem void Pos_Ordem(TArvore a) { Pos_Ordem_Recursivo(a.raiz); } // ------------------------------------ Pos_Ordem_Recursivo void Pos_Ordem_Recursivo (TNodo *ptnodo) { if (ptnodo != NULL) { Pos_Ordem_Recursivo(ptnodo->ptesq);

Page 167: Dados_C

167

Pos_Ordem_Recursivo(ptnodo->ptdir); printf("%d ",ptnodo->info); } } 7.5 Árvore de Busca Binária

Uma árvore de busca binária (BST - Binary Search Tree) é uma árvore binária cujas chaves (informações ou dados) aparecem em ordem crescente quando a árvore é percorrida em ordem in-Fixado (esquerda -> raiz -> direita, ou seja, caminhamento ERD). Onde a chave de cada nó da árvore deve ser:

• maior ou igual que qualquer chave na sua sub-árvore esquerda;

• menor ou igual que qualquer chave na sua sub-árvore direita.

Em outras palavras, a ordem esquerda-raiz-direita das chaves deve ser crescente. 36) Escreva um programa em C que cria a árvore binária ordenada (a, b, c, d, e, f, g). O programa deve permitir ao usuário buscar o endereço de uma determinada informação, ou seja, uma letra de ‘a’ até ‘z’. Solução do problema proposto (36): // busca.c // Compilador: Dev-C++ 4.9.9.2 #include <stdio.h> #define ESC 27 typedef char TDados; typedef struct Nodo { struct Nodo *esq; TDados info; struct Nodo *dir; } TNodo; typedef TNodo TArvore; // --------------------------------------------- Cria

Page 168: Dados_C

168

TArvore *Cria(TArvore *esq, TDados info, TArvore* dir) { TArvore *p; p = (TArvore*) malloc(sizeof(TArvore)); if (p == NULL) { printf("ERRO FATAL: Falta de Memória\n"); getchar(); exit(0); } else { p->info = info; p->esq = esq; p->dir = dir; } return p; } // --------------------------------------------- Vazia int Vazia(TArvore *a) { if (a == NULL) return(1); else return(0); } // --------------------------------------------- Caminhamento_In_Fixado void Caminhamento_In_Fixado(TArvore *a) { if (!Vazia(a)) { Caminhamento_In_Fixado(a->esq); printf("\nEndereço: %p - Info: %c", a, a->info); Caminhamento_In_Fixado(a->dir); } } // --------------------------------------------- Destroi TArvore *Destroi(TArvore *a) { if (!Vazia(a)) { Destroi(a->esq); Destroi(a->dir);

Page 169: Dados_C

169

free(a); } return(NULL); } // --------------------------------------------- Busca TArvore *Busca(TArvore *raiz, TDados chave) { TArvore *a1; if (raiz == NULL) return(NULL); else if (raiz->info == chave) // busca na raiz return(raiz); else { a1 = Busca(raiz->esq,chave); // busca na sub-árvore esquerda if (a1 == NULL) a1 = Busca(raiz->dir,chave); // busca na sub-árvore direita return(a1); } } // --------------------------------------------- Programa Principal int main(void) { TArvore *a,*a1,*a2,*a3,*a4,*a5,*a6,*arv; TDados info; system(“cls”); a1 = Cria(NULL,'a',NULL); a2 = Cria(NULL,'c',NULL); a3 = Cria(a1,'b',a2); a4 = Cria(NULL,'e',NULL); a5 = Cria(NULL,'g',NULL); a6 = Cria(a4,'f',a5); a = Cria(a3,'d',a6); printf("\nCaminhamento In-Fixado: "); Caminhamento_In_Fixado(a); printf("\nESC - Abandona"); do { printf("\nInfo: "); do { info = getchar(); } while (!(info >= 'a' && info <= 'z') && info != ESC); if (info != ESC) { arv = Busca(a,info);

Page 170: Dados_C

170

printf("\nEndereço do Nodo [%c]: %p", info, arv); } } while (info != ESC); return(0); } 7.6 Árvore AVL

O objetivo principal na utilização de árvores AVL é diminuir o custo de acesso as informações desejadas, ou seja, organizar a árvore de forma a otimizar a busca em uma árvore binária.

Os algoritmos de árvore AVL são muito parecidos com os

algoritmos de uma árvore binária, a diferença está no esforço necessário para se manter uma árvore AVL balanceada.

Para manter uma árvore balanceada, precisa-se constantemente

refazer a estrutura da árvore nas operações de inserção ou exclusão de elementos. Árvores AVL, B e B++ são árvores balanceadas. Balanceamento em Árvores

Diz-se que uma árvore está balanceada (equilibrada), se todos os nós folhas estão à mesma distância da raiz, ou seja, uma árvore é dita balanceada quando as suas sub-árvores à esquerda e à direita possuem a mesma altura. Quando uma árvore não está balanceada, chama-se degenerada.

O balanceamento de uma árvore binária pode ser: estático ou

dinânico (AVL). O balanceamento estático de uma árvore binária consiste em construir uma nova versão da árvore, reorganizando-a, enquanto que no balanceamento dinâmico (AVL) a cada nova operação realizada na árvore binária, ela sobre rotações deixando-a balanceada.

O termo AVL foi colocado em homenagem aos matemáticos

russos Adelson-Velskii e Landis.

Page 171: Dados_C

171

Adelson-Velskii e Landis em 1962 apresentaram uma árvore de busca binária que é balanceada levando-se em consideração a altura das suas sub-árvores, ou seja, uma árvore AVL é uma árvore binária de pesquisa onde a diferença em altura entre as sub-árvores esquerda e direita é no máximo 1 (positivo ou negativo).

Está diferença é chamado de fator de balanceamento (fb). Este

fator deve ser calculado para todos os nós da árvore binária. O fator de balanceamento de um nó folha é sempre zero, ou seja, fb = 0.

O fator de balanceamento (fb) é um número inteiro igual a:

fb (nodo) = altura (subárvore direita) – altura (subárvore esquerda);

Definição: Uma árvore binária vazia é sempre balanceada por altura. Se T não é vazia e Te e Td são sub-árvores da esquerda e direita, respectivamente, então T é balanceada por altura se: a) Te e Td são balanceadas por altura; b) altura Te – altura Td for igual a 0, 1 ou -1. 7.6.1 Inserção em uma árvore AVL

Quando um novo elemento é inserido em uma árvore binária, é necessário verificar se esta inserção quebrou a propriedade de balanceamento da árvore, ou seja, é necessário calcular o fator de balanceamento dos nodos da árvore. Se o Fb de algum nodo for diferente de 0, 1 ou -1, é necessário reestruturar a árvore para que volte a ser balanceada.

Na inserção de um nodo em uma árvore AVL, pode-se ter quatro situações distintas, cuja a forma de tratamento é diferente:

a) Inserção dos nós: 10, 20 e 30

b) Inserção dos nós: 30, 20 e 10

Page 172: Dados_C

172

c) Inserção dos nós: 10, 30 e 20

d) Inserção dos nós: 30, 10 e 20

Note que nas quatro situações acima (a, b, c e d), o fator de

balanceamento de algum nodo ficou fora da faixa [-1..1], ou seja, algum nodo teve o Fator de Balanceamento (Fb) 2 ou -2. Para cada caso acima, aplica-se um tipo de rotação a árvore: a) Rotação simples à esquerda

Page 173: Dados_C

173

b) Rotação simples à direita

c) Rotação dupla à esquerda

(rotação simples à direita + rotação simples à esquerda)

d) Rotação dupla à direita

(rotação simples à esquerda + rotação simples à direita)

Dicas: a) Para identificar quando uma rotação é simples ou dupla deve-se observar os sinais do Fb:

Page 174: Dados_C

174

• Sinal for igual, a rotação é simples • Sinal for diferente a rotação é dupla

b) Se Fb for positivo (+) a rotação para à esquerda c) Se Fb for negativa (-) a rotação para à direita 37) Escreva um programa em C que cria uma árvore binária balanceada utilizando as características de uma árvore AVL (10, 20, 30, 40, ...). O programa deve permitir a entrada de números inteiros até que o usuário digite zero (0). Ao final a árvore é exibida. Solução do problema proposto (37): // avl.c #include <stdio.h> #include <stdlib.h> #define TRUE !0 #define FALSE 0 typedef int TDados; typedef struct nodo { TDados chave; struct nodo *esq,*dir; int bal; }*TNodo; // ------------------------------------ Procura_AVL int Procura_AVL(int x, TNodo *p) { TNodo pDeTNodo; pDeTNodo = (*p); if (!pDeTNodo) return(FALSE); else if (x<pDeTNodo->chave) return(Procura_AVL(x, &pDeTNodo->esq)); else if (x>pDeTNodo->chave) return(Procura_AVL(x, &pDeTNodo->dir)); else return(TRUE); } // ------------------------------------ Insere_AVL

Page 175: Dados_C

175

int Insere_AVL(int x, TNodo *t, int *h) { TNodo p, q, pDeTNodo; pDeTNodo = (*t); if (!pDeTNodo) { pDeTNodo = (TNodo) malloc(sizeof (struct nodo)); if (pDeTNodo == NULL) abort(); *t = pDeTNodo; *h = TRUE; pDeTNodo->chave = x; pDeTNodo->esq = NULL; pDeTNodo->dir = NULL; pDeTNodo->bal = 0; return(TRUE); } else if (x<pDeTNodo->chave) { if (!Insere_AVL(x, &pDeTNodo->esq, h)) return(FALSE); if (*h) switch(pDeTNodo->bal) { case 1: pDeTNodo->bal = 0; *h = FALSE; break; case 0: pDeTNodo->bal = (-1); break; case -1: p = pDeTNodo->esq; if (p->bal == (-1)) { // Rotação Simples pDeTNodo->esq = p->dir; p->dir = pDeTNodo; pDeTNodo->bal = 0; pDeTNodo = p; *t = pDeTNodo; } else { // Rotação Dupla q = p->dir; p->dir = q->esq; q->esq = p; pDeTNodo->esq = q->dir; q->dir = pDeTNodo; pDeTNodo->bal = (q->bal == (-1)) ? 1 : 0; p->bal = (q->bal == 1) ? (-1) : 0; pDeTNodo = q;

Page 176: Dados_C

176

*t = pDeTNodo; } pDeTNodo->bal = 0; *h = FALSE; break; } return(TRUE); } else if (x>pDeTNodo->chave) { if (!Insere_AVL(x, &pDeTNodo->dir, h)) return(FALSE); if (*h) switch(pDeTNodo->bal) { case -1: pDeTNodo->bal = 0; *h = FALSE; break; case 0: pDeTNodo->bal = 1; break; case 1: p=pDeTNodo->dir; if (p->bal == 1) { // Rotação Simples pDeTNodo->dir = p->esq; p->esq = pDeTNodo; pDeTNodo->bal = 0; pDeTNodo = p; *t = pDeTNodo; } else { // Rotação Dupla q = p->esq; p->esq = q->dir; q->dir = p; pDeTNodo->dir = q->esq; q->esq = pDeTNodo; pDeTNodo->bal = (q->bal == 1) ? (-1) : 0; p->bal = (q->bal == (-1)) ? 1 : 0; pDeTNodo = q; *t = pDeTNodo; } pDeTNodo->bal = 0; *h = FALSE; break; } return(TRUE); } else { *h = FALSE;

Page 177: Dados_C

177

return(FALSE); } } // ------------------------------------ Exibe_AVL void Exibe_AVL(TNodo pt,int indent) { int i; if (pt) { Exibe_AVL(pt->dir, indent+1); for (i = 0;i < indent;i++) printf(" "); printf("%d (%d)\n",pt->chave, pt->bal); Exibe_AVL(pt->esq, indent+1); } } // ------------------------------------ main int main(void) { TNodo raiz; int chave, dh; system("cls"); printf("Árvore AVL (0 - Sair)\n\n"); raiz = NULL; do { printf("Chave: "); scanf("%d", &chave); if (chave != 0) { if (!Insere_AVL(chave, &raiz, &dh)) printf("ERRO: Chave Repetida\n"); if (!Procura_AVL(chave, &raiz)) printf("ERRO: Chave Perdida\n"); } } while (chave != 0); printf("\nÁRVORE AVL\n\n"); Exibe_AVL(raiz,0); printf("\n"); system("pause"); } Programa online que demonstra inserções e remoções em uma árvore AVL: http://www.site.uottawa.ca/~stan/csi2514/applets/avl/BT.html

Page 178: Dados_C

178

7.6.2 Remoção em uma árvore AVL

Quando um elemento é removido de uma árvore balanceada AVL, é necessário verificar se esta operação quebrou a propriedade de balanceamento da árvore, ou seja, é necessário calcular o Fator de Balanceamento dos nodos da árvore. Se o Fb de algum nodo for diferente de 0, 1 ou -1, é necessário reestruturar a árvore para que volte a ser balanceada.

Na remoção de um nodo em uma árvore AVL, pode-se ter quatro situações distintas, cuja a forma de tratamento é diferente:

a) Remoção do nó: 10

Resultado: Com a remoção do nodo 10 a árvore ficará desbalanceada, pois o nodo raiz ficará com Fb = 2. A solução será fazer uma rotação simples à esquerda.

b) Remoção do nó: 40

Resultado: Com a remoção do nodo 40 a árvore ficará desbalanceada, pois o nodo raiz ficará com Fb = -2. A solução será fazer uma rotação simples à direita.

Page 179: Dados_C

179

c) Remoção do nó: 10

Resultado: Com a remoção do nodo 10 a árvore ficará desbalanceada, pois o nodo raiz ficará com Fb = 2. A solução será fazer uma rotação dupla à esquerda (rotação simples à direita + rotação simples à esquerda).

d) Remoção do nó: 40

Resultado: Com a remoção do nodo 40 a árvore ficará desbalanceada, pois o nodo raiz ficará com Fb = 2. A solução será fazer uma rotação dupla à direita (rotação simples à esquerda + rotação simples à direita).

Page 180: Dados_C

180

8. Grafos 8.1 Conceitos Um grafo G é definido como G (V, A) onde V é um conjunto finito e não vazio de vértices e A é um conjunto finito de arestas, ou seja, linhas, curvas ou setas que interligam dois vértices.

“Grafo é um par ordenado de conjuntos disjuntos (V, A), onde V é

um conjunto arbitrário que se designa por conjunto dos vértices e A um subconjunto de pares não ordenados de elementos (distintos) de V que se designa por conjunto das arestas.”

Um vértice é representado por um ponto ou círculo representando um nó, nodo ou informação. Uma aresta pode ser uma reta, seta ou arco representando uma relação entre dois nodos.

Quando uma aresta possui indicação de sentido (uma seta), ela é

chamada de arco, caso contrário é chamada de linha (veja grafo acima).

Orientação é a direção para a qual uma seta aponta, um grafo deste tipo é chamado grafo dirigido ou orientado.

Page 181: Dados_C

181

G(4,7) – 4 vértices e 7 arestas Cardinalidade (ordem) de um conjunto de vértices é igual a quantidade de seus elementos. Grafo denso possui alta cardinalidade de vértices, enquanto que grafo pouco povoado possui baixa cardinalidade. A ordem (V) de um grafo G é o número de vértices do grafo enquanto que a dimensão (A) é o número de arestas do grafo. No exemplo acima, a ordem do grafo é 4 enquanto que a dimensão é 7.

O conjunto de arcos do grafo acima é:

{<A,A>, <A,B>, <A,C>, <A,D>, <C,D>, <F,C>, <F,G>, <D,F>} 1 2 3 4 5 6 7 8

Um vértice que não possui nenhuma aresta incidente é chamado de isolado (figura abaixo). Um grafo com nenhum vértice é chamado de vazio.

Page 182: Dados_C

182

Passeio é uma seqüência de vértices e arestas onde o caminho é um passeio sem vértices repetidos (seqüência de vértices em que cada dois vértices consecutivos são ligados por um arco). Trajeto é um passeio sem arestas repetidas.

Passeio é uma seqüência <v0, a1, v1, a2, ..., vk-1, ak, vk> onde v0, v1,....,vk são vértices, a1, a2,...., ak são arcos e, para cada (i, ai) é um arco de vi-1 a vi. O vértice v0 é o início do passeio e o vértice vk é o seu término. Um caminho é um passeio sem vértices repetidos. A dimensão de um caminho ou trajeto é chamado de comprimento.

Ciclo é um caminho de comprimento não nulo fechado, ou seja, tem os vértices extremos iguais (é um passeio onde v0 = vk.). Circuito é um trajeto de comprimento não nulo fechado (é um ciclo sem vértices, com exceção feita a v0 e vk).

Laço é uma aresta que retorna ao mesmo vértice. Se a aresta não tiver seta ela é dita sem orientação. Diz-se que uma aresta é incidente com os vértices que ela liga, não importando a orientação. Dois vértices são adjacentes se estão ligados por uma aresta. Um vértice é dito isolado se não existe aresta incidente sobre ele. Duas arestas incidentes nos mesmos vértices, não importando a orientação, a1 = (v,w) e a2 = (v,w), então as arestas são paralelas.

Page 183: Dados_C

183

Diz-se que o grafo é conexo se para cada par de vértices existe pelo menos um passeio que os une.

Chama-se grafo não-orientado ou não-dirigido se as arestas

representam relacionamento nas duas direções, ou seja, as arestas não possuem uma seta indicando sentido.

Grafo não-orientado ou não-dirigido

V = { Paulo, Adriane, Paola, Roberta } A = { (Paulo, Adriane) , (Paulo, Paola) , (Adriane, Paola) , (Adriane, Roberta) } Explicação do grafo acima: O exemplo representa uma relação de amizade, ou seja, se Paulo é amigo de Adriane o inverso é verdade, isto se chama: relação simétrica.

Quando um grafo possui arcos (seta indicando uma direção) ele é denominado grafo dirigido ou dígrafo (veja próxima figura).

Grafo dirigido ou dígrafo

Page 184: Dados_C

184

Explicação do grafo acima: O exemplo representa uma relação de subordinação, ou seja, se “Paulo” é chefe de “Adriane” o inverso não é verdade, isto se chama: relação não-simétrica. Grau de um vértice, em um grafo não-dirigido, é o número de arestas incidentes ao vértice. Em um grafo dirigido, pode-se dividir o grau em dois: grau de emissão (número de arestas que saem do vértice) e grau de recepção (número de arestas que chegam no vértice). Toda árvore é um grafo, mas nem todo grafo é uma árvore. Um grafo onde existe um número associado a cada arco (peso) é chamado de rede ou grafo ponderado. Um exemplo deste tipo é um grafo representando cidades e distâncias entre as cidades (veja figura abaixo).

Grau de um Vértice: É igual ao número de arestas que são incidentes ao vértice. Um laço é contado duas vezes.

No exemplo acima, o vértice A tem grau 3 enquanto que o vértice B tem grau 1. Em um grafo dirigido o grau de um vértice é a soma do número de arestas que saem e chegam no vértice. Tipos de Grafos a) Simples: É um grafo que não possui laços nem arestas paralelas.

Page 185: Dados_C

185

b) Dirigido (dígrafo ou direcionado): Consiste de dois conjuntos finitos: a) vértices e b) arestas dirigidas, onde cada aresta é associada a um par ordenado de vértices chamados de nós terminais.

c) Completo: Um grafo completo de n vértices, denominado Kn, é um grafo simples com n vértices v1, v2, . . . , vn, cujo conjunto de arestas contém exatamente uma aresta para cada par de vértices distintos.

d) Ciclo: Um grafo ciclo de n vértices, denominado Cn, onde n é maior ou igual a 3, é um grafo simples com n vértices v1, v2, . . . , vn, e arestas v1v2, v2v3, . . ., vn−1vn, vnv1.

Page 186: Dados_C

186

e) Multigrafo: É um grafo que não possui laços, mas pode ter arestas paralelas.

f) Valorado: É um grafo em que cada aresta tem um valor associado (peso), ou seja, possui um conjunto de valores (pesos) associados a cada aresta.

g) Planar: É um grafo onde não há cruzamento de arestas.

h) Imersível: Um grafo é imersível em uma superfície S se puder ser representado geograficamente em S de tal forma que arestas se cruzem nas extremidades (vértices). Um grafo planar é um grafo que é imersível no plano. Um exemplo de grafo imersível é a representação das conexões de uma placa de circuito impresso, onde as arestas não

Page 187: Dados_C

187

podem se cruzar, ou seja, os cruzamentos são permitidos apenas nas extremidades. i) Regular: Um grafo é regular quando todos os seus vértices têm o mesmo grau. Grafos completos com 2, 3, 4, e 5 vértices são grafos regulares.

Grafos podem ser representados de duas formas: Matriz de Adjacências (forma apropriada para representar grafos densos) ou Lista de Adjacências (forma apropriada para representar grafos esparsos). 8.2 Representação por Lista e Matriz de Adjacências 8.2.1 Lista de Adjacências Um grafo pode ser representado por uma lista Adjacente[vi] = [va, vb] onde va, vb, ... representam os vértices que se relacionam com o vértice vi. Lista de Adjacências para um grafo dirigido:

Lista de Adjacências para um grafo não-dirigido:

Page 188: Dados_C

188

8.2.2 Matriz de Adjacências Um grafo pode ser representado por uma matriz A = (aij), onde aij representa o número de arestas de vi para vj. Matriz de Adjacências para um grafo dirigido:

Matriz de Adjacências para um grafo não-dirigido:

Page 189: Dados_C

189

8.3 Percurso em Amplitude e Percurso em Profundidade

Existem dois critérios para percorrer grafos: Percurso em Amplitude e Percurso em Profundidade. Em ambos os percursos parte-se de um nodo qualquer escolhido arbitrariamente e visita-se este nodo. A seguir, considera-se cada um dos nodos adjacentes ao nodo escolhido. Percurso em amplitude ou caminhamento em amplitude:

a) Seleciona-se um vértice para iniciar o caminhamento. b) Visitam-se os vértices adjacentes, marcando-os como visitados. c) Coloca-se cada vértice adjacente numa fila. d) Após visitar os vértices adjacentes, o primeiro da fila torna-se o novo vértice inicial. Reinicia-se o processo. e) O caminhamento termina quanto todos os vértices tiverem sido visitados ou o vértice procurado for encontrado.

Percurso em profundidade ou caminhamento em profundidade:

a) Seleciona-se um vértice para iniciar o caminhamento. b) Visita-se um primeiro vértice adjacente, marcando-o como visitado. c) Coloca-se o vértice adjacente visitado numa pilha. d) O vértice visitado torna-se o novo vértice inicial. e) Repete-se o processo até que o vértice procurado seja encontrado ou não haja mais vértices adjacentes. Se verdadeiro, desempilha-se o topo e procura-se o próximo adjacente, repetindo o algoritmo. f) O processo termina quando o vértice procurado for encontrado ou quando a pilha estiver vazia e todos os vértices tiverem sido visitados.

8.4 Determinação do Caminho Mínimo

O caminho de um vértice a outro vértice é mínimo se não existe outro caminho entre eles que tenha menos arcos.

O problema de encontrar o caminho mais curto entre dois

nós de um grafo ou uma rede é um dos problemas clássicos da Ciência da Computação. Este problema consiste, genericamente, em encontrar o caminho de menor custo entre dois nós da rede, considerando a soma dos custos associados aos arcos percorridos.

Page 190: Dados_C

190

O mais famoso algoritmo para resolver o problema de caminho mínimo em grafos é o algoritmo de Dijkstra (1959). Este algoritmo apenas funciona se os custos associados aos arcos não forem negativos, mas isso não é muito importante na maioria dos problemas práticos pois, em geral, os custos associados aos arcos são em geral grandezas fisicamente mensuráveis. Algoritmo de Dijkstra

Dado um grafo, G=(V,E) , dirigido ou não, com valores não negativos em cada arco ou ramo, utiliza-se o algoritmo de Dijkstra para encontrar a distância mínima entre um vértice inicial (s) e um vértice final (v). Ele determina a distância mínima entre s e os outros vértices na ordem dessas distâncias mínimas, ou seja, os vértices que se encontram mais próximos de s em primeiro lugar.

O algoritmo vai usar dist(v), uma estrutura que armazena e

referencia a distância de s ao nó v. De início, dist(s)=0 , pois a menor distância de s a si mesmo será sempre zero.

O símbolo LIMITE representa um valor maior que o

comprimento de qualquer caminho sem ciclos em G. Por outro lado, se dist(v) = LIMITE, indica que ainda não foi encontrado nenhum caminho com distância mínima entre s e v. De início, dist(v) = LIMITE. Será utilizado um conjunto S que contém os vértices cuja distância a s é mínima e conhecida naquele momento da execução do algoritmo. Esta distância será definitiva para cada um deles. Este conjunto será de início constituído somente pelo nó s, com dist(s) = 0. Descrição do Algoritmo Começa-se considerando a distância ao próprio s como zero. Faze-se então dist(s)= 0. Para todos os outros vértices, considera-se a sua distância a s como valendo LIMITE. Fazer então dist(v) = LIMITE. O processo de introdução de um nó Vn não pertencente a S, assumindo portanto que V(S) é diferente do conjunto vazio, consiste no seguinte: 1. Qualquer um que seja Vm não pertencente a S tal que v foi o último nó a 'entrar' em S e (V, Vm) pertencente ao conjunto V, se dist(Vm) >

Page 191: Dados_C

191

dist(V) + distância associada a (V,Vm) então dist(Vm) = dist(V) + distância associada a (V, Vm);

2. Determinar qualquer que seja Vm não pertencente a S o menor de entre os valores de dist(vm). Seja dist(vj) esse valor;

3. Fazer Vn = Vj , dist(Vn) = dist(Vj) e Vn passa a ser o novo elemento de S;

Se o vértice vn coincidir com o vértice final então dist(Vn) é a menor distância entre s e Vn , e parar a execução. Se não coincidir, voltam-se a repetir os passos 1 , 2 e 3.

Se não for possível aplicar aqueles passos, ou porque V(S) igual ao conjunto vazio ou qualquer que seja Vm não pertencente a S então (V, Vm) não pertence ao conjunto V, então não é possível determinar a distância mínima de s ao vértice final.

38) Escreva um programa em C que cria um grafo representando a ligação entre seis cidades com suas respectivas distâncias (São Paulo, Rio de Janeiro, Vitória, Recife, Salvador e Natal). O programa deve permitir a entrada da cidade origem (0..5) e da cidade destino (0..5) e exibir o caminho mínimo entre estas duas cidades através da utilização do algoritmo de Dijkstra.

Solução do problema proposto (38): // Dijkstra.c

Page 192: Dados_C

192

#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_VERTICES 6 #define LIMITE 32767 #define TRUE !0 #define FALSE 0 typedef struct { char adj; int valor; } Tinfo; // ------------------------------------ newline void newline(void) { printf("\n"); } // ------------------------------------ Inicializa_Grafo void Inicializa_Grafo(Tinfo grafo[][MAX_VERTICES]) { int l, c; for (l = 0; l < MAX_VERTICES; l++) for (c = 0; c < MAX_VERTICES; c++) { grafo[l][c].valor = LIMITE; grafo[l][c].adj = 'N'; } } // ------------------------------------ Cria_Aresta void Cria_Aresta(Tinfo grafo[][MAX_VERTICES], int origem, int destino, int info) { grafo[origem][destino].adj = 'S'; grafo[origem][destino].valor = info; grafo[destino][origem].adj = 'S'; grafo[destino][origem].valor = info; } // ------------------------------------ Imprime_Grafo void Imprime_Grafo (Tinfo grafo[][MAX_VERTICES], char Rot[][5]) { int l, c; for (l = 0; l < MAX_VERTICES; l++) printf(" %d", l); newline(); for (l = 0; l < MAX_VERTICES; l++) printf(" %s", Rot[l]); newline(); } // ------------------------------------ Imprime_Matriz void Imprime_Matriz(Tinfo grafo[][MAX_VERTICES])

Page 193: Dados_C

193

{ int l, c; printf("Matriz de Adjacencias\n"); for (l = 0; l < MAX_VERTICES; l++) { for (c = 0; c < MAX_VERTICES; c++) printf("%5d ",grafo[l][c].valor); newline(); } newline(); } // ------------------------------------ Entrada_Origem_Destino void Entrada_Origem_Destino(int tam, int *origem, int *destino) { printf("\n Origem [0..%d]: ", tam); do { scanf("%d",origem); } while (*origem < 0 || *origem > tam); printf("Destino [0..%d]: ", tam); do { scanf("%d",destino); } while (*destino < 0 || *destino > tam); } // ---------------------------------------------- Dijkstra long int Dijkstra(Tinfo grafo[][MAX_VERTICES], int origem, int destino, int precede[]) { int i, k; int distancia[MAX_VERTICES]; int menor_cam [MAX_VERTICES]; int atual, dc, menordist, novadist; char parar = 'N'; for (i = 0; i < MAX_VERTICES; i++) { distancia[i] = LIMITE; menor_cam [i] = FALSE; precede[i] = -1; } menor_cam [origem] = TRUE; distancia[origem] = 0; atual = origem; k = atual; while (atual != destino && parar == 'N') { menordist = LIMITE; dc = distancia[atual]; for (i = 0; i < MAX_VERTICES; i++) { if (menor_cam [i] == FALSE) { if (grafo[atual][i].adj =='S') novadist = dc + grafo[atual][i].valor; else novadist = grafo[atual][i].valor; if (novadist < distancia[i]) { distancia[i] = novadist; precede[i] = atual; }

Page 194: Dados_C

194

if (distancia[i] < menordist) { menordist = distancia[i]; k = i; } } } if (atual == k) parar = 'S'; else { atual = k; menor_cam [ atual] = FALSE; } } return((long) distancia[destino]); } // ------------------------------------ Imprime_Cidades void Imprime_Cidades(void) { int i; char cidades[MAX_VERTICES][30] = {"[0] - (Spa) - Sao Paulo", "[1] - (Rio) - Rio de janeiro", "[2] - (Vit) - Vitoria", "[3] - (Rec) - Recife", "[4] - (Sal) - Salvador", "[5] - (Nat) - Natal"}; for (i = 0;i < MAX_VERTICES;i++) printf("%s\n",cidades[i]); newline(); } // ------------------------------------ main int main(void) { Tinfo grafo[MAX_VERTICES][MAX_VERTICES]; int precede[MAX_VERTICES]; char rotulos[MAX_VERTICES][5] = {"Spa", "Rio", "Vit", "Rec", "Sal", "Nat"},tecla; int origem, destino, aux1, aux2; long int result; Inicializa_Grafo(grafo); Cria_Aresta(grafo, 0, 1, 300); Cria_Aresta(grafo, 0, 3, 400); Cria_Aresta(grafo, 0, 4, 100); Cria_Aresta(grafo, 1, 2, 100); Cria_Aresta(grafo, 1, 5, 70); Cria_Aresta(grafo, 2, 3, 50); Cria_Aresta(grafo, 4, 5, 50); Cria_Aresta(grafo, 3, 5, 150); do { system("cls"); Imprime_Matriz(grafo); Imprime_Cidades(); Imprime_Grafo(grafo, rotulos); Entrada_Origem_Destino(MAX_VERTICES-1,&origem, &destino); result = Dijkstra(grafo, origem, destino, precede); if (result == LIMITE || result == 0) printf("\nNao ha trajeto entre %s e %s", rotulos[origem], rotulos[destino]); else {

Page 195: Dados_C

195

printf("\nMenor caminho entre %s e %s = %ld", rotulos[origem], rotulos[destino], result); printf("\nCaminho INVERSO Percorrido: "); aux1 = precede[destino]; aux2 = destino; while (aux1 != origem) { printf("\n %s -> %s (%d)", rotulos[aux1], rotulos[aux2], grafo[aux1][aux2].valor); aux2 = aux1; aux1 = precede[aux1]; printf("\n %s -> %s (%d)", rotulos[aux1], rotulos[aux2], grafo[aux1][aux2].valor); } } newline(); printf("\nRepetir [S/N]? "); do { tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla)); return(0); }