80264-estruturas de dados usando c (reparado)

223
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. 1

Upload: marcelo-jose-ramos

Post on 31-Jul-2015

73 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: 80264-Estruturas de Dados Usando C (Reparado)

Universidade Católica de PelotasCentro Politécnico

Ciência da Computação

Estruturas de Dados em Cpor

Prof. Dr. Paulo Roberto Gomes [email protected]

[email protected]

http://infovis.ucpel.tche.br/luzzardihttp://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

Sumário

1

Page 2: 80264-Estruturas de Dados Usando C (Reparado)

1. Tipos de Dados

1.1 Conceitos Básicos1.2 Tipos Primitivos1.3 Construção de Tipos (Estruturados ou Complexos)

1.3.1 Strings1.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éticos1.4.2 Relacionais1.4.3 Lógicos

2. Vetores e Matrizes

2.1 Conceitos Básicos

3. Listas Lineares

3.1 Listas Genéricas3.2 Tipos de Representações

3.2.1 Lista Representada por Contigüidade Física3.2.2 Lista Representada por Encadeamento3.2.3 Lista Encadeada com Descritor3.2.4 Lista Duplamente Encadeada3.2.5 Listas com disciplinas de Acesso

3.2.5.1 Filas3.2.5.1.1 Fila com Vetor3.2.5.1.2 Fila Circular3.2.5.1.3 Fila com Alocação Dinâmica

3.2.5.2 Pilhas3.2.5.2.1 Pilha com Vetor3.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 Deques3.3 Representação por Contigüidade Física3.4 Representação por Encadeamento

2

Page 3: 80264-Estruturas de Dados Usando C (Reparado)

4. Arquivos

4.1 Sistema de Arquivo Bufferizado4.2 Argumentos argc e argv

5. Pesquisa de Dados

5.1 Pesquisa Seqüencial5.2 Pesquisa Binária5.3 Cálculo de Endereço (Hashing)

6. Classificação de Dados (Ordenação)

6.1 Classificação por Força Bruta6.2 Vetor Indireto de Ordenação (Tabela de Índices)6.3 Classificação por Encadeamento6.4 Métodos de Classificação Interna

6.4.1 Método por Inserção Direta6.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ásicos7.2 Árvores Binárias7.3 Representações

7.3.1 Representação por Contigüidade Física7.3.2 Representação por Encadeamento

7.4 Caminhamentos em Árvores7.4.1 Caminhamento Pré-Fixado (Pré-Ordem)7.4.2 Caminhamento In-Fixado (Central)7.4.3 Caminhamento Pós-Fixado7.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ária7.6 Árvores AVL

7.6.1 Inserção em uma árvore AVL7.6.2 Remoção em uma árvore AVL

8. Grafos

3

Page 4: 80264-Estruturas de Dados Usando C (Reparado)

8.1 Conceitos 8.2 Representação por Lista e Matriz de Adjacências

8.2.1 Lista de Adjacências8.2.2 Matriz de Adjacências

8.3 Percurso em Amplitude e Percurso em Profundidade 8.4 Determinação do Caminho Mínimo

4

Page 5: 80264-Estruturas de Dados Usando C (Reparado)

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;

4) E as operações que podem ser efetuadas.

5

Page 6: 80264-Estruturas de Dados Usando C (Reparado)

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 valoreschar 1 8 -128 à 127Int 2 16 -32768 à 32767

float 4 32 -3.4E-38 à 3.4E+38double 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”;

1.3.2 Vetor (Agregados Homogêneos)

Tipo de dado que permite que uma variável possua vários elementos, todos do mesmo tipo.

6

Page 7: 80264-Estruturas de Dados Usando C (Reparado)

Exemplo:

#define MAX 10float 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;

Acesso aos elementos

tempo->horas, tempo->minutos ou tempo->segundos ou

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

1.3.4 Ponteiros (Apontadores)

7

Page 8: 80264-Estruturas de Dados Usando C (Reparado)

É 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>

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

8

Page 9: 80264-Estruturas de Dados Usando C (Reparado)

