<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Algoritmos - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Aprenda a codificar - de graça. Tutoriais de programação em Python, JavaScript, Linux e muito mais. ]]>
        </description>
        <link>https://www.freecodecamp.org/portuguese/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Algoritmos - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/portuguese/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Wed, 03 Jun 2026 21:23:55 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/portuguese/news/tag/algoritmos/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ Algoritmo de dividir para conquistar: significado explicado e com exemplos ]]>
                </title>
                <description>
                    <![CDATA[ O que são os algoritmos de Dividir para Conquistar? Dividir para conquistar é um paradigma algorítmico semelhante ao paradigma  Greedy (ou "ambicioso") e ao paradigma da Programação Dinâmica. Um algoritmo de  dividir para conquistar típico resolve um problema usando os passos a seguir.  1. Dividir: quebrar o ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmo-de-dividir-para-conquistar-significado-explicado-e-com-exemplos/</link>
                <guid isPermaLink="false">6563f1fc13a65603e6501dee</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Rosa ]]>
                </dc:creator>
                <pubDate>Mon, 27 Nov 2023 02:12:26 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/11/5f9c9f04740569d1a4ca4062.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/divide-and-conquer-algorithms/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Divide and Conquer Algorithm   Meaning: Explained with Examples</a>
      </p><h2 id="o-que-s-o-os-algoritmos-de-dividir-para-conquistar"><strong>O que são os algoritmos de Dividir para Conquistar?</strong></h2><p>Dividir para conquistar é um paradigma algorítmico semelhante ao paradigma <em>Greedy</em> (ou "ambicioso") e ao paradigma da Programação Dinâmica. Um algoritmo de <strong>dividir para conquistar</strong> típico resolve um problema usando os passos a seguir.</p><ol><li><strong><strong>Divid</strong>ir</strong>: quebrar o problema em subproblemas do mesmo tipo. Esse passo envolve a divisão de um problema em subproblemas menores. Os subproblemas devem representar uma parte do problema original. Esse passo geralmente assume uma abordagem recursiva para dividir o problema até que nenhum subproblema possa ser dividido ainda mais. Nesse estágio, subproblemas se tornam atômicos por natureza, mas ainda representam alguma parte do problema em questão.</li><li><strong><strong>Conqu</strong>istar</strong>: resolver recursivamente esses subproblemas. Esse passo recebe vários subproblemas menores para serem resolvidos. Em geral, nesse nível, os problemas são considerados "resolvidos" por eles mesmos.</li><li><strong><strong>Combin</strong>ar</strong>: combinar adequadamente as respostas. Quando os subproblemas menores são resolvidos, esse estágio os combina recursivamente até que eles formulem uma solução para o problema original. Essa abordagem algorítmica funciona recursivamente e os passos de conquistar e unir ficam tão próximos que parecem ser apenas um.</li></ol><p>Esse método geralmente permite que haja uma grande redução na complexidade de tempo.</p><p>Por exemplo, a <em>Bubble Sort</em> (ordenação de "bolha") usa uma complexidade de <code>O(n^2)</code>, enquanto o <em>quicksort </em>(ordenação rápida, uma aplicação do tipo Dividir para Conquistar) reduz a complexidade de tempo para <code>O(nlog(n))</code>. A busca linear tem complexidade de tempo de <code>O(n)</code>, enquanto a busca binária (uma aplicação do tipo Dividir para Conquistar) reduz a complexidade de tempo para <code>O(log(n))</code>.</p><p>A seguir, vemos alguns algoritmos padrão que são do tipo Dividir para Conquistar.</p><p>A <strong>busca binária</strong> &nbsp;(em inglês, <em>Binary Search</em>)é um algoritmo de pesquisa. Em cada etapa, o algoritmo compara o elemento de entrada (x) com o valor do elemento do meio em um <em>array</em>. Se o valor corresponder, retorna-se o índice do meio. Do contrário, se x for menor que o elemento do meio, o algoritmo faz a recursão para percorrer o lado à esquerda do elemento do meio. Se for maior, faz a recursão para percorrer o lado à direita do elemento do meio.</p><p>A <strong>ordenação rápida</strong> (em inglês, <em>Quick Sort</em>)<strong> </strong>é um algoritmo de ordenação. O algoritmo seleciona um elemento pivô, reorganiza os elementos do <em>array</em> de tal maneira que todos os elementos menores que o elemento de pivô selecionado sejam movidos para a esquerda do pivô, enquanto todos os elementos maiores que o elemento de pivô selecionado sejam movidos para a direita dele. Por fim, o algoritmo recursivamente ordena os <em>subarrays</em> à esquerda e à direita do elemento de pivô.</p><p>A <strong>ordenação por mistura</strong> (em inglês, <em>Merge Sort</em>) também é um algoritmo de ordenação. O algoritmo divide o <em>array </em>em duas metades, as ordena recursivamente e, finamente, mistura as duas metades ordenadas. A complexidade de tempo desse algoritmo é de <code>O(nLogn)</code> no melhor caso, no caso médio ou no pior caso. Sua complexidade de tempo pode ser entendida facilmente do fato de que a recorrência/recursão tem como equação: <code>T(n) = 2T(n/2) + n</code>.</p><p>O <strong>par de pontos mais próximos</strong>:<strong> </strong>o problema é encontrar o par de pontos mais próximo em um conjunto de pontos em um plano x-y (ou cartesiano). O problema pode ser resolvido com uma complexidade de tempo de <code>O(n^2)</code> calculando as distâncias de cada par de pontos e comparando as distâncias para encontrar a distância menor. O algoritmo de Dividir para Conquistar resolve o problema em tempo de <code>O(nLogn)</code>.</p><p>O <strong>algoritmo de <strong>Strassen </strong></strong>é um algoritmo eficaz para a multiplicação de duas matrizes. Um método simples para multiplicar duas matrizes precisa de 3 laços aninhados e tem como complexidade de tempo <code>O(n^3)</code>. O algoritmo de Strassen multiplica duas matrizes com complexidade de tempo de <code>O(n^2.8974)</code>.</p><p>O <strong>algoritmo de transformada rápida de Fourier</strong><strong> <strong>(FFT)</strong></strong><strong>, de <strong>Cooley–Tukey </strong></strong>é o algoritmo mais comum para a FFT. Ele é um algoritmo de dividir para conquistar que funciona com complexidade de tempo de <code>O(nlogn)</code>.</p><p>O <strong>algoritmo de <strong>Karatsuba </strong></strong>foi o primeiro algoritmo de multiplicação assintoticamente mais rápido que o algoritmo quadrático padrão. Ele reduz a multiplicação de dois números de <em>n</em> algarismos a, no máximo, n^1,585 &nbsp;(que é uma aproximação do log de 3 na base 2) produtos de um único algarismo. Ele é, portanto, mais rápido do que o algoritmo clássico, que exige n^2 produtos de um único algarismo.</p><h3 id="dividir-para-conquistar-x-programa-o-din-mica"><strong>Dividir para conquistar x Programação dinâmica</strong></h3><p>Os dois paradigmas dividem o problema em questão em subproblemas e resolvem os subproblemas. Como selecionar um dos paradigmas para determinado problema? Dividir para conquistar deve ser usado quando os mesmos subproblemas não são avaliados várias vezes. Do contrário, deve-se usar a programação dinâmica ou a memoização.</p><p>Por exemplo, a busca binária é um algoritmo de dividir para conquistar. Nele, jamais avaliamos os mesmos subproblemas novamente. Por outro lado, para calcular o n-ésimo número de Fibonacci, deve-se preferir a programação dinâmica.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmo de preenchimento por inundação explicado ]]>
                </title>
                <description>
                    <![CDATA[ O algoritmo de preenchimento por inundação [https://pt.wikipedia.org/wiki/Preenchimento_por_inunda%C3%A7%C3%A3o] (do inglês, flood fill) é usado principalmente para determinar uma área limitada conectada a um determinado nó em uma matriz multidimensional. É muito semelhante à ferramenta do balde em programas como o Microsoft Paint. A implementação mais usada do algoritmo é uma função ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmo-de-preenchimento-por-inundacao-explicado/</link>
                <guid isPermaLink="false">65431a6d935ea203f011a691</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Rosa ]]>
                </dc:creator>
                <pubDate>Thu, 02 Nov 2023 04:15:05 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/11/5f9c9e26740569d1a4ca3b9a.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/flood-fill-algorithm-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Flood Fill Algorithm Explained</a>
      </p><p>O <a href="https://pt.wikipedia.org/wiki/Preenchimento_por_inunda%C3%A7%C3%A3o">algoritmo de preenchimento por inundação</a> (do inglês, <em>flood fill</em>) é usado principalmente para determinar uma área limitada conectada a um determinado nó em uma matriz multidimensional. É muito semelhante à ferramenta do balde em programas como o Microsoft Paint.</p><p>A implementação mais usada do algoritmo é uma função recursiva baseada em pilha (em inglês, <em>stack</em>), e é sobre isso que falaremos a seguir.</p><h3 id="como-funciona"><strong><strong>Como funciona<strong>?</strong></strong></strong></h3><p>O problema é bastante simples e sua solução, geralmente, segue os passos abaixo:</p><ol><li>Assuma a posição como ponto de partida.</li><li>Decida se quer ir nas quatro direções (<strong><strong><strong><strong>N, S, </strong></strong>O<strong><strong>, </strong></strong>L</strong></strong>) ou em 8 direções (<strong><strong><strong><strong>N, S, </strong></strong>O<strong><strong>, </strong></strong>L<strong><strong>, N</strong></strong>O<strong><strong>, NE, S</strong></strong>O<strong><strong>, SE</strong></strong></strong></strong>).</li><li>Escolha uma cor substituta e uma cor de destino.</li><li>Viaje nas direções selecionadas.</li><li>Se o bloco no qual você parar for da cor de destino, substitua a cor pela cor substituta.</li><li>Repita os passos 4 e 5 até ter chegado a todos os lugares dentro dos limites.</li></ol><p>Vamos pegar a matriz multidimensional abaixo como exemplo:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/11/63b353b2864f97151e1319d0d4ff9866ce7822eb.png" class="kg-image" alt="63b353b2864f97151e1319d0d4ff9866ce7822eb" width="400" height="400" loading="lazy"></figure><p>O quadrado vermelho é o ponto de partida e os quadrados em cinza são os limites.</p><p>Para mais detalhes, veja um trecho do código que descreve a função:</p><pre><code class="language-none">int wall = -1;

void flood_fill(int pos_x, int pos_y, int target_color, int color)
{
  
   if(a[pos_x][pos_y] == wall || a[pos_x][pos_y] == color) // se não houver um limite ou se eu já não estive lá
      return;                                              // volte
   
   if(a[pos_x][pos_y] != target_color) // se não for da cor, volte
      return;
   
   a[pos_x][pos_y] = color; // marque o ponto de modo que eu saiba que passei por ele. 
   
   flood_fill(pos_x + 1, pos_y, color);  //depois, posso ir para o sul
   flood_fill(pos_x - 1, pos_y, color);  // o norte
   flood_fill(pos_x, pos_y + 1, color);  // o leste
   flood_fill(pos_x, pos_y - 1, color);  // ou o oeste
   
   return;

}</code></pre><p>Como vemos acima, meu ponto de partida é (4,4). Depois de chamar a função para as coordenadas de início, <strong><strong><strong><strong>x = 4</strong></strong></strong></strong> e <strong><strong><strong><strong>y = 4</strong></strong></strong></strong>, posso começar a conferir se há ou não um limite ou uma cor no local. Se for válido, marco o local com uma cor (<em>color</em>) e começo a verificar os quadrados adjacentes.</p><p>Indo para o sul, chegarei ao ponto (5,4) e a função é executada novamente.</p><h3 id="problema-do-exerc-cio"><strong>Problema do exercício</strong></h3><p>Sempre considerei que resolver um ou mais problemas usando um algoritmo recém aprendido é a melhor maneira de entender o conceito por completo.</p><p>Aqui temos um problema:</p><p><strong>Instrução: </strong>em uma matriz bidimensional, você recebe um número n fornecido de <strong>"<strong><strong><strong>i</strong></strong></strong>lhas"</strong>. Tente achar a maior área de uma ilha e o número da ilha correspondente. 0 significa água e qualquer outro x entre 1 e n significa um quadrado da superfície correspondente à ilha x.</p><p><strong>Entradas</strong></p><ul><li><strong><strong><strong><strong>n</strong></strong></strong></strong> – o número de ilhas.</li><li><strong><strong><strong><strong>l,</strong></strong></strong> <strong><strong><strong>c</strong></strong></strong></strong> – as dimensões da matriz.</li><li>o próximo número, onde <strong><strong><strong><strong>l</strong></strong></strong></strong> é a linha e <strong><strong><strong><strong>c</strong></strong></strong></strong> é a coluna, dando a <strong><strong><strong><strong>l</strong></strong></strong></strong>-ésima linha da matriz.</li></ul><p><strong>Resultado</strong></p><ul><li><strong><strong><strong><strong>i</strong></strong></strong></strong> – o número da ilha com a maior área.</li><li><strong><strong><strong><strong>A</strong></strong></strong></strong> - a área da <strong><strong><strong><strong>i</strong></strong></strong></strong>-ésima ilha.</li></ul><p><strong><strong><strong><strong>Ex</strong></strong></strong>emplo<strong><strong><strong>:</strong></strong></strong></strong></p><p>Você tem a entrada abaixo:</p><pre><code class="language-none">2 4 4
0 0 0 1
0 0 1 1
0 0 0 2
2 2 2 2</code></pre><p>Para qual delas você terá a ilha número 2 como sendo a maior área, com 5 quadrados.</p><h3 id="dicas"><strong>Dicas</strong></h3><p>O problema é bem fácil, mas aqui você tem algumas dicas:</p><pre><code class="language-text">1. Use o algoritmo de preenchimento por inundação sempre que encontrar uma nova ilha.
2. Diferente do que ocorre no código de exemplo, você deve percorrer a ára da ilha, não a do oceano (blocos com 0).</code></pre> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmo de Euclides: MDC (máximo divisor comum) explicado com exemplos em várias linguagens ]]>
                </title>
                <description>
                    <![CDATA[ Para tratarmos deste assunto, primeiramente, é preciso conhecer a respeito do máximo divisor comum (MDC ou GCD – no inglês, Greatest Common Divisor) e da operação MOD (de módulo). Máximo divisor comum (MDC ou GCD) O MDC de dois ou mais números inteiros é o maior número inteiro que divide ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmo-de-euclides-mdc-maximo-divisor-comum-explicado-com-exemplos-em-varias-linguagens/</link>
                <guid isPermaLink="false">64eaace678917103e8a169cd</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Daniel Rosa ]]>
                </dc:creator>
                <pubDate>Sun, 27 Aug 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/08/euclid-image.jpeg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/euclidian-gcd-algorithm-greatest-common-divisor/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Euclidian Algorithm: GCD (Greatest Common Divisor) Explained with C++ and Java Examples</a>
      </p><p>Para tratarmos deste assunto, primeiramente, é preciso conhecer a respeito do máximo divisor comum (MDC ou GCD – no inglês, <em>Greatest Common Divisor</em>) e da operação MOD (de módulo).</p><h4 id="m-ximo-divisor-comum-mdc-ou-gcd-"><strong>Máximo divisor comum (MDC ou GCD)</strong></h4><p>O MDC de dois ou mais números inteiros é o maior número inteiro que divide es números anteriormente mencionados de tal modo que o resto da divisão é zero.</p><p>Exemplo:<br>O MDC de 20 e 30 é 10 &nbsp;<em><em>(10 </em>é o maior número que divide <em>20 </em>e<em> 30 </em>com o resto <em>0)</em></em><br>O MDC de 42, 120, 285 = 3 &nbsp;<em><em>(3 </em>é o maior número que divide <em>42, 120 </em>e<em> 285 </em>com o resto <em>0)</em></em></p><h4 id="opera-o-mod"><strong>Operação "mod"</strong></h4><p>O operador de módulo (mod) mostra o resto da divisão quando dividimos dois números inteiros. Escrevemos isso assim:<br><code>A mod B = R</code></p><p>Isso significa que, ao dividir A e B, temos o resto R. Essa operação difere da operação regular de divisão, que nos dá o quociente.</p><p>Exemplo:<br>7 mod 2 = 1 &nbsp;<em><em>(</em>Ao dividir <em>7 </em>por<em> 2 </em>temos o resto<em> 1)</em></em><br>42 mod 7 = 0 &nbsp;<em><em>(</em>Ao dividir<em> 42 </em>por<em> 7 </em>temos o resto<em> 0)</em></em></p><p>Tendo entendido os dois conceitos acima, será fácil de entender o algoritmo de Euclides.</p><h3 id="algoritmo-de-euclides-para-o-m-ximo-divisor-comum-mdc-ou-gcd-"><strong>Algoritmo de Euclides para o máximo divisor comum (MDC ou GCD)</strong></h3><p>O algoritmo de Euclides encontra o máximo divisor comum (que chamaremos nos algoritmos de 'GCD') de 2 números.</p><p>Você entenderá melhor esse algoritmo ao vê-lo em funcionamento. Supondo que você deseja calcular o GCD de 1220 e de 516, vamos aplicar o algoritmo de Euclides:</p><p>Pseudocódigo do algoritmo<br>Passo 1: &nbsp;<strong>suponha que <strong><code>a, b</code> &nbsp;</strong>sejam os dois números</strong><br>Passo 2: &nbsp;<strong><strong><code>a mod b = R</code></strong></strong><br>Passo 3: &nbsp;<strong>considere que<strong> &nbsp;<code>a = b</code> &nbsp;</strong>e que<strong> &nbsp;<code>b = R</code></strong></strong><br>Passo 4: &nbsp;<strong>repita os passos 2 e 3 até que<strong> <code>a mod b</code> &nbsp;</strong>seja maior do que<strong> 0</strong></strong><br>Passo 5: &nbsp;<strong><strong>GCD = b</strong></strong><br>Passo 6: fim</p><p>Código em JavaScript para encontrar o GCD:</p><pre><code>function gcd(a, b) {
  var R;
  while ((a % b) &gt; 0)  {
    R = a % b;
    a = b;
    b = R;
  }
  return b;
}
</code></pre><p>Código em JavaScript para encontrar o GCD usando a recursividade:</p><pre><code>function gcd(a, b) {
  if (b == 0)
    return a;
  else
    return gcd(b, (a % b));
}
</code></pre><p>Código em C para encontrar o GCD usando a recursividade:</p><pre><code>int gcd(int a, int b) 
{ 
    // Tudo pode ser dividido por 0  
    if (a == 0) 
       return b; 
    if (b == 0) 
       return a; 
  
    // Caso de base 
    if (a == b) 
        return a; 
  
    // a é maior 
    if (a &gt; b) 
        return gcd(a-b, b); 
    return gcd(a, b-a); 
}
</code></pre><p>Código em C++ para encontrar o GCD:</p><pre><code>int gcd(int a,int b) {
  int R;
  while ((a % b) &gt; 0)  {
    R = a % b;
    a = b;
    b = R;
  }
  return b;
}
</code></pre><p>Código em Python para encontrar o GCD usando recursividade:</p><pre><code>def gcd(a, b):
  if b == 0:
    return a:
  else:
    return gcd(b, (a % b))
