Você está na página 1de 47

Estrutura de

Dados

Profa. Dra. Vera Prudência Caminha


veraprudencia@id.uff.br

ICEx/UFF

Volta Redonda-RJ
© 2002-2021
Estrutura de Dados

Sumário

Complexidade de Algoritmos .................................................................. 03


Passos Elementares ................................................................................. 03
Complexidade de Tempo ......................................................................... 06
Análise dos Piores Casos ……………………………………………………. 12
Notação-O ................................................................................................. 13
Regras de Notação-O ............................................................................... 13
Definição Formal de Notação-O ............................................................. 14
Exercícios .................................................................................................. 16
Ordenação Simples .................................................................................. 17
Busca Sequencial em Vetor Ordenado ................................................... 20
Busca Binária ........................................................................................... 21
Exercícios ……………………………………………………………………… 22
Listas Lineares Estáticas ........................................................................ 23
Pilha Sequencial ...................................................................................... 23
Exercícios ……………………………………………………………………… 25
Fila Sequencial ........................................................................................ 27
Exercícios ……………………………………………………………………… 29
Fila Circular ............................................................................................. 33
Listas Sequenciais não ordenadas .......................................................... 37
Exercícios ……………………………………………………………………… 39
Listas Sequenciais ordenadas ................................................................. 43
Exercícios .................................................................................................. 44

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 2
Estrutura de Dados

Complexidade de Algoritmos

A característica mais importante de um algoritmo é o seu


tempo de execução.

O objetivo do estudo de complexidade é obter uma equação


matemática que permita a análise do comportamento do algoritmo
segundo dois parâmetros:

Tempo (gasto na execução)


Espaço (memória ocupada)

As medidas obtidas são independentes da máquina em que o


algoritmo executará.

Passo elementar de um algoritmo

É qualquer computação cujo o tempo de execução pode ser


limitado somente por uma constante que não esteja relacionada nem
com a quantidade e nem com os valores de entrada submetidos ao
algoritmo.

O processo de execução de um algoritmo pode ser dividido em


etapas elementares, denominados passos.

Cada passo consiste na execução de um numero fixo de


operações básicas cujos tempos de execução são considerados
constantes.

Exemplos de passos elementares:

1) Operações aritméticas (soma, produto, comparação, etc.);


2) Operações Lógicas (&&, ||, !);
3) Atribuições;
4) Desvios condicionais;
5) Acesso a elementos em vetores;
6) Acesso a campo struct;

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 3
Estrutura de Dados

7) Obtenção de endereço;
8) Alteração/Obtenção de conteúdo através de ponteiros;
9) Chamada de função;
10) Retorno de valores.

Ex. 1: Fatorial

