Apostila de Estrutura de Dados - C#

Fazer download em pdf ou txt
Fazer download em pdf ou txt
Você está na página 1de 41

Apostila de Estrutura de Dados e Algoritmos em C#

Prof. Ms. Eduardo R. Marcelino

[email protected]

2009
ndice

Tipos Abstratos de Dados (TAD) ...................................................................................................................... 2


Tipos de Dados Concretos (TDC)...................................................................................................................... 2
Limitaes de uma implementao.................................................................................................................... 2
Complexidade Computacional........................................................................................................................... 4
PILHAS ............................................................................................................................................................ 6
Exerccios sobre Pilhas.................................................................................................................................... 11
FILAS............................................................................................................................................................. 12
LISTAS .......................................................................................................................................................... 15
Exerccios Sobre Listas, Filas e Pilhas............................................................................................................. 18
Apontadores ou Ponteiros............................................................................................................................... 19
Pilhas utilizando apontadores .......................................................................................................................... 20
rvores ........................................................................................................................................................... 23
Caminhamento em rvores............................................................................................................................. 24
rvores Binrias ............................................................................................................................................. 25
rvores Binrias de Busca .............................................................................................................................. 26
Implementao de uma rvore Binria de Busca em C# ................................................................................. 28
Listas Simplesmente Encadeadas .................................................................................................................... 31
Listas Duplamente Encadeadas ....................................................................................................................... 31
Listas circulares .............................................................................................................................................. 32
Grafos ............................................................................................................................................................. 33
Recursividade ou Recurso ............................................................................................................................. 34
Ordenao....................................................................................................................................................... 36
Pesquisa em Memria Primria ....................................................................................................................... 40

Referncias bsicas para o material desta apostila:

[1]PEREIRA, Silvio do Lago. Estruturas de dados fundamentais Conceitos e aplicaes. So Paulo:


rica, 1996.

[2]ZIVIANI, Nivio. Projeto de Algoritmos com implementaes em Pascal e C, 2. ed. So Paulo:


Pioneira Thomson Learning, 2005.

[3]GOODRICH, M.T., TAMASSIA, R. Estruturas de Dados e Algoritmos em Java, 2. ed. Porto Alegre:
Bookman, 2002.

[4] FORBELLONE, A.L.V., EBERSPACHER, H.F., Lgica de Programao A Construo de Alboritmos e


Estrutura de Dados. 2. ed. So Paulo: 2000. Makron Books.

1
Tipos A bstratos de Dados (TAD)
formado por um conjunto de valores e por uma srie de funes que podem ser aplicadas sobre estes
valores. Funes e valores, em conjunto, constituem um modelo matemtico que pode ser empregado
para modelar e solucionar problemas do mundo real, servido para especificar as caractersticas
relevantes dos objetos envolvidos no problema, de que forma eles se relacionam e como podem ser
manipulados. O TAD define o que cada operao faz, mas no como o faz.

EX: TAD para uma PILHA:


Empilha (valor) Insere um valor no topo da pilha
Entrada: valor. Sada: nenhuma.

Desempilha - Retira um valor do topo da pilha e o devolve.


Entrada: nenhuma. Sada: valor.

Tipos de Dados Concretos (TDC)


Sendo o TAD apenas um modelo matemtico, sua definio no leva em considerao como os valores
sero representados na memria do computador, nem se preocupa com o tempo que ser gasto para
aplicar as funes (rotinas) sobre tais valores. Sendo assim, preciso transformar este TAD em um tipo
de dados concreto. Ou seja, precisamos implementar (programar) as funes definidas no TAD.
durante o processo de implementao que a estrutura de armazenamento dos valores especificada, e
que os algoritmos que desempenharo o papel das funes so projetados.

Tipo Abstrato de Dados Implementao Tipo de Dados Concreto

Limitaes de uma implementao


importante notar que nem todo TAD pode ser implementado em toda sua generalidade. Imagine por
exemplo um TAD que tenha como funo mapear todos os nmeros primos. Claramente, este um tipo
de dados abstrato que no pode ser implementado universalmente, pois qualquer que seja a estrutura
escolhida para armazenar os nmeros primos, nunca conseguiremos mapear num espao limitado de
memria um conjunto infinito de valores.
Frequentemente, nenhuma implementao capaz de representar um modelo matemtico
completamente; assim, precisamos reconhecer as limitaes de uma implementao particular. Devemos
ter em mente que podemos chegar a diversas implementaes para um mesmo tipo de dados abstrato,
cada uma delas apresentando vantagens e desvantagens em relao s outras. O projetista deve ser
capaz de escolher aquela mais adequada para resolver o problema especfico proposto, tomando como
medidas de eficincia da implementao, sobretudo, as suas necessidades de espao de
armazenamento e tempo de execuo. Abaixo, veja o quadro com a complexidade de alguns jogos:

Damas - 5 x 10 na potncia 20 Poker americano (Texas Xadrez - 1 x 10 na potncia 45


Cerca de holdem) - 10 na potncia 18 Cerca de
500.000.000.000.000.000.000 Cerca de 1.000.000.000.000.000.000.000.000.000.000.
posies possveis. 1.000.000.000.000.000.000 000.000.000.000.000 posies possveis.
S em 1994 um programa foi posies possveis. Em 1997, um supercomputador venceu um
capaz de vencer um campeo O campeonato mundial de campeo mundial depois de penar muito. Para
mundial. Em 2007 o jogo foi humanos contra mquinas criar um programa imbatvel, porm, seria
"solucionado" a ponto de ser comeou nesta semana, com preciso de um computador quntico, uma
possvel um programa imbatvel favoritismo para os humanos. mquina que por enquanto s existe na cabea
Um programa imbatvel pode dos cientistas.
surgir nos prximos anos

2
Trecho da entrevista com JONATHAN SCHAEFFER, pesquisador na rea de inteligncia artificial.

Disponvel em:

http://circuitointegrado.folha.blog.uol.com.br/arch2007-07-22_2007-07-28.html#2007_07-
25_07_57_02-11453562-0

FOLHA - Voc pretende continuar trabalhando no problema para chegar ao que os matemticos
chamam de uma "soluo forte", mapeando cada uma das posies do jogo?

SCHAEFFER - No, por uma boa razo. Pondo em perspectiva, vemos que as damas possuem 5 x 10 na
potncia 20 posies. Muitas pessoas tm computadores com discos rgidos de 100 gigabytes [10 na
potncia 11 bytes]. Se voc tiver uma mquina "hiper-super" voc deve ter um disco de 1 terabyte [10
na potncia 12]. Se voc for a um dos 50 supercomputadores mais poderosos do mundo, voc encontrar
um disco de 1 petabyte [10 na potncia 15]. Um disco rgido desses custa cerca de US$ 1 milho. As
damas tm 10 na potncia 20 posies. Para poder gravar a soluo forte do problema eu precisaria de
500 mil petabytes _o que custaria US$ 500 bilhes hoje. Acho que no muito factvel. Se eu
processasse a soluo, eu simplesmente no teria onde salv-la.

Referncias:
PEREIRA, Silvio do Lago. Estruturas de dados fundamentais Conceitos e aplicaes. So
Paulo: rica, 1996.

3
Complexidade Computacional
Fonte: http://www.dca.fee.unicamp.br/~ting/Courses/ea869/faq1.html

O que um problema computvel?

Um problema computvel se existe um procedimento que o resolve em um nmero finito de passos, ou seja se existe
um algoritmo que leve sua soluo.
Observe que um problema considerado "em princpio" computvel pode no ser tratvel na prtica, devido s limitaes
dos recursos computacionais para executar o algoritmo implementado.

Por que importante a anlise de complexidade computacional de um algoritmo?

A complexidade computacional de um algoritmo diz respeito aos recursos computacionais - espao de memria e tempo
de mquina - requeridos para solucionar um problema.
Geralmente existe mais de um algoritmo para resolver um problema. A anlise de complexidade computacional
portanto fundamental no processo de definio de algoritmos mais eficientes para a sua soluo. Apesar de parecer
contraditrio, com o aumento da velocidade dos computadores, torna-se cada vez mais importante desenvolver
algoritmos mais eficientes, devido ao aumento constante do "tamanho" dos problemas a serem resolvidos.

O que entendemos por tamanho de um problema?

O tamanho de um problema o tamanho da entrada do algoritmo que resolve o problema. Vejamos os seguintes
exemplos:
A busca em uma lista de N elementos ou a ordenao de uma lista de N elementos requerem mais operaes medida
que N cresce;
O clculo do fatorial de N tem o seu nmero de operaes aumentado com o aumento de N;
A determinao do valor de F_N na sequncia de Fibonacci F_0, F_1, F_2, F_3, ... envolve uma quantidade de adies
proporcional ao valor de N.

