Logo Passei Direto
Buscar
Material
páginas com resultados encontrados.
páginas com resultados encontrados.
left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

left-side-bubbles-backgroundright-side-bubbles-background

Crie sua conta grátis para liberar esse material. 🤩

Já tem uma conta?

Ao continuar, você aceita os Termos de Uso e Política de Privacidade

Prévia do material em texto

Algoritmo e
complexidade
Algoritmo: 
É a estrutura básica para
a resolução de problemas
computacionais. Como uma
receita de bolo: tem um
conjunto de instruções finitas,
que devem ser seguidas em
determinada ordem para se
resolver um problema. 
Complexidade
Quanto maior for a variedade de
problemas a serem resolvidos,
maior será a complexidade do
algoritmo.
 problemão 
 
 probleminhas
sub-rotinas
Sub-rotina
Também chamada de subprograma,
é um bloco de instrução que
realiza tarefas específicas. É
utilizada para diminuir a
complexidade de um problema,
subdividindo-o em pequenas
tarefas (mini problemas). 
Objetivos da Sub-rotina
Dividir e estruturar um
algoritmo em partes logicamente
coerentes;
Testar os trechos de código
separadamente com maior
facilidade;
Aumentar a legibilidade de um
programa;
Evitar a repetição de sequências
de comandos em vários locais do
código.
Vantagens da Sub-rotina
Clareza e legibilidade no
algoritmo;
Construção independente;
Testes individualizados;
Simplificação da manutenção;
Reaproveitamento de algoritmos.
Programação Estruturada
Paradigma da programação utilizado
para a decomposição de problemas
(subdividir problemões em
probleminhas). Nesta abordagem, o
método mais adequado para decompor
problemas é o TOP-DOWN. 
MÉTODO TOP-DOWN
Permite que o programa tenha uma
estrutura parecida com a de um
ORGANOGRAMA. O problema é dividido
em “refinamentos sucessivos” e as
partes que derivam dessa divisão
chamam-se “módulos” ou
“subalgoritmos”. 
Divida o problema em suas partes
principais;
1.
Analise a divisão para garantir
a coerência;
2.
Se alguma parte ainda estiver
complexa, volte para o passo 1.
3.
Analise o resultado para
garantir o entendimento e a
coerência. 
4.
Refinamentos Sucessivos
Declarando sub-rotinas
Uma sub-rotina é um bloco com início
e fim, sendo identificada por um
nome, pelo qual será referenciada em
qualquer momento e em qualquer parte
do programa, assim como uma FUNÇÃO
ou MÉTODO.
Sintaxe da sub-rotina:
 //sub-rotina (método) que calcula o dobro de um