int fatorial (int n) {

int i, fat;

fat = 1; //passo de atribuição

i=1; //passo de atribuição

for( ;i<=n; ) { // passo de desvio

fat = fat *i; // passo de multiplicação e de atribuição

i++;// passo de incremento

return (fat); // passo de retorno

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 4
Estrutura de Dados

Ex. 2: Sequência de Fibonacci (0,1,1,2,3,5,8,13...)

int fibo(int n)

{ int i, x, y z;

x=0; // passo de atribuição

y=1; // passo de atribuição

i=1; // passo de atribuição

while(i<n) { //passo de desvio

z=x+y; // passo de soma e atribuição

x=y; // passo de atribuição

y=z; // passo de atribuição

return x; //passo de retorno

Ex. 3: Busca Sequencial em vetor não ordenado

int busca(int v[], int x)

{ int i=0; // passo de atribuição

for( ;i<n; ) // passo desvio

{ if(v[i]==x) //acesso ao vetor e comparação

return(i); // passo de retorno

i++; //passo de incremento

return(-1); // passo de retorno

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 5
Estrutura de Dados

Ex. 4: MDC

int mdc(int a, int b) {

while(a!=0 && b!=0) { // passo de comparação, lógico e desvio

if(a>b) // passo de desvio

a = a - b; // passo de subtração e atribuição

else

b = b - a; // passo de subtração e atribuição

return (a+b); // passo de soma e retorno

Complexidade de Tempo

Estamos interessados em calcular o número de passos


elementares executados por um algoritmo em dois extremos:

a) Melhor Caso: dentre todas as entradas possíveis de tamanho n,


qual é a que faz o algoritmo executar o menor número de passos
elementares?

b) Pior Caso: dentre todas as entradas possíveis de tamanho n, qual


é a que faz o algoritmo executar o maior número de passos
elementares?

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 6
Estrutura de Dados

Ex: Busca sequencial (vetor não ordenado)

Entrada: o vetor com n elementos e a chave x.

Melhor Caso: o elemento procurado está na primeira posição


do vetor.

Pior Caso: o elemento procurado não está no vetor.

- Analisando o melhor caso:

int busca(int v[], int x)

{ int i=0; 1

for( ;i<n; ) 1

{ if(v[i]==x) 1

return(i); 1

i++;

return(-1);

No. de passos: 1+1+1+1 = 4

- Analisando o Pior Caso:

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 7
Estrutura de Dados

int busca(int v[], int x)

{ int i=0; 1

for( ;i<n; ) n+1

{ if(v[i]==x) 1*n

return(i); 1*n

i++; 1*n

return(-1); 1

No. de passos: 1+ n+1 + n*(1+1+1) +1 = 2 + n + 3n +1 = 4n+3

Ex: Considerando as marcações conte os passos das funções abaixo no


melhor e no pior caso:

int fatorial(int n) {

int i, fat;

i=1; 1

fat=1; 1

for( ;i<=n; ) n+1

fat=fat*i; 1*n

i++; 1*n

return (fat); 1

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 8
Estrutura de Dados

Nesse caso o número de passos no melhor caso e no pior caso são


iguais, pois o corpo de for será feito n+1 vezes para calcular o
fatorial de qualquer número n inteiro positivo.

No. de passos do melhor caso: 1+1+n+1+n+n+1 = 3n+4

No. de passos do pior caso: 1+1+n+1+n+n+1 = 3n+4

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 9
Estrutura de Dados

Ex. 2: Soma dos elementos em um vetor de inteiros não vazio.

int somaVetor(int v[], int n)


{
int soma, i ;
soma=0; 1
i=0; 1
while(i<n) n+1
{
soma = soma + v[i]; 1 * n
i++; 1
}
return (soma); 1
}

No. de passos (Melhor Caso): 1+ 1+n+1+ (1+1)*n+1 = 3n+4

No. de passos (Pior Caso): 1+ 1+n+1+(1+1)*n+1 = 3n+4

Neste caso o número de passos no melhor caso e no pior caso são


iguais, pois o corpo do for será feito n+1 vezes para calcular a soma de
todos os elementos do vetor de tamanho n (n>=1).

Ex. 3: Busca em matriz quadrada nxn

Melhor Caso: o elemento está na posição [0][0].

int buscaMatriz(int m[][], int x)


{
int i, j;
i=0; 1
for( ;i<n; ) 1
{
j=0; 1
for( ;j<n; ) 1
{
if(m[i][j]==x) 1
return(1); 1
j++;
}
i++;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 10
Estrutura de Dados

return(0);
}

No. de passos (Melhor Caso): 1+1+1+1+1+1 = 6

Pior Caso: o elemento não ser encontrado na matriz.

int buscaMatriz(int m[][], int x)


{
int i, j;
i=0; 1
for( ;i<n; ) n+1
{
j=0; 1*n
for( ;j<n; ) (n+1)*n
{
if(m[i][j]==x) 1*n*n
return(1); 1*n*n
j++; 1*n*n
}
i++; 1*n
}
return(0); 1
}

No. de passos (Pior Caso): 1 + n+1 + n*(1+ n+1+ n*(1+1+1) + 1) +1 =


2+n+n*(4n + 3) + 1= 2 + n + 4n2 + 3n +1 = 4n²+4n+3

Exercício:

1) Conte os passos no pior caso nos trechos de código abaixo:

a) int soma = 0;1


for(i=0; i<=n; i++) n+1+1=n+2
soma++; n+1

No. Passos = 1+ n+2 + n+1 = 2n+4

b) soma=0;
for(i=1; i<n; i++)
soma++;

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 11
Estrutura de Dados

c) soma=0;
for(i=1; i<=n; i++)
soma++;

d) soma=0;
for(i=0; i<n; i++)
for(j=0; j<n; j++)
soma++;

e) soma=0;
for(i=0; i<=n; i++)
for(j=1; j<n; j++)
soma++;

f) soma=0;
for(i=0; i<=n; i++)
for(j=1; j<n; j++)
for(k=2; k<n; k++)
soma++;

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 12
Estrutura de Dados

Análise dos Piores Casos

Um entendimento básico de como age um algoritmo é


importante, porém, normalmente estamos interessados em como um
algoritmo atua no pior caso.
Existem 4 razões principais para analisar os piores casos:

1) Muitos algoritmos executam suas piores performances boa parte do


tempo;
2) O melhor caso não fornece muita informação;
3) Determinar o caso de desempenho médio não é fácil;
4) O pior caso nos dá um limite máximo.

Ex: Vamos montar uma tabela e ver como se comportam os


algoritmos da Busca Sequencial (Alg1) e da Busca em Matriz
Quadrada (Alg2) quando n (tamanho dos dados) cresce:

Alg1 Alg2
n 4n+3 4n²+4n+3
0 3 3
1 7 11
2 11 27
4 19 83
8 35 291
16 67 1.091
128 515 66.051
1024 4.099 4.198.403

Da tabela, podemos tirar dois resultados importantes:

1) Os termos constantes das equações não influenciam no resultado


final da análise.
2) As diferenças entre os algoritmos ficam mais evidentes quando n
tende ao infinito.

Usando essa metodologia não vamos conseguir expressar o


tempo de execução de um algoritmo em segundos ou minutos. Ao

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 13
Estrutura de Dados

invés disso, para as equações de tempo, vamos usar a terminologia de


análise de algoritmos, dizemos que:
“ Alg1 é da ordem n” e “Alg2 é da ordem n²”