{ 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();}

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 NULOp ................................... Endereço da memória RAM*p ................................. Conteúdo do ponteiro&p ................................. Endereço do ponteiro

9

Page 10: 80264-Estruturas de Dados Usando C (Reparado)

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

10

Page 11: 80264-Estruturas de Dados Usando C (Reparado)

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 elementovetor[MAX-1].............. último elementom[0][0]...................... primeiro elementom[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]);

Exercícios:

11

Page 12: 80264-Estruturas de Dados Usando C (Reparado)

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

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.

12

Page 13: 80264-Estruturas de Dados Usando C (Reparado)

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.

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 lista3) Xn é o último nodo da lista

13

Page 14: 80264-Estruturas de Dados Usando C (Reparado)

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.

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

14

Page 15: 80264-Estruturas de Dados Usando C (Reparado)

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;· 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.

15

Page 16: 80264-Estruturas de Dados Usando C (Reparado)

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”.

Representação: A lista X está representada por um vetor V de “m” elementos.

Componentes de uma Lista

16

Page 17: 80264-Estruturas de Dados Usando C (Reparado)

· 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;

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

17

Page 18: 80264-Estruturas de Dados Usando C (Reparado)

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);}

}

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;

18

Page 19: 80264-Estruturas de Dados Usando C (Reparado)

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); 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

19

Page 20: 80264-Estruturas de Dados Usando C (Reparado)

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; 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){

20

Page 21: 80264-Estruturas de Dados Usando C (Reparado)

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

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

21

Page 22: 80264-Estruturas de Dados Usando C (Reparado)

#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

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();}

22

Page 23: 80264-Estruturas de Dados Usando C (Reparado)

// ------------------------------------ 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, 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;

23

Page 24: 80264-Estruturas de Dados Usando C (Reparado)

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; }}

// ------------------------------------ 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)

24

Page 25: 80264-Estruturas de Dados Usando C (Reparado)

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

início = -1fim = -1

Lista cheia

início = 0fim = 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;

25

Page 26: 80264-Estruturas de Dados Usando C (Reparado)

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){ 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;

26

Page 27: 80264-Estruturas de Dados Usando C (Reparado)

}

// ---------------------------------------------- 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->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");

27

Page 28: 80264-Estruturas de Dados Usando C (Reparado)

break; case LISTA_CHEIA: printf("\nERRO: Lista Cheia");

break; }}

// ---------------------------------------------- Exibe_Lista

void Exibe_Lista(TLISTA x){ int i;

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;

28

Page 29: 80264-Estruturas de Dados Usando C (Reparado)

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);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;}

29

Page 30: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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_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;

30

Page 31: 80264-Estruturas de Dados Usando C (Reparado)

case LISTA_CHEIA_DIREITA: printf("\nERRO: Lista Cheia na DIREITA"); break;

}}

// ---------------------------------------------- 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

31

Page 32: 80264-Estruturas de Dados Usando C (Reparado)

void Cria_Lista(TLISTA *x);int Inclui_Inicio(TLISTA *x, TDADOS dado);int Inclui_Fim(TLISTA *x, TDADOS dado);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;

} }

32

Page 33: 80264-Estruturas de Dados Usando C (Reparado)

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)

{ 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

{

33

Page 34: 80264-Estruturas de Dados Usando C (Reparado)

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;

printf("\nLista: "); if (x.inicio == -1) printf("VAZIA");

34

Page 35: 80264-Estruturas de Dados Usando C (Reparado)

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, um elemento aponta (possui o endereço, posição de memória do próximo elemento) para o próximo.

35

Page 36: 80264-Estruturas de Dados Usando C (Reparado)

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);void destroi_Lista(TLISTA *l);

// ------------------------------------ Programa Principal

36

Page 37: 80264-Estruturas de Dados Usando C (Reparado)

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; p->dado = d; p->elo = NULL; }

else { p->dado = d;

37

Page 38: 80264-Estruturas de Dados Usando C (Reparado)

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);}

}

// ------------------------------------ Remove_Ultimo

int Remove_Ultimo(TLISTA *l){ TNODO *p,*q;

if (l->primeiro == NULL) return(LISTA_VAZIA);

38

Page 39: 80264-Estruturas de Dados Usando C (Reparado)

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)