número
public static int calcularDobro(int numero) {
return numero * 2; 
}
public static void main(String[ ] args) {
//chamada da sub-rotina
int resultado = calcularDobro(5);
System.out.println(“O dobro de 5 é: ” + resultado); 
}
Várias sub-rotinas
Um programa pode ter mais de uma
sub-rotina. Depois de executar a
primeira sub-rotina, o controle
volta para o programa principal. Em
seguida, a segunda sub-rotina é
executada e, ao fim, o programa
principal retoma o controle. 
Fluxo de execução:
Parâmetros são os valores que uma
sub-rotina pode receber antes de
iniciar sua execução. Através dos
parâmetros, é possível tornar uma
sub-rotina mais genérica e,
portanto, mais reutilizável.
EX: em vez de um método que calcula
a tabuada de 8, um método que
calcula a tabuada de qualquer número
digitado pelo usuário. 
Parâmetros e sub-rotinas
Passagem por valor
Na passagem de parâmetros por valor,
o valor da variável na chamada é
copiado para a variável da função. 
As alterações feitas dentro da
função ou método NÃO são salvas na
variável original.
EXEMPLO:
public class ExemploPassagemPorValor {
 public static void main(String[] args) {
 int numero = 10;
 System.out.println("Antes de chamar o
método: " + numero);
 modificarValor(numero);
 System.out.println("Depois de chamar o
método: " + numero);
 }
 public static void modificarValor(int valor) {
 valor = 20;
 System.out.println("Dentro do método: " +
valor);
 }
}
Passagem por referência
Na passagem de parâmetros por
referência, os endereços de memória
das variáveis são acessados
diretamente.
As alterações feitas dentro da
função ou método também ocorrem na
variável original.
EXEMPLO:
class ExemploObjeto {
 int valor;
 ExemploObjeto(int valor) {
 this.valor = valor;
 }
}
public class ExemploPassagemPorReferencia {
 public static void main(String[] args) {
 ExemploObjeto obj = new ExemploObjeto(10);
 System.out.println("Antes de chamar o
método: " + obj.valor);
 modificarObjeto(obj);
 
continuação...
System.out.println("Depois de chamar o método: "
+ obj.valor);
 }
 public static void modificarObjeto(ExemploObjeto
objeto) {
 objeto.valor = 20;
 System.out.println("Dentro do método: " +
objeto.valor);
 }
}
OBRIGATÓRIOS NUMA FUNÇÃO:
Tipo de retorno
Nome da função
Corpo da função
NÃO OBRIGATÓRIOS:
Parâmetros
OBSERVAÇÕESOBSERVAÇÕES
Uma Função SEMPRE retorna um valor.
OBSERVAÇÕESOBSERVAÇÕES
Procedimento NÃO retorna valor.
Os tipos primitivos (int, char,
boolean, float...) não são
suficientes para representar todos
os tipos de informação, nem servem
para guardar mais de uma informação. 
Veremos agora que os tipos
primitivos podem ser usados para
construir estruturas mais complexas,
como vetores (arrays), matrizes e
registros. 
GUARDANDO + INFO
Conhecido também como: 
Variáveis indexadas
Variáveis compostas
Variáveis subscritas
Arranjos
Vetores
Tabelas em memória
Arrays
Arranjo Unidimensional
Homogêneo
Um vetor é um tipo de variável
homogênea que repete determinado
dado N vezes. Ou seja, é um arranjo
de elementos armazenados na memória
principal, com várias colunas e
APENAS uma linha (arranjo
unidimensional). 
VETOR
EXEMPLO:
Segue abaixo um exemplo de vetor com 10
elementos (ou seja, 10 variáveis), todas com
o mesmo nome, porém diferenciadas pela
posição no arranjo (indicada por um índice
que inicia em 0):
Variáveis em azul
(cada uma com um valor)
Índice abaixo das variáveis
(começa em 0)
EXEMPLO:
Representamos o vetor do exemplo anterior em
ALGORITMO da seguinte forma:
Após o igual, 
vem o valor da
variável
Posição da variável (índice) 
entre colchetes [ ]
DECLARANDO VETORES
Declaramos um vetor assim em Java:
tipo[ ] nomeDoVetor; 
Depois de declarar um vetor, é possível
inicializá-lo especificando o seu tamanho
(ou seja, quantos elementos ele terá): 
int[ ] numerosInteiros = new int[ 5 ]; 
Ou, então, se já souber os valores,
inicializando-o diretamente:
int[ ] numerosInteiros = {1, 2, 3, 4, 5}; 
Tipo de variável heterogênea que
trabalha com vários dados de tipos
diferentes (campos) em uma mesma
estrutura. Os elementos de um
registro são alocados em posições de
memória adjacentes. 
REGISTRO
DECLARANDO REGISTROS
Declaramos um registro assim em Pascal:
type 
Pessoa = record 
nome: string[50]; 
idade: integer; 
end;
Declarando uma variável do tipo registro:
 pessoa1: Pessoa;
Atribuindo valores aos campos do registro:
pessoa1.nome := 'João'; 
pessoa1.idade := 30;
Exibindo os valores:
writeln('Nome: ', pessoa1.nome); 
writeln('Idade: ', pessoa1.idade); 
end.
Tipo especial de variável que serve
para guardar o endereço de memória
de outra variável (já declarada
anteriormente no programa). Permite
acessar uma variável de forma
indireta. 
PONTEIRO
Vantagens do Ponteiro
Permite fazer a passagem de
parâmetros por referência;
Pode ser usado no lugar de uma
matriz;
Manipula os elementos de uma
matriz mais facilmente;
Permite criar estruturas de
dados mais complexas (listas
encadeadas e árvores binárias);
Aloca e desaloca memória
dinamicamente;
Passa para uma função o endereço
de outra função. 
DECLARANDO PONTEIROS
Declaramos um ponteiro assim em C:
int main() { 
int valor = 10; // Declara uma variável int
int *ponteiro; // Declara um ponteiro int 
Modificando o valor usando o ponteiro:
Exibindo os valores:
 *ponteiro = 20; 
printf("Novo valor da variável 'valor': %d\n",
valor); 
printf("Novo valor armazenado no ponteiro:
%d\n", *ponteiro); 
return 0;
 }
Especificá-lo através de suas
propriedades;
1.
Definir sua arquitetura por meio
da estrutura de dados;
2.
Indicar o seu nível de
complexidade, considerando o seu
tempo de execução e o espaço
ocupado na memória;3.
Implementá-lo em uma linguagem
de programação;
4.
Executar testes. 5.
Definindo um algoritmo
Desempenho;
Simplicidade;
Clareza;
Segurança;
Funcionalidade;
Modularidade;
Interface amigável. 
Características de um
(bom) algoritmo
É uma característica fundamental
para os algoritmos. Um algoritmo
correto é aquele que gera a saída
esperada para qualquer conjunto de
entradas válidas. Após gerar a saída
esperada, o algoritmo também deve
terminar sua execução. 
Corretude
A eficiência de um algoritmo pode se
associar à:
Quantidade de armazenamento que
utiliza;
Quantidade de tráfego que gera
em uma rede de computadores;
Quantidade de dados que precisa
mover do/para o disco.
Eficiência
Tempo de execução
Tamanho da entrada
 a ser processada
Eficiência =
Método de Cramer x Método de Gauss
Ambos os algoritmos calculam o tempo de
operação de um computador real, para avaliar
sua eficiência. 
Cramer(n!) x Gauss(n³)
+ simples + complexo
inviável para
entradas maiores 
Crescimento
estável
O desempenho de um algoritmo deve
ser desassociado do desempenho da
máquina que o executará. A análise
de um algoritmo se baseia em 2 tipos
de complexidade:
1) Espacial: quanto de memória usa.
II) Temporal: 
 -> Tempo de execução
 -> Número de instruções necessárias
para a execução.
Análise de algoritmos
Serve para prever quanto tempo um
algoritmo vai demorar para terminar
sua execução à medida que o problema
fica maior. Aqui, não nos
preocupamos com números exatos, mas
com a tendência: se o tempo de
execução vai dobrar, triplicar etc. 
Assim sendo, considerando-se um
algoritmo com tamanho de entrada n,
a eficiência de um algoritmo é
definida por uma função f(n) e a
análise assintótica descreve a
eficiência do algoritmo quando n
torna-se grande. 
 
Análise Assintótica
É a forma matemática de se prever o
pior caso de cada algoritmo, isto é,
a configuração de código que demanda
o maior número de operações para a
solução do problema. 
LEMBRE-SE: a análise da complexidade
de um algoritmo é SEMPRE feita na
visão do pessimista, ou seja, na
análise do pior caso apresentado ao
algoritmo.
A notação O
Melhor caso:
O(n): tempo de execução proporcional
ao tamanho da lista. 
EX: se n = 10, levará 10 unidades de
tempo para ser executado.
Pior caso:
O(n²): se n dobrar, o tempo de
execução vai multiplicar por 4. 
EX: se n = 10 levava 10 unidades de
tempo para ser executado, n = 20
pode demorar cerca de 40 unidades (4
vezes mais que o tempo de n = 10). 
Exemplos de notação O
Operações aritméticas;
Comparações;
Atribuições;
Resolver um ponteiro ou
referência;
Indexação em um arranjo;
Chamadas/retornos de funções e
métodos.
São as operações primitivas usadas
pela máquina para realizar certas
atividades. 
Passos Básicos
OPERAÇÕES BÁSICAS
É baseada na quantidade de passos
básicos executados para se encontrar
a solução de um problema.
Um algoritmo geralmente tem
componentes que são formados por
passos básicos, os quais dão sua
contribuição para a complexidade do
algoritmo. 
 
Complexidade do
algoritmo
Uma componente CONJUNTIVA de um
algoritmo é a parte do código que a
gente tem certeza que será executada
toda vez que executarmos o
algoritmo, sem exceção. 
Desempenho do algoritmo = 
SOMA das componentes conjuntivas.
Pior caso = SOMA do pior caso de
cada uma das conjuntivas.
Componente Conjuntiva
Uma componente DISJUNTIVA de um
algoritmo é a parte do código que
pode ou não ser executada, a
depender dos valores colocados no
algoritmo. 
Desempenho do algoritmo = 
considerar a MAIOR COTA superior
entre as duas componentes.
Pior caso = pior caso de execução
entre as duas disjuntivas.
Componente Disjuntiva
Quando analisamos a complexidade de
um algoritmo, os termos de ordem
mais alta “absorvem” os menores, ou
seja, os termos que crescem mais
devagar se tornam irrelevantes
quando o tamanho da entrada (n) é
muito grande. 
Princípio da absorção
EX: imagine um algoritmo com tempo de execução
descrito por f(n) = 3n² + 5n + 10.
Temos 3 termos: 3n² (quadrático), 5n (linear) e 10
(constante). 
Quando n aumenta bastante, o termo 3n² cresce
muito mais rápido que os outros, tornando 5n e 10
irrelevantes (ele os absorve). Assim, dizemos que a
complexidade do algoritmo é O(n²). 
Refere-se ao comportamento de um
algoritmo quando o tamanho da
entrada (n) cresce infinitamente. É
o limite superior de quanto tempo ou
recursos o algoritmo vai precisar à
medida que o número de dados cresce. 
Máximo Assintótico
EX: se temos dois algoritmos:
O(n) --> complexidade linear
O(n²) --> complexidade quadrática
Quando n aumenta muito, o algoritmo O(n²)
crescerá bem mais rapidamente que o O(n). Assim
sendo, O(n²) possui um máximo assintótico MAIOR
que O(n). 
Referem-se ao pior caso possível
para o tempo de execução ou o uso de
recursos de um algoritmo. Significa
que olharemos o cenário mais
desfavorável, em que o algoritmo
levaria o maior tempo para terminar
ou consumiria o máximo de memória. 
Complexidades
Pessimistas
ATRIBUIÇÃO: a complexidade num
algoritmo desse tipo indica quanto
tempo leva para realizar uma
atribuição de valor em um programa.
COMPLEXIDADE: O(n) tempo constante. 
Ou seja, atribuiu executou. 
Complexidades
Pessimistas
REPETIÇÃO: a complexidade num
algoritmo desse tipo indica quanto
tempo leva para executar estruturas
de repetição (loops).
COMPLEXIDADE: O(n) tempo constante. 
 
Complexidades
Pessimistas
REPETIÇÃO ANINHADA: a complexidade
num algoritmo desse tipo indica
quanto tempo leva para executar
estruturas de repetição (loops), uma
dentro da outra.
COMPLEXIDADE: O(n²) quadrática. 
 
Complexidades
Pessimistas
CONDIÇÃO: a complexidade num
algoritmo desse tipo indica quanto
tempo ele leva para executar, a
depender das condições que verifica.
COMPLEXIDADES: 
O(1)-> CONDIÇÃO SIMPLES (IF) 
O(n)-> CONDIÇÃO COMPOSTA (IF-ELSE) 
 
Complexidades
Pessimistas
CHAMADAS ÀS SUB-ROTINAS: a
complexidade num algoritmo desse
tipo indica quanto tempo ele leva
para chamar métodos e/ou funções.
COMPLEXIDADE: O(n) tempo constante. 
 
Complexidades
Pessimistas
Técnica em que uma função chama a si
mesma para resolver um problema.
Pode ser usada para definir uma
sequência de objetos, funções de
objetos e conjuntos. 
Recursividade
Método de resolução de problemas que
envolve quebrar um problema em
pequenas partes, até chegar num
tamanho em que possa ser resolvido
normalmente. Em geral, a recursão
abrange uma função que chama a si
mesma (função recursiva). 
Recursão
Função que quebra o problema em
pequenas partes e se chama para
resolvê-las (chamadas recursivas).
Depois que encontra uma solução
direta para o problema, ela encerra
sua execução (caso base). 
EX: fatorial de um número n 
Função recursiva
Caso base: Se n = 1, então n! = 1
Chamadas recursivas: 
se n > 1, então n! = n(n - 1)!
A função que calcula o fatorial de n continua
chamando a si mesma com n - 1, até que atinja o caso
base (n = 1). 
Também chamada de DEFINIÇÃO
INDUTIVA, é uma maneira de definir
um conceito simples ou estrutura,
passo a passo, começando com um caso
básico e, em seguida, estabelecendo
regras para lidar com casos mais
complexos, a partir dos casos mais
simples. 
Definição Recursiva
Este princípio é utilizado para
provar a correção de algoritmos
recursivos ou para demonstrar que
uma propriedade é válida para todas
as entradas de um determinado
problema. 
Indução matemática
Caso base da indução: provar que um
valor é verdadeiro para o menor valor
possível, em geral, n = 0 ou n = 1. 
Passo Indutivo: assumir que a afirmação
é válida para um valor qualquer n = x 
Lista de objetos enumerados em
determinada ordem. Uma sequência
recursiva explicita seu(s)
primeiro(s) valor(es) e define
outros valores na sequência, em
termos dos valores iniciais.
EX: sequência de potências de 2Sequência
a ^ 0 = 1
a ^ n = 2 ^ n, tal que n = {0, 1, 2, 3, 4...}
É a adição de uma sequência de
quaisquer tipos de números
(parcelas) usando-se uma notação. 
Imagine que você queira somar os
números de 1 a 5. Podemos
representar essa soma assim: 
Somatório
i = representação do número
i = 1 representa o valor inicial de i
O 5 (em cima do Sigma) é o valor final de i
O i (na frente do Sigma) é a fórmula ou valor 
da soma a cada passo. 
Estrutura que possui elementos
finitos e ordenados.
Conjuntos
Caso base dos conjuntos: especificar
uma coleção inicial de elementos. 
Passo recursivo dos conjuntos: criar
regras para formar novos elementos a
partir daqueles que já estão no conjunto. 
Algoritmo Recursivo
x
Algoritmo Iterativo
Recursivo Iterativo
Resolve um problema
dividindo-o em problemas
menores. O algoritmo
chama a si mesmo com
esses subproblemas até
que atinja um caso base,
onde o problema é
simples o suficiente
para ser resolvido sem
chamadas recursivas. 
Resolve um problema
usando estruturas de
repetição, como loops
(for, while). Ele executa
repetidamente um bloco de
código até que uma
condição de término seja
atendida. Em vez de
chamar a si mesmo, ele
controla o problema
através de variáveis. 
3 leis da Recursão
LEI I
Um algoritmo recursivo deve ter um CASO
BÁSICO (ou seja, um ponto onde a função será
encerrada, porque o problema já foi
resolvido).
***CASO BÁSICO DE FUNÇÃO FATORIAL --> n == 0 
3 leis da Recursão
LEI II
Um algoritmo recursivo deve mudar o seu
estado e se aproximar do caso básico. 
(os dados devem ser alterados tendo em vista
diminuir os problemas, para resolvê-los
melhor. 
3 leis da Recursão
LEI III
Um algoritmo recursivo deve chamar a si
mesmo, recursivamente. 
Na recursão linear, a função faz uma única
chamada recursiva a cada passo do algoritmo.
Por isso, a recursão linear é a mais
adequada para encontrar o elemento máximo em
uma lista.
Tipos de Recursividade
Recursão linear
Aqui, a chamada recursiva é a última
operação a ser realizada pela função. Isto
é, após a chamada recursiva, a função não
faz mais nada, apenas retorna o resultado.
FUNÇÃO RECURSIVA EM CAUDA --> 
RESULTADO FINAL DA CHAMADA RECURSIVA 
= 
RESULTADO FINAL DA FUNÇÃO 
Tipos de Recursividade
Recursão em cauda
Tipos de Recursividade
Recursão Indireta
É quando uma função invoca a si mesma
através de outra(s) função(ões). 
FUNÇÃO RECURSIVA INDIRETA --> P chama Q, que chama R,
que chama P. 
Tipos de Recursividade
Recursão Aninhada
É quando uma função recursiva pode receber
um argumento que inclui outra função
recursiva. 
Desvantagens
Vantagens
Função Recursiva
Torna o código mais simples e fácil de
entender;
Código menor e mais elegante;
Útil para problemas com subdivisões
repetidas.
Consome muita memória e corre o risco de
stack overflow (quando há excesso de
dados a serem armazenados);
Depuração mais complexa;
Processamento mais lento (por causa das
chamadas de função).
***A quantidade de chamadas recursivas deve ser finita,
pois funções recursivas utilizam bastante memória. 
Desvantagens
Vantagens
Função Iterativa
Uso da memória mais eficiente;
Processamento mais rápido;
Fácil de depurar e testar.
Solução mais difícil e menos intuitiva;
Torna o código maior e mais complexo;
Escrita menos elegante do código.
Método de organizar um conjunto de objetos
em uma ordem (ascendente ou descendente).
Colocar um conjunto de dados em determinada
ordem predefinida permite que o acesso a
eles ocorra de maneira mais eficiente. 
É feita usando-se como base uma CHAVE DE
ORDENAÇÃO (campo do item, que serve para
comparação). É por meio dessa chave que
podemos saber se um elemento está à frente
ou não de outros no conjunto de dados. 
Processo de Ordenação
Ordenação
Aquele que coloca os elementos de dada
sequência em uma ordem predefinida. 
Algoritmos de ordenação interna (in-place): o
conjunto de dados é pequeno e cabe todo na memória
principal. Os dados a se ordenar estão em um VETOR
ou em uma TABELA, e a ordenação é feita por um
campo chamado CHAVE, que identifica cada elemento
do vetor que forma a tabela.
Algoritmos de ordenação externa: o conjunto de
dados NÃO cabe na memória principal. Dados são
armazenados como registros, podendo ser acessados
sequencialmente ou em blocos. 
Processo de Ordenação
Algoritmo de ordenação
MÉTODO: ORDENAÇÃO POR INSERÇÃO
INSERTION SORT (inserção direta): pega um elemento
de cada vez, inserindo-o em seu devido lugar (no
array). É como uma mão de cartas no baralho, que a
gente vai ordenando de forma crescente, uma por
uma. 
SHELL SORT: divide o conjunto de dados em
sublistas, escolhendo um intervalo (GAP) que
determina a distância entre os elementos que serão
comparados. Para cada sublista, ocorre a inserção
direta, mas comparando e trocando elementos que
estão distantes (de acordo com o valor do GAP). O
valor do GAP reduz a cada iteração (até chegar a 1)
e o processo continua até tudo estar ordenado. 
Processo de Ordenação
Tipos de Algoritmo de ordenação
MÉTODO: ORDENAÇÃO POR TROCA
BUBBLE SORT (Bolha): em cada etapa, cada elemento é
comparado com o próximo, havendo a troca se o
elemento não estiver na ordem certa. A comparação é
realizada até que as trocas não sejam mais
necessárias. 
 
QUICK SORT (Troca e partição): o algoritmo de
ordenação interna MAIS RÁPIDO. Escolhe um pivô,
divide o array em torno desse pivô e ordena as
partições (sublistas) recursivamente. 
Processo de Ordenação
Tipos de Algoritmo de ordenação
MÉTODO: ORDENAÇÃO POR SELEÇÃO
SELECTION SORT (Seleção direta): a cada passo,
seleciona o melhor elemento (maior ou menor, a
depender da ordenação) para ocupar aquela posição
do array. 
HEAP SORT: extrai, repetidamente, o maior (ou
menor) elemento de uma estrutura, até que todos
elementos estejam ordenados. Também quebra o
problema maior em partes menores (um heap de
tamanho reduzido a cada iteração). 
Processo de Ordenação
Tipos de Algoritmo de ordenação
MÉTODO: ORDENAÇÃO POR INTERCALAÇÃO
MERGE SORT: o Merge Sort divide os dados em
conjuntos cada vez menores para, depois, ordená-los
e combiná-los por meio da intercalação.
Representado por uma ÁRVORE BINÁRIA (cada nó =
chamada recursiva, nó raiz = chamada principal e
folhas = casos bases ou vetores de 1 ou 2 números. 
 
Processo de Ordenação
Tipos de Algoritmo de ordenação
Processo de Ordenação
Funcionamento do Merge Sort
Vamos ordenar os números 72943861 usando Merge
Sort.
Partimos o vetor sempre na metade:
Processo de Ordenação
Funcionamento do Merge Sort
Depois, fazemos uma chamada recursiva para
partir a primeira partição do vetor: 
Processo de Ordenação
Funcionamento do Merge Sort
Uma nova chamada recursiva é realizada para
partir, mais uma vez, a partição: 
Processo de Ordenação
Funcionamento do Merge Sort
Mais duas chamadas recursivas para o caso base
encontrado (7 e 2):
Processo de Ordenação
Funcionamento do Merge Sort
Em seguida, ocorre a intercalação para ordenar
o caso base (7 e 2). 
Processo de Ordenação
Funcionamento do Merge Sort
O mesmo é feito para o outro caso base (9 e
4):
Processo de Ordenação
Funcionamento do Merge Sort
É feita uma intercação para ordenar a primeira
partição do valor:
Processo de Ordenação
Funcionamento do Merge Sort
O mesmo processo ocorre com a segunda
partição:
Processo de Ordenação
Funcionamento do Merge Sort
Por último, há uma intercalação para ordenar
as duas principais partições do vetor: 
Comparando algoritmos
Desvantagens
Vantagens
Bubble Sort
Usa pouca memória adicional;
É simples e fácil de entender e
implementar;
É estável (mantém a ordem relativa de
elementos iguais);
Melhor caso = O(n). 
Ruim com grande quantidade de dados ou
que demandem velocidade --> Complexidade
= O(n²);
É ineficiente: faz trocas desnecessárias
e é lento. 
Desvantagens
Vantagens
SelectionSort
Usa pouca memória adicional;
É simples e fácil de entender e
implementar;
Faz menos trocas que o Bubble Sort.
Ruim com grande quantidade de dados -->
Complexidade = O(n²);
É instável (não preserva a ordem entre
dois elementos iguais);
Desempenho consistente, mas ruim (sempre
percorre a lista toda, independentemente
do estado inicial dos dados. 
Desvantagens
Vantagens
Insertion Sort
Usa pouca memória adicional;
É simples e fácil de entender e
implementar;
É estável (mantém a ordem relativa de
elementos iguais);
Melhor caso = O(n). 
Ruim com grande quantidade de dados -->
Pior caso = O(n²);
Faz muitas comparações e movimentações;
Não indicado para listas com elementos
muito desordenados. 
Desvantagens
Vantagens
Shell Sort
É uma adaptação melhorada do Insertion;
Menos trocas e comparações (diminui o
número de incrementos para comparação
dos elementos do vetor);
É fácil e simples de implementar;
Mais eficiente que Bubble, Insertion e
Selection Sort;
Bom para quantidade moderada de
elementos. 
Pior caso = algo entre O(n²) e O(n3/2);
É instável;
Menos eficiente que Quick e Merge Sort. 
Desvantagens
Vantagens
Merge Sort
Mesma complexidade para todos os casos:
O(n log n);
É eficiente com grande quantidade de
dados;
É estável (mantém a ordem relativa de
elementos iguais).
Requer memória adicional;
Mais lento que o Quick Sort;
Mais complicado de implementar e
entender. 
Desvantagens
Vantagens
Quick Sort
Complexidade média de: O(n log n);
Usa pouca memória adicional;
É eficiente com grande quantidade de
dados e com elementos desordenados.
Pior caso = O(n²) --> quando se escolhe
mal o pivô, por exemplo;
É instável (não preserva a ordem dos
elementos iguais);
Pode exigir uma recursão profunda, o que
pode causar estouro de pilha (stack
overflow). 
Stack Overflow
A pilha de execução é uma área da memória
reservada para guardar informações sobre as
chamadas de funções, tais como variáveis
locais e endereços de retorno.
STACK OVERFLOW é um erro que acontece quando
a pilha de um programa se enche além da sua
capacidade, causando um erro de execução. 
Árvore Binária
Estrutura de dados hierárquica, na qual cada
NÓ possui, no máximo, 2 FILHOS (filho
esquerdo e filho direito). Representada em
memória por ALOCAÇÃO DINÂMICA. É composta
por:
NÓ: representa cada elemento da árvore.
Pode conter um valor, um ponteiro para o
filho direito e outro para o esquerdo;
RAIZ (root): o nó principal, de onde
todos os outros se derivam.
 
FILHOS: são os nós que vêm imediatamente
abaixo de outro nó. Cada nó pode ter até
2 filhos. 
FOLHA: é um nó sem filhos. 
Árvore Binária
SUBÁRVORE: é uma árvore formada por um
nó e todos os seus descendentes (seus
filhos e “netos”);
ALTURA: medida pelo número de arestas
(ou conexões) do caminho mais longo da
raiz até uma folha. 
Neste exemplo, o nó 8 é a raiz, 3 e 10 são seus
filhos, e 1, 6, 14 são descendentes de 3 e 10. Os
nós 1, 4, 7, 13 são folhas, pois não têm filhos.
Árvore Binária
AGREGADO HETEROGÊNEO: chamado também de
REGISTRO (Record) ou ESTRUTURA (Structure),
é capaz de armazenar objetos de dados de
tipos diferentes, acessando-os através do
mesmo identificador e distinguindo-os a
través de um seletor. 
Usado para representar o NÓ de uma árvore. 
A árvore é representada por uma referência para a sua
raiz, ponteiro para a raiz, que, recursivamente, aponta
para as raízes das subárvores esquerda e direita. 
Ponteiro para a raiz da árvore: 
Árvore Binária
ÁRVORE VAZIA: representada quando o ponteiro
raiz aponta para o endereço 0 (zero). Assim,
para inicializar uma árvore vazia, basta
colocar o ponteiro raiz como NULO. 
Na figura abaixo, o nó raiz da árvore é armazenado no
endereço $100, que contém um agregado heterogêneo com a 
chave 10 e dois ponteiros $150 e $70, respectivamente,
para o endereço das subárvores esquerda e direita da
raiz. 
Árvore Binária
PERCURSO: processo de visitar todos os nós
da árvore seguindo determinada ordem.
Visitar é o mesmo que realizar uma operação
(NÃO é acessar o conteúdo armazenado em um
elemento).
TIPOS DE PERCURSO:
Pré-ordem: raiz -> esquerda -> direita
Em ordem: esquerda -> raiz -> direita
Pós-ordem: esquerda -> direita -> raiz
***OBS: a visita mais simples que podemos
definir é a IMPRESSÃO do rótulo da chave
armazenado no nó visitado. 
Árvore Binária de Busca
É uma estrutura de dados em que cada nó tem,
NO MÁXIMO, 2 filhos. 
REGRAS: 
valor de cada nó à esquerda valor do nó
raiz/referência. 
 
Repare que, se pegarmos o nó 80, perceberemos que o nó
à sua esquerda vale 60 (menor que 80) e o nó à sua
direita vale 90 (maior que 80). O mesmo é válido para
qualquer nó que tomarmos como referência.
ALGORITMO DE BUSCA: É usado nas operações de
inserção e remoção de nós em árvores
binárias de busca. 
Sendo X a chave que queremos encontrar, o
algoritmo compara X com a raiz, para ver se
ele está lá. Caso X não esteja na raiz, o
algoritmo parará de executar. 
Se X o algoritmo é executado na
subárvore esquerda. 
Se X > RAIZ -> o algoritmo é executado na
subárvore direita. 
Árvore Binária de Busca
EXEMPLO 1:
Árvore Binária de Busca
Se buscarmos a chave 80 na árvore acima, o algoritmo
irá retornar:
1) A referência para o nó com a chave 80;
II) A referência para o nó de chave 40, que é o pai do
nó que contém a chave 80;
III) O Boolean TRUE, indicando que a chave que buscamos
foi encontrada. 
 
EXEMPLO 1I:
Árvore Binária de Busca
Se buscarmos a chave 25 na árvore acima, o algoritmo
irá retornar:
1) A referência para o nó com a chave 30;
II) A referência para o nó de chave 20, que é o pai do
nó que contém a chave 30;
III) O Boolean FALSE, indicando que a chave que
buscamos NÃO foi encontrada. 
 