Notação-O

É o tipo de notação mais comum usada para expressar a


performance de um algoritmo de maneira formal.

Expressamos a performance de um algoritmo como uma função


do tamanho de dados que processa. Isto é, para alguns dados de
tamanho n, descrevemos sua performance como uma função f(n).

Estamos interessados na taxa de crescimento de f, que descreve


a rapidez com que a performance do algoritmo irá decair a medida
que o tamanho de dados que processa se torna arbitrariamente
grande.

A taxa de crescimento ou complexidade assintótica de um


algoritmo é significativa porque descreve o quanto um algoritmo é
eficiente no que diz respeito a entradas arbitrárias.

A notação-O reflete a ordem de crescimento do algoritmo.

Regras simples de notação-O:

1) Termos constantes são expressos como O(1).

Formalmente:
O(c) = O(1)

Ex. 1:
void funcao()
{
int i=0; 1
while(i<30) 30+1
{ printf("%d\t",i); 30
i++; 30
© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 14
Estrutura de Dados

}
}
T(n) = 1+31+30+30 = 92 → O(1)

Ex. 2: T(n) = 1.000 → O(1)

Ex.3: T(n) = 1.000.000 → O(1)

2) Constantes multiplicativas são omitidas.

Formalmente:

O(c*T) = c*O(t) = O(t)

Ex. 1:
T(n)= 3n+5 → O(3n) = 3*O(n) = O(n)

Ex. 2:
T(n) = 6n2 + 7n + 2 → O(6n 2 ) = 6*O(n2) = O(n2)

3) A adição é efetuada tornando-se a máxima.


Formalmente:

O(T1) + O(T2) = O(T1+T2) = max(O(T1)+O(T2))

Ex. 1: T1 e T2 descrevem duas tarefas executadas em sequência num


algoritmo.
T1(n) = n
T2(n) = n²
O(n) + O(n²) = O(n+n²) = max(O(n)+O(n²)) = O(n²)

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 15
Estrutura de Dados

4) Multiplicações não são alteradas.


Formalmente:

O(T1) * O(T2) = O(T1*T2)

Ex. 1: em um loop aninhado, as iterações externas são descritas por


T1 e as internas por T2.
T1(n) = n
T2(n) = n
O(n)*O(n) = O(n*n) = O(n²)

Definição Formal de Notação-O

Definição: Considere uma função f(n) não negativa para os inteiros


n≥0. Dizemos que f(n) é O(g(n)), isto é, f(n) é da ordem g(n), se existir
um inteiro n0 e uma constante c>0 tais que, para todo inteiro n≥n0,
f(n) ≤ c*g(n).

Ex: Considere as funções f e g abaixo:


f(n) = 10n
g(n) = n²
Pergunta: f(n) é O(g(n))?

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 16
Estrutura de Dados

Suponha c=1, então temos:


n f(n) g(n)
1 10 1
4 40 16
6 60 36
8 80 64
9 90 81
10 100 100
11 110 121
12 120 144
13 130 169

Logo, g(n) domina assintóticamente f(n), ou seja, f(n) é O(g(n)). Dessa forma,
10n≤ n2 para c=1 e n0=10. Isto é, para valores de n<10, f(n) gasta mais
tempo do que g(n), quando n=10, f(n) e g(n) gastam o mesmo tempo e para
n>10, g(n) será sempre maior do que f(n).

Exemplos de complexidades mais comuns:

Função
(Complexidade) Nome Exemplo

c → 0(1) Constante Loop definido


log n → O(log n) Logarítmica Busca Binária, Árvores
n → O(n) Linear Busca Sequencial, Fatorial
n log n → O(n log n) n log n Mergesort
n² → O(n²) Quadrática Matriz bidimensional
n³ → O(n³) Cúbica Matriz tridimensional
2n → O(2n) Exponencial Torre de Hanói
n! → O(n!) Fatorial Problema do Caixeiro Viajante

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 17
Estrutura de Dados

Exercícios:

1)Tenho dois algoritmos que resolvem um problema P, ambos com


complexidade de tempo no pior caso O(f(n)). Posso afirmar que os dois
algoritmos tem tempos de execução idênticos para entradas de mesmo
tamanho?

2) Considere duas funções f e g. A primeira delas é O(n) e a segunda é O(n²).


É correto afirmar que a função f vai ser sempre mais rápida que a função g?
Exemplifique.

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 18
Estrutura de Dados

Ordenação Simples

Definição: É processo pelo qual um conjunto de dados é colocado em


ordem crescente ou decrescente. O objetivo é facilitar a localização dos
membros de um conjunto de dados. Quando se ordena uma
informação apenas uma parte da informação é usada com a chave de
ordenação.

Vamos estudar três métodos de ordenação simples: Bubblesort,


Selectionsort e Inserctionsort.

Considere a ordenação de um vetor de inteiros de tamanho definido.

#define N 5