{ l->primeiro = p->elo; free(p); p = l->primeiro; }

}}

39

Page 40: 80264-Estruturas de Dados Usando C (Reparado)

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){ TLISTA l; TDADOS valor; int erro;

Cria_Lista(&l); do { printf("Valor: "); scanf("%d",&valor); if (valor != 0)

{

40

Page 41: 80264-Estruturas de Dados Usando C (Reparado)

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; p->elo = NULL; }

else { p->dado = d; p->elo = l->primeiro; l->primeiro = p; }

return(SUCESSO); }}

41

Page 42: 80264-Estruturas de Dados Usando C (Reparado)

// ------------------------------------ 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 { printf("Lista Encadeada: "); p = l.primeiro; while (p != NULL)

{ printf("%02d ",p->dado); p = p->elo; }

return(SUCESSO); }}

42

Page 43: 80264-Estruturas de Dados Usando C (Reparado)

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);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;

43

Page 44: 80264-Estruturas de Dados Usando C (Reparado)

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;}

// ------------------------------- 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)

{

44

Page 45: 80264-Estruturas de Dados Usando C (Reparado)

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;

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;

45

Page 46: 80264-Estruturas de Dados Usando C (Reparado)

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 |");}

// ----------------------------------------- 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; }

}

46

Page 47: 80264-Estruturas de Dados Usando C (Reparado)

}

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:

47

Page 48: 80264-Estruturas de Dados Usando C (Reparado)

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;

// ------------------------------------ Prototypes

48

Page 49: 80264-Estruturas de Dados Usando C (Reparado)

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();}

// ------------------------------------ Cria_Lista

void Cria_Lista (TDESCRITOR *l){

49

Page 50: 80264-Estruturas de Dados Usando C (Reparado)

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); }}

// ------------------------------------ Consulta_Ultimo

int Consulta_Ultimo(TDESCRITOR d,TDADOS *dado){

50

Page 51: 80264-Estruturas de Dados Usando C (Reparado)

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; }

}}

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):

51

Page 52: 80264-Estruturas de Dados Usando C (Reparado)

// 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);

// ------------------------------- Programa Principal

int main(void){ TLISTA l; TDADOS valor; int erro; char tecla1,tecla2;

52

Page 53: 80264-Estruturas de Dados Usando C (Reparado)

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);

elseerro = 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);}

} while (tecla1 != 'F'); Destroi_Lista(&l);}

// ------------------------------- Cria_Lista

void Cria_Lista(TLISTA *l){ l->primeiro = NULL; l->n = 0; l->ultimo = NULL;}

53

Page 54: 80264-Estruturas de Dados Usando C (Reparado)

// ------------------------------- 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); 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;

54

Page 55: 80264-Estruturas de Dados Usando C (Reparado)

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;

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;

55

Page 56: 80264-Estruturas de Dados Usando C (Reparado)

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; 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

56

Page 57: 80264-Estruturas de Dados Usando C (Reparado)

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;

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

57

Page 58: 80264-Estruturas de Dados Usando C (Reparado)

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;

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

58

Page 59: 80264-Estruturas de Dados Usando C (Reparado)

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;

} 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;

59

Page 60: 80264-Estruturas de Dados Usando C (Reparado)

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){ 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)++;

60

Page 61: 80264-Estruturas de Dados Usando C (Reparado)

} 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: "); 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

61

Page 62: 80264-Estruturas de Dados Usando C (Reparado)

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

{ 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); }

}}

62

Page 63: 80264-Estruturas de Dados Usando C (Reparado)

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)

63

Page 64: 80264-Estruturas de Dados Usando C (Reparado)

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 VaziaDestroi_Fila(f); Desfaz a FILAerro = Insere_ Fila(f, i); Insere o dado "i" no fim da FILAerro = Exclui_ Fila (f); Retira da FILA o primeiro elementoerro = Consulta_ Fila (f, j); Copia em "j" o primeiro elemento da FILA

64

Page 65: 80264-Estruturas de Dados Usando C (Reparado)

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