</code></pre><p>Código em Java para encontrar o GCD usando recursividade:</p><pre><code>static int gcd(int a, int b)
{
  if(b == 0)
  {
    return a;
  }
  return gcd(b, a % b);
}
</code></pre><p>Você também pode usar o algoritmo de Euclides para encontrar o máximo divisor comum de mais de dois números. Como o GCD é associativo, a operação a seguir também seria válida: &nbsp;<code>GCD(a,b,c) == GCD(GCD(a,b), c)</code></p><p>Calcule o GCD dos dois primeiros número, depois encontre o GCD do resultado e do número seguinte. Exemplo: &nbsp;<code>GCD(203,91,77) == GCD(GCD(203,91),77) == GCD(7, 77) == 7</code></p><p>Você pode encontra o GCD de &nbsp;<code>n</code> &nbsp;números da mesma maneira.</p><h2 id="o-que-o-algoritmo-de-euclides-estendido"><strong>O que é o algoritmo de Euclides estendido?</strong></h2><p>Esta é uma extensão do algoritmo de Euclides. Ela também calcula os coeficientes x, y, de tal modo que:</p><p><code>ax+by = gcd(a,b)</code></p><p><code>x</code> e <code>y</code> também são conhecidos como os coeficientes da identidade de Bézout.</p><p>Código em C para o algoritmo de Euclides estendido</p><pre><code>struct Triplet{
	int gcd;
	int x;
	int y;
};
Triplet gcdExtendedEuclid(int a,int b){
	// Case de base
	if(b==0){
		Triplet myAns;
		myAns.gcd = a;
		myAns.x = 1;
		myAns.y = 0;
		return myAns;

	}
	Triplet smallAns = gcdExtendedEuclid(b,a%b);
	// Algoritmo de Euclides estendido

	Triplet myAns;
	myAns.gcd = smallAns.gcd;
	myAns.x  = smallAns.y;
	myAns.y = (smallAns.x - ((a/b)*(smallAns.y)));
	return myAns;	
}</code></pre> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Como resolver o problema da Torre de Hanói – um guia ilustrativo do algoritmo ]]>
                </title>
                <description>
                    <![CDATA[ > Tradução realizada em português europeu Antes de começarmos, vamos discutir o que é o problema da Torre de Hanói. Bem, é um simples puzzle onde o objetivo é mover um bloco completo de discos de uma posição inicial para outra posição. São seguidas três regras simples:  1. Apenas ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/como-resolver-o-problema-da-torre-de-hanoi-um-guia-ilustrado-do-algoritmo/</link>
                <guid isPermaLink="false">63a435676389b20662b612f1</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Afonso Branco ]]>
                </dc:creator>
                <pubDate>Tue, 31 Jan 2023 21:00:00 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/5f9ca6e9740569d1a4ca739a.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/analyzing-the-algorithm-to-solve-the-tower-of-hanoi-problem-686685f032e3/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">How to Solve the Tower of Hanoi Problem - An Illustrated Algorithm Guide</a>
      </p><blockquote>Tradução realizada em português europeu</blockquote><p>Antes de começarmos, vamos discutir o que é o problema da Torre de Hanói. Bem, é um simples puzzle onde o objetivo é mover um bloco completo de discos de uma posição inicial para outra posição. São seguidas três regras simples:</p><ol><li>Apenas pode ser movido um disco de cada vez.</li><li>Cada movimento consiste em pegar no disco mais acima na pilha e colocá-lo no topo de outra pilha. Por outras palavras, um disco apenas pode ser movido se for o disco que está mais acima na pilha.</li><li>Não pode ser colocado um disco maior sobre um menor.</li></ol><p>Agora, vamos tentar imaginar um cenário. Vamos supor que temos uma pilha com três discos. O nosso objetivo é mover esta pilha da <strong>origem<strong> A</strong></strong> para o <strong><strong>destino C</strong></strong>. Como fazemos isto?</p><p>Antes de lá chegarmos, vamos imaginar que existe um <strong>ponto<strong> </strong>intermédio<strong> B</strong></strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/0_UB4f9VNg1RRs4k93.png" class="kg-image" alt="0_UB4f9VNg1RRs4k93" width="500" height="375" loading="lazy"><figcaption><a href="http://www.texample.net/tikz/examples/towers-of-hanoi/">Três discos</a></figcaption></figure><p>Podemos utilizar B como ajuda para completar essa tarefa. Agora, estamos prontos para avançar. Vamos analisar cada um dos passos:</p><ol><li>Mover o primeiro disco de A para C</li><li>Mover o primeiro disco de A para B</li><li>Mover o primeiro disco de C para B</li><li>Mover o primeiro disco de A para C</li><li>Mover o primeiro disco de B para A</li><li>Mover o primeiro disco de B para C</li><li>Mover o primeiro disco de A para C</li></ol><p>Resolvemos o nosso problema!</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/1_fLOJ9bbxmHuFgYCeeRslhA.gif" class="kg-image" alt="1_fLOJ9bbxmHuFgYCeeRslhA" width="320" height="98" loading="lazy"><figcaption>Torre de Hanói com 3 discos. <a href="https://pt.wikipedia.org/wiki/Torre_de_Han%C3%B3i">Wikipédia</a></figcaption></figure><p>Podes observar a imagem animada acima para uma compreensão melhor do problema.</p><p>Agora, vamos tentar criar o algoritmo para resolver o problema. Espera, temos aqui uma nova palavra: "<strong><strong>Algoritm</strong>o</strong>". O que é isso? Alguma ideia? Sem problema, vamos ver.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/0_B4f6VtfIxmB04Od1.jpg" class="kg-image" alt="0_B4f6VtfIxmB04Od1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/01/0_B4f6VtfIxmB04Od1.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/01/0_B4f6VtfIxmB04Od1.jpg 800w" sizes="(min-width: 720px) 720px" width="800" height="533" loading="lazy"><figcaption>Fotografia criada por <a href="https://unsplash.com/@brucemars?utm_source=medium&amp;utm_medium=referral">bruce mars</a>, extraída do <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3 id="o-que-um-algoritmo"><strong>O que é um algoritmo?</strong></h3><p>Um algoritmo é um dos conceitos mais importantes para um programador de software. Na verdade, acho que não é apenas importante para desenvolvimento de software ou programação, mas para toda a gente. Os algoritmos afetam-nos na nossa vida do dia a dia. Vamos ver como.</p><p>Vamos supor que trabalhas num escritório. Então, todas as manhãs fazes uma série de tarefas em sequência: primeiro acordas, depois vais à casa de banho, tomas o pequeno-almoço, preparas-te para o trabalho, sais de casa e, depois, podes apanhar um táxi ou autocarro, ou começar a andar em direção ao escritório e, depois de um determinado tempo, chegas ao escritório. Podes dizer que todos esses passos formam um <strong><strong>algoritm</strong>o</strong>.</p><p>De forma simples, um algoritmo é um conjunto de tarefas. Espero que não te tenhas esquecido dos passos que realizamos para mover os três discos da pilha A para a C. Também podes dizer que esses passos são o algoritmo para resolver o problema da Torre de Hanói.</p><blockquote><em>Em matemática e ciências da computação<em>, </em>um<em> algoritm</em>o é uma especificação ambígua de como resolver uma classe de problemas<em>. </em>Os <em>Algoritm</em>o<em>s </em>podem realizar cálculo<em>, </em>processamento de dados e tarefas de raciocínio automatizado<em>. — <a href="https://pt.wikipedia.org/wiki/Algoritmo">Wikip</a></em><a href="https://pt.wikipedia.org/wiki/Algoritmo">é</a><em><a href="https://pt.wikipedia.org/wiki/Algoritmo">dia</a></em></em></blockquote><p>Se observares estes passos, podes ver que fizemos a mesma tarefa várias vezes — mover discos de uma pilha para outra. A esses passos dentro de passos podemos chamar <strong><strong>recursi</strong>vidade</strong>.</p><h3 id="recursividade"><strong>Recursividade</strong></h3><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/1_fsYHEgadIdn0fJt-cBXDHQ.gif" class="kg-image" alt="1_fsYHEgadIdn0fJt-cBXDHQ" width="384" height="312" loading="lazy"><figcaption>Recursão – <a href="https://giphy.com/gifs/homer-simpson-the-simpsons-3ov9jQX2Ow4bM5xxuM">giphy</a></figcaption></figure><p><strong><a href="https://pt.wikipedia.org/wiki/Recursividade_(ci%C3%AAncia_da_computa%C3%A7%C3%A3o)"><strong>Recursi</strong>vidade</a><strong> </strong></strong>é chamar a mesma ação a partir dessa ação, tal como na imagem acima.</p><p>Portanto, existe um regra para quando fazemos algum trabalho recursivo: deve existir uma condição para parar a execução da ação. Espero que tenhas compreendido os conceitos básicos sobre recursividade.</p><p>Agora, vamos tentar criar um procedimento que nos ajuda a resolver o problema da Torre de Hanói. Estamos a tentar criar a solução utilizando pseudocódigo<strong><strong>. </strong></strong>Pseudocódigo é um método de escrita de código utilizando a língua inglesa.</p><pre><code>tower(disk, source, intermediate, destination)
{

}</code></pre><p>Esse é o esqueleto da nossa solução. Recebemos o número total de discos como um argumento. Depois temos de passar a origem, local intermédio e destino de modo a podermos compreender o mapa que vamos utilizar para completar a tarefa.</p><p>Agora, precisamos de encontrar um <strong>estado t<strong>erminal</strong></strong>. O estado terminal é o estado onde vamos deixar de chamar a função.</p><pre><code>IF disk is equal 1</code></pre><p>No nosso caso, este seria o nosso estado terminal. Quando existir apenas um disco na pilha, é simples realizar este último passo e, depois disso, a nossa tarefa estará concluída. Não te preocupes se isto não for claro para ti. Quando chegarmos ao final, este conceito será mais claro.</p><p>Bem, encontramos o nosso ponto de estado terminal onde movemos o nosso disco para o destino, deste modo:</p><pre><code>move disk from source to destination</code></pre><p>Agora, chamamos a nossa função novamente ao passar estes argumentos. Neste caso, dividimos a pilha de discos em duas partes. O disco maior (<strong>enésimo</strong> disco) está numa parte e todos os outros discos (<strong><strong>n-1</strong></strong>) estão na segunda parte. Aí, chamamos o método duas vezes para (n-1).</p><pre><code>tower(disk - 1, source, destination, intermediate)</code></pre><p>Tal como indicamos, passamos <strong><strong>total_disks_on_stack — 1</strong></strong> como argumento. Então, movemos novamente o nosso disco, deste modo:</p><pre><code>move disk from source to destination</code></pre><p>Depois disso, chamamos novamente o nosso método, desta forma:</p><pre><code>tower(disk - 1, intermediate, source, destination)</code></pre><p>Vamos observar o nosso pseudocódigo completo:</p><pre><code>tower(disk, source, inter, dest)

IF disk is equal 1, THEN
      move disk from source to destination
   ELSE
      tower(disk - 1, source, destination, intermediate)   // Step 1
      move disk from source to destination                 // Step 2
      tower(disk - 1, intermediate, source, destination)   // Step 3
   END IF
   
END</code></pre><p>Esta é a árvore para três discos:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/1_LEkUpm8-CoxGko2f84gjOg.jpeg" class="kg-image" alt="1_LEkUpm8-CoxGko2f84gjOg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/01/1_LEkUpm8-CoxGko2f84gjOg.jpeg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/01/1_LEkUpm8-CoxGko2f84gjOg.jpeg 800w" sizes="(min-width: 720px) 720px" width="800" height="315" loading="lazy"><figcaption>Árvore da Torre de Hanói (3 discos)</figcaption></figure><p>Este é o código completo em Ruby:</p><pre><code class="language-rb">def tower(disk_numbers, source, auxilary, destination)
  if disk_numbers == 1
    puts "#{source} -&gt; #{destination}"
    return
  end
  tower(disk_numbers - 1, source, destination, auxilary)
  puts "#{source} -&gt; #{destination}"
  tower(disk_numbers - 1, auxilary, source, destination)
  nil
end</code></pre><p>Chama <code>tower(3, 'source','aux','dest')</code></p><p>Resultado:</p><pre><code>source -&gt; dest
source -&gt; aux
dest -&gt; aux
source -&gt; dest
aux -&gt; source
aux -&gt; dest
source -&gt; dest</code></pre><p>Foram necessários sete passos para os três discos chegarem ao destino. Chamamos a isto um <strong>método r<strong>ecursiv</strong>o</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2023/01/0_VXmzOesqL7l18gAr.jpg" class="kg-image" alt="0_VXmzOesqL7l18gAr" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2023/01/0_VXmzOesqL7l18gAr.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/2023/01/0_VXmzOesqL7l18gAr.jpg 800w" sizes="(min-width: 720px) 720px" width="800" height="533" loading="lazy"><figcaption>Fotografia criada por <a href="https://unsplash.com/@aronvisuals?utm_source=medium&amp;utm_medium=referral">Aron Visuals</a>, extraída do <a href="https://unsplash.com/?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3 id="c-lculo-de-complexidade-de-tempo-e-de-espa-o"><strong>Cálculo de complexidade de tempo e de espaço</strong></h3><h4 id="complexidade-de-tempo-texto-do-link-em-ingl-s-"><strong><a href="https://www.techopedia.com/definition/22573/time-complexity" rel="noopener">Complexidade de tempo</a> (texto do link em inglês)</strong></h4><p>Quando executamos código ou uma aplicação na nossa máquina demora tempo — ciclos de CPU. Não é igual em todos os computadores, no entanto. Por exemplo, o tempo de processamento de um Core I7 e de um dual core é diferente. Para resolver este problema, existe um conceito utilizado na ciência da computação chamado <strong>complexidade de tempo</strong>.</p><blockquote>Complexidade de tempo é um conceito em ciências da computação que lida com a quantificação da quantidade de tempo gasto por um conjunto de código ou algoritmo para processar ou executar como uma função da quantidade do input.<br><br>Por outras palavras, complexidade de tempo é essencialmente eficiência, ou quanto tempo uma função do programa demora a processar um determinado input. — <a href="https://www.techopedia.com/definition/22573/time-complexity" rel="noopener">techopedia</a></blockquote><p>A complexidade de tempo dos algoritmos é geralmente expressada utilizando a <strong>notação de O grande</strong>. É uma notação assimptótica para representar a complexidade de tempo.</p><p>Agora, o <strong><strong>t</strong>e<strong>m</strong>po</strong> necessário para mover <strong><strong>n</strong></strong> discos é <strong><strong>T(n).</strong></strong> Existem duas chamadas recursivas para (<strong><strong>n-1</strong></strong>). Existe uma operação de tempo constante para mover um disco de um ponto inicial para um destino, que seja <strong><strong>m1</strong></strong>. Portanto:</p><pre><code>T(n) = 2T(n-1) + m1    ..... eq(1)</code></pre><p>E</p><pre><code>T(0) = m2, a constant   ...... eq(2)
From eq (1)
T(1) = 2T(1-1) + m1
     = 2T(0)+m1
     = 2m2 + m1 ..... eq(3) [From eq 2]
T(2) = 2T(2-1) + m1
     = 2T(1) + m1
     = 4m2 + 2m1 + m1 .... eq(4) [From eq(3)]
T(3) = 2T(3-1) + m1
     = 2T(2) + m1
     = 8m2 + 4m1 + 2m1 + m1  [From eq(4)]</code></pre><p>A partir destes padrões — eq(2) até ao último — podemos dizer que a complexidade de tempo deste algoritmo é <strong><strong>O(2^n)</strong></strong> ou <strong><strong>O(a^n)</strong>,</strong> onde <strong><strong>a</strong></strong> é uma constante maior do que 1. Por isso, tem complexidade de tempo exponencial. Para um único aumento no tamanho do problema, o tempo necessário é o dobro do anterior. Isto é computacionalmente muito caro. A maior parte dos programas recursivos recebem tempo exponencial, e é por isso que é muito difícil escrevê-los de maneira iterada.</p><h4 id="complexidade-de-espa-o-texto-do-link-em-ingl-s-"><strong><a href="https://www.cs.northwestern.edu/academics/courses/311/html/space-complexity.html" rel="noopener">Complexidade de espaço</a> (texto do link em inglês)</strong></h4><p>Depois da explicação da análise da complexidade de tempo, acho que consegues adivinhar o que é isso. É o cálculo do espaço necessário em memória RAM para executar código ou uma aplicação.</p><p>No nosso caso, o espaço para o parâmetro por cada chamada é independente de <strong><strong>n</strong></strong>,<strong><strong> </strong></strong>o que significa que é constante. Que seja <strong><strong>J</strong></strong>. Quando realizamos a segunda chamada recursiva, a primeira termina. Isso significa que podemos reutilizar o espaço depois de finalizar a primeira. Portanto:</p><pre><code>T(n) = T(n-1) + k .... eq(1)
T(0) = k, [constant] .... eq(2)
T(1) = T(1-1) + k
     = T(0) + k
     = 2K
T(2) = 3k
T(3) = 4k</code></pre><p>Então, a complexidade de espaço é <strong><strong>O(n)</strong></strong>.</p><p>Depois destas análises, podemos ver que a complexidade de tempo deste algoritmo é exponencial, mas a complexidade de espaço é linear.</p><h3 id="conclus-o"><strong>Conclusão</strong></h3><p>Depois deste artigo, espero que agora consigas compreender melhor o puzzle da <strong><strong>To</strong>rr<strong>e </strong>de<strong> Han</strong>ó<strong>i</strong></strong> e como resolvê-lo. Além disso, tentei fornecer algum conhecimento básico sobre <strong><strong>algoritm</strong>o<strong>s, </strong>a sua importância<strong>, recursi</strong>vidade<strong>, pseudocód</strong>igo<strong>, complexi</strong>dade de tempo<strong> </strong></strong>e<strong><strong> </strong>complexidade de espaço<strong>. </strong></strong>Se quiseres obter informações mais detalhadas sobre estes tópicos, aqui estão os links para alguns cursos on-line bastante conhecidos:</p><ol><li><a href="https://www.coursera.org/course/algs4partI" rel="noopener">Algoritmos, Parte I</a>, da Coursera</li><li><a href="https://www.coursera.org/course/algs4partII" rel="noopener">Algoritmos, Parte II</a>, da Coursera</li><li><a href="https://www.udacity.com/course/data-structures-and-algorithms-in-python--ud513" rel="noopener">O Curso da Google da Udacity</a></li><li><a href="https://learn.freecodecamp.org/" rel="noopener">Certificação em Algoritmos e Estruturas de Dados em JavaScript (300 horas)</a></li></ol><p>Podes visitar o <a href="https://github.com/dipto0321/datastructures-and-algorithm" rel="noopener">repositório do autor sobre estruturas de dados e algoritmos</a><strong><strong> </strong></strong>para ver as outras soluções dele para diversos problemas.</p><p>O autor pode ser encontrado nestas redes sociais: <a href="https://github.com/dipto0321/" rel="noopener">GitHub</a> | <a href="https://twitter.com/Diptokmk47" rel="noopener">Twitter </a>| <a href="https://www.linkedin.com/in/diptokarmakar47/" rel="noopener">LinkedIn</a></p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Manual de introdução aos algoritmos - com exemplos em JavaScript ]]>
                </title>
                <description>
                    <![CDATA[ Oi, pessoal! Neste artigo, vamos dar uma olhada em algoritmos, um tópico chave quando se trata de ciência da computação e desenvolvimento de software. Algoritmo é uma palavra chique e às vezes intimidadora, mas frequentemente incompreendida. Parece algo muito difícil e complexo, mas, na verdade, nada mais é do que ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/</link>
                <guid isPermaLink="false">62c865338e388806e97faf0c</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Thiago Costa Barbosa ]]>
                </dc:creator>
                <pubDate>Tue, 12 Jul 2022 02:23:06 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/pexels-guduru-ajay-bhargav-1044302-1-.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/introduction-to-algorithms-with-javascript-examples/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Introduction to Algorithms Handbook – with JavaScript Examples</a>
      </p><p>Oi, pessoal! Neste artigo, vamos dar uma olhada em algoritmos, um tópico chave quando se trata de ciência da computação e desenvolvimento de software.</p><p>Algoritmo é uma palavra chique e às vezes intimidadora, mas frequentemente incompreendida. Parece algo muito difícil e complexo, mas, na verdade, nada mais é do que um conjunto de passos que devem ser dados para atingir um determinado objetivo.</p><p>Eu diria que o conhecimento básico sobre algoritmos consiste principalmente em duas coisas:</p><ul><li>Notação assintótica (que usamos para comparar o desempenho de um algoritmo com outro).</li><li>Um conhecimento geral de algoritmos clássicos usados ​​para tarefas muito frequentes, como pesquisar, classificar e percorrer.</li></ul><p>É exatamente isso que vamos ver aqui.😉<br>Vamos lá!</p><h2 id="sum-rio"><strong>Sumário</strong></h2><ul><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#o-que-um-algoritmo">O que é um algoritmo?</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#complexidade-algor-tmica">Complexidade algorítmica</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#algoritmos-de-pesquisa">Algoritmos de pesquisa</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#pesquisa-linear">Pesquisa linear</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#pesquisa-bin-ria">Pesquisa binária</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#algoritmos-de-ordena-o">Algoritmos de ordenação</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-por-bolha">Ordenação por bolha</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-por-sele-o">Ordenação por seleção</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-por-inser-o">Ordenação por inserção</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-por-mistura">Ordenação por mistura</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-r-pida">Ordenação rápida</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#ordena-o-radix">Ordenação Radix</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#algoritmos-de-travessia">Algoritmos de travessia</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#pesquisa-por-largura-bfs-">Pesquisa por largura (Breadth-first search, ou BFS)</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#pesquisa-por-profundidade-dfs-">Pesquisa por profundidade (Depth-first search, ou DFS)</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#pr-ordem-por-dfs">Pré-ordem por DFS</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#p-s-ordem-por-dfs">Pós-ordem por DFS</a></li><li>	<a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#em-ordem-por-dfs">Em ordem por DFS</a></li><li><a href="https://www.freecodecamp.org/portuguese/news/manual-de-introducao-aos-algoritmos-com-exemplos-em-javascript/#conclus-o">Conclusão</a></li></ul><h1 id="o-que-um-algoritmo"><strong>O que é um algoritmo?</strong></h1><p>Como mencionado anteriormente, um algoritmo é apenas um conjunto de etapas que precisam ser executadas em ordem para atingir um determinado objetivo.</p><p>Eu acho que quando as pessoas ouvem a palavra algoritmo pela primeira vez, elas imaginam algo assim...</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg" class="kg-image" alt="markus-spiske-FXFz-sW0uwo-unsplash" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/markus-spiske-FXFz-sW0uwo-unsplash.jpg 2000w" sizes="(min-width: 720px) 720px" width="2000" height="1333" loading="lazy"><figcaption>Uma cena de Matrix ou Mr. Robot</figcaption></figure><p>Na verdade, porém, a imagem abaixo seria a mais correta...</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/frank-holleman-rN_RMqSXRKw-unsplash.jpg" class="kg-image" alt="frank-holleman-rN_RMqSXRKw-unsplash" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/frank-holleman-rN_RMqSXRKw-unsplash.jpg 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/07/frank-holleman-rN_RMqSXRKw-unsplash.jpg 1000w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1600/2022/07/frank-holleman-rN_RMqSXRKw-unsplash.jpg 1600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/frank-holleman-rN_RMqSXRKw-unsplash.jpg 2000w" sizes="(min-width: 720px) 720px" width="2000" height="1500" loading="lazy"><figcaption>Um livro de receitas</figcaption></figure><p>Um algoritmo é como uma receita, no sentido de que indicará os passos necessários que precisam ser seguidos para atingir seu objetivo.</p><p>Uma receita para fazer pão pode ser:</p><pre><code>1- Misture farinha, sal, água e fermento
2- Deixe a massa crescer
3- Leve ao forno por 30 minutos
4- Deixe esfriar e aproveite</code></pre><p>Comentário à parte: espero que você aprecie o fato de eu estar ensinando como codificar e cozinhar ao mesmo tempo, tudo de graça. Aproveite que não é sempre! 😜</p><p>Um algoritmo para identificar se uma palavra é um <a href="https://pt.wikipedia.org/wiki/Pal%C3%ADndromo">palíndromo</a> ou não poderia ser:</p><pre><code class="language-javascript">function isPalindrome(word) {
    // Passo 1- Colocar um ponteiro em cada extremo da palavra
    // Passo 2 - Percorrer a string "internamente"
    // Passo 3 - A cada iteração, verificar se os ponteiros representam valores iguais
    // Se esta condição não for cumprida, a palavra não é um palíndromo
    let left = 0
    let right = word.length-1

    while (left &lt; right) {
        if (word[left] !== word[right]) return false
        left++
        right--
    }
    
    return true
}