void troca (int *a, int *b) {

int aux;

aux= *a;

*a=*b;

*b=aux;

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 19
Estrutura de Dados

1. Ordenação por trocas (bubblesort)

void bubbleSort (int v[], int max) {

// max vale N-1

int i, trocou=1;

while(trocou) {

trocou=0;

for(i=0; i<max; i++) {

if(v[i]>v[i+1]) {

troca(&v[i], &v[i+1]);

trocou=1;

max--;

Exercício: Faça o acompanhamento da função bubbleSort e ordene o vetor


abaixo passo a passo:

8 5 4 2 6

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 20
Estrutura de Dados

2) Ordenação por seleção

void selectionSort(int v[])

int i, j, menor;

for(j=0; j<N-1; j++) {

menor = j;

for(i=j+1; i<N; i++) {

if(v[i]<v[menor])

menor=i;

troca(&v[j], &v[menor]);

Exercício: Faça o acompanhamento da função selectionSort e ordene o vetor


abaixo passo a passo:

8 5 4 2 6

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 21
Estrutura de Dados

3) Ordenação por inserção

void inserctionSort(int v[])

int i, j;

for(j=1; j<N; j++) {

for(i=j; i>0 && v[i-1]>v[i]; i--)

troca(&v[i-1], &v[i]);

Exercício: Faça o acompanhamento da função inserctionSort e ordene o


vetor abaixo passo a passo:

8 5 4 2 6

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 22
Estrutura de Dados

Busca em Vetor Ordenado

1) Busca Sequencial:

int buscaSeqOrd (int v[], int chave) {

int i;

for(i=0; i< N && chave > v[i]; i++);

if((i==N) || (chave != v[i]))

return 0; // não achou

return 1; // achou

Ex: Dado o vetor abaixo, faça a busca de cada elemento indicado fazendo o
acompanhamento no algoritmo passo a passo:
2 6 9 10 23 25 37
Buscar: 9
Buscar: 24

2) Busca Binária

A ideia é testar um elemento sorteado aleatoriamente a m e compará-lo


com um argumento de busca x.
- Se am for igual a x a busca termina;
- Se am for menor que x, conclui-se que todos os elementos que
possuem índices menores am podem ser eliminados no próximo teste.
- Se am for maior que x, conclui-se que todos os elementos que
possuem índices maiores que am podem ser eliminados no próximo
teste.
A meta é eliminar em cada passo o maior número possível de
elementos em futuras buscas. A solução ótima é escolher a mediana dos
elementos do vetor.

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 23
Estrutura de Dados

int buscaBinaria (int v[], int chave)


{
int ini, fim, meio;
ini=0;
fim=N-1;
while(ini <= fim)
{
meio=(ini+fim)/2;
if(chave == v[meio])
return (meio);
if (chave < v[meio])
fim = meio-1;
else
ini = meio+1;
}
return (-1);
}

Ex: Dado o vetor abaixo, faça a busca binária de cada elemento indicado
fazendo o acompanhamento no algoritmo passo a passo:
2 6 9 10 23 25 37
Buscar: 10
Buscar: 9
Buscar: 37
Buscar: 26

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 24
Estrutura de Dados

Exercícios:

1) Escreva um programa que leia o tamanho n do vetor, aloque memória,


leia n números inteiros sem repetição de valores e preencha o vetor.
Imprima o vetor. Em seguida, mostre um menu com as seguintes opções:

1 - Ordenar por trocas (Bubblesort)


2 - Seleção
3 - Inserção

Após a escolha da opção, mostre o vetor ordenado.

Logo após, leia um valor qualquer e faça a busca na forma sequencial e


binária. Para ambas as buscas mostre uma mensagem "Achou" ou "Não
achou".

2) Reescreva as três funções dos métodos de ordenação de forma que seja


feita a ordenação decrescente dos elementos do vetor.

3) Conte os passos e dê a complexidade no pior caso para os três algoritmos


de ordenação, para a busca sequencial ordenada e a busca binária.

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 25
Estrutura de Dados

Listas Lineares

Existem dois tipos de estruturas de listas lineares:

1) Estruturas Estáticas de Dados: São aquelas definidas antes de efetuar a


operação.

2) Estruturas Dinâmicas de Dados: São aquelas que sofrem alterações


quando estão sendo manipuladas a partir de inserções e remoções.

Estruturas Estáticas de Dados

1) Pilha Sequencial

Definição: É uma lista na qual todas as inserções e remoções seguem o


critério LIFO (Last In First Out). Trabalha-se sempre no topo da pilha.

Exemplos: pilha de livros, pilha de pratos, etc.

Considere:

#define TAM 100

struct tpilha {
int topo;
int pilha[TAM];
};

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 26
Estrutura de Dados

Operações:

1) Inicializar a pilha:

void inicializaPilha(struct tpilha *ps)


{
ps ->topo = -1;
}

2) Testar pilha vazia

int pilhaVazia(struct tpilha *ps){


{
if(ps ->topo == -1)
return 1;
return 0;
}

3) Testar pilha cheia

int pilhaCheia(struct tpilha *ps)


{
if(ps -> topo == TAM-1)
return 1;
return 0;
}

4) Empilhamento

int empilha(struct tpilha *ps, int valor)