typedef int TDADOS;

65

Page 66: 80264-Estruturas de Dados Usando C (Reparado)

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

void Cria_Fila(FILA *f){ f->primeiro = 0; f->ultimo = -1;

66

Page 67: 80264-Estruturas de Dados Usando C (Reparado)

}

// ------------------------------------ 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); }}

// ------------------------------------------ Imprime_Erro

void Imprime_Erro(int erro){ switch (erro)

67

Page 68: 80264-Estruturas de Dados Usando C (Reparado)

{ 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

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

3.2.5.1.2 Fila Circular

68

Page 69: 80264-Estruturas de Dados Usando C (Reparado)

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

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

69

Page 70: 80264-Estruturas de Dados Usando C (Reparado)

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

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);

70

Page 71: 80264-Estruturas de Dados Usando C (Reparado)

}}

// -------------------------------- 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; }}

Como calcular a nova posição:

71

Page 72: 80264-Estruturas de Dados Usando C (Reparado)

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

typedef int TDADOS;

typedef struct nodo { TDADOS info; struct nodo *seg; } TNODO;

72

Page 73: 80264-Estruturas de Dados Usando C (Reparado)

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) Imprime_Erro(erro); else printf("\nValor: %d\n",valor);

break; } } while (tecla != 'F'); Destroi_Fila(&f);}

73

Page 74: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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 { 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);

74

Page 75: 80264-Estruturas de Dados Usando C (Reparado)

}

// ---------------------------------------------- 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);}

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

75

Page 76: 80264-Estruturas de Dados Usando C (Reparado)

LIFO - "Last In First Out" (último a entrar é o primeiro a sair)

Operações sobre Pilhas

Cria_Pilha(p); Cria pilha VaziaDestroi_Pilha(p); Desfaz a pilhaerro = Push(p, i); Empilha o dado "i" no fim da PILHAerro = Pop(p, i); Desempilha o primeiro elementoerro = Consulta_Pilha(p,j); Copia em "j" o primeiro elemento

Identificadores da Pilha

B(p) Base da PILHAT(p) Topo da PILHAL(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.

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

76

Page 77: 80264-Estruturas de Dados Usando C (Reparado)

// -------------------------------- 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: ");

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;

}

77

Page 78: 80264-Estruturas de Dados Usando C (Reparado)

} 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]; 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

78

Page 79: 80264-Estruturas de Dados Usando C (Reparado)

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

// ---------------------------------------------- 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

79

Page 80: 80264-Estruturas de Dados Usando C (Reparado)

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) { 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);

80

Page 81: 80264-Estruturas de Dados Usando C (Reparado)

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;

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 ...");

81

Page 82: 80264-Estruturas de Dados Usando C (Reparado)

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); }}

// ---------------------------------------------- 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); }

82

Page 83: 80264-Estruturas de Dados Usando C (Reparado)

}

// --------------------------------- 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

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();

83

Page 84: 80264-Estruturas de Dados Usando C (Reparado)

}

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.

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

84

Page 85: 80264-Estruturas de Dados Usando C (Reparado)

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); 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;}

85

Page 86: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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); }}

// ---------------------------------------------- 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); }}

86

Page 87: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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

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

87

Page 88: 80264-Estruturas de Dados Usando C (Reparado)

3. Número (Push pilha 2) empilhe na pilha de números4. Operando (Push pilha 1) empilhe na pilha de operandos5. ")" (Pop pilha 2, Pop pilha 2 e Pop pilha 1) execute a operação6. 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: + - * /

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

88

Page 89: 80264-Estruturas de Dados Usando C (Reparado)

// -------------------------------- 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);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);

89

Page 90: 80264-Estruturas de Dados Usando C (Reparado)

erro = Push(&p1,resposta); break;

} } erro = Pop(&p1,&resposta); if (erro) Imprime_Erro(erro); else printf("Resposta: %d",resposta); }

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);

90

Page 91: 80264-Estruturas de Dados Usando C (Reparado)

else return(FALSE);}

// ------------------------------------------ Calcula

int Calcula(int v1, int v2, char op){ 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); }}