EXEMPLO 1II:
Árvore Binária de Busca
Se buscarmos uma chave que está na raiz, o algoritmo
irá retornar:
1) A referência para o nó raiz;
II) NULO na referência para o pai (que o nó raiz não
tem);
III) O Boolean TRUE, indicando que a chave que buscamos 
foi encontrada. 
 
EXEMPLO 1V:
Árvore Binária de Busca
Se buscarmos uma chave em uma árvore vazia, o algoritmo
irá retornar:
1) NULO para o nó que contém a chave;
II) NULO na referência para o pai do nó que contém a
chave;
III) O Boolean FALSE, indicando que a chave que
buscamos NÃO foi encontrada. 
 
ATENÇÃO: a função “Busca” é recursiva, então, na
primeira ativação, temos: raiz = endereço; pai =
NULO; encontrado = FALSE. 
Para determinarmos a complexidade de busca
de uma árvore binária de busca, devemos
determinar qual a árvore de busca de maior
altura. 
 
Complexidade de uma árvore binária de busca
= O(n) -> no pior caso (sendo n o número de
nós). 
Análise de complexidade
A inserção de uma nova chave em uma árvore
binária de busca ocorre SEMPRE em um novo nó
posicionado como uma nova folha da árvore.
Como NÃO é permitido CHAVE DUPLICADA, a
busca pela chave que desejamos inserir na
árvore deverá, obrigatoriamente, falhar e
retornar como resultado o nó que será pai do
novo nó inserido na árvore. 
***Inserção de nó em árvore vazia -> novo(a)
nó/chave = nó raiz. 
Inserção de nós
Considere a árvore binária de busca abaixo:
Inserção de nós
Vamos inserir um nó com chave “45” nela, seguindo os
passos a seguir:
I - Começando pela raiz (50), como 45 é menor que 50,
iremos para o nó da esquerda (30). 
II - No nó da esquerda (30), como 45 é maior que 30,
iremos para o nó da direita (40). 
III - No nó da direita (40), como 45 é maior que 40, o
nó 45 será inserido à direita de 40. 
A remoção pode ocorrer de 3 maneiras:
Remoção de uma folha na árvore binária
de busca;
Remoção de um nó interno com 1 filho;
Remoção de um nó interno com 2 filhos.
 