{
if(pilhaCheia(ps))
return 0;
ps->topo++;
ps -> pilha[ps -> topo] = valor;
return 1;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 27
Estrutura de Dados

5) Desempilhamento

int desempilha (struct tpilha *ps, int *elem)


{
if (pilhaVazia(ps))
return 0;
*elem=ps -> pilha[ps->topo];
ps -> topo--;
return 1;
}

Exercícios:

1) Escreva um programa que leia 10 números inteiros e armazene-os numa


pilha p1. Imprima p1. Logo após, inverta os elementos de p1 numa outra
pilha p2. Imprima p2 e a soma dos elementos de p2.

2) Escreva um programa que leia 20 números reais e insira-os numa pilha


p1. Imprima p1. Em seguida, crie uma cópia de p1 em outra pilha p3 usando
uma pilha p2 como auxiliar. Imprima p3.

Resposta questão 1:

#include <stdio.h>

#define TAM 10

struct tpilha{
int topo;
int pilha[TAM];
};

void inicializaPilha(struct tpilha *ps)


{
ps->topo = -1;
}

int pilhaVazia(struct tpilha *ps)


{
if(ps->topo == -1)
return 1;
return 0;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 28
Estrutura de Dados

int pilhaCheia(struct tpilha *ps)


{
if(ps->topo==TAM-1)
return 1;
return 0;
}

int empilha(struct tpilha *ps, int valor)


{
if(pilhaCheia(ps))
return 0;
ps->topo++;
ps -> pilha[ps -> topo] = valor;
return 1;
}

void desempilha(struct tpilha *ps, int *elem)


{
if (pilhaVazia(ps))
return;
*elem=ps -> pilha[ps->topo];
ps->topo--;
}

int main()
{
struct tpilha p1,p2;
int i, aux, soma=0;

inicializaPilha(&p1);
inicializaPilha(&p2);

for(i=0; i<TAM; i++)


{
printf(“\nDigite o %d elemento: “, i);
scanf(“%d”, &aux);
empilha(&p1, aux);
soma=soma+aux;
}
printf(“\n Impressão Pilha p1 \n\n”);
for(i=0; i<TAM; i++)
{
printf(“%d\t”, p1.pilha[i]);
desempilha(&p1, &aux);
empilha(&p2, aux);
}
printf(“\n Impressão Pilha p2 \n\n”);

for(i=0; <TAM; i++)


printf(“%d\t”, p2.pilha[i]);

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 29
Estrutura de Dados

printf("\nValor da soma dos elementos: %d\n", soma);


}

Fila Sequencial

Definição: é uma lista de dados na qual todas as inserções e remoções


seguem o FIFO (First In First Out). Portanto, insere-se no final (retaguarda)
da fila e remove-se do início (frente) da fila.
Exemplo: Fila do banco

Considere:

#define TAM 100

struct tfila {
int F, R; // F é a frente da fila e R é a retaguarda da fila.
int fila[TAM];
};

fila(f)
F=0 0 1 2 3 4 5 6 7

R=-1

inserefila(f, A)
F=0
A
R=0

inserefila(f, B)
F=0 R=1
A B

inserefila(f, C)
F=0 R=2
A B C

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 30
Estrutura de Dados

inserefila(f, H)
F=0 R=7
A B C D E F G H

removefila(f)
F=1 R=7
B C D E F G H

removefila(f)
F=2 R=7
C D E F G H

removefila(f)
R=7
H
F=7

Operações:

1) Inicialização

void inicializaFila(struct tfila *pf)


{
pf -> R = -1;
pf -> F = 0;
}

2) Verificar Fila Vazia

int fila_vazia(struct tfila *pf)


{
if(pf -> F > pf -> R)
© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 31
Estrutura de Dados

return 1;
return 0;
}

3) Verificar Fila Cheia

int fila_cheia (struct tfila *pf)


{
if(pf -> R == TAM-1)
return 1;
return 0;
}

4) Inserção

int insere_fila(struct tfila *pf, int valor)


{
if(fila_cheia(pf))
return 0;
pf -> R++;
pf->fila[pf->R]=valor;
return 1;
}

5) Remoção

int remove_fila(struct tfila *pf, int *elem)


{
if(fila_vazia(pf))
return 0;
*elem = pf -> fila[pf -> F];
pf ->F++;
return 1;
}

Exercícios:

1) Escreva um programa que leia 10 números inteiros e insira-os numa fila


f1. Imprima f1. Em seguida, inverta os elementos de f1 usando uma pilha
como estrutura auxiliar. Imprima f1 invertida.

2) Escreva um programa que contenha uma estrutura fila para armazenar


20 números reais e crie menu abaixo:

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 32
Estrutura de Dados

1) Inserir elemento

2) Remover elemento

3) Mostrar Fila

4) Sair

- Para cada opção, chame uma função para executar, exceto a opção 4.

- Na opção 1, quando a fila estiver cheia , mostre uma mensagem.

- Na opção 2, se a fila estiver vazia, mostre uma mensagem.

- Na opção 3, mostre o conteúdo presente na fila.

Resposta questão 1:

#include <stdio.h>
#define TAM 10
struct tfila{
int F, R;
int fila[TAM];
};
void inicializafila(struct tfila *pf) {
pf->F=0;
pf->R=-1;
}
int fila_vazia(struct tfila *pf) {
if(pf->F > pf->R)
return 1;
return 0;
}
int fila_cheia(struct tfila *pf){
if(pf -> R == TAM-1)
return 1;

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 33
Estrutura de Dados

return 0;
}

int insere_fila(struct tfila *pf, int valor){


if(fila_cheia(pf))
return 0;
pf->fila[++pf->R]=valor;
return 1;
}
int remove_fila(struct tfila *pf){
int y;
if(fila_vazia(pf))
return 0;
y = pf->fila[pf->F++];
return y;
};
struct tpilha{
int topo;
int pilha[TAM];
};
void inicializapilha(struct tpilha *p){
p->topo=-1;
}
int pilha_cheia(struct tpilha *p){
if( p -> topo == TAM-1)
return 1;
return 0;
}
int pilha_vazia(struct tpilha *p){
if(p->topo == -1)

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 34
Estrutura de Dados

return 1;
return 0;
}
int empilha(struct tpilha *p, int valor){
if(pilha_cheia(p))
return 0;
p->pilha[++p->topo]=valor;
return 1;
}
int desempilha(struct tpilha *p){
int x;
if(pilha_vazia(p))
return 0;
x = p->pilha[p->topo--];
return x;
}
int main() {
struct tpilha p1;
struct tfila f1;
int i, aux;
inicializapilha(&p1);
inicializafila(&f1);
for(i=0; i<TAM;i ++) {
printf("\nDigite o %d valor: ", i+1);
scanf("%d", &aux);
inserefila(&f1, aux);
}
for(i=0; i<TAM; i++)
printf("%d\t", f1.fila[i]);
printf("\n");

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 35
Estrutura de Dados

for(i=0; i<TAM; i++)


empilha(&p1, removefila(&f1));
inicializafila(&f1);
for(i=0; i<TAM; i++)
inseref(&f1, desempilha(&p1));
printf("\nFila: ");
for(i=0; i<TAM; i++)
printf("%d\t", f1.fila[i]);
printf("\n");
}

Problema: Nessa implementação existe a situação em que há espaço para


inserção, mas não se pode inserir pois só se insere no final da fila.

Fila Circular
A implementação da fila circular surgiu da necessidade de inserir
elementos em em uma fila com espaço, mas R pode está na posição TAM-1
da fila.

1a. Solução: Fila Circular com Nó Bobo (dummy node)

Considere:

#define TAM 100


struct tfila {
int R, F;
int fila[TAM];
};

fila(f)
F=0

R=0

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 36
Estrutura de Dados

Operações:

1) Inicialização da fila

void inicializaFila(struct tfila *pf) {


pf->R=0;
pf->F=0;
}

2) Testar fila vazia

int fila_vazia(struct tfila *pf) {


if(pf -> F == pf->R)
return 1;
return 0;
}

3) Testar fila cheia

int fila_cheia(struct tfila *pf){


if((pf -> R+1)%TAM == pf->F)
return 1;
return 0;
}

4) Inserção

int insereFila(struct tfila *pf, int valor) {


if(fila_cheia(pf))
return 0;
pf->R=(pf->R+1)%TAM;
pf->fila[pf ->R] = valor;
return 1;
}

5) Remoção

int removeFila(struct tfila *pf, int *elem)


{
if(fila_vazia(pf))
return 0;
pf -> F = (pf->F+1)%TAM;
*elem = pf->fila[pf->F];
return 1;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 37
Estrutura de Dados

2a. Solução: Fila Circular com Contador

Considere:

#define TAM 100

struct tfila_circ{
int ini;
int fim;
int cont;
int fila[TAM];
};

fila(f)
ini = 0

fim= -1
cont = 0

Operações:

1) Inicialização

void inicializa(struct tfila_circ *pf) {


pf->ini=0;
pf->fim=-1;
pf->cont=0;
}

2) Testar fila vazia

int fila_vazia(struct tfila_circ *pf){


if(pf -> cont == 0)
return 1;
return 0;
}

3) Testar fila cheia

int fila_cheia(struct tfila_circ *pf){


if(pf->cont == TAM)
return 1;
return 0;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 38
Estrutura de Dados

4) Inserção

int insere-fila(struct tfila_circ *pf, int valor){


if(fila_cheia(pf))
return 0;
pf -> cont++;
pf -> fim = (pf -> fim == TAM-1) ? 0 : pf ->fim+1;
pf -> fila[pf -> fim] = valor;
return 1;
}

5) Remoção

int remove_fila(struct tfila_circ *pf, int *elem){


if(fila_vazia(pf))
return 0;
pf->cont--;
*elem=pf->fila[pf->ini];
pf -> ini = (pf ->ini==TAM-1) ? 0 : pf->ini+1;
return 1;
}

Exercícios:

1) Escreva um programa leia duas filas f1 e f2 circulares com nó bobo com