91

Page 92: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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; }}

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 + 1Dir = (m DIV 2) + 1

92

Page 93: 80264-Estruturas de Dados Usando C (Reparado)

Deque Vazio

Esq = -1Dir = -1

Deque Cheio

Esq = 0Dir = 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

typedef int TDADOS;

93

Page 94: 80264-Estruturas de Dados Usando C (Reparado)

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;

} break;

case 'E': switch (ch)

94

Page 95: 80264-Estruturas de Dados Usando C (Reparado)

{ 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)

{ d->esq = m / 2; d->dir = d->esq; }

else d->esq = d->esq - 1;

95

Page 96: 80264-Estruturas de Dados Usando C (Reparado)

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) return(DEQUE_VAZIO); else { *dado = d->v[d->dir]; d->dir = d->dir - 1; if (d->dir < d->esq)

Cria_Deque(d);

96

Page 97: 80264-Estruturas de Dados Usando C (Reparado)

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]);}

// ------------------------------------ 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");

97

Page 98: 80264-Estruturas de Dados Usando C (Reparado)

break; case DEQUE_VAZIO: printf("ERRO: Deque Vazio");

break; } printf(", tecle <ENTER> para continuar"); getchar();}

98

Page 99: 80264-Estruturas de Dados Usando C (Reparado)

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).

Em C existem dois tipos de sistemas de arquivos: bufferizado e não-bufferizado.

99

Page 100: 80264-Estruturas de Dados Usando C (Reparado)

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“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

100

Page 101: 80264-Estruturas de Dados Usando C (Reparado)

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 { printf("Ok, arquivo criado com sucesso\n"); ...

Função: putc

101

Page 102: 80264-Estruturas de Dados Usando C (Reparado)

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 gravadofp é 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

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.

102

Page 103: 80264-Estruturas de Dados Usando C (Reparado)

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 120 ‘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:

C:\>lista lista.c <enter>

// lista.c

#include <stdio.h>

int main(int argc, char *argv[]){ FILE *fp; char ch;

if (argc != 2)

103

Page 104: 80264-Estruturas de Dados Usando C (Reparado)

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 {

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);

104

Page 105: 80264-Estruturas de Dados Usando C (Reparado)

}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 { 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); } }

}

105

Page 106: 80264-Estruturas de Dados Usando C (Reparado)

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); 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);

106

Page 107: 80264-Estruturas de Dados Usando C (Reparado)

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

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

107

Page 108: 80264-Estruturas de Dados Usando C (Reparado)

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 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.

108

Page 109: 80264-Estruturas de Dados Usando C (Reparado)

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; 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);

109

Page 110: 80264-Estruturas de Dados Usando C (Reparado)

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) 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

110

Page 111: 80264-Estruturas de Dados Usando C (Reparado)

#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); 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>

111

Page 112: 80264-Estruturas de Dados Usando C (Reparado)

#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;

// ------------------------------------ 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);

112

Page 113: 80264-Estruturas de Dados Usando C (Reparado)

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);

} 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);

113

Page 114: 80264-Estruturas de Dados Usando C (Reparado)

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; 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

114

Page 115: 80264-Estruturas de Dados Usando C (Reparado)

{ 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]; 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);}

115

Page 116: 80264-Estruturas de Dados Usando C (Reparado)

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.

Solução do problema proposto (23):

116

Page 117: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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); chave = Pesquisa_Sequencial(t,ch,n); if (chave != -1)

{

117

Page 118: 80264-Estruturas de Dados Usando C (Reparado)

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; char nome[21]; float peso; float altura;

} TABELA;

118

Page 119: 80264-Estruturas de Dados Usando C (Reparado)

// ---------------------------------------------- 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);}

// ---------------------------------------------- Ordena_Tabela

void Ordena_Tabela(TABELA t[], int n){ int i,j; TABELA temp;

119

Page 120: 80264-Estruturas de Dados Usando C (Reparado)

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.

O método consiste na comparação do "valor" com a chave localizada na metade da tabela, pode ocorrer:

valor = chave............chave localizadavalor < chave............chave está na primeira metade (esquerda)valor > chave............chave está na segunda metade (direita)

120

Page 121: 80264-Estruturas de Dados Usando C (Reparado)

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++; 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)