Remoção de nós
Considere a árvore binária de busca abaixo:
Remoçãode nós
Vamos remover o nó folha com chave “20” seguindo os
passos a seguir:
I - Localize o nó a ser removido (20). Começando pela
raiz (50), como 20 é menor que 50, iremos para o nó da
esquerda (30). No nó da esquerda (30), como 20 é menor
que 30, vá para a esquerda novamente (20). 
II - Confirme que o nó é uma folha (não tem filhos). 
III - Como o nó 20 não tem filhos (é uma folha), ele
pode ser removido diretamente da árvore. O ponteiro do
pai de 20 (nó 30), que apontava para ele, é
simplesmente ajustado para NULL, removendo o nó 20 da
árvore. 
Remoção de nós
Vamos remover o nó com 1 filho de chave “30” seguindo
os passos a seguir:
I - Localize o nó a ser removido (30). Começando pela
raiz (50), como 30 é menor que 50, iremos para o nó da
esquerda (30). 
II - Confirme que o nó 30 tem só 1 filho (que é o nó 40
à direita). 
III - Substitua o nó (30) pelo seu único filho (40).
Ajuste o ponteiro do pai de 30 (nó 50) apontando-o para
o nó 40. O nó 30 será removido e o nó 40 o substituirá. 
Remoção de nós
Vamos remover o nó com 2 filhos de chave “50” seguindo
os passos a seguir:
I - Localize o nó a ser removido (50 - o nó raiz). 
II - Encontrar o sucessor ou antecessor. Para
substituir o nó 50, pegaremos o seu sucessor, neste
caso, o menor valor da subárvore direita. O menor valor
na subárvore direita é o nó 60, à esquerda de 70. 
III - Substitua o nó (50) pelo valor do seu sucessor
(60). Não se esqueça de remover o nó original 60. 
Árvore Binária Completa
É uma estrutura de dados na qual:
Todos os nós têm 2 filhos (EXCETO no
último nível);
No último nível, os nós podem NÃO estar
completos, mas estão o mais à esquerda
possível. 
 