isPalindrome("neuquen") // true
isPalindrome("Buenos Aires") // false</code></pre><p>Assim como em uma receita, nesse algoritmo temos etapas com uma determinada finalidade, que são executadas em uma determinada ordem para alcançar o resultado que desejamos.</p><p>Seguindo <a href="https://pt.wikipedia.org/wiki/Algoritmo">a Wikipédia</a>:</p><blockquote><em>Um algoritmo é uma sequência finita de instruções bem definidas, normalmente usadas para resolver uma classe de problemas específicos ou para realizar uma computação<em>.</em></em></blockquote><h1 id="complexidade-algor-tmica"><strong>Complexidade algorítmica</strong></h1><p>Agora que sabemos o que é um algoritmo, vamos aprender a comparar diferentes algoritmos entre si.</p><p>Digamos que nos é apresentado este problema:</p><blockquote>Escreva uma função que receba dois parâmetros: um array não vazio de inteiros distintos e um inteiro representando o resultado desejado. Se quaisquer dois números no array, somados, forem igual ao desejado, a função deve retorná-los em um array. Se não houver dois números que, somados, sejam iguais ao resultado desejado, a função deve retornar um array vazio.</blockquote><p>Esta poderia ser uma solução válida para o problema:</p><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
    let result = []
    // Usamos um laço aninhado para testar todas as combinações possíveis de números dentro do array
        for (let i = 0; i &lt; array.length; i++) {
          for (let j = i+1; j &lt; array.length; j++) {
              // Se encontrarmos a combinação certa, colocamos ambos os valores no array result e o retornamos
              if (array[i] + array[j] === targetSum) {
                  result.push(array[i])
                  result.push(array[j])
                  return result
              }
          }
      }
      // Retorna o array result
      return result
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>Esta é outra solução válida:</p><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
    // Ordenar o array e fazer a iteração por ele com um ponteiro em cada extremo
    // A cada iteração, verificar se a soma dos dois ponteiros é maior ou menor que o alvo
    // Se for maior, mover o ponteiro da direita para a esquerda
    // Se for menor, mover o ponteiro da esquerda para a direita
	let sortedArray = array.sort((a,b) =&gt; a-b)
	let leftLimit = 0
	let rightLimit = sortedArray.length-1

	while (leftLimit &lt; rightLimit) {
			const currentSum = sortedArray[leftLimit] + sortedArray[rightLimit]

			if (currentSum === targetSum) return [sortedArray[leftLimit], sortedArray[rightLimit]]
			else currentSum &lt; targetSum ? leftLimit++ : rightLimit--        
	}

	return []
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>E esta é uma terceira solução válida:</p><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
    // Iterar sobre o array uma vez e a cada iteração
    // Verificar se o número que você precisa para chegar ao destino existe no array
    // Se existir, retornar seu índice e o índice de número atual
	let result = []

	for (let i = 0; i &lt; array.length; i++) {
        let desiredNumber = targetSum - array[i]
        if (array.indexOf(desiredNumber) !== -1 &amp;&amp; array.indexOf(desiredNumber) !== i) {
            result.push(array[i])
            result.push(array[array.indexOf(desiredNumber)])
            break
        }
	}

    return result
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>Então, como podemos comparar qual solução é a melhor? Todas elas cumprem o objetivo, certo?</p><p>Mas além da <strong><strong>eficácia</strong></strong> (se o objetivo é alcançado ou não), devemos também avaliar os algoritmos em termos de <strong><strong>eficiência</strong></strong> , ou seja, o que resolve o problema usando a menor quantidade de recursos <strong><strong>em termos de tempo</strong></strong> (tempo de processamento) <strong><strong>e espaço</strong></strong> (uso de memória).</p><p>Um pensamento automático que surge quando se pensa nisso pela primeira vez é: "É só medir quanto tempo leva para o algoritmo ser executado". E isso é válido.</p><p>O problema é que o mesmo algoritmo pode demorar mais ou menos tempo em um computador diferente, devido ao seu hardware e configuração. E mesmo no mesmo computador, pode levar mais ou menos tempo para executar, por conta das tarefas em segundo plano que você está executando naquele determinado momento.</p><p>O que precisamos é de uma maneira objetiva e invariável de medir o desempenho de um algoritmo, e é justamente para isso que serve a <strong><strong>notação assintótica</strong></strong>.</p><p>A notação assintótica (também chamada de notação <strong><strong>Big O</strong></strong>) é um sistema que nos permite <strong><strong>analisar e comparar o desempenho de um algoritmo à medida que sua entrada </strong>aumenta</strong>.</p><p>Big O é um método padronizado para analisar e comparar a complexidade (em termos de tempo de execução e espaço) de diferentes algoritmos. A complexidade Big O de um algoritmo será sempre a mesma, não importa em qual computador você a "calcule", porque a complexidade é calculada com base no fato de que <strong><strong>o número de operações do algoritmo varia quando a entrada varia</strong></strong>. Essa relação continua sempre igual, não importando o ambiente de execução.</p><p>Existem muitas complexidades possíveis que um algoritmo pode ter, mas as mais comuns são as seguintes:</p><ul><li><strong><strong>Constante — O(1):</strong></strong> Quando o número de operações/espaço necessário permanece igual, independentemente da entrada. Tomemos, por exemplo, uma função que recebe um número como entrada e retorna esse número menos 10. Não importa se você inserir 100 ou 1000000 como entrada, essa função sempre executará uma única operação (a subtração de 10), então a complexidade é constante O(1).</li><li><strong><strong>Logarítmica — O(log n):</strong></strong> Quando o número de operações/espaço necessário cresce a uma taxa cada vez mais lenta em comparação ao crescimento da entrada. Esse tipo de complexidade é frequentemente encontrada em algoritmos que adotam uma abordagem de dividir e conquistar ou em algoritmos de busca. O exemplo clássico é a busca binária, em que, dado o conjunto de dados, você continuamente passa/corta pela metade até chegar ao resultado final.</li><li><strong><strong>Linear —</strong> <strong>O(n):</strong></strong> Quando o número de operações/espaço necessário cresce na mesma taxa que a entrada. Por exemplo, um laço que imprime todos os valores encontrados em um array. O número de operações crescerá junto com o comprimento do array, então a complexidade é linear O(n).</li><li><strong><strong>Quadrática — O(n²):</strong></strong> Quando o número de operações/espaço necessário cresce à potência de dois em relação à entrada. Laços aninhados são o exemplo clássico dessa complexidade. Imagine que temos um laço que itera através de um array de números, e dentro desse laço temos outro que itera todo o array novamente. Para cada valor no array estamos iterando sobre o array duas vezes, então a complexidade é quadrática O(n²).</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/2022-05-16_1232131236.png" class="kg-image" alt="2022-05-16_1232131236" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/2022-05-16_1232131236.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/07/2022-05-16_1232131236.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/2022-05-16_1232131236.png 1200w" sizes="(min-width: 720px) 720px" width="1200" height="1037" loading="lazy"><figcaption>Uma representação gráfica de complexidades de algoritmos clássicos</figcaption></figure><p>Observe que a mesma notação é usada quando falamos sobre ambas as complexidades, tempo e espaço. Digamos, por exemplo, que temos uma função que sempre cria um array com um único valor, não importando a entrada que ela receba, então a complexidade do espaço será constante O(1), e assim por diante com os outros tipos de complexidade.</p><p>Para entender melhor tudo isso, vamos voltar ao nosso problema e analisar as soluções.</p><h3 id="exemplo-1-"><strong>Exemplo 1:</strong></h3><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
    let result = []
        for (let i = 0; i &lt; array.length; i++) {
          for (let j = i+1; j &lt; array.length; j++) {
              if (array[i] + array[j] === targetSum) {
                  result.push(array[i])
                  result.push(array[j])
                  return result
              }
          }
      }
      return result
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>Neste exemplo, estamos iterando sobre o array de parâmetros e, para cada valor dentro do array, estamos iterando todo o array novamente procurando por um número que, somado, seja igual ao resultado desejado.</p><p>Então, cada iteração conta como uma tarefa.</p><ul><li>Se tivéssemos <strong><strong>3</strong></strong> números no array, iteraríamos 3 vezes para cada número e mais 9 vezes (3 vezes os três números do array). Total: <strong><strong>12</strong></strong> tarefas.</li><li>Se tivéssemos <strong><strong>4</strong></strong> números no array, iteraríamos 4 vezes para cada número e mais 16 vezes (4 vezes os quatro números do array). Total: <strong><strong>20</strong></strong> tarefas.</li><li>Se tivéssemos <strong><strong>5</strong></strong> números no array, iteraríamos 5 vezes para cada número e mais 25 vezes (5 vezes os cinco números do array). Total: <strong><strong>25</strong></strong> tarefas.</li></ul><p>Você pode ver como o número de tarefas neste algoritmo cresce exponencialmente e desproporcional à entrada. A complexidade para este algoritmo é quadrática – <strong><strong>O(n²)</strong></strong>.</p><p>Sempre que vemos laços aninhados, devemos pensar em complexidade quadrática =&gt; O QUE É RUIM =&gt; Provavelmente, há uma maneira melhor de resolver isso.</p><h3 id="exemplo-2-"><strong>Exemplo 2:</strong></h3><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
	let sortedArray = array.sort((a,b) =&gt; a-b)
	let leftLimit = 0
	let rightLimit = sortedArray.length-1

	while (leftLimit &lt; rightLimit) {
			const currentSum = sortedArray[leftLimit] + sortedArray[rightLimit]

			if (currentSum === targetSum) return [sortedArray[leftLimit], sortedArray[rightLimit]]
			else currentSum &lt; targetSum ? leftLimit++ : rightLimit--        
	}

	return []
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>Aqui, estamos classificando o algoritmo antes de iterarmos nele. E só depois, iteramos apenas uma vez, usando um ponteiro em cada extremo do array e iterando "internamente".</p><p>Essa solução é melhor do que a anterior, pois estamos iterando apenas uma vez. Ainda estamos, no entanto, classificando o array (que geralmente tem uma complexidade logarítmica) e depois iterando uma vez (que é a complexidade linear). A complexidade algorítmica desta solução é <strong><strong>O(n log(n)).</strong></strong></p><h3 id="exemplo-3-"><strong>Exemplo 3:</strong></h3><pre><code class="language-javascript">function twoNumberSum(array, targetSum) {
    // Iterar sobre o array uma vez e, a cada iteração
    // verificar se o número que você precisa obter para chegar ao resultado esperado existe no array
    // Se existir, retornar o índice e o índice do número atual
	let result = []

	for (let i = 0; i &lt; array.length; i++) {
        let desiredNumber = targetSum - array[i]
        if (array.indexOf(desiredNumber) !== -1 &amp;&amp; array.indexOf(desiredNumber) !== i) {
            result.push(array[i])
            result.push(array[array.indexOf(desiredNumber)])
            break
        }
	}

    return result
}

console.log(twoNumberSum([9,1,3,4,5], 6)) // [1,5]
console.log(twoNumberSum([1,2,3,4,5], 10)) // []</code></pre><p>Neste último exemplo, estamos iterando o array apenas uma vez, sem fazer mais nada antes. Essa é a melhor solução, pois estamos realizando o menor número de operações. A complexidade neste caso é linear – <strong><strong>O(n)</strong></strong> .</p><p>Este é realmente <strong><strong>o conceito mais importante por trás dos algoritmos</strong></strong>. Ser capaz de comparar diferentes implementações e entender qual é mais eficiente e por que é realmente um conhecimento importante de se ter. Portanto, se o conceito ainda não estiver claro para você, eu sugiro ler os exemplos novamente, procurar outros recursos ou conferir <a href="https://www.youtube.com/watch?v=8hly31xKli0">esta aula incrível do freeCodeCamp</a> (em inglês).</p><h1 id="algoritmos-de-pesquisa"><strong>Algoritmos de pesquisa</strong></h1><p>Uma vez que você tenha um bom entendimento sobre a complexidade algorítmica, a próxima coisa a saber são os algoritmos populares usados ​​para resolver tarefas de programação muito comuns. Então, vamos começar com as pesquisas.</p><p>Ao procurar um valor em uma estrutura de dados, existem diferentes abordagens que podemos adotar. Vamos dar uma olhada em duas das opções mais usadas e compará-las.</p><h2 id="pesquisa-linear"><strong><strong><strong>Pesquisa linear</strong></strong></strong></h2><p>A pesquisa linear consiste em iterar sobre a estrutura de dados, um valor por vez, e verificar se esse valor é o que estamos procurando. É, provavelmente, o melhor e mais intuitivo tipo de pesquisa que podemos fazer se a estrutura de dados que estamos usando não estiver ordenada.</p><p>Suponha que temos um array de números e que, para esse array, queremos escrever uma função que receba um número como entrada e retorne o índice desse número no array. Caso o número não exista no array, retornamos -1. Uma abordagem possível poderia ser a seguinte:</p><pre><code class="language-javascript">const arr = [1,2,3,4,5,6,7,8,9,10]

const search = num =&gt; {
    for (let i = 0; i &lt; arr.length; i++) {
        if (num === arr[i]) return i
    }
    return -1
}

console.log(search(6)) // 5
console.log(search(11)) // -1</code></pre><p>Como o array não está ordenado, não temos como saber a posição aproximada de cada valor, então o melhor que podemos fazer é verificar um valor por vez. A complexidade desse algoritmo é <strong><strong>linear - O(n)</strong></strong> &nbsp;e, no pior cenário, teremos que iterar sobre todo o array uma vez para obter o resultado da nossa pesquisa.</p><p>A pesquisa linear é a abordagem usada por muitos métodos integrados do JavaScript, como <code>indexOf</code>, <code>includes</code> e <code>findIndex</code>.</p><h2 id="pesquisa-bin-ria"><strong><strong><strong>Pesquisa binária</strong></strong></strong></h2><p>Quando temos uma estrutura de dados ordenada, há uma abordagem muito mais eficiente que podemos adotar: a pesquisa binária. O que fazemos na pesquisa (ou <em>busca</em>) binária é o seguinte:</p><ul><li>Selecionamos o valor médio da nossa estrutura de dados e "perguntamos": este é o valor que estamos procurando?</li><li>Se não for, "perguntamos" se o valor que procuramos é maior ou menor que o valor médio</li><li>Se for maior, "descartamos" todos os valores menores que o valor médio. Se for menor, "descartamos" todos os valores maiores que o valor médio.</li><li>Por fim, repetimos a mesma operação até encontrarmos o valor pesquisado ou até não ser mais possível dividir o "pedaço" restante da estrutura de dados.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/binary_search_1.png" class="kg-image" alt="binary_search_1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/binary_search_1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/binary_search_1.png 693w" width="693" height="313" loading="lazy"><figcaption>Uma representação gráfica da pesquisa (busca) binária</figcaption></figure><p>O que é mais legal na busca binária é que, em cada iteração, estamos descartando aproximadamente metade da estrutura de dados. Isso torna a pesquisa realmente rápida e eficiente. 👌</p><p>Suponha que temos o mesmo array (ordenado) e queremos escrever a mesma função de antes, que recebe um número como entrada e retorna o índice desse número no array. Caso ele não exista no array, retornamos -1. Podemos escrever essa pesquisa binária da seguinte forma:</p><pre><code class="language-javascript">const arr = [1,2,3,4,5,6,7,8,9,10]

const search = num =&gt; {
    // Usaremos três ponteiros.
    // Um no início do array, um no final e outro no meio.
    let start = 0
    let end = arr.length-1
    let middle = Math.floor((start+end)/2)

    // Enquanto não encontramos o número, e o ponteiro inicial for igual ou menor ao ponteiro final
    while (arr[middle] !== num &amp;&amp; start &lt;= end) {
        // Se o número desejado for menor que o meio, descartamos a metade maior do array
        if (num &lt; arr[middle]) end = middle - 1
        // Se o número desejado for maior que o meio, descartamos a metade menor do array
        else start = middle + 1
        // Recalculamos o valor do meio
        middle = Math.floor((start+end)/2)
    }
    // Se saírmos do laço, significa que encontramos o valor ou que o array não pode mais ser dividido
    return arr[middle] === num ? middle : -1
}

console.log(search(6)) // 5
console.log(search(11)) // -1</code></pre><p>Essa abordagem pode parecer "mais código" no início, mas as iterações potenciais são, na verdade, muito menores do que na pesquisa linear. Isso ocorre porque, em cada iteração, estamos descartando aproximadamente metade da estrutura de dados. A complexidade deste algoritmo é <strong><strong>logarítmica</strong></strong> – <strong><strong>O(log n)</strong></strong>.</p><h1 id="algoritmos-de-ordena-o"><strong>Algoritmos de ordenação</strong></h1><p>Ao ordenar estruturas de dados, existem muitas abordagens que podemos adotar. Vamos olhar algumas das opções mais usadas e compará-las.</p><h2 id="ordena-o-por-bolha"><strong>Orden</strong>ação por bolha</h2><p>A ordenação por bolha (do inglês <em>Bubble Sort</em>) itera pela estrutura de dados e compara um par de valores por vez. Se a ordem desses valores estiver incorreta, ele troca suas posições para corrigi-la. A iteração é repetida até que os dados estejam ordenados. Esse algoritmo faz com que valores maiores "borbulhem" até o final do array.</p><p>Esse algoritmo tem uma complexidade <strong><strong>quadrática – O(n²)</strong></strong>, pois vai comparar cada valor com o resto dos valores uma vez.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/Apvay5Jc9.png" class="kg-image" alt="Apvay5Jc9" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/Apvay5Jc9.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/Apvay5Jc9.png 700w" width="700" height="415" loading="lazy"></figure><p>Uma forma de fazer isso é:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

const bubbleSort = arr =&gt; {
    // Definir uma variável como flag
    let noSwaps
	
    // Fazer um laço aninhado
    // com um ponteiro iterando da direita para a esquerda
    for (let i = arr.length; i &gt; 0; i--) {
        noSwaps = true
		// e outro iterando da esquerda para a direita
        for (let j = 0; j &lt; i-1; j++) {
            // Comparar os dois ponteiros
            if (arr[j] &gt; arr[j+1]) {
                let temp = arr[j]
                arr[j] = arr[j+1]
                arr[j+1] = temp
                noSwaps = false
            }
        }
        if (noSwaps) break
    }
}

bubbleSort(arr)
console.log(arr) // [1,2,3,4,5,6,7,8,9,10]</code></pre><h2 id="ordena-o-por-sele-o"><strong>Orden<strong><strong><strong><strong>ação por seleção</strong></strong></strong></strong></strong></h2><p>A ordenação por seleção (do inglês <em>Selection Sort</em>) é semelhante à ordenação por bolha, mas, em vez de colocar os valores maiores no final da estrutura de dados, ela se concentra em colocar os valores menores no início. Os passos que executa são:</p><ul><li>Armazenar o primeiro item da estrutura de dados como o valor mínimo.</li><li>Iterar através da estrutura de dados comparando cada valor com o valor mínimo.</li><li>Se um valor menor for encontrado, identificar esse valor como o novo valor mínimo e troca as posições do novo valor mínimo com o antigo.</li><li>Repetir essa iteração até que a estrutura de dados esteja ordenada.</li></ul><p>Este algoritmo tem uma complexidade <strong><strong>quadrática – O(n²)</strong></strong>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/xL8U4iwf8.png" class="kg-image" alt="xL8U4iwf8" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/xL8U4iwf8.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/xL8U4iwf8.png 604w" width="604" height="737" loading="lazy"></figure><p>Uma forma de fazer isso é assim:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

const selectionSort = arr =&gt; {
    
    for (let i = 0; i &lt; arr.length; i++) {
        let lowest = i
        
        for (let j = i+1; j &lt; arr.length; j++) {
            if (arr[j] &lt; arr[lowest]) {
                lowest = j
            }
        }

        if (i !== lowest) {
            let temp = arr[i]
            arr[i] = arr[lowest]
            arr[lowest] = temp
        }
    }
}

selectionSort(arr)
console.log(arr) // [1,2,3,4,5,6,7,8,9,10]</code></pre><h2 id="ordena-o-por-inser-o"><strong>Orden<strong><strong><strong><strong>ação por inserção</strong></strong></strong></strong></strong></h2><p>A ordenação por inserção (do inglês <em>Insertion Sort</em>) ordena a estrutura de dados criando uma "metade ordenada", que é sempre ordenada corretamente e itera pela estrutura de dados selecionando cada valor e inserindo-o na metade ordenada, no local exato em que deveria estar.</p><p>As etapas que ela faz são:</p><ul><li>Começar escolhendo o segundo elemento na estrutura de dados.</li><li>Comparar este elemento com o anterior e trocar suas posições, se necessário.</li><li>Continuar com o próximo elemento e, se não estiver na posição correta, percorrer a "metade ordenada" para encontrar sua posição correta e inserir ali.</li><li>Repetir o mesmo processo até que toda a estrutura de dados esteja ordenada.</li></ul><p>A complexidade desse algoritmo é <strong><strong>quadrática (O(n²))</strong></strong>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/7T4A0Sfqr.png" class="kg-image" alt="7T4A0Sfqr" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/7T4A0Sfqr.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/7T4A0Sfqr.png 700w" width="700" height="450" loading="lazy"></figure><p>Uma possível aplicação dela é assim:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

const insertionSort = arr =&gt; {
    let currentVal
    
    for (let i = 0; i &lt; arr.length; i++) {
        currentVal = arr[i]

        for (var j = i-1; j &gt;= 0 &amp;&amp; arr[j] &gt; currentVal; j--) {
            arr[j+1] = arr[j]
        }
        
        arr[j+1] = currentVal
    }
    
    return arr
}

insertionSort(arr)
console.log(arr) // [1,2,3,4,5,6,7,8,9,10]</code></pre><p>O problema com as ordenações por bolha, seleção e inserção é que esses algoritmos não são bem dimensionados (todos de complexidade quadrática).</p><p>Há opções muito melhores que podemos escolher quando trabalhamos com grandes conjuntos de dados. Algumas delas são a ordenação por mistura, a rápida e a Radix. Então, vamos dar uma olhada nelas agora!</p><h2 id="ordena-o-por-mistura"><strong>Orden<strong><strong><strong><strong>ação</strong></strong> por m</strong></strong>istura</strong></h2><p>A ordenação por mistura (do inglês <em>Merge Sort</em>) é um algoritmo que decompõe recursivamente a estrutura de dados em valores individuais e depois a compõe novamente de forma ordenada.</p><p>As etapas dele são:</p><ul><li>Quebrar recursivamente a estrutura de dados em metades até que cada "peça" tenha apenas um valor.</li><li>Em seguida, mesclar recursivamente as partes de forma ordenada até voltar ao tamanho da estrutura de dados original.</li></ul><p>Este algoritmo tem complexidade <strong><strong>O(n log n)</strong></strong>, uma vez que a decomposição tem uma complexidade de log n e a comparação tem uma complexidade de n.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/Oiryt3mR92.png" class="kg-image" alt="Oiryt3mR92" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/Oiryt3mR92.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/Oiryt3mR92.png 700w" width="700" height="393" loading="lazy"></figure><p>Podemos escrevê-lo deste modo:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

// Função do merge (mistura)
const merge = (arr1, arr2) =&gt; {
    const results = []
    let i = 0
    let j = 0

    while (i &lt; arr1.length &amp;&amp; j &lt; arr2.length) {
        if (arr2[j] &gt; arr1[i]) {
            results.push(arr1[i])
            i++
        } else {
            results.push(arr2[j])
            j++
        }
    }

    while (i &lt; arr1.length) {
        results.push(arr1[i])
        i++
    }

    while (j &lt; arr2.length) {
        results.push(arr2[j])
        j++
    }

    return results
}

const mergeSort = arr =&gt; {
    if (arr.length &lt;= 1) return arr
    let mid = Math.floor(arr.length/2)
    let left = mergeSort(arr.slice(0,mid))
    let right = mergeSort(arr.slice(mid))
    return merge(left, right)
}

console.log(mergeSort(arr)) // [1,2,3,4,5,6,7,8,9,10]</code></pre><h2 id="ordena-o-r-pida"><strong>Orden</strong>ação rápida</h2><p>A ordenação rápida (do inglês <em>Quick Sort</em>) funciona selecionando um elemento de "pivô" (ou eixo) e encontrando o índice onde ele deve terminar no array classificado.</p><p>O tempo de execução dessa classificação depende em parte de como o pivô é selecionado. Idealmente, deve ser aproximadamente o valor mediano do conjunto de dados que está sendo classificado.</p><p>Os passos desse algoritmo são:</p><ul><li>Identificar o valor do pivô e colocá-lo no índice que deveria ser.</li><li>Executar recursivamente o mesmo processo em cada “metade” da estrutura de dados.</li></ul><p>Este algoritmo tem uma complexidade <strong><strong>O(n log n).</strong></strong></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/MdqPPTf7.png" class="kg-image" alt="MdqPPTf7" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/MdqPPTf7.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/MdqPPTf7.png 960w" sizes="(min-width: 720px) 720px" width="960" height="540" loading="lazy"></figure><p>Uma forma de fazer isso é:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

