Você está na página 1de 93

ESTRUTURAS DE DADOS

Prof. Cristhian Riaño


Tópicos
● Introdução.
● Análise de algoritmos e algoritmos recursivos.
● Notação assintótica.
● Estruturas de dados fundamentais: vetores, vetores multidimensionais, listas
encadeadas.
● Pilhas, filas e deques.
● Árvores.
● Heaps.
● Algoritmos de ordenação.
● Tabelas de dispersão e tabelas de espalhamento.
● Grafos.
Objetivo
● Vamos aprender:

○ Quais são as principais estruturas de dados

○ Como estas estruturas funcionam

○ Como os algoritmos utilizados para acessar os dados armazenados


nestas estrutuas funcionam

● Ao final do curso deveremos saber:

○ Qual estrutura de dados é mais adequada para solucionar determinado


problema e quais são as implicações de sua utilização (custos, etc...)
Tópicos
● Introdução;
● Tipos de Dados;
● Primitivos, Estáticos e Dinâmicos;
● Recursividade;
● Operações;
● Definição Listas lineares;
● Pilhas, Filas e Deques;
● Representação Física;
● Exercicios.
Introdução
Estruturas de dados são encontradas em praticamente todas as áreas da computação

○ Banco de dados

○ Resultados de consultas (lista de dados)

○ Indexação de arquivos de dados (árvores de busca)

○ Sistemas operacionais

○ Controle de processos (filas de espera por recursos)

○ Computação gráfica

○ Manipulação de imagens (matrizes)

○ Compiladores

○ Validação de expressões (pilha)


Introdução
● Estruturas de Um algoritmo deve:

○ Funcionar corretamente

○ Executar o mais rápido possível

○ Utilizar a memória da melhor forma possível

○ Precisamos estudar as suas especificações e tirar conclusões sobre como a


sua implementação (o programa) irá se comportar em geral.
Tipos de Dados
Em um projeto de software, os dois aspectos mais estudados são.
● Quais procedimentos aplicar;
● Sobre quais dados os procedimentos são aplicados.

Em um principio o mais relevante consistia em definir os procedimentos. Com


o surgimento de técnicas para especificação de dados no nível conceitual, a
ênfase passou a ser dividida entre dados e procedimentos ate o ponto de
surgirem técnicas de programação com ênfase nos dados (programação
baseada em objetos).
Tipos de Dados
De uma forma geral diferenciamos os tipos de dados pelo conjunto de valores
que podem assumir e pelo conjunto de operações que podem efetuar com
eles. Podemos separar os tipos de dados em três grupos:
● Primitivos;
● Estáticos;
● Dinâmicos.
Primitivos são aqueles a partir dos quais podemos definir os demais tipos.
● Inteiros;
● Reais;
● Caracteres;
● Lógicos;
● Ponteiros
Tipos de Dados - Primitivos
Cada um desses tipos primitivos tem um conjunto de valores e operações restritos:
● Inteiro: -45; 2; 1037

○ Representa uma quantidade contável de objetos.

○ Operações: Soma, substrução, multiplicação, divisão.


● Real: -4,78; 1,25; 2,333

○ Representa um valor que pode ser fraccionado.

○ Operações: Soma, substrução, multiplicação, divisão.


● Logico: [Verdadeiro, Falso]; [0,1]

○ Representa dois estados

○ Operações: E, NÃO, OU
Tipos de Dados - Primitivos
● Caracter: “A”, “X”, “1”, “+”

○ Representa uma sequencia de dígitos, letras e sinais.

○ Operações: Igualdade, diferença, concatenação.


● Ponteiro: Representa o endereço de um dado na memoria

○ Operações: Igualdade, diferença, soma, subtração, multiplicação e divisão.


Tipos de Dados - Estáticos
Tipos de dados estáticos são aqueles que têm a estrutura completamente definida,
antes de começarmos a efetuar operações. São os tipos de dados definidos em
tempo de execução.
● Definido um tipo estático de dados ele poderá conter mais elementos do que
previsto inicialmente.
● O conteúdo pode ser modificado, mas não a suas posições na memoria.

Um exemplo de estrutura estática é o vetor (“Array”), que é uma agregado


homogêneo de dados.
Tipos de Dados - Dinâmicos
São aqueles que sofrem alteração estrutural, quando estão sendo manipulados, a
medida que ocorrem inserções e retiras de elementos.

Esse tipo de dado não tem tamanho predefinido, só ficando limitado à memoria do
computador em que se está trabalhando.

Desde o ponto de vista logico, um tipo dinâmico de dados não tem limitações,
porém, como os algoritmos de manipulação dessas estruturas são implementadas
em computadores nesta disciplina trataremos suas limitações físicas.
Recursividade
● Um algoritmo que resolve um problema grande em problemas menores, cujas
soluções requerem a aplicação dele mesmo, é chamado recursivo.

Exemplo:A função fatorial pode ser desenvolvida recursivamente.


Matematicamente é definido como:

1 si n = 0 ó 1
n!= 
n(n − 1) si n  1
Analogamente, sua expressão funcional será a seguinte:

1 si n = 0 ó 1
fac(n) = 
n * fac(n − 1) si n  1
Recursividade
Exemplo:A função fatorial pode ser desenvolvida recursivamente.
Matematicamente é definido como:

1 si n = 0 ó 1 1 si n = 0 ó 1
n!=  fac(n) = 
n(n − 1) si n  1 n * fac(n − 1) si n  1

Private Function factorial(ByVal n As Integer)


As Integer
Chamadas recursivas Avaliação de resultados
Factorial(5)=factorial(4)*5 Factorial(1)=1
If n = 1 Then Factorial(4)=factorial(3)*4 Factorial(2)=factorial(1)*2 = 1*2
Factorial(3)=factorial(2)*3 =2
factorial = 1 Factorial(2)=factorial(1)*2 Factorial(3)=factorial(2)*3 = 2*3
Factorial(1)=1 =6
Else Factorial(4)=factorial(3)*4 = 6*4
=24
factorial = n * factorial(n - 1) Factorial(5)=factorial(4)*5 = 24*5
=120
End If
End Function
Exercício 1: Recursividade
Multiplique dois números usando somas sucessivas.
A*B = A+A+A+ . . . . . . . . . +A
Para o caso geral:
A * B = Multi( A, B) = i =1 A = i =1 A + A
B B −1