No exemplo acima: 
Nível 1 -> Nó raiz (nó 1)
Nível II -> Nós 2 e 3 (filhos do nó raiz)
Nível III -> Nós 4, 5 e 6 (filhos dos nós 2 e 3) 
***OBS: NÃO existe árvore binária com n nós cuja
altura seja inferior a 1 + [log n]. 
Árvore Balanceada
É uma árvore binária em que a altura das
subárvores (direita e esquerda), a partir de
qualquer nó, apresenta como valor da
diferença, no máximo, 1. Seu objetivo é
otimizar as operações de busca, inserção e
remoção de nós, mantendo-as eficientes
(próximas de O log n, no pior caso).
EXEMPLOS: 
Árvore AVL;
Árvore Rubro-Negra(Red-Black Tree). 
***OBS: TODA árvore binária de busca
balanceada tem altura proporcional a log n.
Assim, a complexidade, sendo proporcional à
altura, será de O(log n). 
 
Para saber se uma árvore é balanceada ou
não, é preciso:
1) Calcular a altura da subárvore esquerda e
da subárvore direita de cada nó;
II) Verificar a diferença de altura entre as
subárvores. Se a diferença for = 1, a árvore
é balanceada. 
EXEMPLO: 
 
É balanceada ou não?
Identificar o Nó raiz -> Nó 1
Altura da subárvore esquerda (começa a
partir do Nó 2) -> do Nó 2 até os nós 4
OU 5 = Altura I; 
Altura da subárvore direita (começa a
partir do Nó 3) -> do Nó 3 até os nós 6
E 7 = Altura = 2;
Altura da subárvore esquerda - Altura da
subárvore direita = I (árvore
balanceada) 
 
 
É balanceada ou não?
Árvore Zig-Zag
É uma árvore binária de busca em que os nós
alternam entre ter um único filho à esquerda
e um único filho à direita. Tem o PIOR
desempenho possível, uma vez que a
complexidade da busca, além da inserção e
remoção, é de O(n), não sendo possível
construir uma árvore binária de busca maior
do que n. É um exemplo de árvore
DESBALANCEADA. 
 