const pivot = (arr, start = 0, end = arr.length - 1) =&gt; {
    const swap = (arr, idx1, idx2) =&gt; [arr[idx1], arr[idx2]] = [arr[idx2], arr[idx1]]

    let pivot = arr[start]
    let swapIdx = start

    for (let i = start+1; i &lt;= end; i++) {
        if (pivot &gt; arr[i]) {
            swapIdx++
            swap(arr, swapIdx, i)
        }
    }

    swap(arr, start, swapIdx)
    return swapIdx
}

const quickSort = (arr, left = 0, right = arr.length - 1) =&gt; {
    if (left &lt; right) {
        let pivotIndex = pivot(arr, left, right)
        quickSort(arr, left, pivotIndex-1)
        quickSort(arr, pivotIndex+1, right)
    }

    return arr
}

console.log(quickSort(arr)) // [1,2,3,4,5,6,7,8,9,10]</code></pre><h2 id="ordena-o-radix"><strong>Ordena<strong><strong><strong><strong>ção </strong></strong>Radix</strong></strong></strong></h2><p>Radix é um algoritmo que funciona de modo diferente dos vistos anteriormente, no sentido de não comparar valores. Ele é usado para ordenar listas de números e, para isso, explora o fato de que o tamanho de um número é definido pelo número de algarismos que ele possui (quanto mais algarismos, maior o número).</p><p>O que o Radix faz é ordenar os valores por seus algarismos, em ordem. Ele ordena todos os valores pelo primeiro algarismo, depois novamente pelo segundo, depois pelo terceiro… Este processo é repetido tantas vezes quanto o número de algarismos do maior número da lista. E ao final desse processo, o algoritmo retorna a lista totalmente ordenada.</p><p>Os passos que ele faz são:</p><ul><li>Calcular quantos algarismos o maior número tem.</li><li>Percorrer a lista até o maior número de algarismos. Em cada iteração:</li><li>Criar "buckets" para cada algarismo (de 0 a 9) e coloca cada valor em seu bucket correspondente de acordo com o algarismo que está sendo avaliado.</li><li>Substituir a lista existente pelos valores classificados nos buckets, começando em 0 e indo até 9.</li></ul><p>Este algoritmo tem uma complexidade <strong><strong>O(n*k)</strong></strong>, sendo k o número de algarismos que o maior número possui. Dado que não compara valores entre si, esse algoritmo tem um tempo de execução melhor do que os vistos anteriormente, mas somente funcionará em listas numéricas.</p><p>Se quisermos um algoritmo de ordenação agnóstica de dados, provavelmente usaríamos qualquer um dos anteriores.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/EwnCsTr4y.png" class="kg-image" alt="EwnCsTr4y" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/EwnCsTr4y.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/EwnCsTr4y.png 960w" sizes="(min-width: 720px) 720px" width="960" height="540" loading="lazy"></figure><p></p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/wJlnCC_kg.png" class="kg-image" alt="wJlnCC_kg" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/wJlnCC_kg.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/wJlnCC_kg.png 668w" width="668" height="265" loading="lazy"></figure><p>Uma possível implementação poderia ser a seguinte:</p><pre><code class="language-javascript">const arr = [3,2,1,4,6,5,7,9,8,10]

const getDigit = (num, i) =&gt; Math.floor(Math.abs(num) / Math.pow(10, i)) % 10

const digitCount = num =&gt; {
    if (num === 0) return 1
    return Math.floor(Math.log10(Math.abs(num))) + 1
}

const mostDigits = nums =&gt; {
    let maxDigits = 0

    for (let i = 0; i &lt; nums.length; i++) maxDigits = Math.max(maxDigits, digitCount(nums[i]))

    return maxDigits
}

const radixSort = nums =&gt; {
    let maxDigitCount = mostDigits(nums)

    for (let k = 0; k &lt; maxDigitCount; k++) {
        let digitBuckets = Array.from({ length: 10 }, () =&gt; [])
        
        for (let i = 0; i &lt; nums.length; i++) {
            let digit = getDigit(nums[i], k)
            digitBuckets[digit].push(nums[i])
        }

        nums = [].concat(...digitBuckets)
    }

    return nums
}

console.log(radixSort(arr)) // [1,2,3,4,5,6,7,8,9,10]</code></pre><h1 id="algoritmos-de-travessia"><strong>Algoritmos de travessia</strong></h1><p>O último tipo de algoritmo que veremos são os algoritmos de travessia, usados ​​para iterar através de estruturas de dados de formas diferentes (mas, principalmente, árvores e grafos).</p><p>Ao iterar uma estrutura de dados como a de árvore, podemos priorizar as iterações de duas formas: profundidade ou largura.</p><p>Se priorizarmos a profundidade, vamos "descer" por cada galho da árvore, indo do tronco até a folha de cada galho.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-42.png" class="kg-image" alt="image-42" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/image-42.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-42.png 645w" width="645" height="726" loading="lazy"><figcaption>Pesquisa em profundidade</figcaption></figure><p>Se priorizarmos a largura, passaremos por cada "nível" da árvore horizontalmente, iterando por todos os nós que estão no mesmo nível antes de "descer" para o próximo nível.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-39.png" class="kg-image" alt="image-39" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/image-39.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-39.png 648w" width="648" height="733" loading="lazy"><figcaption>Pesquisa por largura</figcaption></figure><p>A decisão de qual algoritmo escolher dependerá em grande parte do valor que procuramos em nossa iteração e de como nossa estrutura de dados está construída.</p><h2 id="pesquisa-por-largura-bfs-"><strong>Pesquisa por largura (BFS)</strong></h2><p>Vamos analisar a BFS primeiro. Como mencionei antes, esse tipo de travessia percorrerá nossa estrutura de dados de modo horizontal. Seguindo esta imagem de exemplo abaixo, os valores seriam percorridos na seguinte ordem: <code>[10, 6, 15, 3, 8, 20]</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-40.png" class="kg-image" alt="image-40" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/07/image-40.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/07/image-40.png 970w" sizes="(min-width: 720px) 720px" width="970" height="738" loading="lazy"></figure><p>Normalmente, as etapas seguidas pelos algoritmos BFS são as seguintes:</p><ul><li>Criar uma fila e uma variável para armazenar os nós que foram "visitados"</li><li>Colocar o nó principal dentro da fila</li><li>Continuar fazendo o laço enquanto houver algo na fila</li><li>Retirar um nó da fila e colocar o valor do nó na variável que armazena os nós visitados</li><li>Se houver a propriedade <em>left</em> no nó que foi removido da fila, adicioná-la à fila</li><li>Se houver uma propriedade <em>right</em> no nó que foi removido da fila, adicioná-la à fila</li></ul><p>Uma forma de fazer isso é a seguinte:</p><pre><code class="language-javascript">class Node {
    constructor(value) {
        this.value = value
        this.left = null
        this.right = null
    }
}

class BinarySearchTree {
    constructor(){ this.root = null; }

    insert(value){
        let newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        let current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value &lt; current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }

    BFS(){
        let node = this.root,
            data = [],
            queue = [];
        queue.push(node);

        while(queue.length){
           node = queue.shift();
           data.push(node.value);
           if(node.left) queue.push(node.left);
           if(node.right) queue.push(node.right);
        }
        return data;
    }
}


const tree = new BinarySearchTree()
tree.insert(10)
tree.insert(6)
tree.insert(15)
tree.insert(3)
tree.insert(8)
tree.insert(20)

console.log(tree.BFS()) // [ 10, 6, 15, 3, 8, 20 ]</code></pre><h2 id="pesquisa-por-profundidade-dfs-"><strong><strong><strong>Pesquisa por </strong></strong>p<strong><strong>rofundidade </strong></strong>(DFS)</strong></h2><p>A DFS por sua vez, fará a iteração através da nossa estrutura de dados de modo vertical. Seguindo o mesmo exemplo que usamos para a BFS, os valores seriam percorridos nessa ordem: <code>[10, 6, 3, 8, 15, 20]</code>.</p><p>Essa maneira de fazer a DFS é chamada de "pré-ordem". Existem, na verdade, três maneiras principais pelas quais a DFS pode ser feita, a diferença entre elas é apenas a alteração da ordem em que os nós são visitados.</p><ul><li><strong><strong>Pré-<strong>ordem</strong>:</strong></strong> visite o nó atual, depois o nó <em>menor</em> e depois o nó <em>maior</em>.</li><li><strong><strong>Pós-ordem:</strong></strong> explore todos os filhos menores e todos os filhos maiores antes de visitar o nó.</li><li><strong><strong>Na ordem:</strong></strong> Explore todos os filhos menores, visita o nó atual e explore todos os filhos maiores.</li></ul><p>Se isso parece confuso, não se preocupe. Não é tão complexo e ficará mais claro depois com os exemplos.</p><h3 id="pr-ordem-por-dfs"><strong>Pré-ordem por DFS</strong></h3><p>No algoritmo de pré-ordem por profundidade, seguimos as etapas de:</p><ul><li>Criar uma variável para armazenar os valores dos nós visitados</li><li>Armazenar a raiz da árvore em uma variável</li><li>Escrever uma função auxiliar que aceite um nó como parâmetro</li><li>Adicionar o valor do nó para a variável que armazena os valores</li><li>Se o nó tiver uma propriedade <em>left </em>(que é um filho com valor menor que o atual), chamar a função auxiliar com o nó <em>left</em> como parâmetro</li><li>Se o nó tiver uma propriedade <em>right </em>(que é um filho com valor maior que o atual), chamar a função auxiliar com o nó <em>right </em>como parâmetro</li></ul><p>Uma maneira de fazer isso é:</p><pre><code class="language-javascript">class Node {
    constructor(value){
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree {
    constructor(){
        this.root = null;
    }
    insert(value){
        var newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        var current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value &lt; current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }

    DFSPreOrder(){
        var data = [];
        function traverse(node){
            data.push(node.value);
            if(node.left) traverse(node.left);
            if(node.right) traverse(node.right);
        }
        traverse(this.root);
        return data;
    }

}


var tree = new BinarySearchTree()
tree.insert(10)
tree.insert(6)
tree.insert(15)
tree.insert(3)
tree.insert(8)
tree.insert(20)

console.log(tree.DFSPreOrder()) // [ 10, 6, 3, 8, 15, 20 ]</code></pre><h3 id="p-s-ordem-por-dfs"><strong>Pós-ordem por DFS</strong></h3><p>No algoritmo de pós-ordem por profundidade, fazemos o seguinte:</p><ul><li>Criar uma variável para armazenar os valores dos nós visitados</li><li>Armazenar a raiz da árvore em uma variável</li><li>Escrever uma função auxiliar que aceite um nó como parâmetro</li><li>Se o nó tiver uma propriedade <em>left</em>, chamar a função auxiliar com o nó <em>left</em> como parâmetro</li><li>Se o nó tiver uma propriedade <em>right</em>, chamar a função auxiliar com o nó <em>right </em>como parâmetro</li><li>Adicionar o valor do nó para a variável que armazena os valores</li><li>Chamar a função auxiliar com o nó atual como parâmetro</li></ul><p>Uma forma possível de fazer isso é a seguinte:</p><pre><code class="language-javascript">class Node {
    constructor(value){
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree {
    constructor(){
        this.root = null;
    }
    insert(value){
        var newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        var current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value &lt; current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }


    DFSPostOrder(){
        var data = [];
        function traverse(node){
            if(node.left) traverse(node.left);
            if(node.right) traverse(node.right);
            data.push(node.value);
        }
        traverse(this.root);
        return data;
    }
}


var tree = new BinarySearchTree()
tree.insert(10)
tree.insert(6)
tree.insert(15)
tree.insert(3)
tree.insert(8)
tree.insert(20)

console.log(tree.DFSPostOrder()) // [ 3, 8, 6, 20, 15, 10 ]</code></pre><h3 id="em-ordem-por-dfs"><strong>Em ordem por DFS</strong></h3><p>No algoritmo em ordem por profundidade, fazemos o seguinte:</p><ul><li>Criamos uma variável para armazenar os valores dos nós visitados</li><li>Armazenamos a raiz da árvore em uma variável</li><li>Escrevemos uma função auxiliar que aceite um nó como parâmetro</li><li>Se o nó tiver uma propriedade <em>left</em>, chamamos a função auxiliar com o nó <em>left </em>como parâmetro</li><li>Adicionar o valor do nó para a variável que armazena os valores</li><li>Se o nó tiver uma propriedade <em>right</em>, chamamos a função auxiliar com o nó <em>right</em> como parâmetro</li><li>Chamamos a função auxiliar com o nó atual como parâmetro</li></ul><p>Uma forma possível de fazer isso é assim:</p><pre><code class="language-javascript">class Node {
    constructor(value){
        this.value = value;
        this.left = null;
        this.right = null;
    }
}

class BinarySearchTree {
    constructor(){
        this.root = null;
    }
    insert(value){
        var newNode = new Node(value);
        if(this.root === null){
            this.root = newNode;
            return this;
        }
        var current = this.root;
        while(true){
            if(value === current.value) return undefined;
            if(value &lt; current.value){
                if(current.left === null){
                    current.left = newNode;
                    return this;
                }
                current = current.left;
            } else {
                if(current.right === null){
                    current.right = newNode;
                    return this;
                } 
                current = current.right;
            }
        }
    }

    DFSInOrder(){
        var data = [];
        function traverse(node){
            if(node.left) traverse(node.left);
            data.push(node.value);
            if(node.right) traverse(node.right);
        }
        traverse(this.root);
        return data;
    }
}


var tree = new BinarySearchTree()
tree.insert(10)
tree.insert(6)
tree.insert(15)
tree.insert(3)
tree.insert(8)
tree.insert(20)

console.log(tree.DFSInOrder()) // [ 3, 6, 8, 10, 15, 20 ]</code></pre><p>Como você deve ter notado, as implementações de pré-ordem, pós-ordem e em ordem são quase iguais, pois o que é alterado é apenas a ordem de como os nós são visitados. Porém o resultado de travessia que chegamos em cada um são bem diferentes e, às vezes, uma forma pode ser até mais útil do que as outras.</p><p>Em relação a quando usar BFS ou DFS, como eu disse, depende de como nossa estrutura de dados está organizada.</p><p>De um modo geral, se tivermos uma árvore ou grafo muito amplo (o que significa que existem muitos nós irmãos no mesmo nível), devemos priorizar a DFS. Se, no entanto, estivermos lidando com uma árvore ou grafo muito grande com ramificações muito longas, devemos priorizar a BFS.</p><p>Na questão do tempo, a complexidade dos dois os algoritmos é a mesma, pois estamos sempre visitando cada nó apenas uma vez. A complexidade de espaço, por outro lado, pode ser muito diferente, dependendo de quantos nós precisam ser armazenados na memória para cada implementação. Portanto, quanto menos nós tivermos que acompanhar, melhor!😊</p><h1 id="conclus-o"><strong>Conclusão</strong></h1><p>Como sempre, espero que você tenha gostado do artigo e que tenha aprendido algo novo. Se quiser, você também pode seguir o autor no <a href="https://www.linkedin.com/in/germancocca/">LinkedIn</a> ou no <a href="https://twitter.com/CoccaGerman">Twitter</a>.</p><p>Até logo!</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/07/6cd09fef66df69d9a3c4c8ab4b8576db.gif" class="kg-image" alt="6cd09fef66df69d9a3c4c8ab4b8576db" width="400" height="424" loading="lazy"></figure> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmos de ordenação explicados com exemplos em Python, Java e C++ ]]>
                </title>
                <description>
                    <![CDATA[ O que é um algoritmo de ordenação? Os algoritmos de ordenação são um conjunto de instruções que recebem um array ou lista como entrada e organizam os itens em uma ordem específica. As ordenações mais comumente realizadas são a numérica ou a alfabética (também chamada de lexicográfica) e podem ser ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmos-de-ordenacao-explicados-com-exemplos-em-python-java-e-c/</link>
                <guid isPermaLink="false">623f38596a6ca90519ad030b</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cayo Dias ]]>
                </dc:creator>
                <pubDate>Sun, 15 May 2022 20:47:27 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/05/5f9c9ede740569d1a4ca3f9d-1.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/sorting-algorithms-explained-with-examples-in-python-java-and-c/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Sorting Algorithms Explained with Examples in JavaScript, Python, Java, and C++</a>
      </p><h2 id="o-que-um-algoritmo-de-ordena-o">O que é um algoritmo de ordenação?</h2><p>Os algoritmos de ordenação são um conjunto de instruções que recebem um array ou lista como entrada e organizam os itens em uma ordem específica.</p><p>As ordenações mais comumente realizadas são a numérica ou a alfabética (também chamada de lexicográfica) e podem ser em ordem crescente (A-Z, 0-9) ou decrescente (Z-A, 9-0).</p><h2 id="por-que-algoritmos-de-ordena-o-s-o-importantes">Por que algoritmos de ordenação são importantes?</h2><p>Os algoritmos de ordenação são importantes em Ciência da Computação porque a ordenação pode, muitas vezes, reduzir a complexidade de um problema. Esses algoritmos têm aplicações diretas em algoritmos de busca, algoritmos de banco de dados, métodos de divisão e conquista, algoritmos de estrutura de dados e muito mais.</p><h3 id="vantagens-e-desvantagens-dos-algoritmos">Vantagens e desvantagens dos algoritmos</h3><p>Ao usar algoritmos diferentes, algumas perguntas devem ser feitas. Qual o tamanho da coleção que está sendo ordenada? Quanta memória está disponível para uso? A coleção precisa crescer?</p><p>As respostas a essas perguntas podem determinar qual algoritmo funcionará melhor para a situação. Alguns algoritmos como a ordenação por intercalação (<em>merge sort</em>, em inglês) podem precisar de muito espaço para serem executados, enquanto a ordenação por inserção nem sempre é a mais rápida, mas não requer muitos recursos para isso.</p><p>Você deve determinar quais são os requisitos do sistema e suas limitações antes de decidir qual algoritmo usar.</p><h2 id="algoritmos-de-ordena-o-comuns">Algoritmos de ordenação comuns</h2><p>Alguns dos algoritmos de ordenação mais comuns são:</p><ul><li>Selection Sort (ordenação por seleção)</li><li>Bubble Sort</li><li>Insertion Sort (ordenação por inserção)</li><li>Merge Sort (ordenação por intercalação)</li><li>Quick Sort (ordenação rápida)</li><li>Heap Sort</li><li>Counting Sort</li><li>Radix Sort</li><li>Bucket Sort</li></ul><p>Antes de detalharmos cada um deles, contudo, vamos aprender um pouco mais sobre como são classificados os algoritmos de ordenação.</p><h3 id="classifica-o-de-um-algoritmo-de-ordena-o">Classificação de um algoritmo de ordenação</h3><p>Os algoritmos de ordenação podem ser categorizados com base nos seguintes parâmetros:</p><ol><li><strong>Baseado no número de trocas ou inversões necessários:</strong> Este é o número de vezes que o algoritmo troca os elementos para ordenar uma entrada. A ordenação por seleção requer o número mínimo de trocas.</li><li><strong>Baseado no número de comparações:</strong> Este é o número de vezes que o algoritmo compara elementos para ordenar uma entrada. Usando a <a href="https://www.freecodecamp.org/portuguese/news/notacao-big-o-explicada-com-exemplos/">notação Big-O</a>, os exemplos de algoritmos de ordenação listados acima requerem, pelo menos, <code>O(nlogn)</code> comparações no melhor caso e <code>O(n^2)</code> comparações no pior caso para a maioria das saídas.</li><li><strong>Baseado no uso ou não de recursão:</strong> Alguns algoritmos, tal como <code>Quick Sort</code> (ordenação rápida), usa técnicas recursivas para ordenar uma entrada. Outros algoritmos de ordenação, como &nbsp;<code>Selection Sort</code> (ordenação por seleção) ou &nbsp;<code>Insertion Sort</code> (ordenação por inserção), usam técnicas não recursivas. Por fim, alguns algoritmos de ordenação, como &nbsp;<code>Merge Sort</code> (ordenação por intercalação), usam tanto técnicas recursivas como não recursivas para ordenar uma entrada.</li><li><strong>Baseado na estabilidade:</strong> Algoritmos de ordenação são considerados &nbsp;<code>estáveis</code> se o algoritmo mantiver a ordem relativa dos elementos com chaves iguais. Em outras palavras, dois elementos equivalentes permanecem, após a ordenação, na mesma ordem em que estavam antes da ordenação.<br>Imagine, por exemplo, que tenhamos um array de entrada <code>[1, 2, 3, 2, 4]</code>. Para ajudar na diferenciação entre os valores que são iguais – neste caso, os <code>2</code> – usaremos <code>2a</code> e <code>2b</code>, tornando o array &nbsp;<code>[1, 2a, 3, 2b, 4]</code>.<br>Algoritmos de ordenação estáveis manterão a ordem de <code>2a</code> e <code>2b</code>. Ou seja, teremos o algoritmo ordenado <code>[1, 2a, 2b, 3, 4]</code>. Já os instáveis, por sua vez, não mantêm a ordem dos valores iguais, resultando em <code>[1, 2b, 2a, 3, 4]</code>. <code>Insertion sort</code> , &nbsp;<code>Merge Sort</code> &nbsp;e &nbsp;<code>Bubble Sort</code> são estáveis, enquanto <code>Heap Sort</code> &nbsp;e <code>Quick Sort</code> não são.</li><li><strong>Baseado na necessidade de espaço adicional:</strong> Alguns algoritmos podem ordenar uma lista sem criar outra inteiramente nova. Chamamos um algoritmo assim de algoritmo de ordenação &nbsp;<code>in loco</code> e ele requer um espaço adicional <code>O(1)</code> constante para a ordenação. Quando os algoritmos não são <code>in loco</code>, &nbsp;uma outra lista é criada durante a ordenação.<br><code>Insertion sort</code> &nbsp;e &nbsp;<code>Quick sort</code> realizam a ordenação <code>in loco</code>, já que os elementos são movidos em torno de um ponto fixo e não utilizam um array à parte durante o processo de ordenação. <br>No <code>Merge sort</code>, por outro lado, o tamanho da entrada deve ser alocado de antemão para armazenar a saída durante o processo de ordenação. Isso exige um espaço adicional na memória para o processo de ordenação.</li></ol><h2 id="bucket-sort">Bucket Sort</h2><p>Bucket Sort é um algoritmo de ordenação por comparação que opera em elementos dividindo-os em diferentes buckets (segmentos menores de mesmo tamanho) e, em seguida, ordenando esses buckets individualmente. Cada bucket é ordenado individualmente usando um algoritmo de ordenação separado ou aplicando o algoritmo de bucket sort recursivamente.</p><p>Bucket Sort é útil principalmente quando a entrada é distribuída uniformemente em um intervalo. Por exemplo, suponhamos que tenhamos recebido um array grande de números de ponto flutuante uniformemente distribuídos entre os limites inferior e superior e precisamos ordená-lo.</p><p>Uma maneira simples de se resolver esse problema seria usar um algoritmo de ordenação, como o Merge sort, o Heap Sort ou o Quick Sort. No entanto, esses algoritmos garantem uma complexidade de tempo de, no melhor caso, <code>O(NlogN)</code>. Usando o bucket sort, contudo, a tarefa acima pode ser concluída em tempo <code>O(N)</code>.</p><h3 id="pseudoc-digo-para-bucket-sort-">Pseudocódigo para Bucket Sort:</h3><pre><code>void bucketSort(float[] a,int n)

{

    for(each floating integer 'x' in a)

    {
        insert x into bucket[n*x]; 
    }

    for(each bucket)

    {
        sort(bucket);
    }

}
</code></pre><h2 id="counting-sort">Counting Sort</h2><p>Counting Sort é uma técnica de ordenação baseada em chaves compreendidas em um intervalo específico. Ela funciona contando o número de objetos com valores de chave distintos (tipo de hash). Em seguida, fazendo alguma operação para calcular a posição de cada objeto na sequência de saída.</p><h3 id="exemplo-">Exemplo:</h3><pre><code>Para simplificar, considere os dados no intervalo de 0 a 9. 
Dados de entrada: 1, 4, 1, 2, 7, 5, 2
  1) Use um array para armazenar a contagem de cada objeto único.
  Índice:       0  1  2  3  4  5  6  7  8  9
  Contagem:     0  2  2  0  1  1  0  1  0  0

  2) Modifique o array de contagem de modo que cada elemento em cada índice armazene a soma das contagens anteriores. 
  Índice:       0  1  2  3  4  5  6  7  8  9
  Contagem:     0  2  4  4  5  6  6  7  7  7