20 números inteiros cada uma. Imprima as filas. Em seguida, chame uma
função que recebe as duas filas e retorna o valor soma dos elementos de f1 e
f2. Imprima o valor da soma na main().

2) Escreva um programa que leia 10 números reais e insira-os numa fila


circular com contador. Imprima a fila. Logo após, forneça o maior, o menor e
a média aritmética dos elementos da fila, respectivamente.

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 39
Estrutura de Dados

Listas Sequenciais

1. Listas Sequenciais não ordenadas

Considere:

#define TAM 100

struct tno {
int chave;
};

struct tlista {
int qtnos;
struct tno lista[TAM];
};

lista(l)
qtnos=0
tno

Operações:

1) Inicialização da lista

void inicializa_lista(struct tlista *pl) {


pl -> qtnos = 0;
}

2) Testar lista vazia

int lista_vazia(struct tlista *pl) {


if(pl -> qtnos == 0)
return 1;
return 0;
}

3) Testar lista cheia

int lista_cheia(struct tlista *pl) {


if(pl ->qtnos==TAM)
return 1;
return 0;
}
© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 40
Estrutura de Dados

4) Percurso

void percurso(struct tlista *pl) {


int i;
for(i=0; i< pl -> qtnos; i++)
printf("%d\t", pl -> lista[i].chave);
}

5) Buscar um elemento na lista

int busca(struct tlista *pl, int elem) {


int i;
for(i=0; i< pl -> qtnos; i++) {
if(elem == pl -> lista[i].chave)
return i; //achou
return -1; //não achou
}

6) Inserção na lista com repetição de chave

int insere_lista(struct tlista *pl, int valor) {


if(pl -> qtnos == TAM)
return -1;
pl -> lista[pl -> qtnos].chave = valor;
pl -> qtnos++;
return 1;
}

7) Inserção sem repetição de chave

int insere_lista2(struct tlista *pl, int valor) {


int i;
if(pl -> qtnos == TAM)
return 0;
i= busca(pl, valor);
if(i>=0) //se achou o valor na lista
return -1;
pl -> lista[pl -> qtnos].chave = valor;
pl -> qtnos++;
return 1;
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 41
Estrutura de Dados

8) Remoção

int remove_lista(struct tlista *pl, int valor) {


int i;
if(lista_vazia(pl))
return 0;
i=busca(pl, valor);
if(i<0) //se não achou o valor na lista
return -1;
pl->qtnos--;
pl -> lista[i].chave = pl->lista[pl->qtnos].chave;
return 1;
}

Exercícios:

1) Escreva um programa que leia os dados (nome, matricula, notas n1 e n2 e


média) de 10 alunos de uma turma e insira-os numa lista sequencial não
ordenada sem repetição. Em seguida, gere um menu com as seguintes
opções:
1- Imprimir a lista
2- Buscar uma matricula na lista
3- Buscar um nome na lista
4- Remover os dados de um aluno na lista
5- Sair

- Para qualquer outra opção imprima "Opção Invalida".


- O programa termina quando o usuário escolher a opção 5.

Resposta:

#include <stdio.h>
#include <string.h>
#define TAM 10

struct tno{
int matricula;
float n1, n2, media;
char nome[60];
};

struct tlista{
int qtnos;
struct tno lista[TAM];
};

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 42
Estrutura de Dados

void inicializa_lista(struct tlista *pl)


{
pl -> qtnos = 0;
}
int lista_vazia(struct tlista *pl)
{
if(pl -> qtnos == 0)
return 1;
return 0;
}
int lista_cheia(struct tlista *pl)
{
if(pl -> qtnos == TAM)
return 1;
return 0;
}

void percurso(struct tlista *pl)


{
int i;
for(i=0; i<pl -> qtnos; i++)
printf("\n Nome: %s\n Matricula: %d\n Nota1:%.2f\n Nota2:%.2f\n
Media: %.2f\n\n", pl->lista[i].nome, pl→lista[i].matricula, pl-
>lista[i].n1, pl->lista[i].n2, pl->lista[i].media);
}

int buscamat(struct tlista *pl, int mat)


{
int i;
for(i=0; i<pl->qtnos; i++){
if(mat == pl -> lista[i].matricula)
return i;
}
return -1;
}

int buscanome(struct tlista *pl, char name[])


{
int i;
for(i=0; i<pl -> qtnos; i++){
if(!strcmp(name, pl -> lista[i].nome))
return i;
}
return -1;
}

int insere_lista(struct tlista *pl, float n1, float n2, int mat, float media,
char nome[])
{
int i;
if(lista_cheia(pl))

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 43
Estrutura de Dados

return 0;
i= buscamat(pl, mat);
if(i>=0)
return -1;
pl->lista[pl->qtnos].matricula=mat;
pl->lista[pl->qtnos].n1=n1;
pl->lista[pl->qtnos].n2=n2;
pl->lista[pl->qtnos].media=media;
strcpy(pl->lista[pl->qtnos].nome, nome);
pl-> qtnos++;
return 1;
}

int remove_lista(struct tlista *pl, int mat)