O algoritmo DSW (Day-Stout-Warren) é uma
técnica utilizada para balancear árvores
binárias de busca. Pode ser dividido em 2
fases:
I - Transformar a árvore em uma “espinha”
(lista encadeada);
II - Reconstruir a árvore balanceada a
partir da espinha. 
Vamos tomar como exemplo a árvore
desbalanceada abaixo:
Algoritmo DSW
Algoritmo DSW
FASE 1: transformar a árvore em uma
“espinha” (lista encadeada), fazendo
rotações para a direita.
Algoritmo DSW
FASE 1I: fazer rotações para a esquerda para
reconstruir a árvore balanceada.
Árvore AVL
É um tipo de árvore binária de busca
balanceada em que a diferença de altura
(fator de balanceamento) entre as subárvores
esquerda e direita (a partir de qualquer nó)
é de NO MÁXIMO 1 (ou seja, pode ser menor ou
igual a 1). 
O balanceamento automático é feito através
de rotações, após inserir ou remover nós,
garantindo que a árvore mantenha a altura
mínima possível (próxima de O log n, em que
n = número de nós). 
 
FB = Altura esquerda - Altura direita 
FB = -1, 0 ou 1 -> árvore balanceada
***OBS: AVL = Adelson-Velsky e Landis, os
autores desse tipo de árvore.
Inserção em Árvores AVL
Ocorre da seguinte maneira:
Inserir um nó como se faz em uma árvore
binária de busca, comparando o valor do
nó a ser inserido com o nó atual, e indo
para a esquerda ou para a direita;
Após a inserção, o fator de
balanceamento de cada nó ancestral em
relação ao nó inserido é atualizado.
Se o fator de balanceamento de algum nó
for maior que 1 ou menor que -1, a
árvore está desbalanceada, o que vai
demandar uma rotação (ou sequência de
rotações) para corrigir isso. 
Remoção em Árvores AVL
Ocorre da seguinte maneira:
Remover um nó como se faz em uma árvore
binária de busca;
Após a remoção, o fator de balanceamento
de cada nó ancestral em relação ao nó
inserido é atualizado.
Se o fator de balanceamento de algum nó
for maior que 1 ou menor que -1, a
árvore está desbalanceada, o que vai
demandar uma rotação (ou sequência de
rotações) para corrigir isso. 
***OBS: quando é necessário aplicar uma
rotação para preservar a propriedade AVL,
esta rotação deve ser aplicada no nó de
menor nível que se encontre desregulado. 
Grafos são estruturas matemáticas usadas
para modelar relações entre objetos. São
compostos por:
Vértices (ou Nós): representam os
objetos.
Arestas: conectam os vértices e indicam
a relação entre eles. 
Um grafo é representado por um par ordenado
de conjuntos disjuntos (V,E), sendo V ->
conjunto arbitrário de vértices e E -> o
conjunto não ordenado de arestas. 
GRAFOS
GRAFO DIRECIONADO (DÍGRAFO): as arestas têm
uma direção, ou seja, a relação é
unidirecional. 
GRAFO NÃO DIRECIONADO: as arestas não
possuem uma direção, isto é, a relação é
bidirecional. 
GRAFO PONDERADO: aqui, as arestas têm um
peso ou custo associado (peso/custo = um
tipo de valor, como distância, tempo etc.). 
GRAFO NÃO PONDERADO: as arestas não têm
peso. 
GRAFOS
Vamos criar um grafo = G(V,E) para
interligar os estados da região sudeste do
Brasil: 
V = {ES, MG, SP, RJ} -> vértices são os
estados do sudeste
E = {(MG, SP), (RJ, SP), (MG, ES), (RJ, MG), 
(RJ, ES)} -> arestas representam a ligação
entre os estados (se os estados têm uma
fronteira geográfica entre si) 
Exemplo de Grafo
EXTREMIDADE: representa os vértices conectados
por uma aresta. 
VÉRTICES ADJACENTES: dois vértices são
adjacentes quando estão conectados por uma
aresta. São vértices “vizinhos”.
 