Qual é o menor valor que B pode tomar?

○ A*B=A quando B=1 si B = 1


A
Multi( A, B) = 
○ A*B=0 quando B=0 Multi( A, B − 1) + A si B  1
Exercício 1: Recursividade
A si B = 1
○ A*B=A quando B=1 Multi( A, B) = 
Multi( A, B − 1) + A si B  1

Private Function multiplica(ByVal A As Integer, ByVal B As Integer) As


Integer
If B = 1 Then
multiplica = A
Else
multiplica = multiplica(A, B - 1) + A
End If
End Function
Exercício 2: Recursividade

Intercalar os dígitos de dois números, os números têm os mesmos dígitos. A


função módulo permite extrair o último dígito de cada número e compor um novo
número, quando número A e número B são de um único dígito, ou seja, menos
de 10 terão atingido o caso base.
. A *10 + B si A  10 ó B  10
Intercala( A, B) = 
 Intercala( Adiv10, Bdiv10) *100 + ( A mod 10) *10 + ( B mod 10) eoc
Exercício 2: Recursividade
A *10 + B si A  10 ó B  10
Intercala( A, B) = 
 Intercala( Adiv10, Bdiv10) *100 + ( A mod 10) *10 + ( B mod 10) eoc

Private Function intercala(ByVal A As Integer, ByVal B As Integer) As Double


Dim ma As Integer, mb As Integer, novoNum As Integer
If A < 10 Then
intercala = A * 10 + B
Else
ma = A Mod 10
mb = B Mod 10
novoNum = ma * 10 + mb
intercala = intercala(A \ 10, B \ 10) * 100 + novoNum
End If
End Function
Exercício 3: Recursividade
Encontre a soma dos primeiros N números pares.

SumaPares(n) = 2 + 4 + 6 + . . . . . .+ 2*(n-1) + 2*n

Option Explicit
Dim num As Integer
Private Function sumaPares(ByVal n As Integer) As Integer
If n = 1 Then
sumaPares = 2
List1.AddItem 2
Else
sumaPares = sumaPares(n - 1) + 2 * n
List1.AddItem (2 * n)
End If
End Function
Operações
Independentemente do tipo de dado, existem operações clássicas que podemos
efetuar.
● Criação, alguns linguagens de programação exigem que o tipo de dados seja
declarado antes de ser referenciado, de qualquer forma em algum momento a
estrutura de dados será criada com uma definição de criação.
● Percurso é a operação em que referenciamos todos os elementos de uma
estrutura de dados.
● Busca usada para selecionar um determinado elemento cuja complexidade
depende da estrutura utilizada e do tempo estimado para selacao de um
determinado elemento. (Verificar, utilizar ou atualizar o conteúdo de um
elemento ou ainda retirar e inserir novos elementos).
● Alteração modifica o conteúdo
Operações
● Retirada

○ No caso de estruturas estáticas não podemos que retirar um elemento, no máximo podemos
retirar os valores que ele contem, substituindo-os por outros.

○ Em uma estrutura dinâmica podemos retirar quantos elementos desejarmos, onde cada retirada
diminuímos a sua quantidade de elementos.

● Inserção, aumenta a quantidade de elementos de uma estrutura em estruturas


de dados dinâmicas.
Definição Listas lineares

Uma lista linear é uma estrutura dinâmica caraterizada por uma sequencia
ordenada de elementos, no sentido da sua posição relativa:

𝐸1 , 𝐸2 , . . . , 𝐸𝑛
Tal que:
1. Existem 𝑛 elementos na sequência;
2. 𝐸1 é o primeiro elemento da sequência;
3. 𝐸𝑛 é o ultimo elemento da sequência;
4. Para todo i, j entre 1 e n, se i<j, então o elemento 𝐸𝑖 , antecede o elemento 𝐸𝑗 ;
5. Caso i=j-1, 𝐸𝑖 é o antecessor de 𝐸𝑗 e 𝐸𝑗 é o sucessor de 𝐸𝑖 .

Uma lista linear é uma estrutura dinâmica homogênea.


Definição Listas lineares

Existem vários exemplos de listas lineares, tais como:

● Fila de clientes em um banco: existe o primeiro da fila, o ultimo da fila e os


clientes fazem com que exista uma “ordem” de atendimento1.
● Fichário: existe um numero finito de fichas que esta agrupado segundo uma
“ordem” preestabelecida, de tal forma que conseguimos identificar cada ficha.
Definição Listas lineares

Existem varias operações que podem ser realizadas em listas lineares.


● Percurso (Verificar a presença dos alunos);
● Inserção (Novos alunos são matriculados);
● Retirada (Alguns alunos eventualmente desistem);
Definição Listas lineares
● Percurso

Operação que permite utilizar cada um dos elementos de uma lista linear, de tal forma que:

○ O primeiro elemento a ser utilizado é o primeiro elemento da lista linear;

○ Para utilizar um elemento 𝐸𝑗 , todos os elementos de 𝐸1 até 𝐸𝑗 − 1 já foram utilizados;

○ O ultimo elemento utilizado é o ultimo elemento linear.


● Busca

Operação que procura um elemento especifico da lista linear. A busca pode ser efetuada de duas
formas:

○ Elemento é identificado por sua posição relativa na lista;

○ Elemento é identificado pelo seu conteúdo.


Definição Listas lineares
● Inserção

Operação que incorpora um elemento X a uma lista linear de tal forma que:

○ O elemento X terá um sucessor e/ou um antecessor.

○ A inserção é realizada de tal forma que ao inserir o elemento X na posição i, onde i está no
intervalo de 1 a (n+1), esse elemento passa a ser o i-ésimo elemento da lista linear;

○ O número de elementos (n) é acrescido de uma unidade.


Definição Listas lineares
● Retirada

Operação que retira um elemento X de uma lista linear de tal forma que:

○ Se 𝐸𝑖 é o elemento retirado, o seu sucessor passa a ser o sucessor de seu antecessor, ou seja,
𝐸𝑖 + 1 passa a ser o sucessor de 𝐸𝑖 +1. Se 𝐸𝑖 é o primeiro elemento, o seu sucessor passa a
ser o primeiro; se 𝐸𝑖 é o ultimo elemento, o seu antecessor passa a ser o ultimo;

○ O número de elementos (n) é decrescido de uma unidade.


Pilhas, Filas e Deques
Existem alguns tipos de listas lineares que possuem características especiais que
definem respeito à forma como cada uma delas é manipulada, ou seja, qual o
comportamento das operações de inserção e retirada. Essas operações são
restritas às extremidades das listas lineares, ou seja, os elementos do “interior” da
lista não são manipulados

● Phila: Uma Pilha, “Stack” é uma lista linear onde as operações de inserção e
retirada são efetuadas apenas no final da lista linear, ou seja:

○ A inserção de um elemento X torna-o o ultimo da lista linear;

○ A retirada é sempre efetuada sobre o elemento 𝐸𝑛 .


Pilhas
Existem alguns tipos de listas lineares que possuem características especiais que
definem respeito à forma como cada uma delas é manipulada, ou seja, qual o
comportamento das operações de inserção e retirada. Essas operações são
restritas às extremidades das listas lineares, ou seja, os elementos do “interior” da
lista não são manipulados

● Pilha: Uma Pilha, “Stack” é uma lista linear onde as operações de inserção e
retirada são efetuadas apenas no final da lista linear, ou seja:

○ A inserção de um elemento X torna-o o ultimo da lista linear;

○ A retirada é sempre efetuada sobre o elemento 𝐸𝑛 .


Pilhas
Devido às características das operações da pilha, o ultimo elemento a ser inserido
será o primeiro a ser retirado.

Exemplo:

Em uma rua sem saída, tão estreita que apenas um carro passa por vez, o
primeiro carro a sair será o ultimo a ter entrado. Observe ainda que podemos
retirar qualquer carro e não podemos inserir um carro de tal forma que ele não
seja o ultimo.
Filas
Uma fila “queue”, é uma lista linear onde as operações de inserção são efetuadas
apenas no final e as de retirada, apenas no inicio da lista linear, ou seja:
● A inserção de um elemento X torna-o o último da lista linear;
● A retirada é sempre efetuada sobre o elemento 𝐸1 .

Devido às características das operações da fila, o primeiro elemento a ser inserido


será o primeiro a ser retirado. Estruturas desse tipo são conhecidas como “FIFO”.

Exemplo: Uma fila de banco, onde os clientes que chegam ficam ao final da fila os
que saem, o fazem do inicio da fila
Deque
Um deque, “double-ended queue” é uma lista linear onde as operações de
inserção e retirada podem ser efetuadas tanto no inicio quanto no final da lista
linear, ou seja :
● A inserção de um elemento X pode torná-lo ou o primeiro ou o ultimo da lista
linear;
● A retirada é sempre efetuada sobre o elemento 𝐸1 ou 𝐸𝑛

Existem duas variações de Deque:


● Deque de entrada restrita, onde a inserção só pode ser efetuada ou no inicio
ou no final da lista linear.
● Deque de saída restrita, onde a retirada só pode ser efetuada ou no inicio ou
no final da lista linear.
A restrição faz com que uma das operações só possa ser efetuada em uma das
extremidades do deque.
Deque
Podemos perceber que pilha e fila são casos particulares de deque, dependendo
das restrições que fizemos quanto à inserção e retirada.

Exemplos:
● Para montar a composição de um trem, vagões são inseridos e retirados
sempre nas extremidades.
● Em uma barca para transporte de automóveis, estes podem entrar e sair por
qualquer uma das extremidades.
Representação Física
Existem duas formas de implementação:
● Alocação sequencial, onde usamos estruturas estáticas;
● Alocação encadeada, onde usamos ponteiros para identificar as sequencias de
elementos da lista linear.

A maneira mais simples de armazenarmos uma lista na memória é colocando os


seus nós em posições sucessivas.

Tamanho do nó
(TamNo)

Localizacao inicial da lista (LocIni)


Representação Física
Considere que Loc é a localização de um nó na memoria, TamNó é o número de
“Unidade de memoria” que cada nó ocupa, ou seja o tamanho do nó (em bytes,
palavras, etc.), 𝑋𝑛 o n-ésimo nó de uma lista 𝑋 e que LocIni = Loc(X1)

• O acesso a qualquer nó em uma lista linear


Tamanho do nó alocada sequencialmente baseia-se em uma
(TamNó) formula matemática. Com isso temos um tempo
fixo de acesso a qualquer nó, dada a sua ordem na
Localizacao inicial da lista (LocIni)
estrutura.

Loc (X1) = LocIni • A alocação sequencial exige uma área contigua de


Loc (X2) = LocIni + TamNó memoria correspondente ao tamanho previsto da
Loc (X3) = LocIni + (2*TamNó) estrutura
.
.
.
Loc (Xj) = LocIni+ ((j-1)*TamNó )
Lista Lineares em Geral
Uma lista linear em geral, armazenada em memória de forma sequencial necessita, de
uma variável ponteiro externa (PtrIni) que contenha o endereço do primeiro nó da
estrutura. Uma vez conhecido esse endereço podemos ter acesso a todos os nós da
lista.

O algoritmo de busca de um nó em uma lista linear sequencial existente, cujo endereço