{encontrando o maior nmero}


maior:= numero[1]; 1 operao
for contador := 2 to 10 do // n operaes
if numero[contador] > maior then // 1 operao
maior := numero[contador]; // 1 operao

A complexidade dada por n elementos (o elemento que determina a taxa de crescimento do algoritmo acima)
ou O(n) (linear)

For T:=1 to 100 do // n operaes


For X := 1 to 100 do // n operaes
Writeln(T, X); // 1 operao

A complexidade dada por n2 (T*X) elementos ou O(n2) (quadrdico)

4
Funes limitantes superiores mais conhecidas:

Melhor Pior
Constante Logartmica Linear Quadrtica Polinomial Exponencial
O(1) O(log n) O(n) O(n2) O(nk) com k>=1 O(an) com a > 1

Crescimento de vrias funes

n log n n n2 2n
2 1 2 4 4
4 2 4 16 16
8 3 8 64 256
16 4 16 256 65.536
32 5 32 1.024 4.294.967.296
64 6 64 4.096 1,84 x 10 19
128 7 128 16.384 3,40 x 10 38
256 8 256 65.536 1,18 x 10 77
512 9 512 262.144 1,34 x 10154
308
1024 10 1024 1.048.576 1,79 x 10

5
PILHA S
(http://pt.wikibooks.org/wiki/Estrutura_de_Dados_II/Pilhas)

Uma pilha uma estrutura de dados onde em todas as inseres, retiradas e acessos ocorrem
apenas em um dos extremos (no caso, em seu topo).

Os elementos so removidos na ordem inversa daquela em que foram inseridos de modo que o
ltimo elemento que entra sempre o primeiro que sai, por isto este tipo de estrutura
chamada LIFO (Last In - First Out).

O exemplo mais prtico que costuma utilizar-se para entender o processo de pilha como uma pilha
de livros ou pilha de pratos, no qual ao se colocar diversos elementos uns sobre os outros, se
quisermos pegar o livro mais abaixo deveremos tirar todos os livros que estiverem sobre ele.

Empilhar
Desempilhar

Topo da pilha: ltimo


elemento inserido

Base da pilha: primeiro


Tamanho = 5 elemento inserido

Uma pilha geralmente suporta as seguintes operaes bsicas:

TOP (topo): acessa-se o elemento posicionado no topo da pilha;


PUSH (empilhar): insere um novo elemento no topo da lista;
POP (desempilhar): remove o elemento do topo da lista.
ISEmpty (vazia?): indica sea pilha est vazia.
Size (tamanho): retorna a quantidade de elementos da pilha.

Exemplo de utilizao de uma pilha:

Operao Pilha (topo) ----- (base) Retorno Tamanho da Pilha


Empilhar(1) 1 1
Vazia False 1
Empilhar(2) 2,1 2
Topo 2,1 2 2
Empilhar(7) 7,2,1 3
Tamanho 7,2,1 3 3
Desempilhar 2,1 7 2
Desempilhar 1 2 1
Desempilhar 1 0
Vazia True 0

6
// Esta pilha armazena em cada posio da pilha um dado do tipo String.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PilhaEstatica
{
// definio da classe Pilha
public class Pilha
{
private const int CAPACIDADE = 10; //define o tamanho maximo desta uma pilha.
private string[] dados = new string[CAPACIDADE]; // vetor para guardar os dados da pilha.
private int topo = -1; // varivel que ir indicar a posio no vetor do topo da pilha.

// este mtodo retorna true se a pilha estiver vazia


public bool vazia()
{
return tamanho() == 0;
}

// este mtodo informa o tamanho da pilha


public int tamanho()
{
return topo + 1;
}

// este mtodo empilha um valor string na pilha


public void empilha(string p_valor)
{
if (tamanho() == CAPACIDADE)
{
topo++;
dados[topo] = p_valor;
}
else
{
Console.WriteLine("A PILHA ESTA CHEIA!!!");
}
}

// este mtodo desempilha um valor da pilha


public string desempilha()
{
if (vazia() == true)
{
Console.WriteLine("A pilha est vazia!!!");
return "";
}
else
{
topo--;
return dados[topo + 1];
}
}

// este mtodo devolve o valor que est no topo


public string retornatopo()
{
if (vazia() == true)
{
Console.WriteLine("A pilha est vazia!!!");
return "";
}
else
{
return dados[topo];
}
}
}

7
// classe do programa principal.
class Pilha_Vetor
{
static void Main(string[] args)
{
int opcao;
string valor;
Pilha minhaPilha = new Pilha(); // cria uma instncia da classe pilha!

do
{
Console.Write("\n\n Escolha: 1-> empilha 2->desempilha " +
" 3->topo 4-> tamanho 9-> sair : ");
opcao = Convert.ToInt32(Console.ReadLine());

if (opcao == 1)
{
Console.Write(">>Digite o valor que deseja empilhar: ");
valor = Console.ReadLine();
minhaPilha.empilha(valor);
}
else if (opcao == 2)
{
valor = minhaPilha.desempilha();
Console.WriteLine(">>Desempilhado: {0} \n\n", valor);
}
else if (opcao == 3)
{
valor = minhaPilha.retornatopo();
Console.WriteLine(">>Valor no topo: {0} \n\n", valor);
}
else if (opcao == 4)
{
Console.WriteLine(">>Tamanho da pilha: {0}", minhaPilha.tamanho());
}
else if (opcao == 9)
{
// sai do programa
}
}
while (opcao != 9);
}
}

8
// Esta pilha armazena em cada posio da pilha um dado heterogneo( cdigo e nome)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace PilhaEstatica
{
// Estrura para armazenar dados heterogneos no vetor.
public struct registro
{
public int codigo;
public string nome;
};

// definio da classe Pilha


public class Pilha
{
private const int CAPACIDADE = 10; //define o tamanho maximo desta uma pilha.
private registro[] dados = new registro[CAPACIDADE];//vetor para guardar os dados da pilha.
private int topo = -1; // varivel que ir indicar a posio no vetor do topo da pilha.

// este mtodo retorna true se a pilha estiver vazia


public bool vazia()
{
return tamanho() == 0;
}

// este mtodo informa o tamanho da pilha


public int tamanho()
{
return topo + 1;
}

// este mtodo empilha um valor string na pilha


public void empilha(registro p_valor)
{
if (tamanho() == CAPACIDADE)
{
topo++;
dados[topo] = p_valor;
}
else
{
Console.WriteLine("A PILHA ESTA CHEIA!!!");
}
}

// este mtodo desempilha um valor da pilha


public registro desempilha()
{
if (vazia() == true)
{
Console.WriteLine("A pilha est vazia!!!");

registro nada;
nada.codigo = 0;
nada.nome = "";
return nada;
}
else
{
topo--;
return dados[topo + 1];
}
}

// este mtodo devolve o valor que est no topo


public registro retornatopo()
{
if (vazia() == true)
{

9
Console.WriteLine("A pilha est vazia!!!");
registro nada;
nada.codigo = 0;
nada.nome = "";
return nada;
}
else
{
return dados[topo];
}
}
}

// classe do programa principal.


class Pilha_Vetor
{
static void Main(string[] args)
{
int opcao;
registro dado;
Pilha minhaPilha = new Pilha(); // cria uma instncia da classe pilha!

do
{
Console.Write("\n\n Escolha: 1-> empilha 2->desempilha " +
" 3->topo 4-> tamanho 9-> sair : ");
opcao = Convert.ToInt32(Console.ReadLine());

if (opcao == 1)
{
Console.Write(">>Digite o nome para empilhar: ");
dado.nome = Console.ReadLine();
Console.Write(">>Digite o cdigo para empilhar: ");
dado.codigo = Convert.ToInt32( Console.ReadLine() );

minhaPilha.empilha(dado);
}
else if (opcao == 2)
{
dado = minhaPilha.desempilha();
Console.WriteLine(">>Desempilhado: Cdigo: {0} Nome: {1} \n\n",
dado.codigo, dado.nome);
}
else if (opcao == 3)
{
dado = minhaPilha.retornatopo();
Console.WriteLine(">>Dado no topo: Cdigo: {0} Nome: {1} \n\n",
dado.codigo, dado.nome);
}
else if (opcao == 4)
{
Console.WriteLine(">>Tamanho da pilha: {0}", minhaPilha.tamanho());
}
else if (opcao == 9)
{
// sai do programa
}
}
while (opcao != 9);
}
}

10
Exerccios sobre Pilhas

1. Crie uma pilha que manipule a seguinte estrutura:

Tfuncionario = registro
Nome : string;
Salario : Real;
end;

Depois faa um programa para testar esta pilha (como no programa exemplo sobre
pilhas).

A pilha deve possuir os seguintes mtodos:

Empilhar (p_funcionario); Empilhar um dado do tipo da estrutura que voc definir.


Desempilhar : Tfuncionario; Desempilhar um valor e retornar o valor desempilhado
RetornaTopo : Tfuncionario; Retorna o valor que est no topo da pilha
Tamanho : integer; Retorna o tamanho da pilha
Listar Exibe na tela os elementos da pilha, ou exibe pilha vazia.
SomaSalarios : real Retorna a soma de todos os salrios de todos os funcionrios.

Observe que no h o mtodo vazio. Portanto, para saber se a pilha est vazia, voc
dever utilizar o mtodo Tamanho.

2. Preencha a tabela abaixo, de acordo com os mtodos executados na primeira coluna:

Operao Pilha (topo) ----- (base) Retorno Tamanho da Pilha


Tamanho
Empilhar(O)
Empilhar(Z)
RetornaTopo
Empilhar(O)
Vazio?
Desempilhar
Tamanho
Empilhar(X)
RetornaTopo
Desempilhar
Empilhar(O)
Empilhar(B)

Veja o exemplo de preenchimento na documentao entregue sobre Pilhas.

11
F ILA S
Uma Fila (Queue) um caso particular de Listas Lineares que obedece ao critrio FIFO (First In, Firts Out,
ou Primeiro a Entrar, Primeiro a Sair). Numa fila os elementos so inseridos em uma das extremidades e
retirados na outra extremidade. Existe uma ordem linear para a fila que a ordem de chegada. Filas
so utilizadas quando desejamos processar itens de acordo com a ordem primeiro-que-chega, primeiro-
atendido.

Uma pilha normalmente possui as seguintes operaes (mtodos):

Enfileira ( valor ) Insere o valor no final da fila.


Desenfileira Retorna o elemento do incio da fila, desenfileirando-o.
Vazia Informa se a fila est fazia
Tamanho retorna o tamanho da fila
RetornaInicio retorna o elemento do incio da fila, mas no o desenfileira.
RetornaFim retorna o elemento do final da fila, mas no o desenfileira.

Em uma implementao com arranjos (vetores), os itens so armazenados em posies contguas da


memria. Por causa das caractersticas da fila, a operao enfileira faz a parte de trs da fila expandir-se
e a operao desenfileira faz a parte da frente da fila contrair-se. Consequentemente a fila tende a
caminhar pela memria do computador, ocupando espao na parte de trs e descartando espao na parte
da frente. Com poucas inseres e retiradas de itens, a fila vai ao encontro do limite do espao de
memria alocado para ela.

A soluo para o problema acima imaginar um vetor como um crculo, em que a primeira posio segue
a ltima. Observe que a fila segue o sentido horrio. Conforme os elementos vo sendo desenfileirados, a
fila anda no sentido horrio. O mesmo ocorre com os itens que vo sendo enfileirados. Para evitar
sobrepor elementos no vetor, devemos verificar o tamanho da fila antes de efetuar a operao enfileirar.
Os elementos Frente e Trs so variveis que indicaro em que posio no vetor esto o primeiro e o
ltimo elemento inserido na fila.
Frente

Trs Implementao circular para filas

Exemplo de utilizao de uma Fila:

Operao Fila trs --- frente Retorno Tamanho da Fila


Tamanho 0 0
Enfileirar(A) A 1
Enfileirar(B) BA 2
Vazia BA False 2
Enfileirar(C) CBA 3
RetornaFrente CBA A 3
RetornaTras CBA C 3
Desenfilera CB A 2
Desenfilera C B 1
Desenfilera C 0
Vazia True 0
RetornaFrente ERRO 0

12
// Implementao de uma fila circular em C# utilizando vetor

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FilaEstatica
{

class Fila{
const int CAPACIDADE = 10; // capacidade mxima da fila
private int quantidade = 0; // qtde de elementos enfileirados
private int inicio = 0; // indica qual a primeira posio da fila
private int fim = 0; // indica a prxima posio
private string[] dados = new string[CAPACIDADE]; // este vetor ir armazenar os dados da fila

// retorna o tamanho da fila


public int tamanho()
{
return quantidade;
}

// enfileira um valor string


public void enfileirar( string p_valor )
{
if (tamanho() == CAPACIDADE)
{
Console.WriteLine("A fila est cheia!!!!");
}
else
{
dados[ fim ] = p_valor;
fim = (fim + 1) % CAPACIDADE;
quantidade++;
}
}

// remove o primeiro elemento da fila e devolve.


public string desenfileira()
{
if (tamanho() == 0)
{
Console.WriteLine("A fila est vazia!");
return "";
}
else
{
string valor = dados[inicio];
inicio = (inicio + 1) % CAPACIDADE;
quantidade --;
return valor;
}
}
} // fim da classe Fila

// programa principal para testar a fila circular.


class FilaEstatica
{
static void Main(string[] args)
{
string opcao, valor;
Fila minhafila = new Fila();

Console.WriteLine("Sistema em C# para testar a execuo de uma fila circular\n");

do
{
Console.WriteLine("\n\nDigite: 1->Enfileirar 2->Desenfileirar " +

13
"3-> Tamanho 9->Sair");
opcao = Console.ReadLine();

switch (opcao){
case "1":
Console.WriteLine("Digite um valor para enfileirar:");
valor = Console.ReadLine();
minhafila.enfileirar( valor );
break;
case "2":
Console.WriteLine("Desenfileirado: {0}" , minhafila.desenfileira());
break;
case "3":
Console.WriteLine("Tamanho da fila:{0}", minhafila.tamanho() );
break;
case "9":
Console.WriteLine("Saindo do sistema...");
break;
default:
Console.WriteLine("Opo invlida!!!");
break;
}

}while (opcao != "9");


}
} // fim da classe FilaEstatica

14
LISTAS

http://www.inf.ufsc.br/~ine5384-hp/Capitulo2/EstruturasLista.html

Uma Estrutura de Dados Lista um conjunto de dados dispostos e/ou acessveis em uma seqncia
determinada.

Este conjunto de dados pode possuir uma ordem intrnseca (Lista Ordenada) ou no.
Este conjunto de dados pode ocupar espaos de memria fisicamente consecutivos,
espelhando a sua ordem, ou no.
Se os dados estiverem dispersos fisicamente, para que este conjunto seja uma lista, ele deve
possuir operaes e informaes adicionais que permitam que seja tratado como tal (Lista
Encadeada).

O conjunto de operaes a ser definido depende de cada aplicao. Um conjunto de operaes


necessrio a uma maioria de aplicaes :

1. Criar uma lista linear vazia.


2. Inserir um novo item imediatamente aps o i-simo item.
3. Retirar o i-simo item.
4. Localizar o i-simo item para examinar e/ou alterar o contedo de seus componentes.
5. Combinar duas ou mais listas lineares em uma lista nica.
6. Partir uma lista linear em duas ou mais listas.
7. Fazer uma cpia da lista linear.
8. Ordenar os itens da lista em ordem ascendente ou descendente, de acordo com alguns de
seus componentes.
9. Pesquisar a ocorrncia de um item com um valor particular em algum componente.

IMPLEMENTAO DE LISTAS LINEARES POR MEIO DE ARRANJOS

Os itens da lista so armazenados em posies contguas de memria.


A lista pode ser percorrida em qualquer direo.
A insero de um novo item pode ser realizada aps o ltimo item com custo constante.
A insero de um novo item no meio da lista requer um deslocamento de todos os itens
localizados aps o ponto de insero.
Retirar um item do incio da lista requer um deslocamento de itens para preencher o espao
deixado vazio.

15
Abaixo, listagem de uma implementao de Lista com Arranjos:

using System;
using System.Collections.Generic;
using System.Text;

namespace ListaEstatica
{

public class Lista


{
private const int CAPACIDADE = 10;
private string[] dados = new string[CAPACIDADE];
private int quantidade = 0;

public int tamanho()


{
return quantidade;
}

public void insereNaPosicao(int p_posicao, string p_valor)


{
if (tamanho() == CAPACIDADE)
{
Console.WriteLine("A lista est cheia!!!\n\n");
}
else
{
quantidade++;
for (int i = tamanho() - 1; i > p_posicao; i--)
{
dados[i] = dados[i - 1];
}
dados[p_posicao] = p_valor;
}
}

public string removeDaPosicao(int posicao)


{
if (tamanho() == 0)
{
Console.WriteLine("A lista est vazia!!!!");
return "";
}
else
{
string aux = dados[posicao];
for (int i = posicao; i < tamanho() -1 ; i++)
{
dados[i] = dados[i + 1];
}
quantidade--;
return aux;
}
}

public void insereNoInicio(string p_valor)


{
insereNaPosicao(0, p_valor);
}

public void insereNoFim(string p_valor)


{
insereNaPosicao(tamanho(), p_valor);
}

16
public void imprimeLista()
{
Console.WriteLine("\n\nImpresso dos dados da lista:\n");
for (int i = 0; i < tamanho(); i++)
{
Console.WriteLine(dados[i]);
}
}
} // fim da classe lista

class Program_ListaEstatica
{
static void Main(string[] args)
{
string opcao, valor;
int posicao;
Lista minhaLista = new Lista();
Console.WriteLine("Sistema em C# para testar a execuo de uma lista esttica\n");
do
{
Console.WriteLine("\nDigite: \n 1-> Inserir no incio \n 2-> Inserir no fim \n" +
"3-> Inserir em uma posio \n 4-> Tamanho \n 5-> Listar \n " +
"6-> Remover elemento de uma posio \n 9-> Sair");
opcao = Console.ReadLine();
switch (opcao)
{
case "1":
Console.WriteLine("Digite um valor para inserir no incio:");
valor = Console.ReadLine();
minhaLista.insereNoInicio(valor);
break;
case "2":
Console.WriteLine("Digite um valor para inserir no fim:");
valor = Console.ReadLine();
minhaLista.insereNoFim(valor);
break;
case "3":
Console.WriteLine("Digite um valor para inserir:");
valor = Console.ReadLine();
Console.WriteLine("Digite a posio:");
posicao = Convert.ToInt32(Console.ReadLine());
minhaLista.insereNaPosicao (posicao, valor);
break;
case "4":
Console.WriteLine("Tamanho da lista:{0}", minhaLista.tamanho());
break;
case "5":
minhaLista.imprimeLista();
break;
case "6":
Console.WriteLine("Digite a posio que deseja remover:");
posicao = Convert.ToInt32(Console.ReadLine());
Console.WriteLine("Removido: {0} ", minhaLista.removeDaPosicao(posicao));
break;
case "9":
Console.WriteLine("Saindo do sistema...");
break;
default:
Console.WriteLine("Opo invlida!!!");
break;
}
} while (opcao != "9");
}
}
}

17
Exerccios Sobre Listas, Filas e Pilhas

Sabendo-se que possvel implementar uma Pilha e uma Fila utilizando a estrutura de dados LISTA,
faa:

Exerccio 1:
Dada a Estrutura de Dados LISTA descrita e implementada acima, faa o programa principal de
forma que ele simule uma Pilha para armazenamento de strings.
Deve executar os mtodos: Empilhar, Desempilhar e Tamanho.

Exerccio 2:
Dada a Estrutura de Dados LISTA descrita e implementada acima, faa o programa principal de
forma que ele simule uma Fila para armazenamento de strings.
Deve executar os mtodos: Enfileira, Desenfileira e Tamanho.

18
Apontadores ou Ponteiros
timo material sobre apontadores:
http://br.geocities.com/cesarakg/pointers.html
http://www.deei.fct.ualg.pt/IC/t20_p.html

Um apontador um tipo de varivel especial, cujo objetivo armazenar um endereo da memria. Ou seja, ele no
armazena um valor como Ol mundo ou 55. Ao invs disso, ele armazena um endereo na memria e, neste
endereo, encontra-se uma informao til, como um texto ou um nmero.

Um apontador contm o endereo de um lugar na memria.

Quando voc executa comandos tais como:


I := 10 ou J := 1

voc est acessando o contedo da varivel. O compilador procura automaticamente o endereo da varivel e acessa o
seu contedo. Um apontador, entretanto, lhe permite determinar por si prprio o endereo da varivel.

Ex:
Endereo Contedo Varivel apontador:
I := 42; E456 E123 PonteiroParaI
E455
E454
PonteiroParaI := &I;
Varivel Inteira I:

E123 42

Para mostrar o contedo do endereo para o qual a o ponteiro aponta, podemos utilizar:

Writeln( *PonteiroParaI ); { exibir 42 }

&X retorna o endereo da varivel x


*p o contedo do endereo p

Para que o apontador saiba exatamente o tamanho da informao para a qual ele aponta na memria (veja figura
abaixo), uma varivel do tipo apontador deve estar associada a um tipo especfico de varivel ou registro. Para criar uma
varivel do tipo apontador, coloque o smbolo ^ na frente do tipo da varivel.

Cada tipo de dado ocupa um tamanho


diferente na memria. O apontador precisa
saber para qual tipo de dado ele vai
apontar, para que ele possa acessar
corretamente a informao.

A esquerda temos 3 tipos de dados em


Pascal: Byte na primeira linha, word na
segunda e longint na terceira. Veja que os
tamanhos so diferentes!

19
Exemplo de um programa que utiliza apontadores:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Ponteiros
{
class ProgramPonteiro
{
static void Main(string[] args)
{
// para rodar este programa, voc deve configurar o seu projeto para rodar cdigo no protegido.
// para tanto, v ao menu project-> properties (ultima opo do menu) -> escolha a aba BUILD
// e marque a opo "ALLOW UNSAFE CODE". Salve o projeto e compile-o com F5.
unsafe
{
int* p1; // cria uma varivel que pode apontar para uma
//outra varivel inteira
int numero = 7;

// &numero = endereo da varivel numero


p1 = &numero; // o ponteiro p1 vai apontar para o mesmo endereo
// que a varivel numero

// *p1 -> valor armazenado no endereo apontado por p1


Console.WriteLine( "Varivel nmero: {0} ponteiro: {1}", numero, *p1);

Console.Write("\nDigite um valor para o ponteiro:");


*p1 = Convert.ToInt32(Console.ReadLine());

Console.WriteLine("Varivel nmero: {0} ", numero);

Console.ReadLine();
}
}
}
}

Pilhas utilizando apontadores


As pilhas criadas utilizando-se apontadores so mais eficientes, pois no precisamos especificar o tamanho inicial,
tampouco necessrio definir um Limite. Cada elemento da pilha aponta para o prximo, utilizando para isso
apontadores.

Topo Cada elemento da pilha possui:

Anterior Valor que deve ser empilhado.


Apontador para o elemento anterior na pilha.
Anterior
A base da pilha tem como elemento anterior o valor NIL.
Base
Anterior

NIL

Para criar uma pilha utilizando apontadores, precisamos definir uma estrutura que possua os seguintes campos:

program Ppilha;
Type
Apontador = ^TElemento;

TElemento = record
Anterior : apontador;
dado : String;

20
end;

Pilha = Object
Topo : Apontador;
Procedure Empilha( valor : string );
...

{pilha utilizando apontadores} var elemento : apontador;


program Ppilha; Begin
uses crt; {reserva memria para o novo elemento}
type new (elemento);
{tipo de dado Apontador para apontar para
um elemento da pilha} elemento^.anterior := topo;
Apontador = ^TElemento; elemento^.dado := p_valor;

{estrutura que representa um elemento na pilha}


TElemento = record if tamanho > 0 then
anterior : apontador; begin
dado : String; elemento^.anterior := topo;
end; end;
topo := elemento;
{estrutura que representa uma pilha} quantidade := quantidade + 1;
Pilha = Object end;
Topo : Apontador;
quantidade : integer;
{desempilha um valor string}
procedure inicializa; function Pilha.desempilha : string;
function Tamanho : integer; var elemento : apontador;
procedure Listar; Begin
procedure empilha( p_valor : String ); if (tamanho = 0) then
function desempilha : string; begin
end; desempilha := '';
writeln('A piloha esta vazia!');
{inicia uma pilha vazia} end
procedure Pilha.inicializa; else
begin begin
topo := nil; desempilha := topo^.dado;
quantidade := 0; {guarda p/ depois liberar o espaco}
end; elemento := topo;
topo := topo^.anterior;
dispose(elemento);
{retorna o tamanho da pilha} quantidade := quantidade - 1;
function Pilha.Tamanho : integer; end;
begin end;
Tamanho := quantidade;
end;
{programa principal para testar a pilha}
var
{lista os elementos da pilha} MinhaPilha : Pilha;
procedure Pilha.Listar; valor : string;
var elemento : Apontador; tecla : char;
begin begin
if tamanho > 0 then clrscr;
begin MinhaPilha.inicializa;
writeln('Informaes contidas na pilha');
elemento := topo; repeat
while elemento <> NIL do writeln;
begin writeln('Informe 1->Empilhar 2->Desempilhar '
Writeln( elemento^.dado ); + '3->Tamanho 4->Listar e 5->Sair');
elemento := elemento^.anterior; readln(tecla);
end;
end if (tecla = '1') then
else begin
begin write('Informe algo p/ empilhar: ');
writeln('A pilha esta vazia!!!'); readln(valor);
end; MinhaPilha.empilha( valor );
end; end

else if (tecla = '2') then


{empilha um valor string} begin
procedure Pilha.empilha( p_valor : String ); writeln('Desempilhado: ' ,

21
minhaPilha.Desempilha ); end;
end until (tecla = '5');
else if (tecla = '3') then end.
begin
writeln('Tamanho: ' , MinhaPilha.Tamanho );
end
else if (tecla = '4') then
begin
MinhaPilha.Listar;

22
rvores
Material retirado da referncia [3].

Uma rvore um tipo abstrato de dados que armazena elementos de maneira hierrquica. Como exceo do elemento
do topo, cada elemento tem um elemento pai e zero ou mais elementos filhos. Uma rvore normalmente desenhada
colocando-se os elementos dentro de elipses ou retngulos e conectando pais e filhos com linhas retas. Normalmente o
elemento topo chamado de raiz da rvore, mas desenhado como sendo o elemento mais alto, com todos os demais
conectados abaixo (exatamente ao contrrio de uma rvore real).

EX: Uma rvore que representa a estrutura de pastas em um Hard Disk:

C:\ Raiz

Arquivos de programas Meus documentos Windows

Office 2000 MSN Trabalhos Provas System32 Media Config

1 Semestre 2 Semestre

Uma rvore T um conjunto de nodos que armazenam elementos em relacionamentos pai-filho com as seguintes
propriedades:
T tem um nodo especial, r, chamado de raiz de T.
Cada nodo v de T diferente de r tem um nodo pai u.
Se um nodo u pai de um nodo v, ento dizemos que v filho de u.
Dois nodos que so filhos de um mesmo pai so irmos.
Um nodo externo (ou folha) se no tem filhos.
Um nodo interno se tem um ou mais filhos.
Um subrvore de T enraizada no nodo v a rvore formada por todos os descendentes de v em T (incluindo o
prprio v).
O ancestral de um nodo tanto um ancestral direto como um ancestral do pai do nodo.
Um nodo v descendente de u se u um ancestral de v. Ex: na figura acima, Meus documentos ancestral
de 2 Semestre e 2 Semestre descendente de Meus documentos.
Seja v um nodo de uma rvore T. A profundidade de v o nmero de ancestrais de v, excluindo o prprio v.
Observe que esta definio implica que a profundidade da raiz de T 0 (zero). Como exemplo, na figura acima, a
profundidade do nodo Trabalhos 2, e a profundidade do nodo 2 semestre 3.
A altura de um nodo o comprimento do caminho mais longo desde nodo at um n folha ou externo. Sendo
assim, a altura de uma rvore a altura do nodo Raiz. No exemplo acima, a rvore tem altura 3. Tambm se diz
que a altura de uma rvore T igual profundidade mxima de um nodo externo de T.

A figura abaixo representa uma subrvore da rvore acima. Esta subrvore possui 5 nodos, onde 2 so nodos internos
(Meus Documentos e Trabalhos) e 3 so nodos externos (Provas, 1 Semestre e 2 Semestre). A altura desta subarvore
2.
Meus documentos

Trabalhos Provas

1 Semestre 2 Semestre

23
Os principais mtodos de uma rvore so:
Raiz: Retorna a raizda rvore
Pai(nodo): Retorna o pai de um nodo. Ocorre um erro se nodo for a raiz.
Filho(nodo): Retorna os filhos de um nodo.
Nodo_eh_Interno(nodo): Testa se um nodo do tipo interno.
Nodo_eh_externo(nodo): Testa se um nodo do tipo externo.
Nodo_eh_raiz(nodo): Testa se um nodo a raiz.
Tamanho: Retorna a quantidade de nodos de uma rvore.

Caminhamento em rvores
O caminhamento de uma rvore a maneira ordenada de percorrer todos os nodos da rvore (percorrer todos os seus
ns, sem repetir nenhum e sem deixar de passar por nenhum). utilizada, por exemplo, para consultar ou alterar as
informaes contidas nos ns.

Caminhamento prefixado: Um nodo visitado antes de seus descendentes. Exemplo de aplicao: Imprimir um
documento estruturado.

Os nmeros em vermelho indicam a ordem em que os nodos so visitados. Caso fossem impressos, o resultado seria:
Documento, Captulo 1, Seo 1.1, Seo 1.2, Captulo 2, Seo 2.1, Seo 2.2, Seo 2.3, Referncias.

Caminhamento ps-fixado: Neste caminho, um nodo visitado aps seus descendentes. Exemplo de aplicao:
Calcular o espao ocupado por arquivos em pastas e sub-pastas.

Os nmeros em vermelho indicam a ordem em que os nodos so visitados. Caso fossem impressos, o resultado seria:
H1c.doc 3k, h1nc.doc 2k, homeworks/, DDR.java 10k, Stocks.java 25k, Robot.java 20k, programs/, todo.txt 1k,
cs16/.

24
rvores Binrias
Material retirado da referncia [2] e [3].

Uma rvore binria uma rvore ordenada na qual todo nodo tem, no mximo, dois filhos. Uma rvore binria
imprpria aquela que possui apenas 1 filho. J uma rvore binria prpria aquela em que todo nodo tem zero ou dois
filhos, ou seja, todo nodo interno tem exatamente 2 filhos. Isso porque um nodo externo no tem filhos, ou seja, zero
filhos. Para cada filho de um nodo interno, nomeamos cada filho como filho da esquerda e filho da direita. Esses
filhos so ordenados de forma que o filho da esquerda venha antes do filho da direita.

A rvore binria suporta mais 3 mtodos adicionais:

Filho_da_esquerda(nodo): Retorna o filho da esquerda do nodo.


Filho_da_direita(nodo): Retorna o filho da direita do nodo.
Irmo(nodo): Retorna o irmo de um nodo

Caminhamento adicional para rvores binrias

Caminhamento interfixado: pode ser informalmente considerado como a visita aos nodos de uma rvore da esquerda
para a direita. Para cada nodo v, o caminhamento interfixado visita v aps todos os nodos da subrvore esqueda de v e
antes de visitar todos os nodos da subrvore direita de v.

Os elementos acessados por este


caminhamento formam a expresso:

2 x (a 1) + (3 x b)

Os parnteses foram colocados para facilitar.

O Caminhamento de Euler: sobre uma rvore binria T pode ser informalmente definido como um passeio ao redor
de T, no qual iniciamos pela raiz em direo ao filho da esquerda e consideramos as arestas de T como sendo paredes
que devemos sempre manter nossa esquerda. Cada nodo de T visitado trs vezes pelo caminhamento de Euler.

Prioridade das aes para efetuar o caminhamento:

Ao pela esquerda (antes do caminho sobre


a subrvore esquerda de v);
Ao por baixo (entre o caminhamento sobre
as duas subrvores de v);
Ao pela direita (depois do caminhamento
sobre a subrvore direita de v).

Propriedades de uma rvore binria

Seja T uma rvore binria (prpria) com n nodos e seja h a altura de T. Ento T tem as seguintes propriedades:

1. O nmero de nodos externos de T pelo menos h+1 e no mximo 2h.


2. O nmero de nodos internos de T pelo menos h e no mximo 2h 1.
3. O nmero total de nodos de T pelo menos 2h +1 e no mximo 2h+1 -1.
4. A profundidade de T pelo menos log(n+1) -1 e no mximo (n-1)/2.

25
rvores Binrias de Busca
Material retirado da referncia [2] e [3].

Uma rvore de pesquisa binria uma rvore binria em que todo n interno contm um registro, e, para cada n,
todos os registros com chaves menores esto na subrvore esquerda e todos os registros com chaves maiores esto na
subrvore direita.

Podemos usar uma rvore binria de pesquisa T para localizar um elemento com um certo valor x percorrendo para
baixo a rvore T. Em cada nodo interno, comparamos o valor do nodo corrente com o valor do elemento x sendo
pesquisado.
Se a resposta da questo for menor, ento a pesquisa continua na subrvore esquerda.
Se a resposta for igual, ento a pesquisa terminou com sucesso.
Se a resposta for maior, ento a pesquisa continua na subrvore direita.
Se encontrarmos um nodo externo (que vazio), ento a pesquisa terminou sem sucesso.

Ordem em que os 58
elementos foram
inseridos:
58,90,62,75,31,25, 31 90
12,42,36

25 42 62

12 36 75

A figura acima representa uma rvore binria de pesquisa que armazena inteiros. O caminho indicado pela linha azul
corresponde ao caminhamento ao procurar (com sucesso) 36. A linha pontilhada vermelha corresponde ao
caminhamento ao procurar (sem sucesso) por 70. Observe que o tempo de execuo da pesquisa em uma rvore
binria de pesquisa T proporcional altura de T.
Endereo N do pai

Estrutura para armazenar um nodo da rvore binria:


N da Informao N da
esquerda do n direita
(valor)

Exemplo para os valores inseridos na ordem:


1. Maria E1 Nil
2. Mnica E2 Maria E3
3. Daniela

Os nodos folha sem


valor so necessrios
para que a rvore seja E2 E3
E1 E1
prpria

E4 Daniela E5 E6 Mnica E7

E4 E2 E5 E2 E6 E3 E7 E3

Nil Nil Nil Nil Nil Nil Nil Nil

26
Algoritmo para pesquisar um valor em uma rvore binria de pesquisa:

Pesquisa( nodo, valor_pesquisado ) : Retorno


Inicio
se Nodo_eh_externo(Nodo) = verdadeiro ento
escreva( Erro: Valor procurado no est na rvore!);
pesquisa := nil
caso contrrio
Se valor_pesquisado < nodo.valor ento
pesquisa ( nodo.esquerda, valor_pesquisado )
caso contrrio se valor_pesquisado > nodo.valor ento
pesquisa ( nodo.direita, valor_pesquisado )
caso contrrio
pesquisa := nodo.valor;
Fim

Algoritmo para inserir um valor em uma rvore binria de pesquisa:

Insere( nodo, NovoValor ) O mtodo CriaNodoExterno cria um


Incio nodo externo (sem valor e sem
filhos)
se Nodo_eh_externo(nodo) = verdadeiro ento
CriaNodoExterno( nodo.esquerda )
CriaNodoExterno( nodo.direita )
Nodo.valor := NovoValor
caso contrrio
se NovoValor < nodo.valor ento
Insere ( nodo.esquerda , NovoValor)
caso contrrio se NovoValor > nodo.valor ento
Insere ( nodo.direita , NovoValor)
caso contrrio
escreva(O valor j existe na rvore.);
Fim;

27
Implementao de uma rvore Binria de Busca em C#

Endereo N do pai

N da Informao do n N da
esquerda (valor) direita

// classe para reprentar 1 Nodo na rvore


class Nodo
{
private Nodo no_pai = null;
private Nodo no_direita = null;
private Nodo no_esquerda = null;
private int valor = 0;

public int get_valor() { return valor; }

public void set_valor(int v) { valor = v; }

public void set_no_pai(Nodo no) { no_pai = no;}

public void set_no_direita(Nodo no) { no_direita = no; }

public void set_no_esquerda(Nodo no) { no_esquerda = no; }

public Nodo get_no_pai() { return no_pai; }

public Nodo get_no_direita() { return no_direita; }

public Nodo get_no_esquerda() { return no_esquerda; }


}

// classe da rvore de pesquisa binria


class ArvoreBin
{
private Nodo raiz = null; // raiz da rvore
private int qtde = 0; // qtde de nos internos
private string resultado = "";

public int qtde_nos_internos() // devolve a qtde de ns internos


{
return qtde;
}

public bool no_eh_externo(Nodo no) // verifica se um determinado Nodo externo


{
return (no.get_no_direita() == null) && (no.get_no_esquerda() == null);
}

public Nodo cria_No_externo(Nodo Nopai)// cria um Nodo externo


{
Nodo no = new Nodo();
no.set_no_pai(Nopai);
return no;
}

28
public void insere(int valor) // insere um valor int
{
Nodo no_aux;

if (qtde == 0)
{
// rvore vazia, devemos criar o primeiro Nodo, que ser a raiz
no_aux = new Nodo();
raiz = no_aux;
}
else
{
// localiza onde deve ser inserido o novo n.
no_aux = raiz;
while (no_eh_externo(no_aux) == false)
{
if (valor > no_aux.get_valor())
no_aux = no_aux.get_no_direita();
else
no_aux = no_aux.get_no_esquerda();
}
}
// este era um Nodo externo e portanto no tinha filhos.
// Agora ele passar a ter valor. Tambm devemos criar outros 2
// Nodos externos (filhos) para ele.
no_aux.set_valor(valor);
no_aux.set_no_direita(cria_No_externo(no_aux));
no_aux.set_no_esquerda(cria_No_externo(no_aux));
qtde++;
}

private void Le_Nodo(Nodo no)


{
if (no_eh_externo(no))
return;

Le_Nodo(no.get_no_esquerda());
resultado = resultado + " - " + Convert.ToInt32(no.get_valor());
Le_Nodo(no.get_no_direita());
}

// devolve um string com os elementos da rvore, em ordem crescente


public string listagem()
{
resultado = "";
Le_Nodo(raiz);
return resultado;
}
}

29
Interface com o Usurio:

Cdigo da interface com o usurio:

public partial class Form1 : Form


{
private ArvoreBin minhaArvore = new ArvoreBin();

public Form1()
{
InitializeComponent();
}

private void button1_Click(object sender, EventArgs e)


{
try
{
minhaArvore.insere(Convert.ToInt32(txtValor.Text));
listBox1.Items.Add("Inserido: " + txtValor.Text);
}
catch{
MessageBox.Show("Valor invlido! Digite apenas nmeros!");
}
txtValor.Clear();
txtValor.Focus();
}

private void button2_Click(object sender, EventArgs e)


{
listBox1.Items.Add(minhaArvore.listagem());
}

private void button3_Click(object sender, EventArgs e)


{
listBox1.Items.Add("Qtde: " + minhaArvore.qtde_nos_internos() );
}

private void button4_Click(object sender, EventArgs e)


{
Close();
}
}

30
Listas Simplesmente Encadeadas

Material retirado da referncia [2], [3] e [4].

Em uma lista simplesmente encadeada, cada elemento contm um apontador que aponta para a o elemento seguinte. Na
implementao de listas utilizando vetores, os dados ocupavam posies contguas da memria. Sendo assim, sempre
que inclumos ou apagamos um elemento no meio da lista, precisamos reorganizar os dados do vetor, o que
computacionalmente pode ser muito custoso. Nas listas simplesmente encadeadas, os dados no ocupam posies
contguas da memria, portando operaes de remoo e incluso so executadas muito mais rapidamente. Um
elemento de uma lista simplesmente encadeada pode ser definido como na figura abaixo:
DADOS

Endereo Prximo

Quando criamos uma lista utilizando apontadores, precisamos ter uma varivel que aponta sempre para o incio da lista.
Abaixo, temos um exemplo de uma lista simplesmente encadeada para armazenar nomes em ordem alfabtica:

Endereo do Ana Cludia Maria


Primeiro: E1 E1 E2 E2 E3 E3 NIL

Para incluir um novo elemento, por exemplo, o nome Daniela, devemos apenas alterar o apontador prximo do
elemento que est no endereo E2. Veja abaixo:

Endereo do Ana Cludia Maria


Primeiro: E1 E1 E2 E2 E4 E3 NIL

Daniela
E4 E3

Observe que a ordem dos endereos no importa. O que importa a ordem que eles esto encadeados!

O mesmo ocorre ao se remover um elemento da lista. Veja abaixo como ficaria a remoo do elemento Cludia:

Endereo do Ana Daniela Maria


Primeiro: E1
E1 E4 E4 E3 E3 NIL

Cludia
E2 E4

Listas Duplamente Encadea das


Material retirado da referncia [2], [3] e [4].

A diferena de uma lista duplamente encadeada para uma lista simplesmente encadeada que em uma lista duplamente
encadeada cada elemento contm um segundo apontador que aponta para o elemento que o antecede. Assim, voc no
precisa se preocupar mais com o incio da lista. Se voc tiver um apontador para qualquer elemento da lista, pode
encontrar o caminho para todos os outros elementos. Em uma lista duplamente encadeada, so necessrias variveis
para apontar para o incio e para o final da lista. Abaixo temos a representao de um elemento de uma lista duplamente
encadeada:

DADOS
Endereo
31
Anterior Prximo
Exemplo de uma lista duplamente encadeada para armazenar nomes em ordem alfabtica:

Alessandra Daniela Mnica


Endereo do Endereo do
Primeiro: E1 E1 E2 E3 ltimo: E3
NIL E2 E1 E3 E2 NIL

Para Inserir e Remover elementos, o processo semelhante ao apresentado na lista simplesmente encadeada. A
diferena que na lista duplamente encadeada necessrio tambm atualizar o campo anterior dos elementos.

Listas circulares
Material retirado da referncia [4].

So listas que possuem a caracterstica especial de ter, como sucessor do fim da lista, seu incio, ou melhor, o fim da lista
aponta para seu incio, formando um crculo que permite uma trajetria contnua na lista. Veja o processo na ilustrao
abaixo:
Endereo do Ana Cludia Maria
Primeiro: E1 E1 E2 E2 E3 E3 E1

32
Grafos
Material sobre grafos: [3], [4] e http://www.inf.ufsc.br/grafos/livro.html

Um grafo um conjunto de pontos, chamados vrtices (ou nodos ou ns), conectados por linhas, chamadas de arestas
(ou arcos). Dependendo da aplicao, arestas podem ou no ter direo, pode ser permitido ou no arestas ligarem um
vrtice a ele prprio e vrtices e/ou arestas podem ter um peso (numrico) associado. Se todas as arestas tm uma
direo associada (indicada por uma seta na representao grfica) temos um grafo dirigido, ou dgrafo. Se todas as
arestas em um grafo foram no-dirigidas, ento dizemos que o grafo um grafo no-dirigido. Um grafo que tem
arestas no-dirigidas e dirigidas chamado de grafo misto.

Vrtice

Aresta

Exemplo de um grafo no-dirigido com 6 vrtices e 7 arestas.

Rio de
So Paulo Janeiro

Braslia

Manaus
Cuiab

Exemplo de um grafo dirigido com 8 arestas e 5 vrtices.

Algumas definies sobre grafos:


Grau: nmero de setas que entram ou saem de um n.
Grau de entrada: nmero de setas que chegam em um n X, in(X).
Grau de sada: nmero de setas que saem de um n X, out(X).
Fonte: todo n, cujo grau de entrada 0(zero).
Sumidouro (poo): todo n, cujo grau de sada 0(zero).

33
Recursividade ou Recurso
Material retirado de:
http://pt.wikipedia.org/wiki/Recursividade (muito bom)
http://pt.wikipedia.org/wiki/Recursividade_%28ci%C3%AAncia_da_computa%C3%A7%C3%A3o%29 (timo)
http://www.di.ufpe.br/~if096/recursao/ (bom)
Referncia [1]

O que Recursividade: Uma Rotina ou Funo recursiva quando ela chama a si mesma, seja de forma direta ou
indireta.

Por exemplo, segue uma definio recursiva da ancestralidade de uma pessoa:


Os pais de uma pessoa so seus antepassados (caso base);
Os pais de qualquer antepassado so tambm antepassados da pessoa em considerao (passo recursivo).

Um outro exemplo simples poderia ser o seguinte:


Se uma palavra desconhecida vista em um livro, o leitor pode tomar nota do nmero da pgina e colocar em
uma pilha (que at ento est vazia). O leitor pode consultar esta nova palavra e, enquanto l o texto, pode
achar mais palavras desconhecidas e acrescentar no topo da pilha. O nmero da pgina em que estas palavras
ocorrem tambm so colocados no topo da pilha. Em algum momento do texto, o leitor vai achar uma frase ou
um pargrafo onde est a ltima palavra anotada e pelo contexto da frase vai descobrir o seu significado. Ento
o leitor volta para a pgina anterior e continua lendo dali. Paulatinamente, remove-se seqencialmente cada
anotao que est no topo da pilha. Finalmente, o leitor volta para a sua leitura j sabendo o significado da(s)
palavra(s) desconhecida(s). Isto uma forma de recurso.

Clculo do Fatorial sem Recurso, usamos apenas Clculo do Fatorial com Recurso direta
uma estrutura de repetio (iterador)
long fat_iterativo(int numero) long fat_recursivo(int numero)
{ {
long r=1; if (numero == 0)
return 1;
for (int i=2; i<= numero; i++) else if (numero >= 2)
{ return numero*fat_recursivo(numero-1);
r = r * i; else
} return numero;
return r; }
}

Teste de Mesa do Fatorial de 5: azul = ida(chamada recursiva) , vermelho = volta (retorno da funo recursiva)

Resposta = Fat_recursivo(5)
120
Fat_recursivo(5) = 5 * Fat_recursivo( 5 1 )
24
Fat_recursivo(4) = 4 * Fat_recursivo( 4 1 )
6
Fat_recursivo(3) = 3 * Fat_recursivo( 3 1 )
2
Fat_recursivo(2) = 2 * Fat_recursivo( 2 1 )
1
Fat_recursivo(1) = 1

Recurso versus Iterao

No exemplo do fatorial, a implementao iterativa tende a ser ligeiramente mais rpida na prtica do que a
implementao recursiva, uma vez que uma implementao recursiva precisa registrar o estado atual do processamento
de maneira que ela possa continuar de onde parou aps a concluso de cada nova excecuo subordinada do
procedimento recursivo. Esta ao consome tempo e memria.
Existem outros tipos de problemas cujas solues so inerentemente recursivas, j que elas precisam manter registros de
estados anteriores. Um exemplo o percurso de uma rvore;

34
Toda funo que puder ser produzida por um computador pode ser escrita como funo recursiva sem o uso de iterao;
reciprocamente, qualquer funo recursiva pode ser descrita atravs de iteraes sucessivas. Todos altoritmo
recursivo pode ser implementado iterativamente com a ajuda de uma pilha, mas o uso de uma pilha, de certa
forma, anula as vantagens das solues iterativas.

Tipos de Recursividade:

Direta: Quando chama a si mesma, quando dada situao requer uma chamada da prpria Rotina em execuo para si
mesma. Ex: O exemplo de fatorial recursivo dado acima.

Indireta: Funes podem ser recursivas (invocar a si prprias) indiretamente, fazendo isto atravs de outras funes:
assim, "P" pode chamar "Q" que chama "R" e assim por diante, at que "P" seja novamente invocada.

Ex:
double Calculo( double a,b ) double Divide( double a, b )
{ {
return Divide(a,b) + a + b; if (b == 0)
Aqui ocorre a
} b = Calculo(a, b + a); recursividade
indireta!
return a/ b;
}

Em cauda: As funes recursivas em cauda formam uma subclasse das funes recursivas, nas quais a chamada
recursiva a ltima instruo a ser executada. Por exemplo, a funo a seguir, para localizar um valor em uma lista
ligada recursiva em cauda, por que a ltima coisa que ela faz invocar a si mesma:

Ex: Vamos usar como exemplo o algoritmo para pesquisar um valor em uma rvore binria de pesquisa:
Pesquisa( nodo, valor_pesquisado ) : Retorno
Inicio
se Nodo_eh_externo(Nodo) = verdadeiro ento
escreva( Erro: Valor procurado no est na rvore!);
pesquisa := nil
caso contrrio
Se valor_pesquisado < nodo.valor ento
pesquisa ( nodo.esquerda, valor_pesquisado )
caso contrrio se valor_pesquisado > nodo.valor ento
pesquisa ( nodo.direita, valor_pesquisado )
caso contrrio
pesquisa := nodo.valor;
Fim

Note que a funo fatorial usada como exemplo na seo anterior no recursiva em cauda, pois depois que
ela recebe o resultado da chamada recursiva, ela deve multiplicar o resultado por VALOR antes de retornar para
o ponto em que ocorre a chamada.

Qual a desvantagem da Recurso?


Cada chamada recursiva implica em maior tempo e espao, pois, toda vez que uma Rotina chamada, todas as variveis
locais so recriadas.

Qual a vantagem da Recurso?


Se bem utilizada, pode tornar o algoritmo: elegante, claro, conciso e simples. Mas, preciso antes decidir sobre o uso da
Recurso ou da Iterao.

35
Ordenao
Material retirado de:
Referncia [2], [3]
http://pt.wikipedia.org/wiki/Algoritmo_de_ordena%C3%A7%C3%A3o (com exemplos em vrias linguagens)

Ordenar corresponde ao processo de rearranjar um conjunto de objetos em uma ordem ascendente ou descendente. O
objetivo principal da ordenao facilitar a recuperao posterior de itens do conjunto ordenado. Imagine como seria
difcil utilizar um catlogo telefnico se os nomes das pessoas no estivessem listados em ordem alfabtica!
Existem diversos mtodos para realizar ordenao. Iremos estudar aqui dois dos principais mtodos.

Bubble sort

O bubble sort, ou ordenao por flutuao (literalmente "por bolha"), um algoritmo de ordenao dos mais simples. A
ideia percorrer o vector diversas vezes, a cada passagem fazendo flutuar para o topo o menor elemento da sequncia.
Essa movimentao lembra a forma como as bolhas em um tanque de gua procuram seu prprio nvel, e disso vem o
nome do algoritmo.

No melhor caso, o algoritmo executa (n2) / 2 operaes relevantes. No pior caso, so feitas 2n2operaes. No caso
mdio, so feitas (5n2) / 2 operaes. A complexidade desse algoritmo de Ordem quadrtica. Por isso, ele no
recomendado para programas que precisem de velocidade e operem com quantidade elevada de dados.
O algoritmo pode ser descrito em pseudo-cdigo como segue abaixo. V um VECTOR de elementos que podem ser
comparados e n o tamanho desse vector.

BUBBLESORT (V[], n)
1 houveTroca := verdade # uma varivel de controle
2 enquanto houveTroca for verdade faa
3 houveTroca := falso
4 para i de 1 at n-1 faa
5 se V[i] vem depois de V[i + 1]
6 ento troque V[i] e V[i + 1] de lugar e
7 houveTroca := verdade

Implementao em C# utilizando For e While


class C_BubbleSort class C_BubbleSort
{ {
static int[] Ordena_BubbleSort(int[] vetor) static int[] Ordena_BubbleSort(int[] vetor)
{ {
int aux; int aux;
bool houvetroca;
for (int i = vetor.Length - 1; i >= 1; i--)
{ do
for (int j = 0; j <= i - 1; j++) {
{ houvetroca = false;
if (vetor[j] > vetor[j + 1]) for (int j = 0; j <= vetor.Length - 2; j++)
{ {
//efetua a troca de valores if (vetor[j] > vetor[j + 1])
aux = vetor[j]; {
vetor[j] = vetor[j + 1]; //efetua a troca de valores
vetor[j + 1] = aux; houvetroca = true;
} aux = vetor[j];
} vetor[j] = vetor[j + 1];
} vetor[j + 1] = aux;
return vetor; }
} }
}
static void Main(string[] args) while (houvetroca == true);
{
int[] dados = new int[10]; return vetor;
}
for (int i = 0; i < dados.Length; i++)
{ static void Main(string[] args)
Console.WriteLine("Informe um nmero"); {
dados[i]=Convert.ToInt16(Console.ReadLine()); int[] dados = new int[10];
}
for (int i = 0; i < dados.Length; i++)
Ordena_BubbleSort(dados); {
Console.WriteLine("Informe um nmero");
Console.WriteLine("\n\nDados ordenados:"); dados[i]= Convert.ToInt16(Console.ReadLine());
for (int i = 0; i < dados.Length; i++) }
{
Console.WriteLine( dados[i] ); Ordena_BubbleSort(dados);
}
Console.ReadKey(); Console.WriteLine("\n\nDados ordenados:");

36
} for (int i = 0; i < dados.Length; i++)
} {
Console.WriteLine(dados[i]);
}

Console.ReadKey();
}
}

Quicksort

O algoritmo Quicksort um mtodo de ordenao muito rpido e eficiente, inventado por C.A.R. Hoare em 1960,
quando visitou a Universidade de Moscou como estudante. Foi publicado em 1962 aps uma srie de refinamentos.

O Quicksort adota a estratgia de diviso e conquista. Os passos so:

1. Escolha um elemento da lista, denominado piv (de forma randmica);


2. Rearranje a lista de forma que todos os elementos anteriores ao piv sejam menores ou iguais a ele, e todos os
elementos posteriores ao piv sejam maiores ou iguais a ele. Ao fim do processo o piv estar em sua posio
final. Essa operao denominada partio;
3. Recursivamente ordene a sublista dos elementos menores e a sublista dos elementos maiores;

A base da recurso so as listas de tamanho zero ou um, que esto sempre ordenadas. O processo finito pois a cada
iterao pelo menos um elemento posto em sua posio final e no ser mais manipulado na iterao seguinte.

Complexidade

O(n lg2 n) no melhor caso e no caso mdio


O(n2) no pior caso;

Implementaes
Algoritmo em portugus estruturado

proc quicksort (x:vet[n] int; ini:int; fim:int; n:int)


var
int: i,j,y,aux;

incio
i <- ini;
j <- fim;
y <- x[(ini + fim) div 2];
repete
enquanto (x[i] < y) faa
i <- i + 1;
fim-enquanto;
enquanto (x[j] > y) faa
j <- j - 1;
fim-enquanto;
se (i <= j) ento
aux <- x[i];
x[i] <- x[j];
x[j] <- aux;
i <- i + 1;
j <- j - 1;
fim-se;
at_que (i >= j);
se (j > ini) ento
exec quicksort (x, ini, j, n);
fim-se;
se (i < fim) ento
exec quicksort (x, i, fim, n);
fim-se;
fim.

37
38
Implementao em C#

Mtodo que efetua a ordenao


static void QuickSort(int[] vetor, int esq, int dir)
{
int pivo, aux, i, j;
int meio;

i = esq;
j = dir;

meio = (int)((i + j) / 2);


pivo = vetor[meio];

do
{
while (vetor[i] < pivo) i = i + 1;
while (vetor[j] > pivo) j = j - 1;

if (i <= j)
{
aux = vetor[i];
vetor[i] = vetor[j];
vetor[j] = aux;
i = i + 1;
j = j - 1;
}
}
while (j > i);

if (esq < j) QuickSort(vetor, esq, j);


if (i < dir) QuickSort(vetor, i, dir);
}

Mtodo MAIN que solicita os nmeros e chama o mtodo quicksort para ordenar.

static void Main(string[] args)


{
int[] dados = new int[10];

Console.WriteLine("Entre com {0} nmeros", dados.Length);


for (int i = 0; i < dados.Length; i++)
{
dados[i] = Convert.ToInt16(Console.ReadLine());
}

QuickSort(dados, 0, dados.Length - 1);

Console.WriteLine("\n\nNmeros ordenados:\n");
for (int i = 0; i < dados.Length; i++)
{
Console.WriteLine ( dados[i]);
}

Console.ReadLine();
}

39
Pesquisa em Memria Primria
Pesquisa seqencial

Retirado de : http://pucrs.campus2.br/~annes/alg3_pesqseq.html
[2]

O mtodo de pesquisa mais simples que existe funciona da seguinte forma: a partir do primeiro registro, pesquise
sequencialmente at encontrar a chave procurada; ento pare. A complexidade desta pesquisa no pior caso n, onde n
o tamanho total do vetor sendo pesquisado.

{Algoritmo em Pascal}
Function PesquisaSequencial(vetor : array of Integer, chave,n : integer) : integer;
Var i: integer;
Achou : boolean;
Begin
PesquisaSequencial := -1; { significa que no encontrou }
Achou := false;
i:= 1;
Repeat
If vetor[i] = chave then
Begin
Achou := true;
PesquisaSequencial := i;
End;
i := i + 1;
Until (i > n) or (achou = true);
End;

Dado o exemplo:

5 7 1 9 3 21 15 99 4 8

No exemplo acima, seriam necessrias 7 iteraes para encontrar o valor 15.

Pesquisa Binria

Retirado de : http://pt.wikipedia.org/wiki/Pesquisa_bin%C3%A1ria
[2]

A pesquisa ou busca binria ( em ingls binary search algorithm ou binary chop ) um algoritmo de busca em vetores
que requer acesso aleatrio aos elementos do mesmo. Ela parte do pressuposto de que o vetor est ordenado, e
realiza sucessivas divises do espao de busca comparando o elemento buscado (chave) com o elemento no meio do
vetor. Se o elemento do meio do vetor for a chave, a busca termina com sucesso. Caso contrrio, se o elemento do meio
vier antes do elemento buscado, ento a busca continua na metade posterior do vetor. E finalmente, se o elemento do
meio vier depois da chave, a busca continua na metade anterior do vetor. A complexidade desse algoritmo da ordem
de log2 n, onde n o tamanho do vetor de busca.

Um pseudo-cdigo recursivo para esse algoritmo, dados V o vetor com elementos comparveis, n seu tamanho e e o
elemento que se deseja encontrar:

BUSCA-BINRIA (V[], inicio, fim, e}


i recebe o ndice no meio de inicio e fim
se V[i] igual a e
ento devolva o ndice i # encontrei e
seno se V[i] vem antes de e
ento faa a BUSCA-BINRIA(V, i+1, fim, e)
seno faa a BUSCA-BINRIA(V, inicio, i-1, e)

40
{Algortmo em Pascal}
function BuscaBinaria (Vetor: array of string; Chave: string; Dim: integer): integer;
var inicio, fim: integer; {Auxiliares que representam o inicio e o fim do vetor analisado}
meio: integer; {Meio do vetor}
begin
fim := Dim; {O valor do ltimo ndice do vetor}
inicio := 1; {O valor do primeiro ndice do vetor}
repeat
meio := (inicio+fim) div 2;
if (Chave = vetor[meio]) then
BuscaBinaria := meio;
if (Chave < vetor[meio]) then
fim:=(meio-1);
if (Chave > vetor[meio]) then
inicio:=(meio+1);
until (Chave = Vetor[meio]) or (inicio > fim);
if (Chave = Vetor[meio]) then
BuscaBinaria := meio
else
BuscaBinaria := -1; {Retorna o valor encontrado, ou -1 se a chave nao foi encontrada.}
end;

rvores de pesquisa

Vide rvores Binrias de Busca.

41

Você também pode gostar