LAÇO: é uma aresta que conecta um vértice a
ele mesmo.
ARESTAS PARALELAS: são arestas que conectam o
mesmo par de vértices (sejam eles direcionados
ou não). 
Conceitos em Grafos
GRAFO SIMPLES: grafo em que cada par de
vértices se conecta por apenas uma aresta (NÃO
há laços nem arestas paralelas). 
GRAFO CONEXO: é quando há um caminho entre
qualquer par de vértices. 
GRAU DO VÉRTICE: é representado pelo número de
arestas que estão conectadas a determinado
vértice. 
Conceitos em Grafos
Na figura ao lado, o nó A
possui grau do vértice = 2,
pois tem ligação com os nós C
e B.
Soma dos graus do vértice = 2 .(nº de arestas) 
Representação de Grafo
 No grafo acima, por exemplo, temos 5 arestas. Assim:
 Soma dos graus do vértice = 2 . 5 = 10
Ao se remover um vértice, também retiramos
todas as arestas que têm o vértice como
extremidade. No exemplo abaixo, removeremos o
vértice (nó) E: 
Remoção de Vértices
Também é possível removeruma ARESTA (a e5):
GRAFO COMPLETO: este grafo contém arestas que
ligam todos os nós. 
GRAFO COMPLEMENTAR: considerando-se o grafo G,
um grafo complementar de G possui os mesmos
vértices de G, mas suas arestas possuem
conexões que opostas às de G (ou seja, se dois
vértices se conectam em G, em G’ eles não
estarão conectados).
 