121

Page 122: 80264-Estruturas de Dados Usando C (Reparado)

{ 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; break; }

else if (valor < t[metade]) { fim = metade - 1; metade = (fim + inic) / 2; } else { inic = metade + 1; metade = (fim + inic) / 2;

122

Page 123: 80264-Estruturas de Dados Usando C (Reparado)

} } 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.

Exemplo:

n = 53entrada: [0..1000]

123

Page 124: 80264-Estruturas de Dados Usando C (Reparado)

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;

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;

124

Page 125: 80264-Estruturas de Dados Usando C (Reparado)

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

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));

125

Page 126: 80264-Estruturas de Dados Usando C (Reparado)

} 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 PrincipalClassificação Externa...................... Memória Secundária

6.1 Classificação por Força Bruta

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):

126

Page 127: 80264-Estruturas de Dados Usando C (Reparado)

// 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]);}

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();}

127

Page 128: 80264-Estruturas de Dados Usando C (Reparado)

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):

// 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)

128

Page 129: 80264-Estruturas de Dados Usando C (Reparado)

{ 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) 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++)

129

Page 130: 80264-Estruturas de Dados Usando C (Reparado)

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]; 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

130

Page 131: 80264-Estruturas de Dados Usando C (Reparado)

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:

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)

{

131

Page 132: 80264-Estruturas de Dados Usando C (Reparado)

*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); 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)

132

Page 133: 80264-Estruturas de Dados Usando C (Reparado)

{ 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); 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.

133

Page 134: 80264-Estruturas de Dados Usando C (Reparado)

6.4.1 Método por Inserção Direta

Neste método ocorre a inserção de cada elemento em outro vetor ordenado.

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:

134

Page 135: 80264-Estruturas de Dados Usando C (Reparado)

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):

// 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];

135

Page 136: 80264-Estruturas de Dados Usando C (Reparado)

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++) 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:

136

Page 137: 80264-Estruturas de Dados Usando C (Reparado)

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>

#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));

137

Page 138: 80264-Estruturas de Dados Usando C (Reparado)

} 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

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:

138

Page 139: 80264-Estruturas de Dados Usando C (Reparado)

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; 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];

139

Page 140: 80264-Estruturas de Dados Usando C (Reparado)

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();}

140

Page 141: 80264-Estruturas de Dados Usando C (Reparado)

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ó.

141

Page 142: 80264-Estruturas de Dados Usando C (Reparado)

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

142

Page 143: 80264-Estruturas de Dados Usando C (Reparado)

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.

143

Page 144: 80264-Estruturas de Dados Usando C (Reparado)

Formas de Representação de Árvores

a) Representação Hierárquica

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

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

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

d) 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

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

144

Page 145: 80264-Estruturas de Dados Usando C (Reparado)

145

Page 146: 80264-Estruturas de Dados Usando C (Reparado)

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.

146

Page 147: 80264-Estruturas de Dados Usando C (Reparado)

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.

147

Page 148: 80264-Estruturas de Dados Usando C (Reparado)

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

148

Page 149: 80264-Estruturas de Dados Usando C (Reparado)

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).

149

Page 150: 80264-Estruturas de Dados Usando C (Reparado)

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 à esquerdainfo: contém a informação do nododir: 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;

150

Page 151: 80264-Estruturas de Dados Usando C (Reparado)

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: DBEACGF7.4.3 Caminhamento Pós-Fixado

151

Page 152: 80264-Estruturas de Dados Usando C (Reparado)

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.

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

152

Page 153: 80264-Estruturas de Dados Usando C (Reparado)

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):

// tree.c// Compilador: Dev-C++ 4.9.9.2

#include <stdio.h>

153

Page 154: 80264-Estruturas de Dados Usando C (Reparado)

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

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 }

154

Page 155: 80264-Estruturas de Dados Usando C (Reparado)

}

// --------------------------------------------- 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){ 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);

155