do primeiro nó é PtrIni, e o valor procurado em um determinado campo do nó é
igual a “00345”
<1> Chave Campo do nó da lista alocada de forma sequencial}
<2> PtrIni {Ponteiro do primeiro nó da lista}
<3> PtrTrb {Ponteiro de trabalho para a busca}
<4>
<5> Inicio
<6> PtrTrb <- PtrIni
<7> ENQUANTO Chave (PtrTrb) <> “00345” Faca
<8> PtrTrb <- PtrTrb +1
<9> Fim
Lista Lineares em Geral
<1> Chave Campo do nó da lista alocada de forma sequencial}
<2> PtrIni {Ponteiro do primeiro nó da lista}
<3> PtrTrb {Ponteiro de trabalho para a busca}
<4> PtrFim {Ponteiro do ultimo nó da lista }
<5> Inicio
<6> PtrTrb <- PtrIni
<7> ENQUANTO (Ptrb <= PtrFim) E Chave (PtrTrb) <> “00345” Faca
<8> PtrTrb <- PtrTrb +1
<9> Fim
Além de posição do primeiro e do ultimo nó da lista são necessárias mas duas
informações para definir o limite da área da memoria que pode ser utilizado para
implementarmos a estrutura:

● A posição que indica o primeiro nó do vetor que pode ser utilizado pela lista
(IndIniLst);

● A posição que indica o ultimo nó do vetor que pode ser utilizado pela lista
(IndFimLst)
Representação Física
Ptrini PtrFim

X X X
IndIni IndFim

IndIniLst IndFimLst

● Se a lista estiver vazia e o primeiro nó for inserido, PtrIni =PtrFim =IndIniLst;


● Se a lista estiver utilizando toda a área de memoria disponível para a sua
implementação, PtrFim =IndFimLst, e dizemos que a lista está cheia.;
● A memoria do comutador pode ser representada como um vetor, onde cada
elemento é uma palavra de memoria;
● Uma lista linear é representada fisicamente na memoria do computador, quando
usamos alocação sequencial, através de um vetor. Na realidade varias listas podem
ser implementadas sobre um mesmo vetor.
Representação Física
A seguir o algoritmo para inserir um nó em uma lista linear sequencial, na posição
indicada pelo ponteiro PtrInserir.
Algoritmo InsereLst
Lista {Lista alocada de forma sequencial}
PtrInserir {Aponta o índice onde deve ser feita a inserção}
Informação {Variável onde deve ser feita a inserção}
INÍCIO
SE PtrIni = #
Então
INICIO
PtrIni <- IndIniLst
PtrFim <- IndIniLst
FIM
SE NAO
INICIO
PtrTrb <- PtrFim +1
ENQUANTO Ptrb > PtrInserir FAÇA
INICIO
Lista (PtrTrb) <- Lista (PtrTrb-1)
PtrTrb <- PtrTrb -1
FIM
Alocação de Memoria
A alocação de memoria, do ponto de vista do programador estará em uma das
categorias apresentadas .
Sequencial Encadeada

Estática Estática sequencial Estática encadeada


Dinâmica Dinâmica sequencial Dinâmica encadeada

● Dizemos alocação estática se a quantidade total de memoria utilizada pelos


dados é previamente conhecida e definida de modo imutável, no próprio
código-fonte do programa.
● Se o programa é capaz de criar novas variáveis enquanto executa, isto é se
áreas de memoria que não foram declaradas no programa passam a existir
durante a sua execução, então dizemos que a alocação é dinâmica.
Alocação Sequencial 𝜀 − 𝑘 𝜀 𝜀+𝑘

... 𝒂𝒊−𝟏 𝒂𝒊 𝒂𝒊+ ...

● Endereço (𝑎𝑖 ) = 𝜀 k
Esquema de alocação sequencial
● Endereço (𝑎𝑖−1 ) = 𝜀 − 𝑘
● Endereço (𝑎𝑖+1 ) = 𝜀 + 𝑘

De forma mais genérica, assumindo que o elemento 𝑎1 encontra-se na célula de


endereço 𝛽 𝛽 + 0𝑘 𝛽 + 1𝑘 𝛽 + 2𝑘 𝛽 + (𝑖 − 1)𝑘 𝛽 + (𝑛 − 1)𝑘

... 𝒂𝟏 𝒂𝟐 𝒂𝟑 ... 𝒂𝒊 ... 𝒂𝒏 ...

Esquema de acesso sequencial k


Alocação Sequencial
A maior vantagem no uso de uma área sequencial de memória para armazenar
uma lista linear é que, dado o endereço inicial 𝛽 da área alocada e o índice i de
um elemento qualquer da lista, podemos acessá-lo imediatamente, com um simple
e rápido calculo.

O ponto fraco de esta forma de armazenamento aparece quando precisamos


inserir ou suprimir elementos do meio da lista, quando então um certo esforço será
necessário para movimentar os elementos, de modo a abrir espaço para inserção,
ou de modo a ocupar espaço liberado por um elemento que foi removido.
Alocação Sequencial
● A alocação sequencial não usa a memoria de forma eficiente, pois aloca um
espaço finito e predeterminado de memoria;
● Não nos fornece a necessária flexibilidade para mantermos, sem um grande
numero de movimentações de nós de lista;
● Na alocação sequencial, a própria sucessão dos endereços de memoria nos da
a sequencia dos nós na estrutura.
Alocação Encadeada
Ao invés de manter os elementos alocados numa área continua de memoria, isto
é, ocupando células consecutivas, na alocação encadeada os elementos podem
ocupar quaisquer células (não necessariamente consecutivas) e, para manter a
relação de ordem linear, juntamente com cada elemento é armazenado o
endereço do próximo elemento da lista.
● Na alocação encadeada, os elementos são armazenados em blocos de
memoria chamados nodos.
● Cada nodo está composto por dois campos: um para armazenar dados e outro
para armazenar endereço.
● Dois elementos especiais devem ser destacados

○ O endereço do primer elemento da lista

○ O endereço do elemento fictício que segue o ultimo elemento da lista (Φ)


Alocação Encadeada
O uso do link da flexibilidade à estrutura nas inserções e
retiradas de nós, visto que não mais serão necessárias as Ponteiro
movimentações para manter a estrutura ordenada.
Informações Link

PtrNovo Formato de um nó

X Y
Y #

Inserção em uma lista encadeada


Alocação Encadeada
X Y
Y

X Y
Z

Z
Y
Inserção em uma lista encadeada
Alocação Encadeada
X Y
Z

Z
Y

X Y
Y

Retirada de uma lista encadeada