GRAFO NULO/VAZIO: é um grafos que possui
APENAS vértices (NÃO tem arestas).
GRAFO CONEXO REGULAR: grafo em que todos os
vértices têm o mesmo grau e estão conectados
de alguma forma. 
Tipos de Grafos
GRAFO DIRECIONADO: também chamado de DÍGRAFO,
é um grafo em que as arestas têm uma direção
(as conexões entre os vértices são
unidirecionais). 
GRAFO SUBJACENTE: um grafo subjacente de um
grafo direcionado pode ser obtido
transformando-se um grafo direcionado em um
grafo não direcionado (ou seja, mantendo os
vértices e conexões, mas ignorando as direções
das arestas).
Tipos de Grafos
MATRIZ: modelo matemático que serve para
representar grafos, utilizando tabelas com linhas
e colunas. 
**MATRIZ DE ADJACÊNCIA: é uma forma de representar
um grafo, na qual as linhas e colunas da matriz
correspondem aos vértices do grafo.
 
Complexidade = O(n²) 
Matriz de um Grafo
**MATRIZ DE INCIDÊNCIA: é uma forma de representar
um grafo, na qual:
LINHAS -> Vértices;
COLUNAS -> Arestas
 
Complexidade = O(nm)
Matriz de um Grafo
Há 2 técnicas de BUSCA EM GRAFOS:
 
1)BUSCA EM PROFUNDIDADE (DFS - DEPTH FIRST
SEARCH): é um algoritmo que explora o grafo indo o
mais fundo possível em cada ramificação, antes de
retroceder e explorar outros caminhos.
PASSO A PASSO:
-> Começa com um vértice inicial e segue um
caminho até o vértice mais profundo possível,
visitando os vértices de forma recursiva ou
utilizando uma PILHA.
-> Se atinge um vértice sem mais vizinhos não
visitados, retrocede até o vértice anterior e
continua a busca por outro caminho não explorado.
-> O processo continua até que todos os vértices
conectados ao vértice inicial sejam visitados. 
Algoritmos de Busca
EXEMPLO BUSCA EM PROFUNDIDADE
Ordem de busca:
Algoritmos de Busca
Há 2 técnicas de BUSCA EM GRAFOS:
 
1I)BUSCA EM LARGURA (BFS - BREADTH FIRST SEARCH):
é um algoritmo que explora o grafo visitando
primeiro os vizinhos mais próximos do vértice
inicial e, depois, os mais distantes.
PASSO A PASSO:
-> Começa com um vértice inicial e visita todos os
seus vizinhos diretos, antes de ir para os
vizinhos dos vizinhos, e assim por diante.
-> Utiliza uma FILA para controlar a ordem de
visita dos vértices (quando um vértice é visitado,
seus vizinhos são postos na fila para serem
processados posteriormente).
-> O processo continua até que todos os vértices
conectados ao vértice inicial sejam visitados. 
Algoritmos de Busca
EXEMPLO BUSCA EM LARGURA
Ordem de busca:
Algoritmos de Busca
COMPARANDO...
Algoritmos de Busca
CAMINHO -> é uma sequência de vértices em que cada
par de vértices consecutivos está conectado por
uma aresta. 
Pode ser:
SIMPLES: NÃO repete vértices;
FECHADO: O ponto inicial e o ponto final são o
mesmo vértice, formando um ciclo. 
**CAMINHO MÍNIMO: consiste no menor caminho entre
dois nós. Pode ser medido pelo número de arestas
ou pelo peso delas. 
Caminho
Utilizado para encontrar o CAMINHO MÍNIMO em
grafos ponderados (ou seja, nos quais as arestas
têm pesos positivos). 
Algoritmo de Dijkstra 
O problema a seguir visa determinar qual o menor
caminho de uma viagem, partindo de e voltando a um
mesmo ponto: 
Problema do Caixeiro
Veremos alguns algoritmos para resolução do
problema acima.
Lista todos os circuitos possíveis;1.
Calcula o custo total de cada circuito;2.
Escolhe um circuito com custo total mínimo.3.
O menor caminho (com custo total de R$676,00) para
o problema anterior seria:
A -> E -> C -> B -> D -> A
Algoritmo da Força Bruta
Seleciona um ponto de partida;1.
Das arestas adjacentes, escolhe a de menor
peso, partindo para o vértice correspondente;
2.
Continua a construir o circuito, indo sempre
para o vizinho mais próximo;
3.
Regressando ao último vértice, volta para
casa. 
4.
O menor caminho (com custo total de R$773,00) para
o problema anterior seria:
A -> C -> E -> D -> B -> A
Algoritmo do Vizinho

Mais conteúdos dessa disciplina