{
int i;
if(lista_vazia(pl))
return 0;
i = buscamat(pl, mat);
if(i<0)
return -1;
pl->qtnos--;
pl->lista[i].matricula=pl->lista[pl->qtnos].matricula;
pl->lista[i].n1=pl->lista[pl->qtnos].n1;
pl->lista[i].n2=pl->lista[pl->qtnos].n2;
pl->lista[i].media=pl->lista[pl->qtnos].media;
strcpy(pl->lista[i].nome, pl->lista[pl->qtnos].nome);
return 1;
}

int main()
{
int op, mat, i;
float p1, p2, med;
char nome[60];
struct tlista l1;
inicializa_lista(&l1);
for(i=0; i<TAM; i++)
{
printf("\nDigite o nome: ");
gets(nome);
printf("\nDigite a matricula: ");
scanf("%d", &mat);
printf("\nDigite a primeira e segunda nota: ");
scanf("%f, %f", &p1, &p2);
med = (p1+p2)/2;
insere_lista(&l1, p1, p2, mat, med, nome);
fgetc(stdin);
}
do{

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 44
Estrutura de Dados

printf("\n 1-Imprimir a lista\n 2-Buscar uma matricula na lista\n


3-Buscar um nome na lista\n 4-Remover um elemento na lista\n 5-
Sair\n Digite uma das opções: ");
scanf("%d", &op);
switch(op) {
case 1:
percurso(&l1);
break;
case 2:
printf("\nDigite a matricula a ser buscada: ");
scanf("%d", &mat);
i = buscamat(&l1, mat);
if(i>=0)
printf("Matricula Encontrada! \n");
else
printf("Matricula não Encontrada! \n");
break;
case 3:
printf("\nDigite o nome a ser buscado: ");
fgetc(stdin);
gets(nome);
i = buscanome(&l1, nome);
if(i>=0){
printf("Nome Encontrado! \n");
printf("\n Nome: %s\n Matricula: %d\n
Nota1:%.2f\n Nota2: %.2f\n Media: %.2f\n", l1.lista[i].nome,
l1.lista[i].matricula, l1.lista[i].n1, l1.lista[i].n2, l1.lista[i].media);
}
else
printf("Nome não Encontrado! \n");
break;
case 4:
printf("Digite a matricula a ser removida: ");
scanf("%d", &mat);
i = remove_lista(&l1, mat);
if(i>0)
printf("Elemento Removido. \n");
else
printf("Elemento não encontrado ou lista
vazia. \n");
break;
case 5:
printf("Finalizar!! \n");
break;
default:
printf("\nOpção inválida");
}
}while(op!=5);
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 45
Estrutura de Dados

2. Listas Sequenciais Ordenadas

Nas listas sequenciais ordenadas as funções abaixo são as mesmas


das listas sequenciais não ordenadas:
- inicializar_lista
- testar vazia,
- testar cheia e
- percurso

Operações:

1) Busca Sequencial Ordenada

int buscaseq(struct tlista *pl, int elem, int *pos)


{
int i;
fo(i=0; i < pl -> qtnos && elem > pl -> lista[i].chave; i++);
*pos=i;
if((i == pl -> qtnos) || (elem != pl -> lista[i].chave))
return 0; //não achou
return 1; //achou
}

2) Busca Binária

int buscabinaria(struct tlista *pl, int elem)


{
int ini=0, fim=pl -> qtnos-1, meio;
while(ini <= fim) {
meio= (ini+fim)/2;
if(elem == pl -> lista[meio].chave)
return(meio); //achou
if(elem > pl -> lista[meio].chave)
ini=meio+1;
else
fim=meio-1;
}
return -1; //não achou
}

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 46
Estrutura de Dados

3) Inserção

int insereOrd(struct tlista *pl, int elem)


{
int i, posicao;
if(pl -> qtnos == TAM)
return 0;
if(buscaseq(pl, elem, &posicao))
return -1;
for(i=pl -> qtnos; i>posicao; i--){
pl -> lista[i].chave = pl→lista[i-1].chave;
}
pl -> lista[posicao].chave = elem;
pl->qtnos++;
return 1;
}

4) Remoção

int removeOrd(struct tlista *pl, int elem)


{
int i, posicao;
if(pl->qtnos==0)
return 0;
if(!buscaseq(*pl, elem, &posicao))
return -1;
for(i=posicao; i< pl ->qtnos-1; i++){
pl -> lista[i].chave = pl→lista[i+1].chave;
}
pl->qtnos--;
return 1;
}

Exercício:

1) Escreva um programa que leia 20 números reais e insira-os numa lista


sequencial ordenada. Imprima a lista. Leia um valor e faça a busca de forma
binária na lista, mostre uma mensagem se achou ou não achou. Leia um
valor e faça a remoção se o valor existir na lista. Imprima a lista novamente.
Crie uma função que inverta a ordem dos elementos na lista, utilizando
para isso uma pilha como estrutura auxiliar. Imprima a lista invertida.

© 2002– 2021 Prof. Dra. Vera Lúcia Prudência dos Santos Caminha – VFI/ICEx/UFF – Volta Redonda-RJ 47

Você também pode gostar