O array de contagem modificado indica a posição que cada objeto ocupará no array de saída.
 
  3) Posicione cada objeto do array de entrada no array de saída de modo que sua posição final seja igual ao seu valor de contagem menos 1.
  Processe os dados de entrada: 1, 4, 1, 2, 7, 5, 2. A contagem do valor (no array de contagem) é 2.
  Assim, coloque o valor 1 no índice 1 (2 - 1) do array de saída. Subtraia 1 do valor de contagem aramazenado no índice 1 do array de contagem, de modo que o próximo valor do array de entrada seja colocado em uma posição, no array de saída, igual a esse valor menos 1.
  
</code></pre><h3 id="propriedades">Propriedades</h3><ul><li>Complexidade espacial: <code>O(K)</code> </li><li>Desempenho no melhor caso: <code>O(n+K)</code></li><li>Desempenho médio: <code>O(n+K)</code></li><li>Desempenho no pior caso: <code>O(n+K)</code></li><li>Estável: Sim (<code>K</code> é o número de elementos distintos no array)</li></ul><h3 id="implementa-o-em-javascript">Implementação em JavaScript</h3><pre><code class="language-js">let numbers = [1, 4, 1, 2, 7, 5, 2];
let count = [];
let i, z = 0;
let max = Math.max(...numbers);      
// inicializar o contador
for (i = 0; i &lt;= max; i++) {
    count[i] = 0;
}
for (i=0; i &lt; numbers.length; i++) {
    count[numbers[i]]++;
}
for (i = 0; i &lt;= max; i++) {
    while (count[i]-- &gt; 0) {
        numbers[z++] = i;
    }
}
// dar como resultado o array ordenado
for (i=0; i &lt; numbers.length; i++) {
    console.log(numbers[i]);
}
</code></pre><h3 id="implementa-o-em-c-">Implementação em C++</h3><pre><code class="language-cpp">#include &lt;iostream&gt;

void countSort(int upperBound, int lowerBound, std::vector&lt;int&gt; numbersToSort) //limites superior e inferior dos números no vetor
{
  int range = upperBound - lowerBound;                  //cria um intervalo grande o suficiente para obter todos os números entre o mínimo e o máximo
  std::vector&lt;int&gt; counts (range);                      //inicializa contagens do tamanho do intervalo
  std::fill(counts.begin(), counts.end(), 0);           //preenche os vetores com zeros
  
  for (int i = 0; i &lt; numbersToSort.size(); i++)
  {
      int index = numbersToSort[i] - lowerBound; //Por exemplo, se 5 for o limite inferior e numberToSort[i] for 5. index será 0 e o       counts[index]+= 1;                         //contagem de 5 será armazenada em counts[0]
  }
  
  std::cout &lt;&lt; counts &lt;&lt; std::endl;
} 
</code></pre><h3 id="implementa-o-em-swift">Implementação em Swift</h3><pre><code class="language-swift">func countingSort(_ array: [Int]) {
  // Cria um array para armazenar a contagem de cada elemento
  let maxElement = array.max() ?? 0
  var countArray = [Int](repeating: 0, count: Int(maxElement + 1))
  
  for element in array {
    countArray[element] += 1
  }
  var z = 0
  var sortedArray = [Int](repeating: 0, count: array.count)

  for index in 1 ..&lt; countArray.count {
    //exibe o elemento de índice o número de vezes necessárias
    while countArray[index] &gt; 0 {
      sortedArray[z] = index
      z += 1
      countArray[index] -= 1
    }
  }
  
  print(sortedArray)
}
</code></pre><h2 id="insertion-sort-ordena-o-por-inser-o-">Insertion Sort (ordenação por inserção)</h2><p>Insertion sort é um algoritmo de ordenação simples para um pequeno número de elementos.</p><h3 id="exemplo--1">Exemplo:</h3><p>Em insertion sort, você compara o elemento &nbsp;<code>chave</code> &nbsp;com os elementos anteriores. Se os elementos anteriores são maiores do que o elemento <code>chave</code>, então você move o elemento anterior para a próxima posição.</p><p>Comece do índice 1 até o tamanho do array de entrada.</p><p>[ 8 3 5 1 4 2 ]</p><p>Passo 1 :</p><pre><code>      chave = 3 //começando no índice 1.

      Aqui a `chave` será comparada com os elementos anteriores.

      Neste caso, `chave` é comparada com 8. Como 8 &gt; 3, movemos o elemento 8 para a próxima posição e inserimos a `chave` na posição anterior.

      Resultado: [ 3 8 5 1 4 2 ]