Page 156: 80264-Estruturas de Dados Usando C (Reparado)

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 fPó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>#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;

156

Page 157: 80264-Estruturas de Dados Usando C (Reparado)

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"); 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;

157

Page 158: 80264-Estruturas de Dados Usando C (Reparado)

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

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){

158

Page 159: 80264-Estruturas de Dados Usando C (Reparado)

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;

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

159

Page 160: 80264-Estruturas de Dados Usando C (Reparado)

{ 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

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

160

Page 161: 80264-Estruturas de Dados Usando C (Reparado)

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); 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.

161

Page 162: 80264-Estruturas de Dados Usando C (Reparado)

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

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

162

Page 163: 80264-Estruturas de Dados Usando C (Reparado)

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); 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); }

}

163

Page 164: 80264-Estruturas de Dados Usando C (Reparado)

// --------------------------------------------- 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); 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

164

Page 165: 80264-Estruturas de Dados Usando C (Reparado)

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.

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

165

Page 166: 80264-Estruturas de Dados Usando C (Reparado)

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

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

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

166

Page 167: 80264-Estruturas de Dados Usando C (Reparado)

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

167

Page 168: 80264-Estruturas de Dados Usando C (Reparado)

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:

168

Page 169: 80264-Estruturas de Dados Usando C (Reparado)

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

b) Se Fb for positivo (+) a rotação para à esquerdac) 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

int Insere_AVL(int x, TNodo *t, int *h)

169

Page 170: 80264-Estruturas de Dados Usando C (Reparado)

{ 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; *t = pDeTNodo; } pDeTNodo->bal = 0; *h = FALSE;

170

Page 171: 80264-Estruturas de Dados Usando C (Reparado)

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; return(FALSE); }}

// ------------------------------------ Exibe_AVL

171

Page 172: 80264-Estruturas de Dados Usando C (Reparado)

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

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

172

Page 173: 80264-Estruturas de Dados Usando C (Reparado)

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 r otaçã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 r otação simples à direita.

173

Page 174: 80264-Estruturas de Dados Usando C (Reparado)

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 r otaçã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 r otação dupla à direita (rotação simples à esquerda + rotação simples à direita).

174

Page 175: 80264-Estruturas de Dados Usando C (Reparado)

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.

175

Page 176: 80264-Estruturas de Dados Usando C (Reparado)

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.

176

Page 177: 80264-Estruturas de Dados Usando C (Reparado)

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.

177

Page 178: 80264-Estruturas de Dados Usando C (Reparado)

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

178

Page 179: 80264-Estruturas de Dados Usando C (Reparado)

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.

179

Page 180: 80264-Estruturas de Dados Usando C (Reparado)

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.

180

Page 181: 80264-Estruturas de Dados Usando C (Reparado)

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

181

Page 182: 80264-Estruturas de Dados Usando C (Reparado)

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:

182

Page 183: 80264-Estruturas de Dados Usando C (Reparado)

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:

8.3 Percurso em Amplitude e Percurso em Profundidade

183

Page 184: 80264-Estruturas de Dados Usando C (Reparado)

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.

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

184

Page 185: 80264-Estruturas de Dados Usando C (Reparado)

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) > 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;

185

Page 186: 80264-Estruturas de Dados Usando C (Reparado)

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

#include <stdio.h>#include <stdlib.h>#include <string.h>

#define MAX_VERTICES 6

#define LIMITE 32767

#define TRUE !0

186

Page 187: 80264-Estruturas de Dados Usando C (Reparado)

#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]){ 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();

187

Page 188: 80264-Estruturas de Dados Usando C (Reparado)

}

// ------------------------------------ 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; } if (distancia[i] < menordist) { menordist = distancia[i]; k = i; } } } if (atual == k) parar = 'S'; else { atual = k; menor_cam [ atual] = FALSE;

188

Page 189: 80264-Estruturas de Dados Usando C (Reparado)

} } 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 { 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 {

189

Page 190: 80264-Estruturas de Dados Usando C (Reparado)

tecla = getchar(); } while (!strchr("SsNn",tecla)); } while (strchr("Ss",tecla)); return(0);}

190