Alocação Encadeada
● A alocação encadeada apresenta como maior vantagem a possibilidade de
inserir ou remover elementos do meio da lista;
● Como os elementos não precisam estar armazenados em posições
consecutivas de memoria nenhum dado preciso ser movimentado;
● Como apenas o primeiro elemento é acessível diretamente através do
endereço, a grande desvantagem da locação encadeada surge quando
desejamos acessar uma posição especifica dentro da lista;
● À alocação cujas estruturas necessitam de ponteiros para fazer a ligação entre
os seus nós chamamos de encadeada.
Alocação Encadeada
● Dois elementos especiais devem ser destacados

○ O endereço do primer elemento da lista

○ O endereço do elemento fictício que segue o ultimo elemento da lista (Φ)


Endereço Conteúdo
L=3FFA 𝒂𝟏 1C34 Primeiro elemento acessível a partir de L

1C34 𝒂𝟐 BD2F não ocupa um endereço consecutivo


BD2F 𝒂𝟑 AC12
.. . . . . . . . . . . .
1000 𝒂𝒊 3ª7B Cada nodo armazena um elemento e o endereço do
.. . . . . . . . . . . . próximo elemento da lista

14F6 𝒂𝒏−𝟏 5D4A


Ultimo elemento da cadeia, o endereço nulo indica
5D4A 𝒂𝑵 𝚽 que o elemento 𝑎𝑛 não tem um sucessor
Alocação Encadeada
<1> ALGORITMO BuscaInsereListaEncadeada
<2>
<3> Nome {Campo do no que pelo qual a lista está ordenada}
<4> PtrAnt {Ponteiro do no anterior ao no de trabalho}
<5> PtrTrb {Ponteiro do trabalho para a busca}
<6>
<7> INICIO
<8>
<9> PtrTrb <- PtrIni
<10> PtrAnt <- PtrTrb
<11> ENQUANTO (PtrTrb <> #)E (Nome (PtrTrb) < Nome) FAÇA
<12> INICIO
<13> PtrAnt <- PtrTrb
<14> PtrTrb <- LINK (PtrTrb)
<15> FIM
<16>
<17> RetiraLed (PtrNovo)
<18> Nome (PtrNovo)<- NomeIncluir
<19> Link (PtrNovo) <-PtrTrb
<20> SE PtrIni = PtrAnt
<21> ENTAO
<22> PtrIni <-PtrNovo
<23> SENAO
<24> Link (PtrAnt)<-PtrNovo
<25> FIM
Lista Circulares Simplesmente Encadedas
A implementação circular do conceito de lista linear em alocação encadeada é
feita aproveitando o ponteiro do ultimo nó da lista, cujo valor é #, para guardar o
endereço do primeiro nó.
● A partir de qualquer nó apontando por PtrIni, podemos alcançar todos os
outros nós da lista apenas “caminhando” através de seus links.
PtrIni

ooo

Lista Circular Simplesmente Encadeada


Lista Circulares Simplesmente Encadeadas
O percurso de uma lista circular encadeada deve levar em consideração que não
temos mais o fim da lisa determinado por #. Não devemos percorrer a lista com
PtrIni para evitar que o algoritmo entre em um loop sem fim.
A técnica mais adequada consiste no uso de uma variável ponteiro de trabalho
(PtrTrb) que assume como valor inicial o conteúdo de PtrIni, como valores
intermediários os conteúdos dos links e como valor final o conteúdo dede PtrIni
indicando o termino do percurso.
Lista Duplamente Encadeadas
Colocar mais um link no nó de tal forma que, além de o nó apontar para o seu
sucessor, também aponta para o seu antecessor. A uma lista com essa
características chamamos de duplamente encadeada.
PtrEsq PtrDir

ooo
# ooo
#

Lista duplamente encadeada

A lista alocada dessa forma nos permite a pesquisa nos seus dois sentidos como
também a inserção e retirada conhecendo-se o endereço de apenas um nó.
Lista Duplamente Encadeadas
No algoritmo de inserção em uma lista duplamente encadeada, apresentado a
seguir, consideramos que o novo nó será inserido à do nó apontado por PtrTrb.

<1> ALGORITMO InsereListEncDuplamente


<2>
<3> Lista {Lista alocada de forma encadeada}
<4> PtrTrb {Nó à direita do qual vamos inserir}
C<5>
<6> INICIO
<7> RetiraLED (PtrNovo)
<8> Lista (PtrNovo) <- Informacao
<9> LinkEsq (PtrNovo) <- PtrTrb
<10> LinkDir (PtrNovo) <- LinkDir (PtrTrb)
<11> LinkEsq (LinkDir (PtrTrb))<- PtrNovo
<12> LinkDir (PtrTrb) <- PtrNovo
<13>FIM
Inserção em uma Lista Duplamente Encadeadas
X Y
Y X

PtrNovo
PtrTrb
X Y

Z Z

Z
X Y

PtrNovo Inserção em uma lista duplamente encadeada


Combinação de Listas
A listas lineares apresentadas podem ser combinada de forma a obtermos
estruturas mais complexas. Podemos por exemplo, ter uma fila encadeada onde
cada nó possui como informação uma pilha sequencial.

Todos os tipos abstratos de dados (pilhas, filas, deques, etc.) podem ser
implementados usando um vetor (array) ou um tipo de estrutura encadeada (lista
encadeada)

Por isso, o vertor e a lista encadeada são chamados de estruturas de dados


fundamentais
Vetores
● Provavelmente o vetor é a estrutura mais comum (simples) usada para agregar
dados
● Estrutura homogênea: conjunto de dados do mesmo tipo
● Cada elemento pode ser acessado pela sua posição (índice)
● Alocação de memória é estática e contigua

○ Uma vez alocado, o tamanho de um vetor está fixado

○ Ex: int a[5];

0 0 0 0 0
Vetores
● Exemplo: int a[5];
● a[0] = 1 1 0 0 0 0

● a[1] = 2 1 2 0 0 0

● a[2] = 3 1 2 3 0 0

● a[3] = 4 1 2 3 4 0

● a[4] = 5 1 2 3 4 5
Vetores Multidimensionais
● Um vetor multidimencional de dimensão n é uma coleção de ítens que são
acessados através de n expressões de subscritos
● Exemplo: (i,j)-ésimo elemento de um vetor x bidimencional é acessado pela
expressão x[i,j]

■ int x[3][5];

■ x[i] seleciona o i-ésimo vetor unidimencional

■ Enquanto, x[i][j] seleciona o j-ésimo elemento deste vetor


● Podemos ter vetores

■ Bidimencionais: int x[3][5];

■ Tridimencionais: int x[3][5][2]

■ Etc.
Matrizes
● Vetores bidimencionais
● Conjunto de dados do mesmo tipo (estrutura homogênea)
● Cada elemento pode ser acessado pela sua posição (índices)
● Alocação estática e sequencial

○ Uma vez alocado, o tamanho de uma matriz está fixado


● Representação (simplificada) na memória
Matriz Sequencial
Uma matriz é generalmente armazenada squencialmente na memória por linhas, ou seja, uma linha após a
outra.

𝐴11 𝐴12 𝐴13 𝐴14


𝐴21 𝐴22 𝐴23 𝐴24
𝐴31 𝐴32 𝐴33 𝐴34
𝐴41 𝐴42 𝐴43 𝐴44

𝐴11 𝐴12 𝐴13 𝐴14 𝐴21 𝐴22 𝐴23 𝐴24 𝐴31 𝐴32 𝐴33 𝐴34 𝐴41 𝐴42 𝐴43 𝐴44

O acesso a um nó deuma matriz baseia-se sempre nos seus indices i e j de uma


matriz A, pocedemos da seguinte forma:
● Calculamos o deslocamento até o primeiro nó da linha i, ou seja, Loc(𝐴𝑖1 ).

Loc(𝐴𝑖1 )= LocIni + [(i-1)*TamNó *N]


Matriz Sequencial
● Calculamos o endereço do nó desejado a partir do endereço do primeiro nó da
linha.

Loc(𝐴𝑖𝑗 )= Loc(𝐴𝑖1 ) + (j-1)*TamNó

Considerando TamNó =1, temos que:

Loc[𝐴23 ] = LocIni + [(i-1)*TamNó *N] +(j-1)*TamNó

Loc[𝐴23 ] = Loc[𝐴11 ] + [(2-1)*1 *4] +(3-1)*1

Loc[𝐴23 ] = Loc[𝐴11 ] + [(2-1)*1 *4] +(3-1)*1

Loc[𝐴23 ] = Loc[𝐴11 ] + 6=6


Matriz Encadeada
Para alocar matrizes de forma encadeada, recorremos ao uso de listas encadeadas, simples
ou duplamente encadeadas, circulares ou nao, para cada linha e coluna.

Considerando ouso de listas simplesmente encadeadas temos o seguinte formato para cada
nó: Valor
Linha Coluna
LinkLinha LinkColuna
Onde Linha e Coluna definem a posicao do elemento na matriz e linkLinha e LinkColuna sao
links que indicam a proxima linha e coluna, respectivamente.

Como existem m*n listas lineares, precisaremos de m*n variaveis ponteiros externas, uma
apontando para o inicio de cada lista.
Matriz Encadeada
C1 C2 C3 Cn

1 1 1 2 1 3 1 4
L1 ooo #

2 1 2 2 2 3 2 4
L2 ooo #

O
O O O
O
O O O
O
O O O

m 1 m 2 m 3 m n
Lm ooo # #
# # #
Matriz Encadeada

Como o número de variaveis é mito grande, criamos uma estrutura auxiliar


onde cada elemento contém o endereco de umas desas listas. Essa
estrutura pode ser sequencial ou encadeada.

Um exemplo clasico de uso de listas lineares encadeadas é a alocacao de


matrizes que têmuma grande quantidade de elementos iguais a 0 (zero),
chamadas de ”matrizes esparsas”.

.
Matriz Encadeada
C
C1 C2 C3 ... Cn
L

L1

1 1 1 2 1 3 1 4
ooo #
L2

2 1 2 2 2 3 2 4
.
. ooo #
.
O
O O O
O
O O O
O
O O O

Lm

m 1 m 2 m 3 m n

# # # ooo # #
Tópicos
● Introdução.
● Estruturas de dados fundamentais: vetores, vetores multidimensionais, listas
encadeadas.
● Pilhas, filas e deques.
● Análise de algoritmos e algoritmos recursivos.
● Notação assintótica.
● Árvores.
● Heaps.
● Algoritmos de ordenação.
● Tabelas de dispersão e tabelas de espalhamento.
● Grafos.
Análise de algoritmos
● Automação: Processo em que uma tarefa deixa de ser desempenhada pelo
homem e passa a ser realizada por máquinas.
● Uma determinada tarefa, para que seja automatizada, deve ter todas as etapas
conhecidas, e a maquina ou dispositivo que desempenhará este processo
deverá estar apta a garantir sua repetibilidade.
● As etapas para resolução de um processo, podemos chamar de especificação
de sequencia ordenada de passos.
● Sequência finita de passos, logicamente organizados, para resolução de um
problema conhecido.
Análise de algoritmos
● Alguns autores restringem a definição de algoritmo para procedimentos que
(eventualmente) terminam

○ Um algoritmo pode repetir um procedimento ou ação infinitamente.


● Se o tamanho do procedimento não é conhecido, não é possível determinar se
ele terminará (Marvin Minsky)
● Para algoritmos que não terminam, o sucesso não pode ser determinado pela
interpretação da resposta e sim por condições impostas pelo próprio
desenvolvedor do algoritmo durante sua execução

○ Exemplo: um algoritmo que nunca termina mas sempre mantém algum


invariante
Análise de algoritmos
● Um algoritmo deve:

○ Funcionar corretamente

○ Executar o mais rápido possível

○ Utilizar a memória da melhor forma possível


● A fim de sabermos mais sobre um algoritmo, podemos analisá-lo

○ Precisamos estudar as suas especificações e tirar conclusões sobre como


a sua implementação (o programa) irá se comportar em geral.
Análise de algoritmos
De um algoritmo podemos determinar:

○ O tempo de processamento de um programa como função de seus dados


de entrada;

○ O espaço de memória máximo ou total requerido para os dados do


programa

○ O comprimento total do código do programa

○ Se o programa chega corretamente ao resultado desejado


Análise de algoritmos
○ A complexidade do programa

■ Facilidade em ler, entender e modificar

○ A robustez do programa

■ Exemplo: como ele lida com entradas errôneas ou inesperadas


Análise de algoritmos
● Estaremos particularmente interessados em analisar o tempo de execução e o espaço de
memória utilizado
● Como comparar algoritmos em função do custo de tempo?

○ Computadores diferentes podem funcionar em frequências diferentes

■ Ainda, diferente hardware (processador, memória, disco, etc.), diferente SO, etc.

○ Compiladores podem otimizar o código antes da execução

○ Um algoritmo pode ser escrito diferente, de acordo com a linguagem de programação


utilizada

○ Além disso, uma análise detalhada, considerando todos estes fatores, seria difícil,
demorada e pouco significativa

■ Tecnologias mudam rapidamente


Análise de algoritmos
● Podemos medir o custo de tempo contando quantas operações são realizadas pelo
algoritmo

○ Atribuições, comparações, operações aritméticas, instruções de retorno, etc.


● Cada operação demora o mesmo tempo ?

○ Não, mas podemos simplificar nossa análise

○ Exemplo: i = i + 1

■ Análise detalhada: 2 x tempo de recuperar uma variável (i e 1) + 1 x tempo da


soma + 1 x tempo para armazenar o valor na variável (i)

■ Análise simplificada: 4 operações


Análise de algoritmos
Como saberemos quantas vezes o loop é executado ?

● Os dados de entrada determinarão quantas vezes o loop é executado

○ Como não faz sentido analisar um algoritmo para apenas um determinado conjunto
de entradas e é impossível fazer esta análise para todas as entradas possíveis,
consideraremos apenas dois cenários: o pior caso e o melhor caso
Análise de algoritmos

Pior Caso

2
3*(n+1) //sendo n o tamanho do vetor, já calculado
3*(n) //considerando o acesso a vetor[i] como uma única operação
0
4*(n)

1
Total = 10n + 6
Análise de algoritmos
Melhor Caso

2
3*(1)
3*(1) //considerando o acesso a vetor[i] como uma única operação
1
0

0
Total = 9
Análise de algoritmos
● Vamos partir do seguinte algoritmo
<1> para i ← 1 até n, faça
<2> para j ← 1 até i, faça
<3> imprima i × j × n
<4> fim-para
<5> fim-para

Para medir o custo do algoritmo, nossa análise consistirá em ver quantas vezes cada passo
é executado.
● Linha <1> Será executada n + 1 vezes.
● Linha <2> Será executada 𝑛 ∗ σ𝑛𝑖=1 𝑖 + 𝑛
● Linha <3> Será executada 𝑛 ∗ σ𝑛𝑖=1 𝑖
● Linha <4> Sem custo
● Linha <5> Sem Custo
Análise de algoritmos
○ Então agora, vamos escrever o tempo de execução do algoritmo, que é a soma
dos tempos de execução para cada instrução executada.

○ 𝑇 𝑛 = 𝑛 + 1 + (𝑛 ∗ σ𝑛𝑖=1 𝑖 + 𝑛) + (σ𝑛𝑖=1 𝑖)
Classificação de Algoritmos
● Iterativo: estruturas de repetições (laços, pilhas, etc.)
● Recursivo: invoca a si mesmo até que certa condição seja satisfeita

● Serial: cada instrução é executada em sequência


● Paralela: várias instruções executadas ao mesmo tempo

● Determinístico: decisão exata a cada passo


● Probabilístico: decisão provável em algum(s) passo(s)

● Exato: resposta exata


● Aproximado: resposta próxima a verdadeira solução
Complexidade de Algoritmos
● Análise de um algoritmo particular

○ Qual é o custo de usar um dado algoritmo para resolver um


problema específico ?

○ Características de devem ser investigadas:

■ Tempo: análise do número de vezes que cada parte do


algoritmo deve ser executada

■ Espaço: estudo da quantidade de memória necessária


Complexidade de Algoritmos
● Análise de uma classe de algoritmos

○ Qual é o algoritmo de menor custo possível para resolver um


problema particular ?

■ Toda uma família de algoritmos é investigada (busca pelo


melhor possível)

○ Coloca-se limites para a complexidade computacional dos


algoritmos pertencentes à classe
Complexidade de Algoritmos
● Custo de um algoritmo

○ Ao determinar o menor custo possível para resolver problemas de uma


classe, tem-se a medida da dificuldade inerente para resolver o problema

○Quando o custo de um algoritmo é igual ao menor custo possível, o


algoritmo é considerado ótimo
● Podem existir vários algoritmos para resolver o mesmo problema

○ É possível compará-los e escolher o mais adequado

○ Se há vários algoritmos com custo de execução dentro da mesma ordem


de grandeza, pode-se considerar tanto os custos reais como os custos
não aparentes, como: alocação de memória, carga de arquivos, etc.
Complexidade de Algoritmos
Análise pela execução

○ A eficiência do programa depende da linguagem (compilada ou interpretada)

○ Depende do sistema operacional

○ Depende do hardware (quantidade de memória, velocidade do processador, etc.)


● Análise pelo modelo matemático

○ Não depende do computador nem da implementação

○ O custo das operações mais signicativas deve ser especificado, e algumas


operações são desprezadas

○ É possível analisar a complexidade do algoritmo dependendo dos dados de


entrada
Complexidade de Algoritmos
Exemplo: descobrir o maior número em uma lista

○ Considerando somente atribuições como operações relevantes e ainda que todas


as atribuições possuem o mesmo custo

○ Como veremos adiante, conforme n aumenta, o valor de n passa a determinar o


custo destas funções

■ Podemos dizer que ambas possuem um custo O(n)


Complexidade de Algoritmos
● Qual o custo do seguinte algoritmo de soma ?

○ Considerando somente atribuições como operações relevantes e ainda que todas


as atribuições possuem o mesmo custo

○ Custo tempo: g(n) = 2 + 2n

■ 2 na inicialização e 2 por repetição

○ Custo (espaço): g(n) = 3

■ 3 variáveis (soma, i, n)
Representação Física – Alocação Sequencial
O algoritmo de busca de um nó em uma lista linear sequencial existente, cujo
endereço do primeiro nó é PtrIni, e o valor procurado em um determinado campo
do nó é igual a “00345”.

<1> Algoritmo Busca 345


<2>
<3> Chave Campo do nó da lista alocada de forma sequencial}
<4> PtrIni {Ponteiro do primeiro nó da lista}
<5> PtrTrb {Ponteiro de trabalho para a busca}
<6>
<7> Inicio
<8> PtrTrb <- PtrIni
<9> ENQUANTO Chave (PtrTrb) <> “00345” Faca
<10> PtrTrb <- PtrTrb +1
<11> Fim
Representação Física – Alocação Sequencial
<1> Algoritmo Busca 345_e_TestaFim
<2>
<3> Chave {Campo do nó da lista alocada de forma sequencial}
<4> PtrIni {Ponteiro do primeiro nó da lista}
<5> PtrTrb {Ponteiro de trabalho para a busca}
<6> PtrFim {Ponteiro do ultimo nó da lista }
<7> Inicio
<8> PtrTrb <- PtrIni
<9> ENQUANTO (Ptrb <= PtrFim) E Chave (PtrTrb) <> “00345” Faça
<10> PtrTrb <- PtrTrb +1
<11> Fim
Representação Física – Alocação Sequencial
Algoritmo para inserir um nó em uma lista linear sequencial, na posição indicada
pelo ponteiro PtrInserir
<1> Algoritmo InsereLst
<2>
<3> Lista {Lista alocada de forma sequencial}
<4> PtrInserir {Aponta o índice onde deve ser feita a inserção}
<5> Informação {Variável onde deve ser feita a inserção}
<6>
Ptrini PtrInserir PtrFim PtrTrb
<7> INÍCIO
<8> SE PtrIni = # {Verificacao de lista vazia}
<9> Então
<10> INICIO
<11> PtrIni <- IndIniLst X X X
<12> PtrFim <- IndIniLst IndIni IndFim
<13> FIM
<14> SE NAO
<15> INICIO {Movimenta outros nós} IndFimLst
<16> PtrTrb <- PtrFim +1 IndIniLst
<17> ENQUANTO Ptrb > PtrInserir FAÇA
<18> INICIO
<19> Lista (PtrTrb) <- Lista (PtrTrb-1)
<20> PtrTrb <- PtrTrb -1
<21> FIM
<22> PtrFim <- PtrFim +1
<23> Fim
<24> Lista (PtrInserir) <-Informação
<25> Fim
Representação Física – Alocação Sequencial
À impossibilidade de inserção de um nó em uma lista, por falta de espaço
disponível, chamamos de overflow.
<1> Algoritmo InsereLstTestaOverflow
<2>
<3> Lista {Lista alocada de forma sequencial}
<4> PtrInserir {Aponta o índice onde deve ser feita a inserção}
<5> Informação {Variável onde deve ser feita a inserção}
<6>
<7> INÍCIO
<8> SE PtrFim = IndFimLst
<9> Então
<10> overflow
<11> SENAO
<12> INICIO {Movimenta outros nós}
<13> PtrTrb <- PtrFim +1
<14> ENQUANTO Ptrb > PtrInserir FAÇA
<15> INICIO
<16> Lista (PtrTrb) <- Lista (PtrTrb-1)
<17> PtrTrb <- PtrTrb -1
<18> FIM
<19> Lista (PtrInserir) <-Informação
<20> PtrFim <- PtrFim +1
<21> Fim
<22> Fim
Representação Física – Alocação Sequencial
A operação de retirada de nós de uma lista linear é análoga à de isnercao, pois
existem problemas semelhantes:
● Movimentação dos nós (Exceto quando esse nó for apontado por PtrFim (ele é
o ultimo)).
● Retirada do único nó
Após a retirada do único nó da lista, PtrFim deve ser modificado para
representar que a lista está vazia.
● Retirada em lista vazia
À impossibilidade de retirada de um nó em uma lista por falta de
informação chamamos underflow
Representação Física – Alocação Sequencial
A seguir o algoritmo de retirada em lista linear, com soluções análogas às utilizadas no
algoritmo anterior