</code></pre><p>Passo 2 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/2.png?raw=true" class="kg-image" alt="[ 3 8 5 1 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      chave = 5 //índice 2

      8 &gt; 5 //movemos o 8 para o índice 2 e inserimos o 5 no índice 1.

      Resultado: [ 3 5 8 1 4 2 ]
</code></pre><p>Passo 3 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/3.png?raw=true" class="kg-image" alt="[ 3 5 8 1 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      chave = 1 //índice 3

      8 &gt; 1     =&gt; [ 3 5 1 8 4 2 ]  

      5 &gt; 1     =&gt; [ 3 1 5 8 4 2 ]

      3 &gt; 1     =&gt; [ 1 3 5 8 4 2 ]

      Resultado: [ 1 3 5 8 4 2 ]
</code></pre><p>Passo 4 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/4.png?raw=true" class="kg-image" alt="[ 1 3 5 8 4 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      chave = 4 //índice 4

      8 &gt; 4   =&gt; [ 1 3 5 4 8 2 ]

      5 &gt; 4   =&gt; [ 1 3 4 5 8 2 ]

      3 &gt; 4   ≠&gt;  pare

      Resultado: [ 1 3 4 5 8 2 ]
</code></pre><p>Passo 5 :</p><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/5.png?raw=true" class="kg-image" alt="[ 1 3 4 5 8 2 ]" width="600" height="400" loading="lazy"></figure><pre><code>      chave = 2 //índice 5

      8 &gt; 2   =&gt; [ 1 3 4 5 2 8 ]

      5 &gt; 2   =&gt; [ 1 3 4 2 5 8 ]

      4 &gt; 2   =&gt; [ 1 3 2 4 5 8 ]

      3 &gt; 2   =&gt; [ 1 2 3 4 5 8 ]

      1 &gt; 2   ≠&gt; pare

      Resultado: [1 2 3 4 5 8]
</code></pre><figure class="kg-card kg-image-card"><img src="https://github.com/blulion/freecodecamp-resource/blob/master/insertion_sort/6.png?raw=true" class="kg-image" alt="[ 1 2 3 4 5 8 ]" width="600" height="400" loading="lazy"></figure><p>O algoritmo mostrado abaixo é uma versão ligeiramente otimizada para evitar trocar o elemento <code>chave</code> em cada iteração. Aqui, o elemento &nbsp;<code>chave</code> &nbsp;será trocado ao final da iteração (step).</p><pre><code>    InsertionSort(arr[])
      for j = 1 to arr.length
         key = arr[j]
         i = j - 1
         while i &gt;= 0 and arr[i] &gt; key
            arr[i+1] = arr[i]
            i = i - 1
         arr[i+1] = key
		</code></pre><p>Aqui está uma implementação detalhada em JavaScript:</p><pre><code class="language-js">function insertion_sort(A) {
    var len = array_length(A);
    var i = 1;
    while (i &lt; len) {
        var x = A[i];
        var j = i - 1;
        while (j &gt;= 0 &amp;&amp; A[j] &gt; x) {
            A[j + 1] = A[j];
            j = j - 1;
        }
        A[j+1] = x;
        i = i + 1;
    }
}
</code></pre><p>Uma implementação rápida em Swift é mostrada abaixo:</p><pre><code class="language-swift">  var array = [8, 3, 5, 1, 4, 2]

  func insertionSort(array:inout Array&lt;Int&gt;) -&gt; Array&lt;Int&gt;{
      for j in 0..&lt;array.count {
          let key = array[j]
          var i = j-1

          while (i &gt; 0 &amp;&amp; array[i] &gt; key){
              array[i+1] = array[i]
              i = i-1
          }
          array[i+1] = key
      }
      return array
  }
</code></pre><p>O exemplo Java é mostrado abaixo:</p><pre><code class="language-java">public int[] insertionSort(int[] arr) {
      for (int j = 1; j &lt; arr.length; j++) {
         int key = arr[j];
         int i = j - 1;
         while (i &gt;= 0 &amp;&amp; arr[i] &gt; key) {
            arr[i+1] = arr[i];
            i -= 1;
         }
         arr[i+1] = key;
      }
      return arr;
}</code></pre><p>E em &nbsp;c....</p><pre><code class="language-c">void insertionSort(int arr[], int n) 
{ 
   int i, key, j; 
   for (i = 1; i &lt; n; i++) 
   { 
       key = arr[i]; 
       j = i-1;
       while (j &gt;= 0 &amp;&amp; arr[j] &gt; key) 
       { 
           arr[j+1] = arr[j]; 
           j = j-1; 
       } 
       arr[j+1] = key; 
   } 
} 
</code></pre><h3 id="propriedades-">Propriedades:</h3><ul><li>Complexidade espacial: O(1)</li><li>Complexidade temporal: O(n), O(n* n), O(n* n) para o melhor, médio e pior caso, respectivamente.</li><li>Melhor caso: o array já está ordenado</li><li>Caso médio: o array está ordenado aleatoriamente</li><li>Pior caso: o array está ordenado em ordem inversa.</li><li>Ordenação in loco: Sim</li><li>Estável: Sim</li></ul><h2 id="heapsort">Heapsort</h2><p>Heapsort é um algoritmo de ordenação eficiente baseado no uso de estruturas de dados denominadas heaps (máxima/mínima). Um heap é uma estrutura de dados baseada em árvore que satisfaz a propriedade heap – ou seja, para um heap máximo, a chave de qualquer nó é menor ou igual à chave de seu pai (se tiver um pai).</p><p>Essa propriedade pode ser aproveitada para acessar o elemento máximo no heap em tempo O(logn) usando o método maxHeapify. Realizamos esta operação n vezes, cada vez movendo o elemento máximo no heap para o topo do heap e extraindo-o do heap para um array ordenado. Assim, após n iterações teremos uma versão ordenada do array de entrada.</p><p>O algoritmo exige que uma estrutura de dados heap seja construída primeiro. O algoritmo não é estável, o que significa que ao comparar objetos com a mesma chave, a ordenação original não será preservada.</p><p>Este algoritmo é executado em tempo O(nlogn) e requer espaço adicional O(1) [O(n) incluindo o espaço necessário para armazenar os dados de entrada], uma vez que todas as operações são executadas inteiramente in loco.</p><p>A complexidade de tempo melhor, pior e média do Heapsort é O(nlogn). Embora heapsort tenha uma complexidade de pior caso melhor do que o quicksort, quando bem implementado, o quicksort é executado mais rapidamente na prática. Heapsort é um algoritmo baseado em comparação, portanto, pode ser usado para conjuntos de dados não numéricos, na medida em que alguma relação (propriedade de heap) possa ser definida sobre os elementos.</p><p>Uma implementação em Java é apresentada abaixo :</p><pre><code class="language-java">import java.util.Arrays;
public class Heapsort {

	public static void main(String[] args) {
		//array de teste
		Integer[] arr = {1, 4, 3, 2, 64, 3, 2, 4, 5, 5, 2, 12, 14, 5, 3, 0, -1};
		String[] strarr = {"hope you find this helpful!", "wef", "rg", "q2rq2r", "avs", "erhijer0g", "ewofij", "gwe", "q", "random"};
		arr = heapsort(arr);
		strarr = heapsort(strarr);
		System.out.println(Arrays.toString(arr));
		System.out.println(Arrays.toString(strarr));
	}
	
	//O(nlogn) TEMPO, O(1) ESPAÇO, NÃO ESTÁVEL
	public static &lt;E extends Comparable&lt;E&gt;&gt; E[] heapsort(E[] arr){
		int heaplength = arr.length;
		for(int i = arr.length/2; i&gt;0;i--){
			arr = maxheapify(arr, i, heaplength);
		}
		
		for(int i=arr.length-1;i&gt;=0;i--){
			E max = arr[0];
			arr[0] = arr[i];
			arr[i] = max;
			heaplength--;
			arr = maxheapify(arr, 1, heaplength);
		}
		
		return arr;
	}
	
	//Cria maxheap a partir de um array
	public static &lt;E extends Comparable&lt;E&gt;&gt; E[] maxheapify(E[] arr, Integer node, Integer heaplength){
		Integer left = node*2;
		Integer right = node*2+1;
		Integer largest = node;
		
		if(left.compareTo(heaplength) &lt;=0 &amp;&amp; arr[left-1].compareTo(arr[node-1]) &gt;= 0){
			largest = left;
		}
		if(right.compareTo(heaplength) &lt;= 0 &amp;&amp; arr[right-1].compareTo(arr[largest-1]) &gt;= 0){
			largest = right;
		}	
		if(largest != node){
			E temp = arr[node-1];
			arr[node-1] = arr[largest-1];
			arr[largest-1] = temp;
			maxheapify(arr, largest, heaplength);
		}
		return arr;
	}
}
</code></pre><p>Implementação em C++</p><pre><code class="language-cpp">#include &lt;iostream&gt;
using namespace std;
void heapify(int arr[], int n, int i) 
{ 
    int largest = i; 
    int l = 2*i + 1;  
    int r = 2*i + 2;
    if (l &lt; n &amp;&amp; arr[l] &gt; arr[largest]) 
        largest = l;
    if (r &lt; n &amp;&amp; arr[r] &gt; arr[largest]) 
        largest = r;
    if (largest != i) 
    { 
        swap(arr[i], arr[largest]); 
  
        
        heapify(arr, n, largest); 
    } 
} 
  
 
void heapSort(int arr[], int n) 
{ 
    
    for (int i = n / 2 - 1; i &gt;= 0; i--) 
        heapify(arr, n, i); 
  
    
    for (int i=n-1; i&gt;=0; i--) 
    { 

        swap(arr[0], arr[i]); 
  
        
        heapify(arr, i, 0); 
    } 
} 
void printArray(int arr[], int n) 
{ 
    for (int i=0; i&lt;n; ++i) 
        cout &lt;&lt; arr[i] &lt;&lt; " "; 
    cout &lt;&lt; "\n"; 
} 
int main() 
{ 
    int arr[] = {12, 11, 13, 5, 6, 7}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
  
    heapSort(arr, n); 
  
    cout &lt;&lt; "Array ordenado: \n"; 
    printArray(arr, n); 
}
</code></pre><h2 id="radix-sort">Radix Sort</h2><p>Prerrequisito: Counting Sort</p><p>QuickSort, MergeSort e HeapSort são algoritmos de ordenação baseados em comparação. CountSort não é. Tem a complexidade de O(n+k), onde k é o elemento máximo do array de entrada. Portanto, se k é O(n), CountSort se torna ordenação linear, o que é melhor do que algoritmos de ordenação baseados em comparação que possuem complexidade de tempo O(nlogn).</p><p>A ideia é estender o algoritmo CountSort para obter uma melhor complexidade de tempo quando k for O(n2). Aí vem a ideia do Radix Sort.</p><h3 id="o-algoritmo-">O algoritmo:</h3><p>Para cada dígito i onde i varia do dígito menos significativo ao dígito mais significativo de um número, ordene o array de entrada usando o algoritmo countsort de acordo com o i-ésimo dígito. Usamos a countsort porque é uma ordenação estável.	</p><p>Exemplo: Suponha que o array de entrada seja:</p><p>10, 21, 17, 34, 44, 11, 654, 123</p><p>Com base no algoritmo, vamos ordenar o array de entrada de acordo com o dígito das unidades (dígito menos significativo).</p><p>0: 10<br>1: 21 11<br>2:<br>3: 123<br>4: 34 44 654<br>5:<br>6:<br>7: 17<br>8:<br>9:</p><p>Assim, o array se torna 10, 21, 11, 123, 24, 44, 654, 17.</p><p>Agora, vamos ordenar de acordo com o dígito da dezena:</p><p>0:<br>1: 10 11 17<br>2: 21 123<br>3: 34<br>4: 44<br>5: 654<br>6:<br>7:<br>8:<br>9:</p><p>Agora, o array se torna: 10, 11, 17, 21, 123, 34, 44, 654.</p><p>Por fim, ordenamos de acordo com o dígito da centena (dígito mais significativo):</p><p>0: 010 011 017 021 034 044<br>1: 123<br>2:<br>3:<br>4:<br>5:<br>6: 654<br>7:<br>8:<br>9:</p><p>O array se torna: 10, 11, 17, 21, 34, 44, 123, 654, o qual está ordenado. É assim que nosso algoritmo funciona.</p><p>Uma implementação em &nbsp;C:</p><pre><code class="language-c">void countsort(int arr[],int n,int place){

        int i,freq[range]={0};         //o intervalo para números inteiros é 10, pois os dígitos variam de 0 a 9
        int output[n];

        for(i=0;i&lt;n;i++)
                freq[(arr[i]/place)%range]++;

        for(i=1;i&lt;range;i++)
                freq[i]+=freq[i-1];
        
        for(i=n-1;i&gt;=0;i--){
                output[freq[(arr[i]/place)%range]-1]=arr[i];
                freq[(arr[i]/place)%range]--;
        }
        
        for(i=0;i&lt;n;i++)
                arr[i]=output[i];
}

void radixsort(ll arr[],int n,int maxx){            //maxx é o elemento máximo do array

        int mul=1;
        while(maxx){
                countsort(arr,n,mul);
                mul*=10;
                maxx/=10;
        }
}
</code></pre><h2 id="selection-sort">Selection Sort</h2><p>Selection Sort é um dos algoritmos de ordenação mais simples. Esse algoritmo recebe seu nome pela maneira como ele percorre o array ao longo das iterações: ele seleciona o menor elemento atual e o troca de lugar.</p><p>Veja como funciona:</p><ol><li>Encontre o menor elemento no array e troque-o de lugar com o primeiro elemento.</li><li>Encontre o segundo menor elemento e troque com o segundo elemento no array.</li><li>Encontre o terceiro menor elemento e troque com o terceiro elemento no array.</li><li>Repita o processo de encontrar o próximo menor elemento e trocá-lo na posição correta até que todo o array esteja ordenado.</li></ol><p>Mas, como você escreveria o código para encontrar o índice do segundo menor valor em um array?</p><p>Uma maneira fácil é notar que o menor valor já foi trocado para o índice 0, então o problema se reduz a encontrar o menor elemento no array começando no índice 1.</p><p>A ordenação por seleção sempre leva o mesmo número de comparações de chave — N(N − 1)/2.</p><h3 id="implementa-o-em-c-c-">Implementação em C/C++</h3><p>O programa C++ a seguir contém uma implementação iterativa e recursiva do algoritmo Selection Sort. Ambas implementações são invocadas na função <code>main()</code>.</p><pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;string&gt;
using namespace std;

template&lt;typename T, size_t n&gt;
void print_array(T const(&amp;arr)[n]) {
    for (size_t i = 0; i &lt; n; i++)
        std::cout &lt;&lt; arr[i] &lt;&lt; ' ';
    cout &lt;&lt; "\n";
}

int minIndex(int a[], int i, int j) {
    if (i == j)
        return i;
    int k = minIndex(a, i + 1, j);
    return (a[i] &lt; a[k]) ? i : k;
}

void recurSelectionSort(int a[], int n, int index = 0) {
    if (index == n)
        return;
    int k = minIndex(a, index, n - 1);
    if (k != index)
        swap(a[k], a[index]);
    recurSelectionSort(a, n, index + 1);
}

void iterSelectionSort(int a[], int n) {
    for (int i = 0; i &lt; n; i++)
    {
        int min_index = i;
        int min_element = a[i];
        for (int j = i + 1; j &lt; n; j++)
        {
            if (a[j] &lt; min_element)
            {
                min_element = a[j];
                min_index = j;
            }
        }
        swap(a[i], a[min_index]);
    }
}

int main() {
    int recurArr[6] = { 100,35, 500, 9, 67, 20 };
    int iterArr[5] = { 25, 0, 500, 56, 98 };

    cout &lt;&lt; "Selection Sort Recursivo"  &lt;&lt; "\n";
    print_array(recurArr); // 100 35 500 9 67 20
    recurSelectionSort(recurArr, 6);
    print_array(recurArr); // 9 20 35 67 100 500

    cout &lt;&lt; "Selection Sort Iterativo" &lt;&lt; "\n";
    print_array(iterArr); // 25 0 500 56 98
    iterSelectionSort(iterArr, 5);
    print_array(iterArr); // 0 25 56 98 500
}
</code></pre><h3 id="implementa-o-em-javascript-1">Implementação em JavaScript</h3><pre><code class="language-js">function selection_sort(A) {
    var len = A.length;
    for (var i = 0; i &lt; len - 1; i = i + 1) {
        var j_min = i;
        for (var j = i + 1; j &lt; len; j = j + 1) {
            if (A[j] &lt; A[j_min]) {
                j_min = j;
            } else {}
        }
        if (j_min !== i) {
            swap(A, i, j_min);
        } else {}
    }
}

function swap(A, x, y) {
    var temp = A[x];
    A[x] = A[y];
    A[y] = temp;
}
</code></pre><h3 id="implementa-o-em-python">Implementação em Python</h3><pre><code class="language-ps">def selection_sort(arr):
    if not arr:
        return arr
    for i in range(len(arr)):
        min_i = i
        for j in range(i + 1, len(arr)):
            if arr[j] &lt; arr[min_i]:
                min_i = j
        arr[i], arr[min_i] = arr[min_i], arr[i]</code></pre><h3 id="implementa-o-em-java">Implementação em Java</h3><pre><code class="language-java">public void selectionsort(int array[])
{
    int n = array.length;            //método para calcular o tamanho do array 
    for (int i = 0; i &lt; n-1; i++)
    {
        int index = i;
        int min = array[i];          // tomando o elemento min como i-ésimo elemento do array
        for (int j = i+1; j &lt; n; j++)
        {
            if (array[j] &lt; array[index])
            {
                index = j;
                min = array[j];
            }
        }
        int t = array[index];         //Troque os elementos de lugar
        array[index] = array[i];
        array[i] = t;
    }
}
</code></pre><h3 id="implementa-o-em-matlab">Implementação em MATLAB</h3><pre><code>function [sorted] = selectionSort(unsorted)
    len = length(unsorted);
    for i = 1:1:len
        minInd = i;
        for j = i+1:1:len
           if unsorted(j) &lt; unsorted(minInd) 
               minInd = j;
           end
        end
        unsorted([i minInd]) = unsorted([minInd i]);    
    end
    sorted = unsorted;
end
</code></pre><h3 id="propriedades-1">Propriedades</h3><ul><li>Complexidade espacial: &nbsp;<strong>O(n)</strong></li><li>Complexidade temporal: &nbsp;<strong>O(n2)</strong></li><li>Ordenação in loco: &nbsp;<strong>Sim</strong></li><li>Estável: &nbsp;<strong>Não</strong></li></ul><h2 id="bubble-sort">Bubble Sort</h2><p>Assim como as bolhas sobem do fundo de um copo, <strong>bubble sort</strong> é um algoritmo simples que ordena uma lista, permitindo que valores mais baixos ou mais altos borbulhem até o topo. O algoritmo percorre uma lista e compara valores adjacentes, trocando-os se não estiverem na ordem correta.</p><p>Com uma complexidade de pior caso de O(n^2), a ordenação por bolhas é muito lenta em comparação com outros algoritmos de ordenação como o quicksort. A vantagem é que é um dos algoritmos de ordenação mais fáceis de entender e implementar do zero.</p><p>Do ponto de vista técnico, o bubble sort é razoável para ordenar arrays de tamanho pequeno ou, especialmente, ao executar algoritmos de ordenação em computadores com recursos de memória notavelmente limitados.</p><h3 id="exemplo--2">Exemplo:</h3><h3 id="primeira-passagem-pela-lista-">Primeira passagem pela lista:</h3><ul><li>Começando com <code>[4, 2, 6, 3, 9]</code>, o algoritmo compara os dois primeiros elementos no array, 4 e 2. Ele os troca porque 2 &lt; 4: <code>[2, 4, 6, 3, 9]</code></li><li>Ele compara os dois próximos valores, 4 e 6. Como 4 &lt; 6, e estes já estão em ordem, o algoritmo segue em frente: <code>[2, 4, 6, 3, 9]</code></li><li>Os próximos dois valores são trocados, porque 3 &lt; 6: <code>[2, 4, 3, 6, 9]</code></li><li>Os dois últimos valores, 6 e 9, já estão em ordem, então o algoritmo não os troca.</li></ul><h3 id="segunda-passagem-pela-lista-">Segunda passagem pela lista:</h3><ul><li>2 &lt; 4, então não há necessidade de trocar as posições: <code>[2, 4, 3, 6, 9]</code></li><li>O algoritmo troca os próximos dois valores, porque 3 &lt; 4: <code>[2, 3, 4, 6, 9]</code></li><li>Sem trocas, porque 4 &lt; 6: <code>[2, 3, 4, 6, 9]</code></li><li>Novamente, 6 &lt; 9, logo, nenhuma troca ocorre: <code>[2, 3, 4, 6, 9]</code></li></ul><p>A lista já está ordenada, mas o algoritmo bubble sort não percebe isso. Em vez disso, ele precisa concluir uma passagem inteira pela lista sem trocar nenhum valor para saber que a lista está ordenada.</p><h3 id="terceira-passagem-pela-lista-">Terceira passagem pela lista:</h3><ul><li><code>[2, 4, 3, 6, 9]</code> =&gt; <code>[2, 4, 3, 6, 9]</code></li><li><code>[2, 4, 3, 6, 9]</code> =&gt; <code>[2, 4, 3, 6, 9]</code></li><li><code>[2, 4, 3, 6, 9]</code> =&gt; <code>[2, 4, 3, 6, 9]</code></li><li><code>[2, 4, 3, 6, 9]</code> =&gt; <code>[2, 4, 3, 6, 9]</code></li></ul><p>Claramente bubble sort está longe de ser o algoritmo de ordenação mais eficiente. Ainda assim, é simples entender e implementar você mesmo.</p><h4 id="propriedades-2">Propriedades</h4><ul><li>Complexidade espacial: O(1)</li><li>Performance no melhor caso: O(n)</li><li>Performance no caso médio: O(n*n)</li><li>Performance no pior caso: O(n*n)</li><li>Estável: Sim</li></ul><h3 id="explica-o-em-v-deo">Explicação em vídeo</h3><p><a href="https://www.youtube.com/watch?v=Jdtq5uKz-w4">Bubble sort algorithm</a> (em inglês)</p><h3 id="exemplo-em-javascript">Exemplo em JavaScript</h3><pre><code class="language-js">let arr = [1, 4, 7, 45, 7,43, 44, 25, 6, 4, 6, 9],
    sorted = false;

while(!sorted) {
  sorted = true;
  for(var i=0; i &lt; arr.length; i++) {
    if(arr[i] &lt; arr[i-1]) {
      let temp = arr[i];
      arr[i] = arr[i-1];
      arr[i-1] = temp;
      sorted = false;
    }
  }
}
</code></pre><h3 id="exemplo-em-java">Exemplo em Java</h3><pre><code class="language-java">public class BubbleSort {
    static void sort(int[] arr) {
        int n = arr.length;
        int temp = 0;
         for(int i=0; i &lt; n; i++){
                 for(int x=1; x &lt; (n-i); x++){
                          if(arr[x-1] &gt; arr[x]){
                                 temp = arr[x-1];
                                 arr[x-1] = arr[x];
                                 arr[x] = temp;
                         }

                 }
         }

    }
    public static void main(String[] args) {

		for(int i=0; i &lt; 15; i++){
			int arr[i] = (int)(Math.random() * 100 + 1);
		}

                System.out.println("array before sorting\n");
                for(int i=0; i &lt; arr.length; i++){
                        System.out.print(arr[i] + " ");
                }
                bubbleSort(arr);
                System.out.println("\n array after sorting\n");
                for(int i=0; i &lt; arr.length; i++){
                        System.out.print(arr[i] + " ");
                }

        }
}
</code></pre><h3 id="exemplo-em-c-">Exemplo em C++</h3><pre><code class="language-cpp">// Implementação por recursão
void bubblesort(int arr[], int n)
{
	if(n==1)	//Caso inicial
		return;
	bool swap_flag = false;
	for(int i=0;i&lt;n-1;i++)	//Após essa passada, o elemento maior é movido para o local desejado.
	{
		if(arr[i]&gt;arr[i+1])
		{
			int temp=arr[i];
			arr[i]=arr[i+1];
			arr[i+1]=temp;
			swap_flag = true;
		}
	}
        // Se não houver a troca de dois elementos no laço, retorna, pois o array está ordenado
	if(swap_flag == false)
		return;
	bubblesort(arr,n-1);	//Recursão para o restante do array
}
</code></pre><h3 id="exemplo-em-swift">Exemplo em Swift</h3><pre><code class="language-swift">func bubbleSort(_ inputArray: [Int]) -&gt; [Int] {
    guard inputArray.count &gt; 1 else { return inputArray } // Garantir que nosso array de entrada tem mais de 1 elemento
    var numbers = inputArray // Argumentos de função são constantes por padrão em Swift, então fazemos uma cópia
    for i in 0..&lt;(numbers.count - 1) {
        for j in 0..&lt;(numbers.count - i - 1) {
            if numbers[j] &gt; numbers[j + 1] {
                numbers.swapAt(j, j + 1)
            }
        }
    }
    return numbers // retorna o array ordenado
} 
</code></pre><h3 id="exemplo-em-python">Exemplo em Python</h3><pre><code class="language-py">def bubbleSort(arr): 
    n = len(arr) 
    for i in range(n):
        for j in range(0, n-i-1):
                if arr[j] &gt; arr[j+1] : 
                        arr[j], arr[j+1] = arr[j+1], arr[j]
    print(arr)
</code></pre><h3 id="exemplo-em-php">Exemplo em PHP</h3><pre><code class="language-php">function bubble_sort($arr) {
    $size = count($arr)-1;
    for ($i=0; $i&lt;$size; $i++) {
        for ($j=0; $j&lt;$size-$i; $j++) {
            $k = $j+1;
            if ($arr[$k] &lt; $arr[$j]) {
                // Troca os elementos nos índices: $j, $k
                list($arr[$j], $arr[$k]) = array($arr[$k], $arr[$j]);
            }
        }
    }
    return $arr;// retorna o array ordenado
}

$arr = array(1,3,2,8,5,7,4,0);
print("Before sorting");
print_r($arr);

$arr = bubble_sort($arr);
print("After sorting by using bubble sort");
print_r($arr);
</code></pre><h3 id="exemplo-em-c">Exemplo em C</h3><pre><code class="language-c">#include &lt;stdio.h&gt;

int BubbleSort(int array[], int n);

int main(void) {
  int arr[] = {10, 2, 3, 1, 4, 5, 8, 9, 7, 6};
  BubbleSort(arr, 10);

  for (int i = 0; i &lt; 10; i++) {
    printf("%d", arr[i]);
  }
  return 0;
}
int BubbleSort(int array[], n)
{
for (int i = 0 ; i &lt; n - 1; i++)
  {
    for (int j = 0 ; j &lt; n - i - 1; j++)     //n é o tamanho do array
    {
      if (array[j] &gt; array[j+1])      // Para o uso com ordem decrescente 
      {
        int swap   = array[j];
        array[j]   = array[j+1];
        array[j+1] = swap;
      }
    }
  }
}
</code></pre><h2 id="quick-sort">Quick Sort</h2><p>Quick sort é um eficiente algoritmo de ordenação que emprega a técnica de divisão e conquista. A complexidade temporal no caso médio do Quick Sort é O(nlog(n)), sendo O(n^2) no pior caso dependendo da seleção do elemento pivô, que divide o array atual em dois subarrays.</p><p>Por exemplo, a complexidade de tempo do Quick Sort é aproximadamente <code>O(nlog(n))</code> quando a seleção do pivô divide o array original em dois sub arrays de tamanhos quase iguais.</p><p>Por outro lado, se o algoritmo, que seleciona o elemento pivô dos arrays de entrada, produz consistentemente 2 sub-arrays com uma grande diferença em termos de tamanho, o algoritmo de ordenação rápida pode atingir a complexidade temporal de pior caso de O(n^2 ).</p><p>As etapas envolvidas no Quick Sort são:</p><ul><li>Escolha um elemento para servir como pivô, neste caso, o último elemento do array é o pivô.</li><li>Particionamento: Ordene o array de forma que todos os elementos menores que o pivô fiquem à esquerda e todos os elementos maiores que o pivô fiquem à direita.</li><li>Execute o algoritmo recursivamente, levando em consideração o pivô anterior para subdividir adequadamente os arrays esquerdo e direito. (Uma explicação mais detalhada pode ser encontrada nos comentários abaixo)</li></ul><h2 id="exemplos-de-implementa-o-em-v-rias-linguagens">Exemplos de implementação em várias linguagens</h2><h3 id="implementa-o-em-javascript-">Implementação em JavaScript:</h3><pre><code class="language-js">const arr = [6, 2, 5, 3, 8, 7, 1, 4];

const quickSort = (arr, start, end) =&gt; {

  if(start &lt; end) {

    // Você pode aprender sobre como o valor do pivô é derivado nos comentários abaixo
    let pivot = partition(arr, start, end);

    // Certifique-se de ler os comentários abaixo para entender por que pivô - 1 e pivô + 1 são usados
    // Estas são as chamadas recursivas do quickSort
    quickSort(arr, start, pivot - 1);
    quickSort(arr, pivot + 1, end);
  } 

}

const partition = (arr, start, end) =&gt; { 
  let pivot = end;
  // Defina i como start - 1 para que ele possa acessar o primeiro índice no caso de o valor em arr[0] ser maior que arr[pivot]
  // Os comentários abaixo explicarão o comentário acima
  let i = start - 1,
      j = start;

  // Incrementar j até o índice anterior ao pivô
  while (j &lt; pivot) {

    // If the value is greater than the pivot increment j
    if (arr[j] &gt; arr[pivot]) {
      j++;
    }

    // Quando o valor em arr[j] for menor que o pivô:
    // incremente i (arr[i] será um valor maior que arr[pivot]) e troque o valor em arr[i] e arr[j]
    else {
      i++;
      swap(arr, j, i);
      j++;
    }

  }

  //O valor em arr[i + 1] será maior que o valor de arr[pivot]
  swap(arr, i + 1, pivot);

  //Você retorna i + 1, pois os valores à esquerda dele são menores que arr[i+1], e os valores à direita são maiores que arr[i + 1]
  // Dessa forma, quando os quicksorts recursivos são chamados, os novos sub-arrays não incluirão este valor pivô usado anteriormente
  return i + 1;
}

const swap = (arr, firstIndex, secondIndex) =&gt; {
  let temp = arr[firstIndex];
  arr[firstIndex] = arr[secondIndex];
  arr[secondIndex] = temp;
}

quickSort(arr, 0, arr.length - 1);
console.log(arr);
</code></pre><h3 id="implementa-o-em-c">Implementação em C</h3><pre><code class="language-c">#include&lt;stdio.h&gt;  
void swap(int* a, int* b) 
{ 
    int t = *a; 
    *a = *b; 
    *b = t; 
}
int partition (int arr[], int low, int high) 
{ 
    int pivot = arr[high];     
    int i = (low - 1);  
  
    for (int j = low; j &lt;= high- 1; j++) 
    { 
        if (arr[j] &lt;= pivot) 
        { 
            i++;    
            swap(&amp;arr[i], &amp;arr[j]); 
        } 
    } 
    swap(&amp;arr[i + 1], &amp;arr[high]); 
    return (i + 1); 
}
void quickSort(int arr[], int low, int high) 
{ 
    if (low &lt; high) 
    {
        int pi = partition(arr, low, high); 
  
        quickSort(arr, low, pi - 1); 
        quickSort(arr, pi + 1, high); 
    } 
} 
  

void printArray(int arr[], int size) 
{ 
    int i; 
    for (i=0; i &lt; size; i++) 
        printf("%d ", arr[i]); 
    printf("n"); 
} 
  

int main() 
{ 
    int arr[] = {10, 7, 8, 9, 1, 5}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
    quickSort(arr, 0, n-1); 
    printf("Sorted array: n"); 
    printArray(arr, n); 
    return 0; 
} 
</code></pre><h3 id="implementa-o-em-python3">Implementação em Python3</h3><pre><code>import random

z=[random.randint(0,100) for i in range(0,20)]

def quicksort(z):
    if(len(z)&gt;1):        
        piv=int(len(z)/2)
        val=z[piv]
        lft=[i for i in z if i&lt;val]
        mid=[i for i in z if i==val]
        rgt=[i for i in z if i&gt;val]

        res=quicksort(lft)+mid+quicksort(rgt)
        return res
    else:
        return z
        
ans1=quicksort(z)
print(ans1)
</code></pre><h3 id="implementa-o-em-matlab-1">Implementação em MATLAB</h3><pre><code>a = [9,4,7,3,8,5,1,6,2];

sorted = quicksort(a,1,length(a));

function [unsorted] =  quicksort(unsorted, low, high)
    if low &lt; high
        [pInd, unsorted] = partition(unsorted, low, high);
        unsorted = quicksort(unsorted, low, pInd-1);
        unsorted = quicksort(unsorted, pInd+1, high);
    end

end

function [pInd, unsorted] = partition(unsorted, low, high)
    i = low-1;
    for j = low:1:high-1
        if unsorted(j) &lt;= unsorted(high)
            i = i+1;
            unsorted([i,j]) = unsorted([j,i]);
            
        end
    end
    unsorted([i+1,high]) = unsorted([high,i+1]);
    pInd = i+1;

end
</code></pre><p>A complexidade espacial do quick sort é &nbsp;<code>O(n)</code> . Esta é uma melhoria em relação a outros algoritmos de ordenação de divisão e conquista, que têm complexidade espacial de &nbsp;<code>O(nlong(n))</code>.</p><p>Quick sort consegue isso alterando a ordem dos elementos dentro de um determinado array. Compare isso com o algoritmo merge sort que cria 2 arrays, cada um com tamanho <code>n/2</code>, a cada chamada da função.</p><p>No entanto existe o problema deste algoritmo de ordenação ser de tempo <code>O(n*n)</code> &nbsp;se o pivô é sempre mantido no meio. Um problema que pode ser contornado utilizando-se um pivô aleatório</p><h3 id="complexidade">Complexidade</h3><p>O uso de memória no melhor, médio e pior caso corresponde a <strong> </strong>n log(n)n, log(n)n e 2log(n) respectivamente. It's not a stable algorithm, and quicksort is usually done in-place with O(log(n)) stack space.</p><p>A complexidade espacial do quick sort é O(n). Esta é uma melhoria em relação a outros algoritmos de ordenação de divisão e conquista, que ocupam espaço O(n log(n)).</p><h2 id="timsort">Timsort</h2><p>Timsort é um algoritmo de ordenação rápido que trabalha com complexidade O(N log(N)) estável.</p><p>Timsort é uma mistura de Insertion Sort e Mergesort. Esse algoritmo é implementado no método Arrays.sort() em Java, bem como nas funções sorted() e sort() em Python. As partes menores são ordenadas usando o Insertion Sort e posteriormente mescladas usando Mergesort.</p><p>Uma rápida implementação em Python:</p><pre><code class="language-py">def binary_search(the_array, item, start, end):
    if start == end:
        if the_array[start] &gt; item:
            return start
        else:
            return start + 1
    if start &gt; end:
        return start

    mid = round((start + end)/ 2)

    if the_array[mid] &lt; item:
        return binary_search(the_array, item, mid + 1, end)

    elif the_array[mid] &gt; item:
        return binary_search(the_array, item, start, mid - 1)

    else:
        return mid

"""
Insertion sort utilizada pelo timsort se o tamanho do array ou da "run" é pequeno.
"""
def insertion_sort(the_array):
    l = len(the_array)
    for index in range(1, l):
        value = the_array[index]
        pos = binary_search(the_array, value, 0, index - 1)
        the_array = the_array[:pos] + [value] + the_array[pos:index] + the_array[index+1:]
    return the_array

def merge(left, right):
    """Recebe duas listas ordenadas e retorna uma única lista ordenada comparando os elementos um de cada vez.
    [1, 2, 3, 4, 5, 6]
    """
    if not left:
        return right
    if not right:
        return left
    if left[0] &lt; right[0]:
        return [left[0]] + merge(left[1:], right)
    return [right[0]] + merge(left, right[1:])

def timsort(the_array):
    runs, sorted_runs = [], []
    length = len(the_array)
    new_run = [the_array[0]]

    # Para cada i no intervalo de 1 até o tamanho do array
    for i in range(1, length):
        #se i está no final da lista
        if i == length - 1:
            new_run.append(the_array[i])
            runs.append(new_run)
            break
        # se o i-ésimo elemento do array for menor que o anterior
        if the_array[i] &lt; the_array[i-1]:
            # se new_run estiver definido como None (nulo)
            if not new_run:
                runs.append([the_array[i]])
                new_run.append(the_array[i])
            else:
                runs.append(new_run)
                new_run = []
        # senão se for igual ou maior que
        else:
            new_run.append(the_array[i])

    # para cada item em runs, adicione o item usando insertion sort
    for item in runs:
        sorted_runs.append(insertion_sort(item))
    
    # para cada run em sorted_runs, mescle-as
    sorted_array = []
    for run in sorted_runs:
        sorted_array = merge(sorted_array, run)

    print(sorted_array)

timsort([2, 3, 1, 5, 6, 7])
</code></pre><h3 id="complexidade-">Complexidade:</h3><p>Tim sort tem uma complexidade estável de O(N log(N)) e se compara muito bem com Quicksort. Uma comparação de complexidades pode ser encontrada neste <a href="https://cdn-images-1.medium.com/max/1600/1*1CkG3c4mZGswDShAV9eHbQ.png">gráfico</a>.</p><h2 id="merge-sort">Merge Sort</h2><p>Merge Sort é um algoritmo que emprega a técnica de <a href="https://guide.freecodecamp.org/algorithms/divide-and-conquer-algorithms">divisão e conquista</a>. Ele divide o array de entrada em duas metades, chama a si mesmo pelas duas metades e depois mescla as duas metades já ordenadas. A maior parte do algoritmo recebe dois arrays ordenados, e temos que mesclá-los em um único array ordenado. Todo o processo de ordenação de um array de N inteiros pode ser resumido em três etapas:</p><ul><li>Dividir o array em duas metades.</li><li>Ordene a metade esquerda e a metade direita usando o mesmo algoritmo recursivamente.</li><li>Mescle as metades ordenadas.</li></ul><p>Existe um algoritmo chamado <a href="https://en.wikipedia.org/wiki/Cheney%27s_algorithm">Two Finger Algorithm</a> que nos ajuda a mesclar dois arrays ordenados. Usar essa sub-rotina e chamar a função merge sort nas metades do array recursivamente nos dará o array ordenado final que estamos procurando.</p><p>Como este é um algoritmo baseado em recursão, temos uma relação de recorrência para ele. Uma relação de recorrência é simplesmente uma maneira de representar um problema em termos de seus subproblemas.</p><p><code>T(n) = 2 * T(n / 2) + O(n)</code></p><p>Colocando em linguagem simples, dividimos o subproblema em duas partes em cada etapa e temos uma quantidade linear de trabalho que precisamos realizar para mesclar as duas metades ordenadas em cada etapa.</p><h3 id="complexidade-1">Complexidade</h3><p>A maior vantagem de usar Merge sort é que a <a href="https://www.youtube.com/watch?v=V42FBiohc6c&amp;list=PL2_aWCzGMAwI9HK8YPVBjElbLbI3ufctn" rel="nofollow">complexidade temporal</a> é apenas n*log(n) para ordenar um array inteiro. É muito melhor do que a complexidade temporal de n^2 de execução de bubble sort ou insertion sort.</p><p>Antes de escrevermos o código, vamos entender como a ordenação por mesclagem funciona com a ajuda de um diagrama.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/news/content/images/2021/06/4712ef1a5d856dbb4af393fcc08a820a38787395.png" class="kg-image" alt="4712ef1a5d856dbb4af393fcc08a820a38787395" width="600" height="400" loading="lazy"></figure><ul><li>Inicialmente temos um array de 6 inteiros não ordenados Arr(5, 8, 3, 9, 1, 2)</li><li>Dividimos o array em duas metades Arr1 = (5, 8, 3) e Arr2 = (9, 1, 2).</li><li>Novamente, nós os dividimos em duas metades: Arr3 = (5, 8) e Arr4 = (3) e Arr5 = (9, 1) e Arr6 = (2).</li><li>Novamente, nós os dividimos em duas metades: Arr7 = (5), Arr8 = (8), Arr9 = (9), Arr10 = (1) e Arr6 = (2).</li><li>Vamos agora comparar os elementos nesses sub-arrays para mesclá-los.</li></ul><h3 id="propriedades--1">Propriedades:</h3><ul><li>Complexidade espacial: O(n)</li><li>Complexidade temporal: O(n*log(n)). A complexidade temporal para o Merge Sort pode não ser óbvia à primeira vista. O fator log(n) entra por causa da relação de recorrência que mencionamos anteriormente.</li><li>Ordenação in-loco: Não em uma implementação típica</li><li>Estável: Sim</li><li>Paralelizável :sim (diversas variantes paralelas são discutidas na terceira edição do livro Introduction to Algorithms Cormen de Leiserson, Rivest, and Stein.)</li></ul><h3 id="implementa-o-em-c--1"><strong>Implementação em C++</strong></h3><pre><code class="language-cpp">void merge(int array[], int left, int mid, int right)
{
    int i, j, k;

    // tamanho do sub-array esquerdo
int size_left = mid - left + 1;

// tamanho do sub-array direito
int size_right =  right - mid;

/* Criar arrays temporários */
int Left[size_left], Right[size_right];

/* Copiar dados para os arrays temporários L[] e R[] */
for(i = 0; i &lt; size_left; i++)
{
    Left[i] = array[left+i];
}

for(j = 0; j &lt; size_right; j++)
{
    Right[j] = array[mid+1+j];
}

// Mesclar os arrays temporários de volta no arr[left..right]
i = 0; // índice inicial do sub-array esquerdo
j = 0; // índice inicial do sub-array direito
k = left; // índice inicial do array mesclado

while (i &lt; size_left &amp;&amp; j &lt; size_right)
{
    if (Left[i] &lt;= Right[j])
    {
        array[k] = Left[i];
        i++;
    }
    else
    {
        array[k] = Right[j];
        j++;
    }
    k++;
}

// Copiar os elementos restantes de Left[]
while (i &lt; size_left)
{
    array[k] = Left[i];
    i++;
    k++;
}

// Copiar os elementos restantes de R[]
while (j &lt; size_right)
{
    array[k] = Right[j];
    j++;
    k++;
}
}

void mergeSort(int array[], int left, int right)
{
    if(left &lt; right)
    {
        int mid = (left+right)/2;

        // Ordenar a primeira e a segunda metade
    mergeSort(array, left, mid);
    mergeSort(array, mid+1, right);

    // Finalmente, mesclá-las
    merge(array, left, mid, right);
}
}</code></pre><h3 id="implementa-o-em-javascript-2"><strong>Implementação em JavaScript</strong></h3><pre><code class="language-js">function mergeSort (arr) {
  if (arr.length &lt; 2) return arr;
  var mid = Math.floor(arr.length /2);
  var subLeft = mergeSort(arr.slice(0,mid));
  var subRight = mergeSort(arr.slice(mid));
  return merge(subLeft, subRight);
}</code></pre><p>Primeiro, verificamos o comprimento do array. Se for 1, simplesmente retornamos o array. Este seria o nosso caso base. Caso contrário, descobriremos o valor do meio e dividiremos o array em duas metades. Agora vamos ordenar ambas metades com chamadas recursivas da função MergeSort.</p><pre><code class="language-js">function merge (a,b) {
    var result = [];
    while (a.length &gt;0 &amp;&amp; b.length &gt;0)
        result.push(a[0] &lt; b[0]? a.shift() : b.shift());
    return result.concat(a.length? a : b);
}</code></pre><p>Quando mesclamos as duas metades, armazenamos o resultado em um array auxiliar. Vamos comparar o elemento inicial do array esquerdo com o elemento inicial do array direito. O que for menor será enviado para o array de resultados e o removeremos dos respectivos arrays usando o operador [shift() . Se ainda restarem valores no array esquerdo ou direito, simplesmente o concatenamos no final do resultado. Aqui está o resultado ordenado:</p><pre><code class="language-js">var test = [5,6,7,3,1,3,15];
console.log(mergeSort(test));

&gt;&gt; [1, 3, 3, 5, 6, 7, 15]</code></pre><h3 id="um-tutorial-sobre-merge-sort-no-youtube">Um tutorial sobre Merge Sort no YouTube</h3><p>Aqui está um bom vídeo do YouTube <a href="https://www.youtube.com/watch?v=TzeBrDU-JaY">que aborda o tópico em detalhes</a> (em inglês).</p><h3 id="implementa-o-em-js">Implementação em JS</h3><pre><code class="language-js">const list = [23, 4, 42, 15, 16, 8, 3]

const mergeSort = (list) =&gt;{
  if(list.length &lt;= 1) return list;
  const middle = list.length / 2 ;
  const left = list.slice(0, middle);
  const right = list.slice(middle, list.length);
  return merge(mergeSort(left), mergeSort(right));
}

const merge = (left, right) =&gt; {
  var result = [];
  while(left.length || right.length) {
    if(left.length &amp;&amp; right.length) {
      if(left[0] &lt; right[0]) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    } else if(left.length) {
        result.push(left.shift())
      } else {
        result.push(right.shift())
      }
    }
  return result;
}

console.log(mergeSort(list)) // [ 3, 4, 8, 15, 16, 23, 42 ]
</code></pre><h3 id="implementa-o-em-c-1">Implementação em C</h3><pre><code class="language-c">#include&lt;stdlib.h&gt; 
#include&lt;stdio.h&gt;
void merge(int arr[], int l, int m, int r) 
{ 
    int i, j, k; 
    int n1 = m - l + 1; 
    int n2 =  r - m; 
  
    
    int L[n1], R[n2]; 
  
    for (i = 0; i &lt; n1; i++) 
        L[i] = arr[l + i]; 
    for (j = 0; j &lt; n2; j++) 
        R[j] = arr[m + 1+ j];
    i = 0; 
    j = 0; 
    k = l; 
    while (i &lt; n1 &amp;&amp; j &lt; n2) 
    { 
        if (L[i] &lt;= R[j]) 
        { 
            arr[k] = L[i]; 
            i++; 
        } 
        else
        { 
            arr[k] = R[j]; 
            j++; 
        } 
        k++; 
    } 
  
    
    while (i &lt; n1) 
    { 
        arr[k] = L[i]; 
        i++; 
        k++; 
    } 
  
    while (j &lt; n2) 
    { 
        arr[k] = R[j]; 
        j++; 
        k++; 
    } 
} 
  
void mergeSort(int arr[], int l, int r) 
{ 
    if (l &lt; r) 
    {  
        int m = l+(r-l)/2; 
  
        
        mergeSort(arr, l, m); 
        mergeSort(arr, m+1, r); 
  
        merge(arr, l, m, r); 
    } 
}
void printArray(int A[], int size) 
{ 
    int i; 
    for (i=0; i &lt; size; i++) 
        printf("%d ", A[i]); 
    printf("\n"); 
} 
int main() 
{ 
    int arr[] = {12, 11, 13, 5, 6, 7}; 
    int arr_size = sizeof(arr)/sizeof(arr[0]); 
  
    printf("Given array is \n"); 
    printArray(arr, arr_size); 
  
    mergeSort(arr, 0, arr_size - 1); 
  
    printf("\nSorted array is \n"); 
    printArray(arr, arr_size); 
    return 0; 
</code></pre><h3 id="implementa-o-em-c--2">Implementação em C++</h3><p>Vamos considerar o array A = {2,5,7,8,9,12,13} e array B = {3,5,6,9,15} e queremos que o array C esteja ordenado em ordem crescente também.</p><pre><code class="language-cpp">void mergesort(int A[],int size_a,int B[],int size_b,int C[])
{
     int token_a,token_b,token_c;
     for(token_a=0, token_b=0, token_c=0; token_a&lt;size_a &amp;&amp; token_b&lt;size_b; )
     {
          if(A[token_a]&lt;=B[token_b])
               C[token_c++]=A[token_a++];
          else
               C[token_c++]=B[token_b++];
      }
      
      if(token_a&lt;size_a)
      {
          while(token_a&lt;size_a)
               C[token_c++]=A[token_a++];
      }
      else
      {
          while(token_b&lt;size_b)
               C[token_c++]=B[token_b++];
      }

}
</code></pre><h3 id="implementa-o-em-python-1">Implementação em Python</h3><pre><code class="language-py">def merge(left,right,compare):
	result = [] 
	i,j = 0,0
	while (i &lt; len(left) and j &lt; len(right)):
		if compare(left[i],right[j]):
			result.append(left[i])
			i += 1
		else:
			result.append(right[j])
			j += 1
	while (i &lt; len(left)):
		result.append(left[i])
		i += 1
	while (j &lt; len(right)):
		result.append(right[j])
		j += 1
	return result

def merge_sort(arr, compare = lambda x, y: x &lt; y):
     #Função lambda usada para ordenar um array em ordem crescente e decrescente.
     #Por padrão, ela ordena o array em ordem crescente
	if len(arr) &lt; 2:
		return arr[:]
	else:
		middle = len(arr) // 2
		left = merge_sort(arr[:middle], compare)
		right = merge_sort(arr[middle:], compare)
		return merge(left, right, compare) 

arr = [2,1,4,5,3]
print(merge_sort(arr))
</code></pre><h3 id="implementa-o-em-java-1">Implementação em Java</h3><pre><code class="language-java">public class mergesort {

	public static int[] mergesort(int[] arr,int lo,int hi) {
		
		if(lo==hi) {
			int[] ba=new int[1];
			ba[0]=arr[lo];
			return ba;
		}
		
		int mid=(lo+hi)/2;
		int arr1[]=mergesort(arr,lo,mid);
		int arr2[]=mergesort(arr,mid+1,hi);
		return merge(arr1,arr2);
	}
	
	public static int[] merge(int[] arr1,int[] arr2) {
		int i=0,j=0,k=0;
		int n=arr1.length;
		int m=arr2.length;
		int[] arr3=new int[m+n];
		while(i&lt;n &amp;&amp; j&lt;m) {
			if(arr1[i]&lt;arr2[j]) {
				arr3[k]=arr1[i];
				i++;
			}
			else {
				arr3[k]=arr2[j];
				j++;
			}
			k++;
		}
		
		while(i&lt;n) {
			arr3[k]=arr1[i];
			i++;
			k++;
		}
		
		while(j&lt;m) {
			arr3[k]=arr2[j];
			j++;
			k++;
		}
		
		return arr3;
		
	}
	
	public static void main(String[] args) {
		int arr[]= {2,9,8,3,6,4,10,7};
		int[] so=mergesort(arr,0,arr.length-1);
		for(int i=0;i&lt;arr.length;i++)
			System.out.print(so[i]+" ");
	}

}
</code></pre><h3></h3> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmo de caminho de custo mínimo de Dijkstra - uma introdução detalhada e visual ]]>
                </title>
                <description>
                    <![CDATA[ Boas-vindas! Se você sempre quis aprender e entender o algoritmo de Dijkstra, este artigo é para você. Você verá como ele funciona nos bastidores com uma explicação gráfica passo a passo. Você aprenderá:  * Conceitos básicos sobre grafos (uma breve revisão).  * Para que o algoritmo de Dijkstra ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmo-de-caminho-de-custo-minimo-de-dijkstra-uma-introducao-detalhada-e-visual/</link>
                <guid isPermaLink="false">6212c18d06c4bd05013004d1</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Cayo Dias ]]>
                </dc:creator>
                <pubDate>Sun, 27 Mar 2022 14:51:49 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/Algorithm-Image-1.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/dijkstras-shortest-path-algorithm-visual-introduction/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Dijkstra's Shortest Path Algorithm - A Detailed and Visual Introduction</a>
      </p><p><strong>Boas-vindas!</strong> Se você sempre quis aprender e entender o algoritmo de Dijkstra, este artigo é para você. Você verá como ele funciona nos bastidores com uma explicação gráfica passo a passo.</p><p><strong>Você aprenderá:</strong></p><ul><li>Conceitos básicos sobre grafos (uma breve revisão).</li><li>Para que o algoritmo de Dijkstra é utilizado.</li><li>Como o algoritmo funciona através de um exemplo passo a passo.</li></ul><p><strong>Vamos começar. ✨</strong></p><h2 id="-introdu-o-aos-grafos">🔹 Introdução aos grafos</h2><p>Vamos começar com uma breve introdução aos grafos.</p><h3 id="conceitos-b-sicos">Conceitos básicos</h3><p>Grafos são estruturas de dados usadas para representar "conexões" entre pares de elementos.</p><ul><li>Esses elementos são chamados de nós (ou vértices). Eles representam objetos, pessoas ou entidades da vida real.</li><li>As conexões entre os nós são chamadas de arestas.</li></ul><p>Esta é uma representação gráfica de um grafo:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-123.png" class="kg-image" alt="image-123" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-123.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-123.png 832w" width="832" height="392" loading="lazy"></figure><p>Os <strong>nós</strong> são representados por círculos coloridos e as <strong>arestas</strong> são representadas por linhas que conectam esses círculos.</p><p><strong>💡 Dica: </strong>dois nós estarão conectados se houver uma aresta entre eles.</p><h3 id="aplica-es">Aplicações</h3><p>Os grafos são diretamente aplicáveis a cenários do mundo real. Por exemplo, poderíamos usá-los para modelar uma rede de transporte, onde os nós representariam instalações que enviam ou recebem produtos e arestas representariam estradas ou caminhos que os conectam (veja abaixo).</p><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-121.png" class="kg-image" alt="image-121" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-121.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-121.png 883w" width="883" height="491" loading="lazy"><figcaption>Rede representada por um grafo</figcaption></figure><h3 id="tipos-de-grafos">Tipos de grafos</h3><p>Os grafos podem ser:</p><ul><li><strong>Não dirigidos: </strong>se para cada par de nós conectados, você pode ir de um nó para o outro em ambas as direções.</li><li><strong>Dirigidos: </strong>se para cada par de nós conectados, você só pode ir de um nó para outro em uma direção específica. Usamos setas em vez de linhas simples para representar arestas dirigidas.</li></ul><figure class="kg-card kg-image-card kg-width-wide kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-124.png" class="kg-image" alt="image-124" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-124.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-124.png 997w" width="997" height="395" loading="lazy"><figcaption>Na caixa à esquerda: "Não dirigido" | Na caixa à direita: "Dirigido"</figcaption></figure><p><strong>💡 Dica:</strong> neste artigo, abordaremos grafos <strong>não dirigidos</strong>.</p><h3 id="grafos-ponderados">Grafos ponderados</h3><p>Um <strong>grafo ponderado</strong> é um grafo cujas arestas têm um "peso" ou "custo". O peso de uma aresta pode representar distância, tempo ou qualquer coisa que modele a "conexão" entre o par de nós que ela conecta.</p><p>Por exemplo, no grafo ponderado abaixo, você pode ver um número em azul próximo de cada aresta. Este número é usado para representar o peso da aresta correspondente.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-126.png" class="kg-image" alt="image-126" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-126.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-126.png 785w" width="785" height="316" loading="lazy"></figure><p><strong>💡 Dica:</strong> esses pesos são essenciais para o Algoritmo de Dijkstra. Você verá por que a seguir.</p><h2 id="-introdu-o-ao-algoritmo-de-dijkstra">🔸 Introdução ao algoritmo de Dijkstra</h2><p>Agora que você conhece os conceitos básicos de grafos, vamos nos aprofundar nesse algoritmo incrível.</p><ul><li>Propósito e casos de uso</li><li>História</li><li>Elementos básicos do algoritmo</li><li>Requisitos</li></ul><h3 id="prop-sito-e-casos-de-uso">Propósito e casos de uso</h3><p>Com o algoritmo de Dijkstra, você poderá encontrar o caminho de menor custo entre nós em um grafo. Particularmente, você poderá <strong>encontrar o caminho de menor custo entre um nó (denominado “origem”) e todos os outros nós do grafo</strong>, produzindo uma árvore de custo-mínimo.</p><p>Este algoritmo é usado em dispositivos GPS para encontrar o caminho mais curto entre a localização atual e o destino. Ele possui ampla aplicação na indústria, principalmente em domínios que requerem modelagem de redes</p><h3 id="hist-ria">História</h3><p>Este algoritmo foi criado e publicado pelo <a href="https://pt.wikipedia.org/wiki/Edsger_Dijkstra">Dr. Edsger W. Dijkstra</a>, um brilhante cientista da computação e engenheiro de software holandês.</p><p>Em 1959, ele publicou um artigo de 3 páginas intitulado "A note on two problems in connexion with graphs" (Uma nota sobre dois problemas em conexão com grafos, em tradução livre), onde explicou seu novo algoritmo.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-112.png" class="kg-image" alt="image-112" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-112.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/image-112.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-112.png 1200w" sizes="(min-width: 720px) 720px" width="1200" height="822" loading="lazy"><figcaption><a href="https://commons.wikimedia.org/wiki/File:Edsger_Dijkstra_1994.jpg">Dr. Edsger Dijkstra</a> na <a href="https://pt.wikipedia.org/wiki/Instituto_Federal_de_Tecnologia_de_Zurique">ETH Zurich</a>, em 1994 (foto: Andreas F. Borchert)</figcaption></figure><p>Durante uma entrevista em 2001, o dr. Dijkstra revelou como e por que desenvolveu o algoritmo:</p><blockquote>Qual é o caminho mais curto para viajar de Roterdã a Groningen? É o algoritmo para o caminho mais curto, que projetei em aproximadamente 20 minutos. Certa manhã, eu estava fazendo compras em Amsterdã com minha noiva e, cansado, sentamos no terraço do café para beber uma xícara de café e eu estava pensando se poderia fazer isso, e então, desenvolvi o algoritmo para o caminho mais curto . Como eu disse, foi uma invenção de 20 minutos. Na verdade, foi publicado em 1959, três anos depois. A publicação continua bem legal. Uma das razões por que ela é tão legal foi que eu a desenhei sem lápis e papel. Sem lápis e papel, você é quase forçado a evitar todas as complexidades evitáveis. Eventualmente, esse algoritmo se tornou, para minha grande surpresa, um dos pilares da minha fama. — Conforme citado no artigo <a href="https://en.wikipedia.org/wiki/Edsger_W._Dijkstra">Edsger W. Dijkstra</a> em <a href="https://dl.acm.org/doi/pdf/10.1145/1787234.1787249">An interview with Edsger W. Dijkstra</a> (Uma entrevista com Edsger W. Dijkstra).</blockquote><p>⭐ <strong>Inacreditável, não?</strong> Em apenas 20 minutos, o Dr. Dijkstra desenvolveu um dos algoritmos mais famosos da história da Ciência da Computação.</p><h3 id="no-es-b-sicas-do-algoritmo-de-dijkstra">Noções básicas do algoritmo de Dijkstra</h3><ul><li>O Algoritmo de Dijkstra basicamente começa no nó que você escolhe (o nó de origem) e analisa o grafo para encontrar o caminho de menor custo entre esse nó e todos os outros nós do grafo.</li><li>O algoritmo mantém o registro da distância mais curta atualmente conhecida de cada nó até o nó de origem e atualiza esses valores se encontrar um caminho de menor custo.</li><li>Uma vez que o algoritmo encontrou o caminho de menor custo entre o nó de origem e outro nó, esse nó é marcado como "visitado" e adicionado ao caminho.</li><li>O processo continua até que todos os nós do grafo tenham sido adicionados ao caminho. Desta forma, temos um caminho que conecta o nó de origem a todos os outros nós seguindo o caminho de menor custo possível para chegar a cada nó.</li></ul><h3 id="requisitos">Requisitos</h3><p>O Algoritmo de Dijkstra só funciona com grafos com pesos positivos. Isso porque, durante o processo, os pesos das arestas devem ser somados para encontrar o caminho de menor custo.</p><p>Se houver um peso negativo no grafo, o algoritmo não funcionará corretamente. Uma vez que um nó tenha sido marcado como "visitado", o caminho atual para esse nó é marcado como o caminho de menor custo para chegar a esse nó. E pesos negativos podem alterar isso se o peso total puder ser decrementado após essa etapa ter ocorrido.</p><h2 id="-exemplo-do-algoritmo-de-dijkstra">🔹 Exemplo do algoritmo de Dijkstra</h2><p>Agora que você sabe mais sobre esse algoritmo, vamos ver como ele funciona nos bastidores com um exemplo passo a passo.</p><p>Temos este grafo:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-76-1.png" class="kg-image" alt="image-76-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-76-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-76-1.png 921w" width="921" height="383" loading="lazy"></figure><p>O algoritmo gerará o caminho de menor custo (neste caso, o caminho mais curto) do nó 0 para todos os outros nós do grafo.</p><p><strong>💡 Dica: </strong>neste caso, assumiremos que o peso das arestas representa a distância entre dois nós.</p><p>Teremos o caminho mais curto do nó <code>0</code> ao nó <code>1</code>, do nó <code>0</code> ao nó <code>2</code>, do nó <code>0</code> ao nó <code>3</code>, e assim por diante para cada nó do grafo.</p><p>Inicialmente, temos esta lista de distâncias (veja a lista abaixo):</p><ul><li>A distância do nó de origem até ele mesmo é 0. Para este exemplo, o nó de origem será o nó 0, mas pode ser qualquer nó que você escolher.</li><li>A distância do nó de origem para todos os outros nós ainda não foi determinada, então usamos o símbolo do infinito para representar isso inicialmente.</li></ul><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-77-1.png" class="kg-image" alt="image-77-1" width="226" height="341" loading="lazy"><figcaption>Tabela de distâncias entre o nó 0 e os demais nós</figcaption></figure><p>Também temos esta lista (veja abaixo) para acompanhar os nós que ainda não foram visitados (nós que não foram incluídos no caminho):</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-78-1.png" class="kg-image" alt="image-78-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-78-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-78-1.png 816w" sizes="(min-width: 720px) 720px" width="816" height="103" loading="lazy"><figcaption>Lista de nós não visitados.</figcaption></figure><p><strong>💡 Dica: </strong>lembre-se de que o algoritmo é concluído quando todos os nós forem adicionados ao caminho.</p><p>Como estamos escolhendo começar no nó <code>0</code>, podemos marcar esse nó como visitado. De forma equivalente, nós o cortamos da lista de nós não visitados e adicionamos uma borda vermelha ao nó correspondente no diagrama:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-87.png" class="kg-image" alt="image-87" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-87.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-87.png 734w" sizes="(min-width: 720px) 720px" width="734" height="53" loading="lazy"></figure><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-83-1.png" class="kg-image" alt="image-83-1" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-83-1.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-83-1.png 876w" width="876" height="418" loading="lazy"></figure><p>Agora precisamos verificar a distância do nó <code>0</code> até seus nós adjacentes. Como você pode ver, estes são os nós &nbsp;<code>1</code> e <code>2</code> (veja as bordas vermelhas):</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-89.png" class="kg-image" alt="image-89" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-89.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-89.png 962w" width="962" height="377" loading="lazy"></figure><p><strong>💡 Dica:</strong> isso não significa que estamos adicionando imediatamente os dois nós adjacentes ao caminho mais curto. Antes de adicionar um nó a este caminho, precisamos verificar se encontramos o caminho mais curto para alcançá-lo. Estamos simplesmente fazendo um processo de exame inicial para ver as opções disponíveis.</p><p>Precisamos atualizar as distâncias do nó <code>0</code> ao <code>1</code> e ao nó <code>2</code> com os pesos das arestas que os conectam ao nó <code>0</code> (origem). Estes pesos são 2 e 6, respectivamente:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-90.png" class="kg-image" alt="image-90" width="198" height="292" loading="lazy"></figure><p>Após atualizar as distâncias dos nós adjacentes, precisamos:</p><ul><li>Selecionar o nó mais próximo do nó de origem com base nas distâncias conhecidas atuais.</li><li>Marcá-lo como visitado.</li><li>Adicioná-lo ao caminho.</li></ul><p>Se verificarmos a lista de distâncias, podemos ver que o nó <code>1</code> tem a menor distância até a origem (distância igual a 2), então, nós o adicionamos ao caminho.</p><p>No diagrama, podemos representar isso com uma aresta vermelha:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94.png" class="kg-image" alt="image-94" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-94.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94.png 958w" width="958" height="412" loading="lazy"></figure><p>Marcamos com um quadrado vermelho na lista para representar que foi "visitado" e que encontramos o caminho mais curto para este nó:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-92.png" class="kg-image" alt="image-92" width="224" height="308" loading="lazy"></figure><p>Riscamos da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-93.png" class="kg-image" alt="image-93" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-93.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-93.png 601w" width="601" height="82" loading="lazy"></figure><p>Agora, precisamos analisar os novos nós adjacentes para encontrar o caminho mais curto para alcançá-los. Analisaremos apenas os nós que são adjacentes aos nós que já fazem parte do caminho mais curto (o caminho marcado com arestas vermelhas).</p><p>Os nós <code>3</code> e <code>2</code> são ambos adjacentes aos nós que já estão no caminho porque estão diretamente conectados aos nós<code>1</code> e <code>0</code>, respectivamente, como você pode ver abaixo. Esses são os nós que analisaremos na próxima etapa.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94--1-.png" class="kg-image" alt="image-94--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-94--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94--1-.png 958w" width="958" height="412" loading="lazy"></figure><p>Como já temos a distância do nó de origem ao nó <code>2</code> anotado em nossa lista, não precisamos atualizar a distância desta vez. Precisamos apenas atualizar a distância do nó de origem ao novo nó adjacente (o nó <code>3</code>):</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-98.png" class="kg-image" alt="image-98" width="205" height="288" loading="lazy"></figure><p>Essa distância é igual a <strong>7</strong>. Vamos ver o porquê.</p><p>Para encontrar a distância do nó de origem para outro nó (neste caso, nó <code>3</code>), somamos os pesos de todas as arestas que formam o caminho mais curto para chegar a esse nó:</p><ul><li><strong>No caso do nó <code>3</code>:</strong> a distância total é <strong>7</strong> porque adicionamos os pesos das arestas que formam o caminho<code>0 -&gt; 1 -&gt; 3</code> (2 &nbsp;para a aresta <code>0 -&gt; 1</code> e 5 para a aresta <code>1 -&gt; 3</code>).</li></ul><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94--2-.png" class="kg-image" alt="image-94--2-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-94--2-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-94--2-.png 958w" width="958" height="412" loading="lazy"></figure><p>Agora que temos a distância até os nós adjacentes, temos que escolher qual nó será adicionado ao caminho. Devemos selecionar o nó <strong>não visitado</strong> com a distância mais curta (atualmente conhecida) até o nó de origem.</p><p>A partir da lista de distâncias, podemos detectar imediatamente que este é um nó <code>2</code> cuja distância é <strong>6</strong>:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-98--1-.png" class="kg-image" alt="image-98--1-" width="205" height="288" loading="lazy"></figure><p>Adicionamos ao caminho graficamente com uma borda vermelha ao redor do nó e uma aresta vermelha:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-96.png" class="kg-image" alt="image-96" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-96.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-96.png 932w" width="932" height="389" loading="lazy"></figure><p>Também o marcamos como visitado adicionando um pequeno quadrado vermelho na lista de distâncias e riscando-o da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-99.png" class="kg-image" alt="image-99" width="221" height="292" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-100.png" class="kg-image" alt="image-100" width="516" height="68" loading="lazy"></figure><p>Agora precisamos repetir o processo para encontrar o caminho mais curto do nó de origem até o novo nó adjacente, que é o nó <code>3</code>.</p><p>Você pode ver que temos dois caminhos possíveis <code>0 -&gt; 1 -&gt; 3</code> ou <code>0 -&gt; 2 -&gt; 3</code>. Vamos ver como podemos decidir qual é o caminho mais curto.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-96--1-.png" class="kg-image" alt="image-96--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-96--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-96--1-.png 932w" width="932" height="389" loading="lazy"></figure><p>O nó <code>3</code> já possui uma distância na lista que foi registrada anteriormente (7, veja a lista abaixo). Essa distância foi resultado de um passo anterior, onde somamos os pesos 5 e 2 das duas arestas que precisávamos cruzar para seguir o caminho <code>0 -&gt; 1 -&gt; 3</code>.</p><p>Mas agora temos outra alternativa. Se escolhermos seguir o caminho<code>0 -&gt; 2 -&gt; 3</code>, precisaríamos seguir duas arestas <code>0 -&gt; 2</code> e <code>2 -&gt; 3</code> com pesos <strong>6</strong> e <strong>8</strong>,<strong> </strong>respectivamente,<strong> </strong>o que representa uma distância total igual a 14.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-101.png" class="kg-image" alt="image-101" width="514" height="294" loading="lazy"></figure><p>Claramente, a primeira distância (existente) é menor (7 x 14), então escolheremos manter o caminho original <code>0 -&gt; 1 -&gt; 3</code>. <strong>Só atualizamos a distância se o novo caminho for mais curto</strong>.</p><p>Portanto, adicionamos este nó ao caminho usando a primeira alternativa: <code>0 -&gt; 1 -&gt; 3</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-104.png" class="kg-image" alt="image-104" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-104.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-104.png 933w" width="933" height="415" loading="lazy"></figure><p>Marcamos este nó como visitado e o riscamos da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-103.png" class="kg-image" alt="image-103" width="204" height="292" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-106.png" class="kg-image" alt="image-106" width="576" height="71" loading="lazy"></figure><p>Agora, repetimos esse processo.</p><p>Precisamos verificar os novos nós adjacentes que não visitamos até agora. Desta vez, esses nós são os nós <code>4</code> e <code>5</code>, já que ambos são adjacentes ao nó <code>3</code>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-104--1-.png" class="kg-image" alt="image-104--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-104--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-104--1-.png 933w" width="933" height="415" loading="lazy"></figure><p>Atualizamos as distâncias desses nós até o nó de origem, sempre tentando encontrar um caminho mais curto, se possível:</p><ul><li><strong>No caso do nó <code>4</code>:</strong> a distância é <strong>17</strong> para o caminho &nbsp;<code>0 -&gt; 1 -&gt; 3 -&gt; 4</code>.</li><li><strong>No caso do nó <code>5</code>:</strong> a distância é <strong>22</strong> para o caminho <code>0 -&gt; 1 -&gt; 3 -&gt; 5</code>.</li></ul><p><strong>💡 Dica:</strong> Observe que só podemos considerar estender o caminho mais curto (marcado em vermelho). Não podemos considerar caminhos que nos levarão através de arestas que não foram adicionadas ao caminho mais curto (por exemplo, não podemos formar um caminho que passe pela aresta<code>2 -&gt; 3</code>).</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-105.png" class="kg-image" alt="image-105" width="350" height="298" loading="lazy"></figure><p>Precisamos escolher qual nó não visitado será marcado como visitado. Neste caso, é o nó <code>4</code> porque tem a distância mais curta na lista de distâncias. Adicionamos graficamente no diagrama:</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-108.png" class="kg-image" alt="image-108" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-108.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-108.png 887w" width="887" height="403" loading="lazy"></figure><p>Também o marcamos como "visitado" adicionando um pequeno quadrado vermelho na lista:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-107.png" class="kg-image" alt="image-107" width="202" height="292" loading="lazy"></figure><p>E o riscamos da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-109.png" class="kg-image" alt="image-109" width="580" height="80" loading="lazy"></figure><p>E repetimos o processo novamente. Verificamos os nós adjacentes: nós <code>5</code> e <code>6</code>. Precisamos analisar cada caminho possível que podemos seguir para alcançá-los a partir de nós que já foram marcados como visitados e adicionados ao caminho.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-108--1-.png" class="kg-image" alt="image-108--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-108--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-108--1-.png 887w" width="887" height="403" loading="lazy"></figure><p><strong>No caso do nó <code>5</code>:</strong></p><ul><li>A primeira opção é seguir o caminho <code>0 -&gt; 1 -&gt; 3 -&gt; 5</code>, que tem uma distância de <strong>22</strong> para o nó de origem (2 + 5 + 15). Essa distância já foi registrada na lista de distâncias em uma etapa anterior.</li><li>A segunda opção seria seguir o caminho <code>0 -&gt; 1 -&gt; 3 -&gt; 4 -&gt; 5</code>, que tem uma distância de <strong>23</strong> para o nó de origem (2 + 5 + 10 + 6).</li></ul><p>Claramente, o primeiro caminho é mais curto, então o escolhemos para nó <code>5</code>.</p><p><strong>No caso do nó <code>6</code>:</strong></p><ul><li>O caminho disponível é <code>0 -&gt; 1 -&gt; 3 -&gt; 4 -&gt; 6</code>, que tem uma distância de <strong>19</strong> para o nó de origem (2 + 5 + 10 + 2).</li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-110.png" class="kg-image" alt="image-110" width="435" height="293" loading="lazy"></figure><p>Marcamos o nó com a distância mais curta (atualmente conhecida) como visitada. Neste caso, o nó<code>6</code>.</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-110-1.png" class="kg-image" alt="image-110-1" width="435" height="293" loading="lazy"></figure><p>E o riscamos da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-111.png" class="kg-image" alt="image-111" width="585" height="65" loading="lazy"></figure><p>Agora temos este caminho (marcado em vermelho):</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-112--1-.png" class="kg-image" alt="image-112--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-112--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-112--1-.png 886w" width="886" height="393" loading="lazy"></figure><p>Apenas um nó ainda não foi visitado, o nó <code>5</code>. Vamos ver como podemos incluí-lo no caminho.</p><p>Existem três caminhos diferentes que podemos seguir para alcançar o nó <code>5</code>a partir dos nós que foram adicionados ao caminho:</p><ul><li><strong>Opção 1: </strong><code>0 -&gt; 1 -&gt; 3 -&gt; 5</code> com uma distância de <strong>22 </strong> (2 + 5 + 15).</li><li><strong>Opção 2: </strong><code>0 -&gt; 1 -&gt; 3 -&gt; 4 -&gt; 5</code> com uma distância de <strong>23</strong> (2 + 5 + 10 + 6).</li><li><strong>Opção 3:</strong> <code>0 -&gt; 1 -&gt; 3 -&gt; 4 -&gt; 6 -&gt; 5</code> com uma distância de <strong>25 </strong>(2 + 5 + 10 + 2 + 6).</li></ul><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-113.png" class="kg-image" alt="image-113" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-113.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/size/w1000/2022/03/image-113.png 1000w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-113.png 1006w" sizes="(min-width: 720px) 720px" width="1006" height="295" loading="lazy"></figure><p>Selecionamos o caminho: <code>0 -&gt; 1 -&gt; 3 -&gt; 5</code> com uma distância de <strong>22</strong>.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-115.png" class="kg-image" alt="image-115" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-115.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-115.png 856w" width="856" height="394" loading="lazy"></figure><p>Marcamos o nó como visitado e o riscamos da lista de nós não visitados:</p><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-114.png" class="kg-image" alt="image-114" width="210" height="301" loading="lazy"></figure><figure class="kg-card kg-image-card"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-116.png" class="kg-image" alt="image-116" width="593" height="68" loading="lazy"></figure><p><strong>E aí está!</strong> Temos o resultado final com o caminho mais curto do nó <code>0</code> até cada nó do grafo.</p><figure class="kg-card kg-image-card kg-width-wide"><img src="https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-115--1-.png" class="kg-image" alt="image-115--1-" srcset="https://www.freecodecamp.org/portuguese/news/content/images/size/w600/2022/03/image-115--1-.png 600w, https://www.freecodecamp.org/portuguese/news/content/images/2022/03/image-115--1-.png 856w" width="856" height="394" loading="lazy"></figure><p>No diagrama, as linhas vermelhas marcam as arestas que pertencem ao caminho mais curto. Você precisa seguir essas arestas para seguir o caminho mais curto para chegar a um determinado nó no grafo a partir do nó <code>0</code>.</p><p>Por exemplo, se você deseja alcançar o nó <code>6</code> a partir do nó <code>0</code>, basta seguir as arestas vermelhas e estará, automaticamente, seguindo o caminho mais curto <code>0 -&gt; 1 -&gt; 3 -&gt; 4 - &gt; 6</code>.</p><h2 id="-resumindo">🔸 Resumindo</h2><ul><li>Os grafos são usados para modelar conexões entre objetos, pessoas ou entidades. Eles têm dois elementos principais: nós e arestas. Os nós representam objetos e as arestas representam as conexões entre esses objetos.</li><li>O algoritmo de Dijkstra encontra o caminho mais curto entre um determinado nó (que é chamado de "nó de origem") e todos os outros nós em um grafo.</li><li>Este algoritmo usa os pesos das arestas para encontrar o caminho que minimiza a distância total (peso) entre o nó de origem e todos os outros nós.</li></ul><p><strong>Eu realmente espero que você tenha gostado do artigo e o tenha achado útil</strong>. Agora, você sabe como o algoritmo de Dijkstra funciona nos bastidores. Siga a autora no Twitter (<a href="https://twitter.com/EstefaniaCassN">@EstefaniaCassN</a>) e <a href="https://www.udemy.com/user/estefania-cn/">confira os cursos da autora on-line</a>.</p> ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Algoritmos de força bruta explicados ]]>
                </title>
                <description>
                    <![CDATA[ Algoritmos de força bruta são exatamente o que parecem – métodos diretos de resolver um problema que dependem de poder computacional puro e de tentar todas as possibilidades em vez de técnicas avançadas para melhorar a eficiência.  Por exemplo, imagine que você tem um pequeno cadeado com uma combinação ]]>
                </description>
                <link>https://www.freecodecamp.org/portuguese/news/algoritmos-de-forca-bruta-explicados/</link>
                <guid isPermaLink="false">62053a05cfef2204db7ed96e</guid>
                
                    <category>
                        <![CDATA[ Algoritmos ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Fábio Garcia Vicente ]]>
                </dc:creator>
                <pubDate>Sat, 19 Feb 2022 22:35:55 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/portuguese/news/content/images/2022/02/5f9c9e20740569d1a4ca3b74.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p data-test-label="translation-intro">
        <strong>Artigo original:</strong> <a href="https://www.freecodecamp.org/news/brute-force-algorithms-explained/" target="_blank" rel="noopener noreferrer" data-test-label="original-article-link">Brute Force Algorithms Explained</a>
      </p><p>Algoritmos de força bruta são exatamente o que parecem – métodos diretos de resolver um problema que dependem de poder computacional puro e de tentar todas as possibilidades em vez de técnicas avançadas para melhorar a eficiência. </p><p>Por exemplo, imagine que você tem um pequeno cadeado com uma combinação 4 números, cada um deles indo de 0 a 9. Você esquece sua combinação, mas você não quer comprar outro cadeado. Como você não consegue se lembrar de nenhum dos números, precisa usar um método de força bruta para abrir a fechadura.</p><p>Você, então, insere todos os números no cadeado a partir do zero e os experimenta um a um: 0001, 0002, 0003 e assim sucessivamente até que ele abra. Na pior das hipóteses, seriam necessárias 10<sup>4</sup>, ou 10 mil tentativas para encontrar sua combinação.</p><p>Um exemplo clássico em ciência da computação é o problema do caixeiro viajante (TSP). Suponha que um vendedor precisa visitar 10 cidades em todo o país. Como determinar a ordem em que essas cidades devem ser visitadas de modo que a distância total percorrida seja minimizada?</p><p>A solução da força bruta é simplesmente calcular a distância total para toda possibilidade de rota possível e, depois, selecionar a mais curta. Isso não é particularmente eficiente, pois é possível eliminar muitas rotas através algoritmos inteligentes.</p><p>A complexidade de tempo da força bruta é <strong><strong><strong><strong>O(m</strong></strong>n<strong><strong>)</strong></strong></strong></strong>, que às vezes é escrito como <strong><strong>O(n*m)<strong><strong> </strong></strong></strong></strong>. Então, se fôssemos procurar uma string de "n" caracteres em uma string de "m" caracteres usando força bruta, faríamos n * m tentativas.</p><h2 id="mais-informa-es-sobre-algoritmos"><strong>Mais informações sobre algoritmos </strong></h2><p>Na ciência da computação, um algoritmo é simplesmente um conjunto de procedimentos passo a passo para solucionar um dado problema. Algoritmos podem ser projetados para realizar cálculos, processar dados ou realizar tarefas automatizadas de raciocínio. </p><p>Veja como a Wikipédia os define:</p><blockquote>Um algoritmo é um método eficaz que pode ser expresso em uma quantidade finita de espaço e tempo e em uma linguagem formal bem definida para calcular uma função. Partindo de um estado e de uma entrada (talvez vazia) iniciais, as instruções descrevem uma computação que, quando executada, prossegue através de um número finito de estados sucessivos bem definidos, eventualmente produzindo uma "saída" e terminando em um estado final. A transição de um estado para o seguinte não é necessariamente determinista; alguns algoritmos, conhecidos como algoritmos aleatórios, incorporam entradas aleatórias. </blockquote><p>Existem certos requisitos que um algoritmo deve cumprir:</p><ol><li>Definitividade: cada etapa do processo é declarada com precisão.</li><li>Computabilidade efetiva: cada etapa do processo pode ser realizada por um computador.</li><li>Finitude: o programa terminará eventualmente com sucesso.</li></ol><p>Alguns tipos comuns de algoritmos incluem:</p><ul><li>algoritmos de classificação</li><li>algoritmos de pesquisa</li><li>algoritmos de compressão.</li></ul><p>As classes de algoritmos incluem:</p><ul><li>de grafos</li><li>de programação dinâmica</li><li>de ordenação</li><li>de busca</li><li>de cordas (strings)</li><li>matemáticos</li><li>de geometria computacional</li><li>de otimização</li><li>diversas</li></ul><p>Embora tecnicamente não sejam uma classe de algoritmos, as estruturas de dados são frequentemente agrupadas com eles.</p><h3 id="efici-ncia"><strong>Eficiência</strong></h3><p>Os algoritmos são mais comumente julgados por sua eficiência e pela quantidade de recursos de computação necessários para concluir sua tarefa.</p><p>Uma maneira comum de avaliar um algoritmo é observar sua complexidade de tempo. Isso mostra como o tempo de execução do algoritmo aumenta à medida que o tamanho da entrada aumenta. Como os algoritmos de hoje precisam operar em grandes entradas de dados, é essencial que nossos algoritmos tenham um tempo de execução razoavelmente rápido.</p><h3 id="algoritmos-de-ordena-o"><strong>Algoritmos de ordenação</strong></h3><p>Os algoritmos de ordenação são de vários tipos, dependendo de sua necessidade. Alguns, muito comuns e amplamente utilizados, são:</p><h4 id="quicksort-ordena-o-r-pida-"><strong><strong><strong><strong>Quicksort</strong></strong></strong> (</strong>Ordenação rápida)</h4><p>Não há discussão sobre ordenação que possa terminar sem a ordenação rápida. Aqui está o conceito básico (em inglês): <a href="http://me.dt.in.th/page/Quicksort/">Quick Sort</a></p><h4 id="mergesort"><strong><strong><strong><strong>Mergesort</strong></strong></strong></strong></h4><p>Um algoritmo de ordenação que se baseia no conceito de como os arrays ordenados são mesclados para gerar um array ordenado. Leia mais sobre isso aqui (em inglês): <a href="https://www.geeksforgeeks.org/merge-sort/">Mergesort</a></p><p>O currículo do freeCodeCamp enfatiza fortemente a criação de algoritmos. Isso ocorre porque aprender algoritmos é uma boa maneira de praticar suas habilidades em programação. Os entrevistadores geralmente testam os candidatos quanto aos algoritmos durante as entrevistas de emprego para desenvolvedores.</p><h2 id="livros-sobre-algoritmos-em-javascript-em-ingl-s-"><strong>Livros sobre algoritmos em <strong>JavaScript</strong> (em inglês)<strong>:</strong></strong></h2><p></p><p><em>Data Structures in JavaScript</em></p><ul><li>Livro gratuito que cobre as estruturas de dados em JavaScript</li><li><a href="https://www.gitbook.com/book/pmary/data-structure-in-javascript/details">GitBook</a></li></ul><p><em><em>Learning JavaScript Data Structures and Algorithms - Second Edition</em></em></p><ul><li>Trata de programação orientada a objetos, herança prototípica, algoritmos de ordenação e busca, quicksort, mergesort, árvores binárias de busca e conceitos avançados de algoritmos</li><li><a href="https://www.amazon.com/Learning-JavaScript-Data-Structures-Algorithms/dp/1785285491">Amazon</a></li><li>ISBN-13: 978-1785285493</li></ul><p><em><em>Data Structures and Algorithms with JavaScript: Bringing classic computing approaches to the Web</em></em></p><ul><li>Trata de recursão, algoritmos de ordenação e busca, listas vinculadas e árvores binárias de busca.</li><li><a href="https://www.amazon.com/Data-Structures-Algorithms-JavaScript-approaches/dp/1449364934">Amazon</a></li><li>ISBN-13: 978-1449364939</li></ul> ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