<1> Algoritmo RetiradaLstTestaUnderflow


<2>
<3> PtrRetirar {Aponta o índice onde deve ser feita a retirada}
<4>
<5> INICIO
<6> SE PtrFim < IndIniLst
<7>
<8> SE PtrFim = IndIniLst {Verificacao de lista vazia}
<9> Então
underflow

<14> SE NAO
<15> INICIO
Informacao <- Lista(PtrRetirar )
<16> PtrTrb <- PtrRetirar
<17> ENQUANTO PtrTrb < PtrFim FAÇA
<18> INICIO
<19> Lista (PtrTrb) <- Lista (PtrTrb+1)
<20> PtrTrb <- PtrTrb +1
<21> FIM
<22> PtrFim <- PtrFim -1
<23> Fim
<25> Fim
Exercícios Aula– Alocação Sequencial
1. Observe que, nos algoritmos InserLsOverflow e RetiraLstTestaUnderflow, PtrIni
tem valor constante. Em alguns casos o número de movimentações
necessárias para manter a lista contigua pode ser reduzido, se não fixaramos
PtrIni. Por exemplo em uma lista com 10 nós, para retirar o segundo nó o
algoritmo RetiraLstTestaUnderflow realiza oito movimentações (Os nós
posteriores ao nó retirado). Poderíamos realizar apenas uma movimentação,
se movêssemos o primeiro nó em direção a PtrFim, o que é possível apenas se
PtrIni puder ser modificado. Criar um algoritmo de inserção e retirada que faça
esta alteração.
2. Baseando-se nos algoritmos apresentados, codifique uma unidade de rotinas
para verificação de resultados.

Você também pode gostar