<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Maurício Antunes</title>
    <description>The latest articles on DEV Community by Maurício Antunes (@mauricioabreu).</description>
    <link>https://dev.to/mauricioabreu</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F91735%2F372c7cef-b9fb-44ab-ae7a-67a3522e6145.jpg</url>
      <title>DEV Community: Maurício Antunes</title>
      <link>https://dev.to/mauricioabreu</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mauricioabreu"/>
    <language>en</language>
    <item>
      <title>Solving "Minimum Genetic Mutation" leetcode</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Thu, 25 Jul 2024 03:07:32 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/solving-minimum-genetic-mutation-leetcode-5e9</link>
      <guid>https://dev.to/mauricioabreu/solving-minimum-genetic-mutation-leetcode-5e9</guid>
      <description>&lt;p&gt;Hello! Today, we are going to solve &lt;a href="https://leetcode.com/problems/minimum-genetic-mutation/" rel="noopener noreferrer"&gt;Minimum Genetic Mutation&lt;/a&gt; on leetcode. &lt;/p&gt;

&lt;p&gt;This challenge uses some concepts we've already talked in other posts: BFS (Breadth-First Search), brute force, sets, and finding the minimum using BFS.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge
&lt;/h2&gt;

&lt;p&gt;Given a gene string that is 8 characters long, containing only the letters &lt;strong&gt;A, C, G, and T&lt;/strong&gt;, find the &lt;strong&gt;minimum mutations&lt;/strong&gt; needed to change the initial gene into a target gene.&lt;/p&gt;

&lt;p&gt;For example, changing "AACCGGTT" to "AACCGGTA" is one mutation.&lt;/p&gt;

&lt;p&gt;One of the &lt;strong&gt;constraints&lt;/strong&gt; of the exercise is the &lt;em&gt;bank&lt;/em&gt;. The bank is a list of &lt;strong&gt;valid&lt;/strong&gt; mutations. To go from the start gene to the end gene and count how many mutations are needed, we must ensure every mutation is valid.&lt;/p&gt;

&lt;p&gt;Note that the &lt;strong&gt;starting point&lt;/strong&gt; is assumed to be valid, so it might not be included in the bank.&lt;/p&gt;

&lt;h2&gt;
  
  
  Our first approach
&lt;/h2&gt;

&lt;p&gt;Seems easy, right? We just need to check the difference in both genes. We don't need this bank.&lt;/p&gt;

&lt;p&gt;Let's implement this approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_mutations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str2&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;mutations&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;zip&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;str2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;mutations&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterates through both strings using &lt;code&gt;zip&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Returns 1 if a char in startGene differs from endGene;&lt;/li&gt;
&lt;li&gt;Sum all the ones.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try this approach and run the tests on leetcode. It passes!&lt;/p&gt;

&lt;p&gt;Now, submit the code. It didn't run, right? You will try to cover edge cases until you have no energy and understand that this challenge is not about differences; it is about &lt;strong&gt;paths&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Take a look at one of the examples where our simple code breaks:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;startGene = "AACCTTGG"
endGene = "AATTCCGG"
bank = ["AATTCCGG","AACCTGGG","AACCCCGG","AACCTACC"]

Output: 4
Expected: -1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It fails because there is no way to change one letter at a time from start gene to end gene while using only valid mutations.&lt;/p&gt;

&lt;p&gt;Let's visualize the problem with a simple diagram:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7pecd8frug203fnaiw7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj7pecd8frug203fnaiw7.png" alt="This diagram illustrates the genetic mutation paths required to transform the gene sequence AACCGGTT into AAACGGTA. The paths include one path with a single mutation, two paths with two mutations, and one path with three mutations. Nodes represent gene sequences, and edges represent valid mutations" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A better approach
&lt;/h2&gt;

&lt;p&gt;Now we know the bank must be used, let's try a correct approach to solve the problem.&lt;/p&gt;

&lt;p&gt;Read the challenge description again. Focus on the highlighted parts like &lt;em&gt;minimum&lt;/em&gt;, &lt;em&gt;valid&lt;/em&gt;, and &lt;em&gt;starting point&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;They all suggest that we need a code that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ends as soon as we reach the target;&lt;/li&gt;
&lt;li&gt;Checks if our moves are valid;&lt;/li&gt;
&lt;li&gt;Has a starting point to iterate from to reach the target.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Breadth-first_search" rel="noopener noreferrer"&gt;Bread-First Search (BFS)&lt;/a&gt; is excelent for finding the shortest or minimum path needed to go from one position to another.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation
&lt;/h2&gt;

&lt;p&gt;Here is the code we are going to explain in detail:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deque&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;minMutation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;startGene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;endGene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="n"&gt;startGene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt; &lt;span class="c1"&gt;# gene, changes
&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;endGene&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;endGene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACGT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                        &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First, we convert our bank in a set to perform O(1) lookups:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;bank&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since we need to check if every mutation is valid, turning the bank into a set is the best approach.&lt;/p&gt;

&lt;p&gt;Then, we create our queue. In Python, we can initialize the queue with some values. Our &lt;em&gt;starting point&lt;/em&gt; is &lt;code&gt;startGene&lt;/code&gt; with a total of mutations that starts from zero.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;([(&lt;/span&gt;&lt;span class="n"&gt;startGene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remember I said this challenge is about paths? With this in mind, we know we need to keep track of the paths we visited, or the genes we already checked, so we don't end up in an infinite loop.&lt;/p&gt;

&lt;p&gt;And now the most complex part:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;endGene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACGT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;while queue&lt;/code&gt; ensures we iterate our queue until it has genes.&lt;/p&gt;

&lt;p&gt;Everytime we pop one node from the queue, we check if it is equal to our target, &lt;code&gt;endGene&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;endGene&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then we have two nested &lt;code&gt;for&lt;/code&gt; loops. They are necessary because we want to replace one letter at a time and check if the mutation is valid. The first loop iterates through the current string, and the second loop tries to replace the current index with A, C, G, or T.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ACGT&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[:&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;gene&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:]&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;attempt_gene&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;changes&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
            &lt;span class="n"&gt;bank&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attempt_gene&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the gene is in the bank we add it to the queue as a valid mutation (or a path).&lt;/p&gt;

&lt;p&gt;We also remove it from the bank, so we don't need to check it again to avoid adding it back to the queue, which would cause an infinite loop.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Time:&lt;/strong&gt; it is O(b) where &lt;em&gt;b&lt;/em&gt; is the bank size.&lt;br&gt;
It is a little tricky, but the reason is that a gene string has a limit of 8 characters. When measuring time complexity we need to drop the constant, no matter how big they are. In our case, the constants are &lt;strong&gt;8&lt;/strong&gt; and &lt;strong&gt;4&lt;/strong&gt;: genes are 8 characters long and we might have ending up replacing each letter by A, C, G or T. 8 * 4 = 32&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space:&lt;/strong&gt; it is also O(b) where &lt;em&gt;b&lt;/em&gt; is the bank size.&lt;br&gt;
We use a queue that can store, in the worst case, all genes from the bank. &lt;br&gt;
There is also the set we use to keep track of visited genes that can contain up to &lt;em&gt;b&lt;/em&gt; genes.&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>bfs</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>Resolvendo o "Rotting Oranges"</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Thu, 18 Jul 2024 23:08:00 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/resolvendo-o-rotting-oranges-25ci</link>
      <guid>https://dev.to/mauricioabreu/resolvendo-o-rotting-oranges-25ci</guid>
      <description>&lt;p&gt;Hoje a leitura é sobre como resolver o leetcode &lt;a href="https://leetcode.com/problems/rotting-oranges/" rel="noopener noreferrer"&gt;Rotting Oranges&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos passar por como resolver o problema, como entender algumas palavras-chave da descrição e o código da solução em Python.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema
&lt;/h2&gt;

&lt;p&gt;Em uma matriz &lt;code&gt;m x n&lt;/code&gt;, cada uma das posição pode ter três valores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;0&lt;/code&gt; - uma posição vazia;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;1&lt;/code&gt; - uma laranja fresquinha;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;2&lt;/code&gt; - uma laranja podre.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A cada minuto, uma laranja fresca que está perto de uma podre vai apodrecer também.&lt;/p&gt;

&lt;p&gt;Precisamos retornar o número de minutos passados até que todas laranjas estejam podres, ou &lt;code&gt;-1&lt;/code&gt; caso não seja possível ter todas elas apodrecidas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pensando no problema
&lt;/h2&gt;

&lt;p&gt;Pra ajudar na resolução do problema, é fundamental que a gente consiga visualizar a disposição dessa matriz com algumas laranjas posicionadas:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhix2awy3069lmjwl7097.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhix2awy3069lmjwl7097.png" alt="duas laranjas podres posicionadas em cantos opostos, uma no canto inferior esquerdo e outro no canto superior direito, próximas de outras laranjas frescas e espaços vazios" width="800" height="560"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Com essa disposição, quantos minutos demoraria para todas ficarem podres?&lt;/p&gt;

&lt;p&gt;A) 5&lt;br&gt;
B) 3&lt;br&gt;
C) 4&lt;/p&gt;

&lt;p&gt;Talvez eu tenha decepcionado você, mas a resposta não é 4 nem 5.&lt;br&gt;
Algumas pessoas pensam que é 4 porque parece ser o maior caminho, se partir da laranja podre que fica embaixo. Outras pessoas pensam que é 5 porque é o número total de laranjas frescas. &lt;/p&gt;

&lt;p&gt;Demoram 3 minutos pra que todas as laranjas fiquem podres.&lt;/p&gt;

&lt;p&gt;O racional por trás é que a cada minuto mais de uma laranja pode ficar podre. Ou seja, as laranjas vão ficando podre &lt;strong&gt;simultaneamente&lt;/strong&gt;. Guarde bem essa palavra.&lt;/p&gt;

&lt;p&gt;Essa matriz é um &lt;strong&gt;grafo&lt;/strong&gt;. Sim, &lt;a href="https://www.linkedin.com/pulse/matrices-graphs-tivadar-danka/" rel="noopener noreferrer"&gt;matrizes são grafos&lt;/a&gt;. É possível codificar em uma matriz um grafo.&lt;/p&gt;

&lt;p&gt;E no que saber isso muda? Primeiramente, isso facilita nossa compreensão sobre o problema. Nosso objetivo é dizer quanto tempo demora até todas laranjas ficarem podres, tentando conectar os pontos (laranjas podres e laranjas frescas). Problemas onde temos conexões entre pontos em uma matriz têm cheiro de grafo. &lt;/p&gt;

&lt;p&gt;Há duas maneiras bem famosas de resolver problemas com grafos: &lt;strong&gt;DFS&lt;/strong&gt; (Depth-First Search) e &lt;strong&gt;BFS&lt;/strong&gt; (Breadth-First Search).&lt;/p&gt;

&lt;p&gt;Pra esse problema, vamos usar &lt;strong&gt;BFS&lt;/strong&gt;. A motivação tem a ver com a palavra &lt;em&gt;simultaneamente&lt;/em&gt;. Quando usamos DFS pra resolver um problema, usamos stack. E uma stack é uma estrutura de dados usada em recursão. Quando usamos recursão, nós esgotamos a possibilidade de um caminho antes  de testar outras opções, o que impede que seja simultâneo.&lt;/p&gt;

&lt;p&gt;Diferente da stack, uma &lt;a href="https://pt.wikipedia.org/wiki/Deque_(estruturas_de_dados)" rel="noopener noreferrer"&gt;deque&lt;/a&gt; vai nos ajudar a procurar as laranjas simultaneamente.&lt;/p&gt;

&lt;p&gt;Nesse problema, por exemplo, usar recursão contaria o caminho partindo de uma única laranja podre, contando de forma errônea.&lt;/p&gt;
&lt;h2&gt;
  
  
  Passos do algoritmo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Contar quantas laranjas frescas temos. Enquanto elas forem apodrecendo, quando chegar a ZERO, podemos usar isso como condição de parada.&lt;/li&gt;
&lt;li&gt;Coletar as posições das laranjas podres. A razão é que, sabendo onde elas estão, podemos usar BFS para iterar em todas as laranjas podres simultaneamente.&lt;/li&gt;
&lt;li&gt;Caminhar em todas direções, exceto nas diagonais, tentando achar novas laranjas para apodrecer.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Código da solução
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;orangesRotting&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;]])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="n"&gt;deltas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
        &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

                &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;deltas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                    &lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt;

                    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="n"&gt;next_row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
                        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
                    &lt;span class="p"&gt;):&lt;/span&gt;
                        &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
                        &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                        &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Apesar do código ser um pouco longo (resolução de problemas de grafo costumam ter bastante código), ele não é tão complexo. Pelo menos não dá aquela sensação de ter que tirar um coelho da cachola por linha.&lt;/p&gt;

&lt;p&gt;Começamos com algumas variáveis importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uma deque pra BFS;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;minutes&lt;/code&gt; pra guardar o resultado final;&lt;/li&gt;
&lt;li&gt;E &lt;code&gt;fresh&lt;/code&gt; pra contar quantas laranjas frescas temos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora contamos quantas laranjas frescas temos e quais são as laranjas podres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vem um bloco ligeiramente complexo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;deltas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;deltas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt;

            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="n"&gt;next_row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
                &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
                &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
                &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;deltas&lt;/code&gt; é uma variável pra guardar todas as direções nas quais podemos caminhar. Em uma matriz &lt;code&gt;m x n&lt;/code&gt; podemos caminhar uma coluna a esquerda, uma linha abaixo, etc.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;while queue and fresh&lt;/code&gt; garante nossa condição de parada. Enquanto a gente tiver laranjas pra adicionar e ainda ter laranjas frescas, precisamos continuar procurando laranjas podres para que elas apodreçam todas as outras que estão ao alcance.&lt;/p&gt;

&lt;p&gt;Agora vem a parte mais &lt;em&gt;tricky&lt;/em&gt;: o &lt;code&gt;for&lt;/code&gt; dentro do &lt;code&gt;while&lt;/code&gt;.&lt;br&gt;
Ele é essencial para que a gente conte os minutos das laranjas podres simultaneamente.&lt;/p&gt;

&lt;p&gt;Você pode fazer o teste aí. Abra um interpretador do Python, importe o deque, crie uma deque e comece a remover os elementos da esquerda e adicionando à direita.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;for&lt;/code&gt; garante que a gente itere, por exemplo, na primeira vez, todas as laranjas podres e continue a seguir a mesma estratégia pras próximas. Se no próximo loop a gente enfileirar mais 3 laranjas podres, vamos iterar nessas 3 laranjas que vão adicionar mais &lt;code&gt;n&lt;/code&gt; laranjas, e assim por diante.&lt;/p&gt;

&lt;p&gt;Agora mais um &lt;code&gt;for&lt;/code&gt;. Esse iterando em todas as direções:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;deltas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;dc&lt;/span&gt;

    &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;next_row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]))&lt;/span&gt;
        &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
    &lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;grid&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
        &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;next_row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;next_col&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
        &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E a condição do meio é extremamente importante:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confere se ainda estamos caminhando dentro da matriz - colunas e linhas válidas;&lt;/li&gt;
&lt;li&gt;Confere se a posição atual é uma laranja e é fresca.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se positivo, adicionamos ela na &lt;em&gt;queue&lt;/em&gt; de laranjas podres e diminuímos a contagem de laranjas frescas.&lt;/p&gt;

&lt;p&gt;Sem esquecer de atualizar o valor pra 2, pois garante que não adicionemos a laranja podre de volta na nossa &lt;em&gt;queue&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;E, pra finalizar, retornamos o valor de minutos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;minutes&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;fresh&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se ainda tiver laranjas frescas, então não foi possível apodrecer todas, retornando &lt;code&gt;-1&lt;/code&gt;. Se não, retornamos o número de minutos passados.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Complexidade de tempo:&lt;/strong&gt; O(m x n)&lt;br&gt;
Precisamos iterar o board inteiro pra descobrir as laranjas, porém, apenas uma vez cada linha/coluna.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complexidade de espaço:&lt;/strong&gt; O(m x n)&lt;br&gt;
Usamos uma queue que, no pior caso, vai ter uma matriz inteira cheia de laranjas podres.&lt;/p&gt;

&lt;p&gt;Não é porque tem vários loops aninhados que a complexidade de tempo é quadrática. É preciso analisar, linha por linha, o que o código faz.&lt;/p&gt;




&lt;p&gt;Esse desafio não é simples e eu demorei algumas horas pra concluir. E esse não foi o código final. Ao todo eu tentei submeter &lt;strong&gt;oito vezes&lt;/strong&gt; na plataforma leetcode.&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>bfs</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>Resolvendo o "Five sort"</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Thu, 23 May 2024 23:17:45 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/resolvendo-o-five-sort-3oi1</link>
      <guid>https://dev.to/mauricioabreu/resolvendo-o-five-sort-3oi1</guid>
      <description>&lt;p&gt;O objetivo desse post é demonstrar como eu pensei e solucionei o desafio citado usando uma técnica chamada two pointers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Desafio
&lt;/h2&gt;

&lt;p&gt;Dado um array de números inteiros, mova os números 5 para o final dele. Exemplo: &lt;code&gt;[1, 5, 2, 5, 3]&lt;/code&gt;&lt;br&gt;
Como resultado, espera-se o seguinte retorno da função: &lt;code&gt;[1, 3, 2, 5, 5]&lt;/code&gt; ou &lt;code&gt;[2, 1, 3, 5, 5]&lt;/code&gt;. O importante é que os 5 estejam no final do array.&lt;/p&gt;
&lt;h2&gt;
  
  
  Abordagem
&lt;/h2&gt;

&lt;p&gt;Há várias maneiras de resolver esse problema. Muitas pessoas deram ótimas soluções &lt;a href="https://x.com/maugzoide/status/1793036399639203883"&gt;nessa thread no Twitter&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Vou abordar nesse texto uma forma mais profunda de ver o problema além do código.&lt;/p&gt;

&lt;p&gt;Uma das maneiras de abordar esse problema é removendo todos os 5 da lista original, adicioná-los em outros lista e concatenar no final (&lt;code&gt;lista1 + lista2&lt;/code&gt;). Ou remover da lista e adicionar no final.&lt;/p&gt;

&lt;p&gt;Não é uma solução ruim, porém, ela tem uma alta complexidade de tempo. Toda vez que você remove um item de um array e ele não é o último, o array precisa ser rearranjado. Isso significa que toda remoção vai adicionar N passos adicionais na sua solução.&lt;/p&gt;

&lt;p&gt;Uma abordagem interessante pro problema é usar a técnica two pointers, assumindo que podemos modificar o array in-place, ou seja, modificar o valor do array que, em Python, por exemplo, é mutável.&lt;/p&gt;

&lt;p&gt;A técnica se baseia em ter dois ponteiros para fazer comparações em diferentes partes do array, enquanto se move e aplica todas as operações necessárias para resolver o problema.&lt;/p&gt;

&lt;p&gt;Nossa abordagem irá comparar os elementos do início com os do fim do array. Com um exemplo de dois elementos (&lt;code&gt;[5, 1]&lt;/code&gt;) fica mais fácil. Começando o algoritmo, comparamos o primeiro valor com o último. O primeiro é 5, então movemos ele pro fim, trocando pelo 1, resultando em &lt;code&gt;[1, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Vamos adicionar dois valores &lt;code&gt;[5, 5, 1, 3]&lt;/code&gt;.&lt;br&gt;
Primeiro e últimos trocam, pois o primeiro é 5. Movemos os dois ponteiros, trocando 5 e 1 também, resultando em &lt;code&gt;[3, 1, 5, 5]&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Abaixo uma demonstração visual (com texto alternativo) do problema sendo resolvido:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj0hspw9naoa0z84ilw4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffj0hspw9naoa0z84ilw4.png" alt="Na imagem temos dois ponteiros, i e j representados em verde e azul respectivamente. Na próxima parte, demonstramos um array com os valores 5, 5, 1, 3 e uma seta mostrando que 5 e 3 trocam de lugar. Na próxima linha, 3, 5, 1, 5, com uma seta mostrando que 5 e 1 trocam de lugar. No fim, o array correto com valores 3, 1, 5, 5" width="800" height="862"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nosso algoritmo se baseia em comparar os elementos do array em direção ao meio do dele. Enquanto o ponteiro &lt;em&gt;i&lt;/em&gt; for menor que o ponteiro &lt;em&gt;j&lt;/em&gt;, nós continuamos a comparar os elementos e trocando quando for necessário.&lt;/p&gt;
&lt;h2&gt;
  
  
  Código
&lt;/h2&gt;

&lt;p&gt;A nossa implementação vai usar Python, uma linguagem muito utilizada em resolução de algoritmos, tanto em plataformas quanto leetcode quando em entrevistas de emprego.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;five_sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
  &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
      &lt;span class="k"&gt;continue&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;j&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="n"&gt;j&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;nums&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Passos do algoritmo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Começamos os ponteiros nas pontas (começo e fim);&lt;/li&gt;
&lt;li&gt;Respeitamos nosso caso base de iterar até que os ponteiros não tenham se encontrado;&lt;/li&gt;
&lt;li&gt;Quando o 5 já está no fim do array, apenas decrementamos o &lt;em&gt;j&lt;/em&gt; para que continuemos a avaliar os outros números;&lt;/li&gt;
&lt;li&gt;Quando o 5 é encontrado no ponteiro &lt;em&gt;i&lt;/em&gt;, precisamos fazer a troca com o número do ponteiro &lt;em&gt;j&lt;/em&gt;;&lt;/li&gt;
&lt;li&gt;Retornamos a lista modificada in-place.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Complexidade
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tempo: O(n)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Independente de quantos elementos tem o array, no pior caso precisamos iterar em todos elementos.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Espaço: O(1)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Nenhum espaço adicional é alocado. Como fazemos a modificação in-place, não há a criação de um array adicional.&lt;/p&gt;

</description>
      <category>twopointers</category>
      <category>algorithms</category>
      <category>python</category>
    </item>
    <item>
      <title>Solving "Minimum time to make rope colorful"</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Sat, 30 Dec 2023 14:22:38 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/solving-minimum-time-to-make-rope-colorful-4n18</link>
      <guid>https://dev.to/mauricioabreu/solving-minimum-time-to-make-rope-colorful-4n18</guid>
      <description>&lt;p&gt;Today we're diving into a challenging problem: &lt;a href="https://leetcode.com/problems/minimum-time-to-make-rope-colorful"&gt;Minimum time to make rope colorful&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding the problem
&lt;/h2&gt;

&lt;p&gt;In this challenge, we are presented to a rope with some balloons on it. Alice, the owner of the balloons, does not like when the rope has consecutive balloons of the same color.&lt;/p&gt;

&lt;p&gt;Each balloon has a different time (in seconds) to remove.&lt;/p&gt;

&lt;p&gt;The goal is to remove the minimum number of balloons to ensure &lt;em&gt;no two consecutive balloons are of the same color&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visualizing the challenge
&lt;/h3&gt;

&lt;p&gt;Look at the image below to better understand our task:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KOYae-nB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hm36lt9ic7fkm24joep.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KOYae-nB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1hm36lt9ic7fkm24joep.png" alt="Balloons1" width="753" height="292"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Initial arrangement of balloons: green(1), blue(2), blue(4), red(3).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Which one do you think we need to remove?&lt;/p&gt;

&lt;p&gt;In this setup, the best choice is to remove the second balloon (blue, 2 seconds).&lt;/p&gt;

&lt;p&gt;For the rope to be colorful in the best time, we need to remove the balloons that takes &lt;em&gt;less time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;After removal, the arrangement looks like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6oP7ta7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ml4fp11fs6tecc0c8dp8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6oP7ta7X--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ml4fp11fs6tecc0c8dp8.png" alt="Balloons2" width="546" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Arrangement after optimal removal: green(1), blue(4), red(3).&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Approaching the solution
&lt;/h2&gt;

&lt;p&gt;One of the difficulties of this problem is that the optimal solution relies on a technique we don't often use daily: greedy algorithm.&lt;/p&gt;

&lt;p&gt;Before explaining how greedy algorithms work, let's write down how we decide which balloons we remove.&lt;/p&gt;

&lt;h3&gt;
  
  
  Algorithm steps
&lt;/h3&gt;

&lt;p&gt;What are the necessary steps to find the minimum time to make the rope colorful?&lt;/p&gt;

&lt;p&gt;One reason this problem is challenging is the difficulty in identifying the underlying pattern. Many problems can be solved by first considering the appropriate data structure — for instance, using Depth-First Search (DFS) for graph problems or recursion for binary tree problems. However, this particular challenge requires a different approach.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterate over the balloon sequence (a string is a sequence).&lt;/li&gt;
&lt;li&gt;Start from the second balloon to easily compare it with the first.&lt;/li&gt;
&lt;li&gt;When encountering balloons of the same color, remove the one taking less time.&lt;/li&gt;
&lt;li&gt;Keep track of the maximum time spent on a single balloon removal.&lt;/li&gt;
&lt;li&gt;Sum up the time for each removal to get the total time.&lt;/li&gt;
&lt;li&gt;If the current balloon is a different color, reset the maximum time to the current balloon's time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This approach highlights the importance of making local optimal choices at each step, a hallmark of greedy algorithms.&lt;/p&gt;

&lt;h3&gt;
  
  
  Greedy algorithm explained
&lt;/h3&gt;

&lt;p&gt;Being "greedy" in this context means making the best immediate choice at each step without reconsidering previous choices. This differs from strategies like &lt;a href="https://pt.wikipedia.org/wiki/Backtracking"&gt;backtracking&lt;/a&gt;, where you might revisit past decisions.&lt;/p&gt;

&lt;p&gt;In this problem, a "sequence" of same-colored balloons forms our local problem. By determining the minimum time to remove balloons in each sequence, we find the overall solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Solution
&lt;/h2&gt;

&lt;p&gt;Below is the Python code that solves the problem. Pay close attention to the part of the code that checks if the balloons are the same color and how we calculate the minimum time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;minCost&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;min_time&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
        &lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                &lt;span class="n"&gt;min_time&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
                &lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
            &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;min_time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Efficiency analysis: time and space complexity
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Time complexity: O(N)&lt;/strong&gt; where &lt;em&gt;N&lt;/em&gt; is the length of the colors string (or neededTime). This is because our solution involves a single &lt;code&gt;for loop&lt;/code&gt; that iterates each color exactly once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Space complexity: O(1)&lt;/strong&gt;. It is because we don't use any extra additional memory. Our solution uses a fixed amount of space, the variables &lt;code&gt;min_time&lt;/code&gt; and &lt;code&gt;max_time_in_sequence&lt;/code&gt;, which remain constant regardless of whether the number of colors is 1 or 100.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;It's important to note that when calculating space complexity, we do not consider the space used by the input itself.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we learned how to solve the challenge "Minimum Time to Make Rope Colorful" and apply greedy algorithm using a greedy algorithm. We also explored how to determine when a greedy approach is the most effective strategy.&lt;/p&gt;

&lt;p&gt;Keys points of this article include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterating through the balloon sequence starting from the second balloon, allowing for a straightforward comparison between adjacent balloons:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;idx&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;colors&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Calculating the minimum time for removal when encountering balloons of the same color:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;min_time&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_time_in_sequence&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;neededTime&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;idx&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>dsa</category>
      <category>greedy</category>
    </item>
    <item>
      <title>Resolvendo "Grupo de Anagramas"</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Wed, 27 Dec 2023 18:01:24 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/resolvendo-grupo-de-anagramas-2hie</link>
      <guid>https://dev.to/mauricioabreu/resolvendo-grupo-de-anagramas-2hie</guid>
      <description>&lt;p&gt;Hoje vamos resolver mais um leetcode. E, dessa vez, o desafio escolhido é o &lt;a href="https://leetcode.com/problems/group-anagrams" rel="noopener noreferrer"&gt;Grupo de Anagramas&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nesse desafio, precisamos agrupar os diferentes anagramas passados como entrada da função.&lt;/p&gt;

&lt;p&gt;Um anagrama é uma palavra que é formada a partir das letras de uma outra palavra. Por exemplo: &lt;em&gt;alegria&lt;/em&gt; e &lt;em&gt;alergia&lt;/em&gt; e &lt;em&gt;regalia&lt;/em&gt;. Todas essas palavras são anagramas.&lt;/p&gt;

&lt;p&gt;O desafio é agrupar todos os anagramas. A entrada da função terá uma lista de diferentes palavras que podem ou não ser anagramas. Esse agrupamento deve ser uma lista de listas.&lt;/p&gt;

&lt;p&gt;Exemplo do site:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Input:&lt;/strong&gt; &lt;code&gt;["eat","tea","tan","ate","nat","bat"]&lt;/code&gt;&lt;br&gt;
&lt;strong&gt;Output:&lt;/strong&gt; &lt;code&gt;[["bat"],["nat","tan"],["ate","eat","tea"]]&lt;/code&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Escrevendo o algoritmo
&lt;/h2&gt;

&lt;p&gt;Esse é um problema de nível médio, o que significa que a solução não vem tão fácil. Muitas dessas questões tem diversas soluções, e o que difere elas é uma menor complexidade te tempo/espaço e, também, ciclomática.&lt;/p&gt;

&lt;p&gt;Vamos dividir o problema em partes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterar sobre a lista de palavras;&lt;/li&gt;
&lt;li&gt;Para cada palavra, vamos calcular a &lt;em&gt;chave&lt;/em&gt; desse anagrama (já vamos explicar o que é isso que chamei de chave);&lt;/li&gt;
&lt;li&gt;Usaremos essa chave para agrupar os anagramas;&lt;/li&gt;
&lt;li&gt;Agruparemos os anagramas em um dicionário, que casa com o conceito de ter uma chave. O valor de cada chave será uma lista de anagramas;&lt;/li&gt;
&lt;li&gt;Retornaremos apenas os valores do dicionário, que vai ser uma lista de listas.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Calculando a chave
&lt;/h3&gt;

&lt;p&gt;O cálculo da chave é essencial pra esse algoritmo. Ela vai servir para agruparmos as palavras que são anagramas.&lt;/p&gt;

&lt;p&gt;O desafio tem uma limitação de que as palavras são todas minúsculas, o que facilita nosso algoritmo.&lt;/p&gt;

&lt;p&gt;A chave é construída calculando quais letras são similares em cada palavra. Vamos usar como exemplo as palavras &lt;em&gt;amor&lt;/em&gt; e &lt;em&gt;roma&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A M O R&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;R O M A&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;A&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;M&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;O&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;R&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Exemplo em imagem:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswimp1xm93c63ot2tmmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fswimp1xm93c63ot2tmmg.png" alt="Anagrama da palavra alegria"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ignorando a ordem das letras de ambas as palavras, teremos dicionários com as mesmas chaves e valores, o que nos leva a anagramas.&lt;/p&gt;

&lt;p&gt;Existe uma forma mais fácil de calcular essa chave.&lt;br&gt;
As palavras são formadas apenas por letras minúsculas, então temos 26 possibilidades de letras e ocorrências delas, correto?&lt;/p&gt;

&lt;p&gt;Come essa informação em mente, podemos criar uma lista de 26 posições e, para cada posição ordinal da letra, incrementar o número de ocorrências dela.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;m&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;alphabet&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao repetir esse algoritmo, obteremos um array com a quantidade de cada letra. Para as palavras propostas, o array será o mesmo, resultando na nossa chave.&lt;/p&gt;

&lt;h2&gt;
  
  
  Escrevendo o código
&lt;/h2&gt;

&lt;p&gt;A solução do problema em Python:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;defaultdict&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;groupAnagrams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;strs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]]:&lt;/span&gt;
        &lt;span class="n"&gt;anagrams&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;defaultdict&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;list&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;strs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;alphabet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="c1"&gt;# 26 letters, a to z
&lt;/span&gt;            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;alphabet&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;letter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nf"&gt;ord&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;a&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
            &lt;span class="n"&gt;anagrams&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;tuple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;alphabet&lt;/span&gt;&lt;span class="p"&gt;)].&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;anagrams&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Explicando o código em partes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O &lt;code&gt;defaultdict&lt;/code&gt; é essencial para salvar as palavras em grupos. Um agrupamento nesse caso é uma lista de valores associados a uma chave.&lt;/li&gt;
&lt;li&gt;Iteramos na lista de palavras;&lt;/li&gt;
&lt;li&gt;Para cada palavra, criamos uma lista do alfabeto, que será nossa chave;&lt;/li&gt;
&lt;li&gt;Incrementamos a posição ordinal de cada letra;&lt;/li&gt;
&lt;li&gt;Usamos esse valor como chave do dicionário &lt;code&gt;anagrams&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;A chave é convertida pra tupla porque em Python a chave &lt;a href="https://wiki.python.org/moin/DictionaryKeys" rel="noopener noreferrer"&gt;precisa ser um valor hasheable&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Observe o uso de &lt;code&gt;append&lt;/code&gt; no dicionário. Essa instrução só funciona porque estamos usando &lt;code&gt;defaultdict&lt;/code&gt;, que nos economiza criar uma lista vazia quando a chave do dicionário ainda não existe.&lt;/p&gt;

&lt;p&gt;E, por fim, retornamos os valores do dicionário, a lista de lista com as palavras agrupadas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Complexidade de tempo e espaço
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Tempo&lt;/strong&gt;: A pior complexidade de tempo desse algoritmo é percorrer a lista de palavras e, para cada palavra, percorrer todas as letras dela. Isso gera uma complexidade &lt;code&gt;O(N X M)&lt;/code&gt; onde &lt;em&gt;N&lt;/em&gt; é o número de palavras e &lt;em&gt;M&lt;/em&gt; é o número de letras de cada uma das palavras.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Espaço&lt;/strong&gt;: A complexidade de espaço é um pouco mais difícil de definir. É &lt;code&gt;O(N)&lt;/code&gt;, onde o &lt;em&gt;N&lt;/em&gt; pode variar entre ser o tamanho da lista do alfabeto (26 posições) ou o tamanho do anagrama. Quanto mais palavras no input, maior vai ser o tamanho do dicionário de palavras agrupadas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Eu acredito que esse desafio é considerado de nível médio porque é necessário ter um momento de inspiração pra entender que a chave que falamos anteriormente é um ponto crucial pra solução do problema.&lt;/p&gt;

&lt;p&gt;Espero que você tenha aprendido algo novo com esse texto, e eu gostaria muito de ter seu feedback nos comentários.&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>leetcode</category>
    </item>
    <item>
      <title>Solving "Binary Tree Paths"</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Sat, 23 Dec 2023 16:35:20 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/solving-binary-tree-paths-4p6i</link>
      <guid>https://dev.to/mauricioabreu/solving-binary-tree-paths-4p6i</guid>
      <description>&lt;p&gt;Today I'm going to explain how to solve &lt;a href="https://leetcode.com/problems/binary-tree-paths/" rel="noopener noreferrer"&gt;"Binary Tree Paths" problem&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Given the root of a binary tree, return all root-to-leaf paths in any order.&lt;/p&gt;

&lt;p&gt;A leaf is a node with no children.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A &lt;em&gt;binary tree&lt;/em&gt; is a data structure characterized by &lt;strong&gt;nodes&lt;/strong&gt;, each having either none or a maximum of two children: left and right. Each connection between nodes is called an &lt;strong&gt;edge&lt;/strong&gt;, and the paths from the &lt;strong&gt;root&lt;/strong&gt; to the &lt;strong&gt;leaves&lt;/strong&gt; are simple known as &lt;strong&gt;path&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Take a look at the image below as an example of a binary tree used in the problem. It illustrates a binary tree and its components:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbomxdgmdc7d4lrigxul8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbomxdgmdc7d4lrigxul8.png" alt="binary tree paths"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this problem, we need to identify and return a list of all the paths in any order.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

Input: root = [1,2,3,null,5]
Output: ["1-&amp;gt;2-&amp;gt;5","1-&amp;gt;3"]


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In the example, there are two paths, both starting from the root and extending to the leaf nodes.&lt;/p&gt;

&lt;p&gt;A leaf node is one that has no children. In this tree, numbers 5 and 3 are leaves as they have no outgoing connections.&lt;/p&gt;

&lt;p&gt;How do we traverse this binary tree, discover the paths, and return them as the challenge requires?&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasoning
&lt;/h2&gt;

&lt;p&gt;First, let's explore how we might approach this without any specific strategy or computational methods. Initially, you start from the root and follow each path to its end, choosing at each step either left or right. You note down each path on paper and proceed to the next until all paths are explored.&lt;/p&gt;

&lt;p&gt;With this intuitive approach in mind, let's transition to formulating the algorithm in Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Traversing
&lt;/h3&gt;

&lt;p&gt;Understanding how to traverse the binary tree is a crucial part of the algorithm. There are two primary methods: depth-first (DFS) and breadth-first (BFS) search.&lt;/p&gt;

&lt;p&gt;DFS is employed when you need to explore one branch of the tree thoroughly before &lt;a href="https://en.wikipedia.org/wiki/Backtracking" rel="noopener noreferrer"&gt;backtraking&lt;/a&gt;. On the other hand, BFS is utilized when you need to visit each level of the tree sequentially before backtracking.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the algorithm
&lt;/h2&gt;

&lt;p&gt;This challenge, although considered easy, contains nuances especially for those new to binary trees and traversal methods.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;a href="https://en.wikipedia.org/wiki/Depth-first_search" rel="noopener noreferrer"&gt;depth-first search&lt;/a&gt; algorithm to traverse the tree;&lt;/li&gt;
&lt;li&gt;In each &lt;a href="https://en.wikipedia.org/wiki/Recursion" rel="noopener noreferrer"&gt;recursion&lt;/a&gt; call, concatenate the current path to the path passed in the initial call;&lt;/li&gt;
&lt;li&gt;As you move left and right, continue concatenating this path with an arrow;&lt;/li&gt;
&lt;li&gt;The recursion base happens upon encountering a leaf node.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Why recursion?
&lt;/h3&gt;

&lt;p&gt;Recursion is particularly suitable for problems involving binary tree due to its capability to handle a problem one step at a time. In a binary tree, each node often branches into two smaller subtrees (if they exist), which reminds us of step-by-step approach added to the call stack.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No need to maintain a stack to traverse the tree;&lt;/li&gt;
&lt;li&gt;No need to implement a branching and backtracking mechanism - recursion handles this for us;&lt;/li&gt;
&lt;li&gt;Recursion and branching walk side-by-side.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Coding
&lt;/h2&gt;

&lt;p&gt;Here is the Python code to solve it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;binaryTreePaths&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Optional&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;TreeNode&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;paths&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's check what this code does in details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Base case&lt;/strong&gt;: we handle empty trees as they are valid binary trees. If there is no root (root is &lt;code&gt;None&lt;/code&gt;), we return immediately.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  DFS
&lt;/h3&gt;

&lt;p&gt;Our main call starts the recursive function from the root with an empty variable for the path.&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;""&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Base case&lt;/strong&gt;: this check is similar to our initial base case. For instance, after examining node 5, we see that both left and right are &lt;code&gt;None&lt;/code&gt;, signaling the end of this path. Subsequently, each of these recursive calls starts unwinding the stack (backtracking).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Path building&lt;/strong&gt;: here, we concatenate the current node's value to the path. This step is crucial as it builds the representation (a string) of the path from the root to the current node.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Reaching the path's end&lt;/strong&gt;: a leaf node is defined as one without children. Upon finding a leaf, we append the path to our list of paths and return, allowing other recursive calls to execute.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Recursive calls&lt;/strong&gt;: if the current node has a left child, we call &lt;code&gt;dfs&lt;/code&gt; recursively on the left child. The right node follows the very same process.&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;br&gt;
    &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Time and space complexity&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;When discussing &lt;strong&gt;time complexity&lt;/strong&gt; in our solution, we observe that for each node in the binary tree, a string concatenation operation is performed. In Python, strings are &lt;em&gt;immutable&lt;/em&gt;, each concatenation results in the creation of a new string. This leads to an &lt;code&gt;O(N²)&lt;/code&gt; operation at each node, where &lt;em&gt;N&lt;/em&gt; represents the number of nodes in the tree.&lt;/p&gt;

&lt;p&gt;As for &lt;strong&gt;space complexity&lt;/strong&gt;, it is primarily impacted by the creation of multiple string objects resulting from these concatenations (&lt;code&gt;O(N)&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;We can do better!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;One of the pain points of our solution is string concatenation. In Python, strings are &lt;strong&gt;immutable&lt;/strong&gt;, so everytime you concatenate a string, it creates a new string including the content of both strings.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Understanding the nuances of the programming language in use is vital, as different languages handle string concatenation differently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Challenge for the reader
&lt;/h3&gt;

&lt;p&gt;Your challenge is to modify the code to avoid string concatenation. After making these changes, evaluate the performance of the revised approach in terms of time and space complexity. Share your findings here in the comments section.&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>leetcode</category>
      <category>binarytree</category>
    </item>
    <item>
      <title>Solving "Word Search" problem</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Mon, 18 Dec 2023 00:17:09 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/solving-word-search-problem-2n6h</link>
      <guid>https://dev.to/mauricioabreu/solving-word-search-problem-2n6h</guid>
      <description>&lt;p&gt;Recently, I attempted to solve &lt;a href="https://leetcode.com/problems/word-search/" rel="noopener noreferrer"&gt;"Word Search" leetcode&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;"Word Search" is a game that the objective is to find a specific word (or even multiple words).&lt;/p&gt;

&lt;p&gt;In this post I'll explain in detail how to tackle this problem.&lt;/p&gt;

&lt;p&gt;Take a look at this image showing the word "LIFE" in a grid: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhc5qoxynsr5yr9hy9o3v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhc5qoxynsr5yr9hy9o3v.png" alt="word search"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How do we solve this problem? How can we write code to search for this word efficiently?&lt;/p&gt;

&lt;h2&gt;
  
  
  Reasoning
&lt;/h2&gt;

&lt;p&gt;Before delving into complex topics like graphs, depth-first search (DFS), and backtracking, let's consider how we solve word searches in real life.&lt;/p&gt;

&lt;p&gt;Initially, we try to locate the first letter of the word, quickly scanning all the letters. Then, we follow the subsequent letters to see if they form the word, checking in all directions but only one at a time.&lt;br&gt;
Then, we follow the next letters in order to check if we find the word. But in which direction? All of them, but one at a time.&lt;/p&gt;

&lt;p&gt;In short:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Locate the first letter (&lt;em&gt;L&lt;/em&gt; in this case);&lt;/li&gt;
&lt;li&gt;Attempt to find the word by following all the letters to the right;&lt;/li&gt;
&lt;li&gt;If we don't find, we try all the letters below, and so on.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this approach in mind, we can move on to the next part.&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing the algorithm
&lt;/h2&gt;

&lt;p&gt;Let's describe the algorithm in simple terms to clarify our thoughts, almost as if talking about them out loud.&lt;/p&gt;

&lt;p&gt;Here are the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Iterate the grid (each row and column);&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;grid[row][col]&lt;/code&gt; matches the first letter of our word, start searching for the remaining letters from there;&lt;/li&gt;
&lt;li&gt;Use &lt;a href="https://en.wikipedia.org/wiki/Depth-first_search" rel="noopener noreferrer"&gt;depth-first search&lt;/a&gt; algorithm;&lt;/li&gt;
&lt;li&gt;In each DFS call, check if the current letter is the next one in the word;&lt;/li&gt;
&lt;li&gt;If not, return False;&lt;/li&gt;
&lt;li&gt;If it matches, continue searching for the other letters;&lt;/li&gt;
&lt;li&gt;When the word is found, return True.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the Python code to solve it:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Solution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;exist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;List&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;]],&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

            &lt;span class="c1"&gt;# check if we don't surpass the grid
&lt;/span&gt;            &lt;span class="n"&gt;row_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;col_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row_outbounds&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;col_outbounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

            &lt;span class="n"&gt;wrong_letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;wrong_letter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

            &lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="c1"&gt;# mark visited
&lt;/span&gt;            &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

            &lt;span class="c1"&gt;# dfs in all directions
&lt;/span&gt;            &lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
                &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
                &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
                &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

            &lt;span class="c1"&gt;# not found, unmark
&lt;/span&gt;            &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


        &lt;span class="c1"&gt;# iterate over the grid
&lt;/span&gt;        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
            &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
                    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's dive deeper into the code.&lt;/p&gt;

&lt;p&gt;This code iterates over the grid, and when it finds the first letter of the word, it starts a search for the subsequent letters. The index parameter plays a crucial role, as explained below.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;dfs&lt;/code&gt; is our recursive function used to traverse the grid in search of the next letters of the word. We increment index each time we find a matching letter. For instance, if we find the letter "L," it corresponds to index zero in the array &lt;code&gt;["L", "I", "F", "E"]&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

    &lt;span class="c1"&gt;# check if we don't surpass the grid
&lt;/span&gt;    &lt;span class="n"&gt;row_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;col_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;row_outbounds&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;col_outbounds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;

    &lt;span class="n"&gt;wrong_letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;wrong_letter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This code also ensures we avoid 'out of range' errors:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;row_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;col_outbounds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;And here, we check if the current letter is correct:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;wrong_letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;word&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;index&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;wrong_letter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Here is what I find to be the most challenging aspect of this solution: traversing the board recursively.&lt;/p&gt;

&lt;p&gt;We save each letter because we need to mark its row and column as visited. Why is this important? It prevents the algorithm from retraversing the same path. In the code, we accomplish this by setting an arbitrary character (in this case, "#") at the position. After exploring all possible paths from this position, we 'unmark' the cell by restoring the saved letter. This step is crucial because it returns the board to its original state, allowing subsequent calls of the dfs function to use this letter for other potential paths.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="n"&gt;letter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="c1"&gt;# mark visited
&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;#&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

&lt;span class="c1"&gt;# dfs in all directions
&lt;/span&gt;&lt;span class="nf"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
    &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
    &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; 
    &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;col&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)):&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;

&lt;span class="c1"&gt;# not found, unmark
&lt;/span&gt;&lt;span class="n"&gt;board&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;row&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="n"&gt;col&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;letter&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;False&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Additionally, it's important to note that we execute the dfs function four times, each call moving in a different direction (up, right, down, left).&lt;/p&gt;

&lt;h2&gt;
  
  
  Time and space complexity
&lt;/h2&gt;

&lt;p&gt;Understanding the time and space complexity is essential to evaluate the performance of your solution.&lt;/p&gt;

&lt;p&gt;Measuring time complexity of "Word Search" is not an easy task. We can describe it as &lt;code&gt;O(R * C * 4 ^ L)&lt;/code&gt; where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;R&lt;/strong&gt; = number of rows in the grid;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;C&lt;/strong&gt; = number of columns in the grid;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;4&lt;/strong&gt; = reflects the four directions (up, down, right, left) from each cell;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;L&lt;/strong&gt; = length of the word.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We can simplify it to &lt;code&gt;O(N ^ 2 * 4 ^ L)&lt;/code&gt; where &lt;em&gt;N&lt;/em&gt; is the larger of the number of rows or columns in the grid (thank you &lt;a class="mentioned-user" href="https://dev.to/viglioni"&gt;@viglioni&lt;/a&gt; )&lt;/p&gt;

&lt;p&gt;As you can see, it is not so efficient.&lt;/p&gt;

&lt;p&gt;Space complexity of the solution is O(L) because &lt;em&gt;L&lt;/em&gt; is the max recursion depth we need to explore all the possibilies before finding the word (or unwinding the stack when it does encounter a wrong letter).&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this article, we explored a detailed solution to the "Word Search" problem, employing a depth-first search (DFS) approach. By breaking down the problem into manageable steps and translating these into Python code, we've demonstrated a practical application of graph traversal algorithms in solving complex problems.&lt;/p&gt;

</description>
      <category>dsa</category>
      <category>leetcode</category>
      <category>graph</category>
      <category>backtracking</category>
    </item>
    <item>
      <title>Finding the largest value in each tree row</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Tue, 24 Oct 2023 23:13:03 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/finding-the-largest-value-in-each-tree-row-djg</link>
      <guid>https://dev.to/mauricioabreu/finding-the-largest-value-in-each-tree-row-djg</guid>
      <description>&lt;p&gt;Recently, a friend sent me an intriguing challenge: finding the largest value in each row of a binary tree. It is a hard but rewarding problem to solve. Understanding how to navigate  data structures is crucial. &lt;/p&gt;

&lt;p&gt;This &lt;em&gt;tree&lt;/em&gt; is a &lt;strong&gt;binary tree&lt;/strong&gt;. In this post we will understand what is a binary tree and how to solve the proposed problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a binary tree?
&lt;/h2&gt;

&lt;p&gt;A binary tree is a tree formed by &lt;strong&gt;nodes&lt;/strong&gt;. A tree might have zero nodes, or just one. The first node of a tree is called root, and the last one is called &lt;strong&gt;leaf&lt;/strong&gt;. A binary tree, just like a real tree, might have many branches, known as &lt;strong&gt;children&lt;/strong&gt;. Children are nodes with parents, and leaves are nodes without children. Every node might have zero, one or two children.&lt;/p&gt;

&lt;p&gt;Let's visualize it:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodj1lh4a4mcbocb1hje6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fodj1lh4a4mcbocb1hje6.png" alt="binary tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every circle in the diagram above represents a node. A node is composed by the following attributes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Value (a.k.a key)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Left&lt;/strong&gt; node&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right&lt;/strong&gt; node&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;em&gt;binary&lt;/em&gt; in binary tree means you can go to either left or right. If you could represent a node using Python, you would end up having the following class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Node&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="bp"&gt;None&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;val&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;left&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;right&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Can you see how the &lt;code&gt;Node&lt;/code&gt; class compares to the visual representation of a node?&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding the largest value in each tree row
&lt;/h2&gt;

&lt;p&gt;Looking at the tree, we know that the largest values are: 1, 3 and 7.&lt;/p&gt;

&lt;p&gt;We could &lt;strong&gt;traverse&lt;/strong&gt; every row of the tree, save every largest value in a list and return it in the end. But finding the largest value in each row is not an easy task.&lt;/p&gt;

&lt;p&gt;Notice we just used the word traverse. Traversing is the act of iterating over the nodes of the tree. We can do it using two techniques: depth first search (DFS) and breadth first search (BFS).&lt;/p&gt;

&lt;p&gt;Let's explore both techniques in detail:&lt;/p&gt;

&lt;h3&gt;
  
  
  Depth first search
&lt;/h3&gt;

&lt;p&gt;In depth first search, you find a node in the tree and keep traversing the tree in that direction to the end (leaves). To implement this technique, we will need to use a &lt;a href="https://en.wikipedia.org/wiki/Stack_(abstract_data_type)" rel="noopener noreferrer"&gt;stack&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;dfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;stack&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;curr_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since trees might be empty (or have no nodes), we first check if the root node is present before proceeding. Otherwise we add the root to the stack and traverse the tree until this stack is not empty.&lt;/p&gt;

&lt;p&gt;Every round of the while loop we add the value of the current node to the list and add the left and right nodes to the stack if they exist.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First we add root (1) value to list&lt;/li&gt;
&lt;li&gt;Then we find a right node (3) and add it to the stack&lt;/li&gt;
&lt;li&gt;We also found a left node (2) and add it to the stack&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ok, now it is one of the most important parts: why are we adding the right node first?&lt;/p&gt;

&lt;p&gt;It is very common to traverse a binary tree from left to right. Since we are using a stack, we always pop the last element. Our stack looks like this in the first iteration: &lt;code&gt;[right, left]&lt;/code&gt;&lt;br&gt;
If we pop from this stack, we always get the left position before right.&lt;/p&gt;
&lt;h3&gt;
  
  
  Breadth first search
&lt;/h3&gt;

&lt;p&gt;In breadth first search we visit every left and right node of the tree in the same iteration - this is why it has &lt;em&gt;breadth&lt;/em&gt; in the name.&lt;/p&gt;

&lt;p&gt;We use this technique when we want to traverse the binary tree by row.&lt;/p&gt;

&lt;p&gt;The code is very similar to the depth first search, except we use a &lt;a href="https://en.wikipedia.org/wiki/Queue_(abstract_data_type)" rel="noopener noreferrer"&gt;queue&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Since we want to iterate each row, we use a queue and the popleft function - it pops the first element of the structure.&lt;br&gt;
Imagine we have the following queue: &lt;code&gt;[1, 10, 20, 30]&lt;/code&gt;. When we perform a &lt;strong&gt;popleft&lt;/strong&gt; operation, the queue becomes &lt;code&gt;[10, 20, 30]&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deque&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bfs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;values&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;curr_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="n"&gt;values&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;values&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;First we add root (1) value to list&lt;/li&gt;
&lt;li&gt;Then we find a left node (2) and add it to the queue&lt;/li&gt;
&lt;li&gt;We also found a right node (3) and add it to the queue&lt;/li&gt;
&lt;li&gt;Then we pop left the queue and we get the node with value 2 and add it to the list, it is now &lt;code&gt;[1, 2]&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;And we, again, add the left and right nodes to the queue&lt;/li&gt;
&lt;li&gt;But we still have the 3 in the queue. Next iteration we will append it to the list, and so on&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Solving the problem
&lt;/h2&gt;

&lt;p&gt;Which algorithm you think should be used to get the largest value from each row of the tree? Think about the tree below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskdgscrn48us3vzhggeg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fskdgscrn48us3vzhggeg.png" alt="binary tree"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we use depth first search, we will get all the values in a vertical way, right? In a depth first traversal, we move down the tree before considering &lt;strong&gt;siblings&lt;/strong&gt;. But when finding the largest value in a row, we need to consider all nodes at the same depth before moving deeper, which aligns more with a horizontal traversal.&lt;/p&gt;

&lt;p&gt;What about breadht first search? It seems the right technique.&lt;/p&gt;

&lt;p&gt;Let's implement this, but first we will reason about the steps:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a queue and add the root to it&lt;/li&gt;
&lt;li&gt;While we have elements in the queue we:

&lt;ul&gt;
&lt;li&gt;Determine the current size of the queue, representing the number of nodes in the current row&lt;/li&gt;
&lt;li&gt;And iterate &lt;em&gt;N&lt;/em&gt; times to get all the values from the row&lt;/li&gt;
&lt;li&gt;For each node in the current row, compare its value with the current maximum value (initialized as negative infinity).&lt;/li&gt;
&lt;li&gt;Compare and define the new max value&lt;/li&gt;
&lt;li&gt;Every time we end this iteration, we add the max value to the list of largest values&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;And the code is as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;collections&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;deque&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;largest_values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="n"&gt;queue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;deque&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;root&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;largest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;size&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;len&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;max_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;float&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-inf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;curr_node&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;popleft&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;max_val&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;max_val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;val&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;left&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;queue&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;curr_node&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;right&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="n"&gt;largest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_val&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;largest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the provided example tree, this code will return the list  &lt;code&gt;[1, 3, 9]&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Solving these challenges is a matter of reading and practicing. With practice, choosing the right data structures and techniques for a given problem becomes more and more intuitive.&lt;/p&gt;

</description>
      <category>leetcode</category>
      <category>algorithms</category>
    </item>
    <item>
      <title>Quem mexeu no meu cache?</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Sun, 16 Oct 2022 11:43:30 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/quem-mexeu-no-meu-cache-5ph</link>
      <guid>https://dev.to/mauricioabreu/quem-mexeu-no-meu-cache-5ph</guid>
      <description>&lt;p&gt;O objetivo desse texto é mostrar como um erro de configuração no cache da sua aplicação pode trazer problemas sérios que vão desde uma dor de cabeça até a perda de dinheiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é caching?
&lt;/h2&gt;

&lt;p&gt;Caching é uma estratégia utilizada para obter dados de forma mais rápida em uma aplicação. Nossa memória recente é uma espécie de cache, pois não precisamos fazer um grande esforço mental pra lembrar. &lt;/p&gt;

&lt;p&gt;Se você está pensando em usar alguma forma de cache na sua aplicação, provavelmente você está com alguma parte da sua aplicação executando devagar, por exemplo. Cache pode ajudar sua aplicação de diversas maneiras, até como mecanismo de resiliência.&lt;/p&gt;

&lt;h2&gt;
  
  
  Errando na configuração do cache
&lt;/h2&gt;

&lt;p&gt;É possível degradar de forma severa uma aplicação ao configurar o cache de maneira errada. Alguns tipos de aplicação são extremamente dependentes de cache, como serviços de storage e multimídia, como transmissão de áudio e vídeo pela internet.&lt;/p&gt;

&lt;p&gt;Para o exemplo deste artigo, vamos usar exemplos usando Ruby e um servidor web famoso, o NGINX.&lt;/p&gt;

&lt;p&gt;Vamos falar de dois tipos de ataque que podem acontecer nos seus servidores.&lt;/p&gt;

&lt;h3&gt;
  
  
  Minha aplicação caiu porque o cache não funcionou!
&lt;/h3&gt;

&lt;p&gt;Cache que não funciona é muito comum, principalmente quando sites usam CDNs externas e as pessoas envolvidas não têm conhecimento de como um erro de configuração pode derrubar a aplicação.&lt;/p&gt;

&lt;p&gt;Vamos supor que você seja dev de um site que contenha várias imagens de produtos, com boa resolução e você adicione caching para que o seu serviço de imagens não seja acionado toda requisição. Essa é uma ótima ideia e há expectativas de que ela vai melhorar a performance da aplicação. Porém, a configuração que você fez coloca os parâmetros da query string como parte da chave de cache. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Chave de cache&lt;/em&gt;&lt;/strong&gt; &lt;em&gt;(cache key) é um hash formado a partir de variáveis especificadas pela configuração das diretivas do servidor. Essa chave é uma maneira de mapear um valor para um hash md5 que vai ser alocado em um grande mapa, de rápido acesso, no seu servidor&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;O que pode dar errado? Essa imagem abaixo exemplifica requests que furam o cache ao usar parâmetros quaisquer na query string.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7AQw745A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xsuvjqxlwkfbhos39h4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7AQw745A--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0xsuvjqxlwkfbhos39h4.png" alt="dois fluxos de request mostrando o erro ao usar a query string como chave de cache" width="880" height="630"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Podemos representar isso com código Ruby, criando funções que definem as chaves de cache, similares às configurações que podemos fazer no NGINX:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'digest'&lt;/span&gt;

&lt;span class="c1"&gt;# Esse é o nosso cache&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;# Uma lista de requests a serem feitos&lt;/span&gt;
&lt;span class="n"&gt;reqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Nossa função que define a cache key de acordo com &lt;/span&gt;
&lt;span class="c1"&gt;# o valor passado&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;good_cache_key_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# Divide a string por "?" e remove o "/"&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'?'&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;size&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Valida que todos os paths vão usar a mesma chave de cache&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;good_cache_key_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;# Agora vamos ter uma nova lista de requests maliciosos (ou não)&lt;/span&gt;
&lt;span class="n"&gt;reqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg?foo=1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg?foo=2'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg?foo=3'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Nossa função que define a cache key de acordo com &lt;/span&gt;
&lt;span class="c1"&gt;# o valor passado mas dessa vez ela não foi pensada corretamente&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;bad_cache_key_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;
    &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Valida que todos os paths vão usar chaves diferentes, &lt;/span&gt;
&lt;span class="c1"&gt;# mesmo que as imagens sejam exatamente as mesmas&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;bad_cache_key_hash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado fica como a seguir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;43eb6cb76f885b82fbfef92cbee4acd1
43eb6cb76f885b82fbfef92cbee4acd1
43eb6cb76f885b82fbfef92cbee4acd1
9a03b0f4730f70964c01f97355823f20
b0affdcb74f872918bb795937f98feaa
0082e52c9b5e1613888e1a26bbc96f37
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Observe que os &lt;strong&gt;3 primeiros valores são iguais&lt;/strong&gt;, provando que a chave será a mesma. Entretanto, os &lt;strong&gt;próximos três requests estão com os valores diferentes&lt;/strong&gt;, mesmo que a imagem seja a mesma.&lt;/p&gt;

&lt;p&gt;O que podemos concluir? Que nosso cache vai ficar furado por uma má definição do hash computado que é usado para mapear requests aos nossos valores.&lt;/p&gt;

&lt;h2&gt;
  
  
  Às vezes vejo uma air fry, às vezes um liquidificador
&lt;/h2&gt;

&lt;p&gt;Você já entrou em um site que a imagem ficava trocando, sem ter um motivo muito óbvio? Provavelmente muitos objetos estão apontando pra mesma cache key.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--xPAGFK3l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujllbfnb1p3fn2pqmybh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--xPAGFK3l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ujllbfnb1p3fn2pqmybh.png" alt="cache key apontando sempre para o mesmo objeto" width="880" height="470"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Esse caso acima está relacionado a um erro de configuração que vai fazer todas as URLs de imagem do seu site apontarem pro mesmo arquivo. Como?&lt;/p&gt;

&lt;p&gt;Lembra que a cache key é o hash md5 que aponta qual lugar do mapa de objetos vai ser consultado? Então, nesse caso a cache key apontou qualquer URL para o mesmo lugar. A primeira requisição trouxe &lt;em&gt;airfry.jpg&lt;/em&gt; e fez cache. As requisições subsequentes vão sempre trazer a imagem da airfry, até que o tempo de cache termine e faça a imagem &lt;strong&gt;expirar&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O exemplo abaixo um Ruby mostra que uma chave de cache mal definida pode fazer todos os objetos apontarem pro mesmo hash:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Esse é o nosso cache&lt;/span&gt;
&lt;span class="n"&gt;cache&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="c1"&gt;# Agora vamos supor que nós deixamos passar que nossa cache key&lt;/span&gt;
&lt;span class="c1"&gt;# foi definida usando a extensão da imagem. Isso é totalmente&lt;/span&gt;
&lt;span class="c1"&gt;# possível quando você usa variáveis erradas ou define&lt;/span&gt;
&lt;span class="c1"&gt;# uma função de cache e não testa ela corretamente&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wrong_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;# pega a extensão do caminho passado&lt;/span&gt;
  &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;last&lt;/span&gt;
  &lt;span class="no"&gt;Digest&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;MD5&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;hexdigest&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# Uma lista de requests a serem feitos&lt;/span&gt;
&lt;span class="n"&gt;reqs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/liquidificador.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s1"&gt;'/airfy.jpg'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;# Valida que todos as chaves são a mesma&lt;/span&gt;
&lt;span class="nb"&gt;puts&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reqs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;wrong_cache_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado fica como a seguir:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;c36bbd258b7ee694eb987221b2b197b0
c36bbd258b7ee694eb987221b2b197b0
c36bbd258b7ee694eb987221b2b197b0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testando as requisições em um servidor web
&lt;/h2&gt;

&lt;p&gt;Foi criado o projeto &lt;a href="https://github.com/mauricioabreu/my-cache-haz-a-problem"&gt;https://github.com/mauricioabreu/my-cache-haz-a-problem&lt;/a&gt; que contém o código do web server e os exemplos didáticos criados para o texto.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache key com query string
&lt;/h3&gt;

&lt;p&gt;Aqui demonstramos o caso em que usar a query string como cache pode degradar sua aplicação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v "http://localhost:8080/items/airfry.jpg?foo=1"

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Content-Length: 101321
&amp;lt; X-Cache-Status: MISS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uma boa estratégia de cache ignoraria esses parâmetros na chave de cache. Um segundo request com os parâmetros &lt;code&gt;foo=2&lt;/code&gt; deveria retornar &lt;em&gt;HIT&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HIT&lt;/strong&gt; - objeto encontrado no cache&lt;br&gt;
&lt;strong&gt;MISS&lt;/strong&gt; - objeto não encontrado no cache. Uma requisição ao upstream será realizada, o objeto será baixado e cacheado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v "http://localhost:8080/items/airfry.jpg?foo=2"

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Content-Length: 101321
&amp;lt; X-Cache-Status: MISS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Imagine essa informação nas mãos erradas?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Não é um erro formar a cache key com parâmetros da query string. Um exemplo bom é se o seu site renderiza imagens com tamanhos predefinidos, como &lt;code&gt;/products/airfry.jpg?width=300&amp;amp;height=250&lt;/code&gt;. Esses parâmetros podem ser usados e vão aumentar a performance do seu cache.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cache key errada
&lt;/h3&gt;

&lt;p&gt;Usar a cache key errada pode deixar as pessoas usuárias do seu site infelizes ao receber sempre a mesma imagem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v http://localhost:8080/products/airfry.jpg

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Content-Length: 101321
&amp;lt; X-Cache-Status: MISS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O primeiro request está OK. O estado do cache é &lt;em&gt;MISS&lt;/em&gt;, ainda não existia no cache e foi resgatado do nosso &lt;em&gt;storage&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Vamos requisitar a imagem de um liquidificador.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl -v http://localhost:8080/products/liquidificador.jpg

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Content-Length: 101321
&amp;lt; X-Cache-Status: HIT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Aqui observamos duas coisas estranhas: o &lt;em&gt;tamanho&lt;/em&gt; e o &lt;em&gt;X-Cache-Status&lt;/em&gt;. Muita coincidência o tamanho da imagem &lt;em&gt;liquidificador&lt;/em&gt; ter o exato mesmo tamanho da imagem da &lt;em&gt;air fry&lt;/em&gt;, não é? Além disso, como uma imagem que nunca requisitamos trouxe um estado de &lt;em&gt;HIT&lt;/em&gt;? Isso aconteceu porque a chave de cache foi baseada, erroneamente, na extensão da requisição, &lt;em&gt;jpg&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Solucionando os problemas
&lt;/h2&gt;

&lt;p&gt;Uma das maneiras mais simples de resolver os problemas apresentados é usar a cache key padrão do NGINX, que é &lt;code&gt;$scheme$proxy_host$request_uri&lt;/code&gt;. Em detalhe:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Scheme&lt;/strong&gt; - HTTP/HTTPS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proxy host&lt;/strong&gt; - host e porta do endereço do storage (ou do upstream usado)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Request URI&lt;/strong&gt; - URI original com os parâmetros da query string&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Algumas dicas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use a cache key default quando puder;&lt;/li&gt;
&lt;li&gt;Verifique suas cache keys antes de colocar seu site em produção;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vozlt/nginx-module-vts"&gt;Colete métricas&lt;/a&gt; de uso do cache. Quanto mais requests com &lt;em&gt;HIT&lt;/em&gt;, melhor vai ser seu &lt;em&gt;cache hit ratio&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;a href="https://www.flaticon.com/free-icons/evil" title="evil icons"&gt;Evil icons created by Smashicons - Flaticon&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Um pouco sobre streaming de vídeo ao vivo</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Fri, 19 Aug 2022 16:16:09 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/um-pouco-sobre-streaming-de-video-ao-vivo-50j3</link>
      <guid>https://dev.to/mauricioabreu/um-pouco-sobre-streaming-de-video-ao-vivo-50j3</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kgj9ozcyioaw06nl4c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj6kgj9ozcyioaw06nl4c.png" title="Workflow live" alt="Imgem contendo as etapas do vídeo. No começo, uma pessoa com seu computador dando play e na sequência o request indo pra CDN, packager, envolvendo a fonte do vídeo e o encoder"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Motivado pela dica do &lt;a class="mentioned-user" href="https://dev.to/leandronsp"&gt;@leandronsp&lt;/a&gt; sobre assuntos técnicos, trago nesse artigo uma introdução a vídeo ao vivo. Atualmente trabalho no time de vídeo ao vivo da Globo, uma das maiores empresas de mídia da América Latina.&lt;/p&gt;

&lt;p&gt;Vídeo ao vivo tem temperado a web há mais de década. Nossas conexões ficaram mais rápidas e nossa vontade de ver vídeo com mais qualidade aumentou com o passar do tempo. A experiência ao assistir vídeo foi ficando cada vez melhor à medida que as pessoas ficaram mais exigentes: buscamos o melhor áudio, melhor imagem e sem falhas nem &lt;em&gt;buffering&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;Pra entender um pouco da complexidade que envolve uma transmissão de vídeo ao vivo, vamos voltar um pouco e entender como chegamos até aqui.&lt;/p&gt;

&lt;h2&gt;
  
  
  Um pouco do passado
&lt;/h2&gt;

&lt;p&gt;Em 1996, a Macromedia inventou o protocolo RTMP (Real-Time Messaging Protocol) destinado a envio de vídeo, áudio e dados. O intuito do protocolo era garantir uma rápida performance e um padrão pra envio de dados em tempo real, e que esses dados pudessem ser decodificados por um player, o Flash.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Embora exista muita piada em cima do término do Flash, o Flash foi por mais de duas décadas um ótimo player de vídeo, tendo sido base para algoritmos de encoding/decoding futuramente&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;O fato de muitos sites de streaming do passado exigirem a instalação de Flash não era à toa. RTMP era o protocolo usado pelas empresas. O player conectava em uma URL diretamente no servidor que estava recebendo o vídeo e as pessoas assistiam diretamente do servidor, usando RTMP diretamente. Mesmo sem Flash atualmente, você pode usar um player como VLC para tocar uma transmissão ao vivo RTMP.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como funciona?
&lt;/h2&gt;

&lt;p&gt;Protocolos, players, encoding e decoding. Ok, mas pra que tudo isso?&lt;/p&gt;

&lt;p&gt;Uma transmissão ao vivo passa por muitas etapas antes de chegar no seu dispositivo tocador de vídeo.&lt;/p&gt;

&lt;p&gt;A partir de agora alguns termos em inglês vão ser utilizados pela falta uso das mesmas em português.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ingest
&lt;/h3&gt;

&lt;p&gt;A fonte do vídeo é a primeira etapa. É nela que definimos qual o modo de captura do vídeo e pra onde vamos publicar a transmissão. O software &lt;a href="https://obsproject.com/" rel="noopener noreferrer"&gt;OBS&lt;/a&gt; ficou muito famoso por ser usado por gamers que fazem streaming na Twitch/Facebook/Youtube. O que essas pessoas estão fazendo é o &lt;em&gt;ingest&lt;/em&gt; do vídeo.&lt;/p&gt;

&lt;p&gt;Há outras formas de publicar um vídeo. É possível usar o &lt;a href="https://ffmpeg.org/" rel="noopener noreferrer"&gt;ffmpeg&lt;/a&gt; como fonte e publicar em um endereço RTMP ou HTTP.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encoding
&lt;/h3&gt;

&lt;p&gt;Essa etapa vai depender muito de como o produto de vídeo aborda o ingest. Quando a fonte é um OBS, apenas um input é enviado para um servidor e, lá nesse serviço, o vídeo passa por um processo de encoding que acaba criando várias qualidades do seu vídeo, para que ele possa ser assistido por diferentes dispositivos e, também, para que sua rede de internet se adapte melhor às qualidades oferecidas. Redes de computadores sofrem degradações diárias que fazem com que seu vídeo não toque na melhor qualidade, pois isso o produto deve pensar nesses casos de uso.&lt;br&gt;
O processo de oferecer mais de uma qualidade de vídeo é essencial para o bom funcionamento da tecnica ABR (Adaptive Bitrate Streaming), usada para escolher a melhor experiência para o player de vídeo e, consequentemente, pra você.&lt;/p&gt;

&lt;p&gt;A etapa de encoding pode ser diferente quando a entrada de vídeo é um SDI (Serial Digital Interface). SDI é uma interface (cabo) usado para trafegar os dados de uma câmera para o encoder. É usando um SDI que eventos ao vivo de grande porte são transmitidos. A fonte do vídeo passa pelo cabo, sem nenhum tipo de codificação, e vai pro encoder processar. No encoder a pessoa técnica terá como escolher como o vídeo vai ser processado (codec de vídeo, codec de áudio, resolução, de metadata e outras dezenas de opções).&lt;/p&gt;

&lt;h3&gt;
  
  
  Packaging
&lt;/h3&gt;

&lt;p&gt;Essa etapa é onde o vídeo é empacotado em diferentes formatos. Todo player de vídeo precisa saber como ler uma URL de um vídeo e tocar o conteúdo descrito nela. É aqui que entram protocolos como HLS e DASH e também os segmentos de vídeo. O processo de empacotar um vídeo é sobre como os segmentos (chunks, que são pedacinhos do vídeo) são criados e como eles são organizados, que são os manifestos de vídeo (também chamados de playlists).&lt;/p&gt;

&lt;p&gt;Uma playlist é uma forma de descrever as qualidades de vídeo oferecidas pela plataforma e, também, os chunks oferecidos por essas playlists.&lt;/p&gt;

&lt;h3&gt;
  
  
  Delivery
&lt;/h3&gt;

&lt;p&gt;Nos primórdios de streaming de vídeo, apenas uma qualidade era ofertada e a escalabilidade da plataforma era complexa e custosa.&lt;/p&gt;

&lt;p&gt;Atualmente, a entrega de vídeo precisa ser rápida e melhor localizada. Quanto mais perto o servidor estiver de você, melhor vai ser sua experiência. Diferente de acessar um site, os dados trafegados em um vídeo são muito maiores do que páginas web que acessamos no dia a dia, e não temos como tomar vantagem do cache local pois o vídeo muda, enquanto as folhas de estilo ou scripts de uma página não.&lt;/p&gt;

&lt;p&gt;Para ter uma entrega mais adequada, a plataforma de vídeo precisa saber como entregar o servidor mais perto da pessoa assistindo. Estamos falando de CDNs (Content Delivery Network), que são redes de entrega de conteúdo que contém inteligência de roteamento e cache para entregar uma experiência personalizada e com menos latência.&lt;/p&gt;

&lt;p&gt;Outro ponto importantíssimo sobre CDNs é caching. Servidores que entregam o conteúdo são protegidos com camadas de cache que reduzem o impacto nos servidores de conteúdo, fazendo com que o mesmo arquivo possa ser requisitado com muito menos latência e resiliência.&lt;/p&gt;

&lt;h3&gt;
  
  
  Play
&lt;/h3&gt;

&lt;p&gt;A parte inicial de um vídeo é o play. Plataformas de streaming têm e evoluem seus próprios players à medida que features vão sendo adicionadas e codecs de vídeo novos precisam ser suportados. A melhora da experiência só é possível se todas as etapas estiverem alinhadas.&lt;/p&gt;

&lt;p&gt;Para o player isso não é diferente. Players precisam estar aptos para tocar o conteúdo entregue pela CDN, caso contrário, seu vídeo nem começará a tocar. E, para estar apto, o player deve possuir suporte ao decoding dos segmentos de vídeo.&lt;/p&gt;




&lt;p&gt;Essa é uma breve introdução sobre vídeo. No próximo, vamos nos aprofundar melhor em alguns termos como codec, CDN, player, buffering, etc.&lt;/p&gt;

&lt;p&gt;Uma das camisetas aqui da Globo é essa, descrevendo as etapas pra entregar vídeo ao vivo&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yh1ysdx4vnczullx5nq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yh1ysdx4vnczullx5nq.jpg" title="camiseta da Globo" alt="foto da camiseta da Globo que é preta com palavras em branco, uma por linha "&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>video</category>
      <category>streaming</category>
    </item>
    <item>
      <title>Uma introdução ao NGINX</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Sat, 21 May 2022 12:25:27 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/uma-introducao-ao-nginx-1jdg</link>
      <guid>https://dev.to/mauricioabreu/uma-introducao-ao-nginx-1jdg</guid>
      <description>&lt;p&gt;Volta e meia leio de algumas pessoas que &lt;strong&gt;NGINX&lt;/strong&gt; (engine X) é complicado. Eu não vou mentir: não é algo simples de se entender e usar de primeira. Primeiramente, não é fácil entender qual a finalidade dele. É um servidor, mas o que ele faz? Como é usado?&lt;/p&gt;

&lt;p&gt;NGINX é um servidor que pode ser usado de diversas maneiras, tais como: load balancer (balancear requests para os servidores de aplicação), proxy reverso (receber um request da internet e repassar para outro servidor ou serviço) e uma das capacidades mais fantásticas do NGINX, na minha opinião, é sua finalidade como cache.&lt;/p&gt;

&lt;p&gt;Uma generosa parte da internet roda sobre NGINX e alguns segmentos bem importantes usam ele como principal servidor, como plataformas de streaming, aplicações de larga escala tais como lojas, redes sociais e, também, é usado como base de muitas CDNs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porque usar NGINX?
&lt;/h2&gt;

&lt;p&gt;Talvez você já tenha se perguntado: porque eu deveria usar NGINX? Não é mais fácil só apontar o DNS para vários IPs?&lt;br&gt;
Isso é possível, mas você não vai ter a robustez de apontar seu DNS para um load balancer que pode balancear seus requests de uma forma mais inteligente, com o apoio de healthcheck caso seus servidores de aplicação não estejam respondendo e de algoritmos de balanceamento, implementando o modelo correto de distribuição de requests.&lt;/p&gt;

&lt;p&gt;Se você já compreende o básico e apenas quer olhar uma configuração escrita com containers rodando uma solução, o &lt;a href="https://github.com/mauricioabreu/nginx-intro" rel="noopener noreferrer"&gt;repositório de exemplo&lt;/a&gt;. Ele está dividido em tags mostrando a evolução das partes do texto.&lt;/p&gt;

&lt;p&gt;Vamos criar nossa primeira configuração, bem básica, com apenas uma rota que sempre retorna 200:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;

&lt;span class="k"&gt;worker_processes&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Vamos usar &lt;em&gt;Docker&lt;/em&gt; com a ajuda do &lt;em&gt;Docker Compose&lt;/em&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;nginx&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;nginx:alpine&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./nginx.conf:/etc/nginx/nginx.conf&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8080:80"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Testando nossa rota:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

curl -v http://localhost:8080

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Server: nginx/1.21.6
&amp;lt; Content-Type: text/plain
&amp;lt; Content-Length: 0
&amp;lt; Connection: keep-alive


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Nossa rota funcionou. Ela retorna 200 como esperado.&lt;/p&gt;

&lt;p&gt;Vamos entender o que essa configuração faz, focando apenas no bloco &lt;em&gt;http&lt;/em&gt;. O bloco http é chamado de contexto, assim como o &lt;em&gt;events&lt;/em&gt;, e ambos está em um contexto chamado &lt;em&gt;main&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;server&lt;/strong&gt; - é um bloco designado a escrever as configurações de um servidor dentro da sua configuração. Você pode ter vários deles, cada um atendendo em uma porta diferente. Você pode expor um servidor para o mundo e ter outro interno, sem cache, por exemplo, ou até driblando a autenticação, por exemplo.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;listen&lt;/strong&gt; - aqui você define em qual porta seu servidor vai aceitar as conexões. Note que o &lt;em&gt;docker-compose.yml&lt;/em&gt; exporta a 80 como 8080 para o host, por isso o request é feito na 8080 (porta 80 do container).&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;location&lt;/strong&gt; - é a diretiva usada para definir as rotas. Elas são bem poderosas. Aceitam expressões regulares, é possível capturar as variáveis e usá-las na configuração. O sistema de location, também, conta com diferentes tipos de match.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sem modificador, o match é feito pelo começo da URI&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;=&lt;/strong&gt; é um match exato&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;~&lt;/strong&gt; é um match com expressão regular&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Esses são os modificadores que mais usei até hoje.&lt;/p&gt;

&lt;p&gt;Nossa location faz match com qualquer URI que comece com &lt;strong&gt;/&lt;/strong&gt;, ou seja, qualquer uma como &lt;em&gt;/compras&lt;/em&gt;, &lt;em&gt;/produtos/lista&lt;/em&gt;, etc.&lt;/p&gt;

&lt;p&gt;Podemos adicionar uma nova location com match exato.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;

&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;/teste&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;"teste&lt;/span&gt; &lt;span class="s"&gt;route"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Agora podemos notar que a rota &lt;em&gt;/teste&lt;/em&gt; tem um body sendo respondido.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

curl -v http://localhost:8080/teste

&amp;lt; HTTP/1.1 200 OK
&amp;lt; Server: nginx/1.21.6
&amp;lt; Content-Type: text/plain
&amp;lt; Content-Length: 11
&amp;lt; Connection: keep-alive
teste route


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Vimos um pouco do básico da configuração, agora é hora de fazermos algo mais avançado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usando como proxy reverso
&lt;/h2&gt;

&lt;p&gt;Uma definição simplista de proxy reverso é um request que você faz em algum endereço interno, como por exemplo &lt;a href="http://meusite.com.br/minhaloja/produtos/1" rel="noopener noreferrer"&gt;http://meusite.com.br/minhaloja/produtos/1&lt;/a&gt; e não sabendo pra onde exatamente esse request vai ser processado. E, após processado, o servidor de proxy reverso te manda o conteúdo da resposta do servidor de aplicação.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bv0938z74oy6xwkjdh6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0bv0938z74oy6xwkjdh6.png" alt="uma imagem descrevendo um request na internet, passando por nginx como proxy reverso e indo pra aplicação final"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos criar uma aplicação de teste que responde na porta 8081, mas que vamos acessar através do nosso NGINX.&lt;/p&gt;

&lt;p&gt;A aplicação vai ser em Python usando Flask.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Flask&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;p&amp;gt;Index page!&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Modificamos nossa configuração do servidor para conter apenas a rota da aplicação. Agora com a adição da diretiva &lt;strong&gt;proxy_pass&lt;/strong&gt;, que é usada para fazer do NGINX um servidor de proxy reverso.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://app:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Observe que usamos o nome do serviço descrito (&lt;em&gt;app&lt;/em&gt;) no docker-compose para referenciar o nome que a aplicação atende.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

curl -v http://localhost:8080
&amp;lt; HTTP/1.1 200 OK
&amp;lt; Server: nginx/1.21.6
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt; Content-Type: text/html;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;utf-8
&lt;span class="go"&gt;&amp;lt; Content-Length: 18
&amp;lt; Connection: keep-alive
&lt;/span&gt;&lt;span class="gp"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;Index page!&amp;lt;/p&amp;gt;
&lt;span class="go"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Podemos ver que a resposta possui um header adicional (&lt;em&gt;Content-Type&lt;/em&gt;), agora, além da resposta que veio da aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como fazer proxy reverso pra múltiplos servidores?
&lt;/h2&gt;

&lt;p&gt;A realidade de uma aplicação em produção, na grande maioria das vezes, é ter mais de um servidor pra servir ela. E isso se deve a inúmeros motivos. Servidores têm recursos finitos (placa de rede, disco, CPU) para atender muitas requisições. E não é apenas sobre isso. E se o servidor sofrer uma falha de hardware? Ou até alguma falha de rede? Inúmeros motivos podem fazer sua aplicação ficar sem um fallback pra esses casos.&lt;/p&gt;

&lt;p&gt;É aí que entramos com a diretiva &lt;strong&gt;upstream&lt;/strong&gt; do NGINX, usada para definir um conjunto de servidores usados para balancear sua aplicação.&lt;/p&gt;

&lt;p&gt;Primeiramente, modificaremos nosso docker-compose para criar duas instâncias da aplicação. Para fins de exemplo, não usaremos a diretiva &lt;code&gt;scale&lt;/code&gt; porque ela possui um balanceamento round robin, e queremos mostrar como usar a diretiva de upstream com múltiplos servidores de aplicação.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;app_1&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8081:8081"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FLASK_RUN_PORT=8081&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FLASK_RUN_HOST=0.0.0.0&lt;/span&gt;
&lt;span class="na"&gt;app_2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;8082:8081"&lt;/span&gt;
  &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FLASK_RUN_PORT=8081&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;FLASK_RUN_HOST=0.0.0.0&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Vamos modificar nossa configuração para criar um upstream e colocar os dois hostnames da nossa aplicação:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;

&lt;span class="k"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;app_1:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;app_2:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kn"&gt;keepalive&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Definimos um upstream chamado app, e é esse nome que usamos na diretiva proxy_pass. Usar proxy_pass tem algumas vantagens em vez de usar o proxy_pass diretamente com um IP ou hostname:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Definir diferentes estratégias de balanceamento dos servidores de aplicação (algoritmos de balanceamento diferente com pesos diferentes para cada servidor);&lt;/li&gt;
&lt;li&gt;Usar keepalive, um número inteiro do total de conexões TCP mantidas do NGINX com o servidor, evitando criar conexões TCP a todo request;&lt;/li&gt;
&lt;li&gt;Resolução de DNS é feita quando o servidor sobe. Sem definir um upstream, um nome como google.com no proxy_pass vai gerar uma consulta de DNS por request. Em larga escala isso pode ser um gargalo (até para o DNS interno da sua empresa, caso exista).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A configuração que fizemos vai balancear os request de forma igual, 50% pra cada servidor. Olhando nos logs, podemos ver isso acontecendo na prática:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

nginx-intro-nginx-1  | 192.168.144.1 - - [20/May/2022:16:33:48 +0000] "GET / HTTP/1.1" 200 18 "-" "curl/7.79.1"
nginx-intro-app_1-1  | 192.168.144.4 - - [20/May/2022 16:33:48] "GET / HTTP/1.0" 200 -
nginx-intro-nginx-1  | 192.168.144.1 - - [20/May/2022:16:33:50 +0000] "GET / HTTP/1.1" 200 18 "-" "curl/7.79.1"
nginx-intro-app_2-1  | 192.168.144.4 - - [20/May/2022 16:33:50] "GET / HTTP/1.0" 200 -
nginx-intro-nginx-1  | 192.168.144.1 - - [20/May/2022:16:33:53 +0000] "GET / HTTP/1.1" 200 18 "-" "curl/7.79.1"
nginx-intro-app_1-1  | 192.168.144.4 - - [20/May/2022 16:33:53] "GET / HTTP/1.0" 200 -
nginx-intro-nginx-1  | 192.168.144.1 - - [20/May/2022:16:33:55 +0000] "GET / HTTP/1.1" 200 18 "-" "curl/7.79.1"
nginx-intro-app_2-1  | 192.168.144.4 - - [20/May/2022 16:33:55] "GET / HTTP/1.0" 200 -


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Agora, vamos dar uma olhada em como funciona o sistema de cache do NGINX.&lt;/p&gt;

&lt;h2&gt;
  
  
  Utilizando o NGINX como um servidor de cache
&lt;/h2&gt;

&lt;p&gt;Muita da informação que consumimos não precisa, necessariamente, estar 100% atualizada a todo o instante. Se temos um arquivo estático como um script, uma foto ou um vídeo em nosso site, não precisamos ir buscar ou gerar esse conteúdo a todo momento. Podemos nos utilizar do NGINX como um servidor de cache em alguns passos, fazendo cache de diversos requests da sua aplicação.&lt;/p&gt;

&lt;p&gt;Primeiro, vamos adicionar uma rota que trabalha com um parâmetro de query:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="nd"&gt;@app.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/hello&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;hello&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;name&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;nobody&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;p&amp;gt;Hello, &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Agora, vamos modificar o NGINX para ele fazer cache das respostas.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight nginx"&gt;&lt;code&gt;

&lt;span class="k"&gt;worker_processes&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;events&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;worker_connections&lt;/span&gt; &lt;span class="mi"&gt;1024&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kn"&gt;proxy_cache_path&lt;/span&gt; &lt;span class="n"&gt;/tmp/cache&lt;/span&gt; &lt;span class="s"&gt;levels=1:2&lt;/span&gt; &lt;span class="s"&gt;keys_zone=app_cache:5m&lt;/span&gt; &lt;span class="s"&gt;max_size=10m&lt;/span&gt; &lt;span class="s"&gt;inactive=20m&lt;/span&gt; &lt;span class="s"&gt;use_temp_path=off&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kn"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;app_1:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="s"&gt;app_2:8081&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kn"&gt;keepalive&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kn"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="n"&gt;/&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kn"&gt;add_header&lt;/span&gt; &lt;span class="s"&gt;X-Cache-Status&lt;/span&gt; &lt;span class="nv"&gt;$upstream_cache_status&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_cache&lt;/span&gt; &lt;span class="s"&gt;app_cache&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_cache_valid&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt; &lt;span class="mi"&gt;1m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_cache_key&lt;/span&gt; &lt;span class="nv"&gt;$host$uri$is_args$args&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_cache_lock&lt;/span&gt; &lt;span class="no"&gt;on&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="kn"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://app&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;O que significa cada diretiva de proxy adicionada?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;proxy_cache_path&lt;/strong&gt; - descreve onde vamos guardar o cache. Os arquivos de cache são guardados em arquivos.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;add_header&lt;/strong&gt; - adiciona um header &lt;em&gt;X-Cache-Status&lt;/em&gt; na resposta para que possamos depurar quando aconteceu &lt;em&gt;MISS&lt;/em&gt;, &lt;em&gt;HIT&lt;/em&gt;, etc.

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HIT&lt;/strong&gt; - conteúdo veio diretamente do cache por estar válido, ainda.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MISS&lt;/strong&gt; - conteúdo não foi achado no cache e precisou ser buscado na origem.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;proxy_cache&lt;/strong&gt; - zona do cache. A mesma zona pode ser usada em outros lugares da configuração.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;proxy_cache_valid&lt;/strong&gt; - define um tempo de validade do cache para um código HTTP específico. Nossa aplicação não define um header &lt;em&gt;Cache-Control&lt;/em&gt;, então, configuramos ele direto no NGINX.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;proxy_cache_key&lt;/strong&gt; - parâmetros usados para montar a chave de cache. Essa montagem de valores é extremamente importante para construir um cache seguro e eficiente.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;proxy_cache_lock&lt;/strong&gt; - impede que mais de um request vá na origem para buscar o mesmo conteúdo. Uma diretiva importante para proteger a origem do conteúdo. As conexões esperam até que o cache seja populado.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Conferindo o resultado, podemos fazer uma requisição pra rota nova:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;

curl -v "http://localhost:8080/hello?name=bento"


&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:04:51 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-app_1-1  | 192.168.176.4 - - [20/May/2022 22:04:51] "GET /hello?name=bento HTTP/1.0" 200 -
nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:04:55 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:04:57 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:04:58 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:04:59 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-nginx-1  | 192.168.176.1 - - [20/May/2022:22:06:05 +0000] "GET /hello?name=bento HTTP/1.1" 200 19 "-" "curl/7.79.1"
nginx-intro-app_2-1  | 192.168.176.4 - - [20/May/2022 22:06:05] "GET /hello?name=bento HTTP/1.0" 200 -


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Observe como o primeiro request bate no NGINX e na app. Os requests em sequência não vão até a app porque eles estão usando o cache que orientamos o servidor a fazer.&lt;/p&gt;

&lt;p&gt;Nos últimos dois requests é possível ver que o request foi pra outra instância da app porque passou os 30 segundos de validade que configuramos, garantindo que o cache precisa ser revalidado e que o balanceamento continua funcionando.&lt;/p&gt;

&lt;h2&gt;
  
  
  Próximos passos
&lt;/h2&gt;

&lt;p&gt;Ao mesmo tempo que isso é o básico de NGINX é, também, o suficiente para você desbravar mais utilidades pra ele. É possível adicionar muita lógica de aplicação no NGINX que pode ajudar você a construir aplicações mais focadas na regra de negócio em vez de focar em balanceamento, cache, autorização, etc.&lt;/p&gt;

&lt;p&gt;NGINX possui suporte a embutir código dinâmicamente, sendo as principais &lt;a href="https://github.com/openresty/lua-nginx-module" rel="noopener noreferrer"&gt;Lua&lt;/a&gt; e &lt;a href="https://www.nginx.com/products/nginx/modules/nginx-javascript/" rel="noopener noreferrer"&gt;Javascript&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nginx</category>
      <category>cache</category>
      <category>proxy</category>
      <category>loadbalancing</category>
    </item>
    <item>
      <title>Construindo o seu próprio emulador, parte 1</title>
      <dc:creator>Maurício Antunes</dc:creator>
      <pubDate>Sun, 24 Oct 2021 12:03:56 +0000</pubDate>
      <link>https://dev.to/mauricioabreu/construindo-o-seu-proprio-emulador-parte-1-3a1o</link>
      <guid>https://dev.to/mauricioabreu/construindo-o-seu-proprio-emulador-parte-1-3a1o</guid>
      <description>&lt;h2&gt;
  
  
  O que é um emulador?
&lt;/h2&gt;

&lt;p&gt;Um emulador emula hardware em software. É uma técnica que habilita um computador &lt;strong&gt;imitar as características de outro hardware&lt;/strong&gt;, como por exemplo, um video game. Existem emuladores famosos como o &lt;a href="https://www.pj64-emu.com/" rel="noopener noreferrer"&gt;Project64&lt;/a&gt; e o &lt;a href="https://www.zsnes.com/" rel="noopener noreferrer"&gt;ZSNES&lt;/a&gt;, usados para emular os video games Nintendo 64 e Super Nintendo respectivamente.&lt;/p&gt;

&lt;p&gt;Além de emular, um emulador pode ir adiante, providenciando uma maior performance, maior qualidade de vídeo e melhor gerenciamento de recursos como CPU e memória.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que aprender sobre emuladores?
&lt;/h2&gt;

&lt;p&gt;Boa pergunta! Entender o básico de emuladores vai te proporcionar uma boa ideia de como computadores funcionam. &lt;/p&gt;

&lt;p&gt;Emuladores são compostos de vários componentes de um computador, como uma memória, uma CPU, um teclado e um display. Cada um desses componentes tem suas características.  O desenvolvimento de um emulador vai te ajudar a entender como um programa é carregado na memória (ROM, &lt;em&gt;read-only memory&lt;/em&gt;), ou como as instruções do programa são interpretadas e executadas e como as informações são mostradas na tela.&lt;/p&gt;

&lt;p&gt;Alguns requisitos são necessários para entender e construir um, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conhecer alguma linguagem de programação;&lt;/li&gt;
&lt;li&gt;Operações lógicas;&lt;/li&gt;
&lt;li&gt;Deslocamento de bits (bit shifting).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você conhece uma linguagem de programação mas não entende sobre operações lógicas e operações de bit, não se preocupe, eu vou colocar referências de estudo e explicar as partes do código que falam sobre isso em detalhes.&lt;/p&gt;

&lt;p&gt;O &lt;em&gt;hello world!&lt;/em&gt; dos emuladores é o &lt;strong&gt;chip-8&lt;/strong&gt;, e é ele que vamos construir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Um pouco de base
&lt;/h2&gt;

&lt;p&gt;Antes de começarmos a falar sobre o emulador que vamos construir, vamos relembrar ou aprender um pouco sobre dois assuntos que precisamos compreender para seguir este guia.&lt;/p&gt;

&lt;h3&gt;
  
  
  Binário e hexadecimal
&lt;/h3&gt;

&lt;p&gt;Nosso sistema número mais comum é o sistema decimal. Isso quer dizer que temos do 0 ao 9 para formarmos outros números, como o 2, o 321, o 9825, etc. Depois do 9, para adicionarmos um número passamos o 9 para 0 e colocamos 1 à esquerda e teremos o 10. Computadores usam outros sistemas de números: binário e hexadecimal.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://pt.wikipedia.org/wiki/Sistema_de_numera%C3%A7%C3%A3o_bin%C3%A1rio" rel="noopener noreferrer"&gt;sistema binário&lt;/a&gt; é constituído de 2 números, o 0 e o 1. &lt;code&gt;Zero é 0&lt;/code&gt;, &lt;code&gt;um é 1&lt;/code&gt; e o 2 troca o 1 pra 0 e adiciona um 1 à esquerda. Contando em binário: 0, 1, 10, 11, 100, 101, 110, 111, 1000 (0, 1, 2, 3, 4, 5, 6, 7, 8).&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://pt.wikipedia.org/wiki/Sistema_de_numera%C3%A7%C3%A3o_hexadecimal" rel="noopener noreferrer"&gt;sistema hexadecimal&lt;/a&gt; conta com 16 símbolos. Isso provê uma maior compactação na representação de números maiores. Por exemplo o 15 é representado pela letra &lt;code&gt;F&lt;/code&gt;. Quanto maior o número, mais isso fica perceptível.&lt;/p&gt;

&lt;p&gt;Todas as instruções do chip-8 são analisadas de forma hexadecimal. A instrução de limpar a tela começa com &lt;code&gt;0x0&lt;/code&gt; e a operação de desenhar é &lt;code&gt;0xD&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lógica binária
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://pt.wikipedia.org/wiki/L%C3%B3gica_bin%C3%A1ria" rel="noopener noreferrer"&gt;Lógica binária&lt;/a&gt; opera em bits. Usamos para manipular valores e fazer comparações.&lt;/p&gt;

&lt;p&gt;Algumas dessas operações você já pode conhecer, mas uma em especial é importante conhecer, o &lt;code&gt;XOR&lt;/code&gt;. O XOR devolve 1 bit sempre que a quantidade de números 1 for ímpar:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

 |  V1 |  V2 |  R  |
 +-----+-----+-----+
 |  0  |  0  |  0  |
 +-----+-----+-----+
 |  1  |  0  |  1  |
 +-----+-----+-----+
 |  0  |  1  |  1  |
 +-----+-----+-----+
 |  1  |  1  |  0  |
 +-----+-----+-----+


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Você pode testar isso com alguma linguagem com &lt;a href="https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop" rel="noopener noreferrer"&gt;REPL&lt;/a&gt; ou usando &lt;code&gt;bash&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
0
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
1
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="k"&gt;$((&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="o"&gt;^&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
0


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Deslocamento de bits
&lt;/h3&gt;

&lt;p&gt;Uma das razões do deslocamento de bits existir é a possibilidade de codificar informações importantes usando menos dados. O receptor da mensagem pode decodificar a mensagem, extraindo valores com significado.&lt;/p&gt;

&lt;p&gt;As instruções do chip-8, por exemplo, são codificadas de 2 em 2 bytes, e cada byte pode ser dividido em 2 &lt;a href="https://pt.wikipedia.org/wiki/Nibble" rel="noopener noreferrer"&gt;nibbles&lt;/a&gt; usando deslocamento de bits. Veremos isso em mais detalhes no próximo post, quando vamos aprender a decodificar as instruções da ROM.&lt;/p&gt;

&lt;p&gt;Geralmente a primeira instrução de uma ROM é limpar a tela, a &lt;code&gt;00E0&lt;/code&gt;. Em binário isso seria &lt;code&gt;00000000&lt;/code&gt; (00) e &lt;code&gt;11100000&lt;/code&gt; (E0).&lt;/p&gt;

&lt;p&gt;A tela é limpa quando o &lt;code&gt;OpCode&lt;/code&gt; é &lt;code&gt;0&lt;/code&gt; e o quarto &lt;code&gt;nibble&lt;/code&gt; é &lt;code&gt;0&lt;/code&gt;. Vamos testar usando o REPL do Python:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;

&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mb"&gt;0b00000000&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xF&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;OpCode&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="mb"&gt;0b11100000&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mh"&gt;0xF&lt;/span&gt; &lt;span class="o"&gt;//&lt;/span&gt; &lt;span class="n"&gt;quarto&lt;/span&gt; &lt;span class="n"&gt;nibble&lt;/span&gt; &lt;span class="n"&gt;chamado&lt;/span&gt; &lt;span class="n"&gt;de&lt;/span&gt; &lt;span class="n"&gt;N&lt;/span&gt;
&lt;span class="mi"&gt;0&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;A instrução de &lt;code&gt;jump&lt;/code&gt; (&lt;code&gt;0x1NNN&lt;/code&gt;) faz o código pular até a instrução de memória &lt;code&gt;NNN&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

0b11010 | 0b100101
1A      | 25

JUMP | 0xA25
0x1  | A25


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;0x1&lt;/code&gt; é a operação de pular e &lt;code&gt;A25&lt;/code&gt; forma o &lt;code&gt;NNN&lt;/code&gt;, o quarto, terceiro e segundo nibble dos dois bytes da instrução.&lt;/p&gt;

&lt;p&gt;Veremos esse assunto em detalhes na parte 2.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é o chip-8?
&lt;/h2&gt;

&lt;p&gt;O chip-8 é uma linguagem interpretada e, também, uma máquina virtual criada por Joe Weisbecker em 1977 para rodar no &lt;a href="https://en.wikipedia.org/wiki/COSMAC_VIP" rel="noopener noreferrer"&gt;COSMAC VIP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa7ev7goqrdkx3tkif1o.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffa7ev7goqrdkx3tkif1o.jpeg" alt="foto de um computador COSMAC VIP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A ideia do Joe era rodar pequenos programas e jogos com a ajuda de um teclado de hexadecimal. Em vez de usar linguagem de máquina, o teclado hexadecimal era usado para digitar instruções que seriam interpretadas.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Interpretadas&lt;/em&gt;? O correto não seria &lt;em&gt;emuladas&lt;/em&gt;? Não. Nesse guia não vamos construir um emulador exatamente, mas sim um &lt;strong&gt;interpretador de instruções chip-8&lt;/strong&gt;. Nosso interpretador vai ler instruções de uma ROM e executá-las uma a uma, em um loop de &lt;strong&gt;ler, decodificar e executar&lt;/strong&gt; as instruções carregadas.&lt;/p&gt;

&lt;p&gt;Mas qual a diferença entre &lt;strong&gt;interpretador&lt;/strong&gt; e &lt;strong&gt;emulador&lt;/strong&gt; nesse caso? O chip-8 é um programa que rodava em um computador. Um emulador imita hardware. Simular o chip-8 significa que vamos escrever uma máquina virtual que interpreta comandos via uma linguagem hexadecimal, porém, trazendo vários conceitos vistos em emulação e arquitetura de computadores como PC (program counter), stack, timers, RAM, ROM, etc.&lt;/p&gt;

&lt;p&gt;O chip-8 é formado por diversos componentes. Abaixo vamos falar de cada um dos componentes de maneira breve.&lt;/p&gt;

&lt;h3&gt;
  
  
  Memória
&lt;/h3&gt;

&lt;p&gt;O espaço de memória deve ser de 4kB (4096 bytes). Toda essa memória é volátil (RAM) e pode ser modificável.&lt;/p&gt;

&lt;p&gt;Uma ROM deve começar começar a ser carregada a partir do endereço &lt;code&gt;0x200&lt;/code&gt; (512 em decimal). Os endereços &lt;code&gt;0x000&lt;/code&gt; até o &lt;code&gt;0x1FF&lt;/code&gt; são reservados para o chip-8, porém vamos construir ele usando nosso computador, então não precisamos nos preocupar com isso.&lt;/p&gt;

&lt;h3&gt;
  
  
  Registradores
&lt;/h3&gt;

&lt;p&gt;Existem dois registradores: &lt;strong&gt;dados&lt;/strong&gt; e de &lt;strong&gt;endereço&lt;/strong&gt;.&lt;br&gt;
Eles são usados para gerenciar dados no chip-8. Há várias operações como adição, subtração, leitura de valores, etc. Essas operações se tornam possíveis quando você tem registradores.&lt;/p&gt;

&lt;p&gt;O registrador de dados é um array com 16 posições de valor inteiro de 8 bits sem sinal (&lt;code&gt;u8&lt;/code&gt;). O endereço &lt;code&gt;0xF&lt;/code&gt; é normalmente usado para configurar uma &lt;em&gt;flag&lt;/em&gt; (&lt;code&gt;0&lt;/code&gt; ou &lt;code&gt;1&lt;/code&gt;), que pode ser usado para indicar se houve colisão ao desenhar no display.&lt;/p&gt;

&lt;p&gt;O registrador de endereço não é um array, mas sim uma espécie de ponteiro. Ele é chamado de &lt;code&gt;index&lt;/code&gt; e é usado para ler e escrever na memória. O registrador de endereços, chamado de &lt;code&gt;I&lt;/code&gt;, também é usado para desenhar as fontes no display, que serão configuradas através de uma das instruções contidas na ROM. &lt;/p&gt;

&lt;h3&gt;
  
  
  Display
&lt;/h3&gt;

&lt;p&gt;Com as dimensões de &lt;strong&gt;64 pixels&lt;/strong&gt; de largura e &lt;strong&gt;32 pixels&lt;/strong&gt; (64x32) de altura, o display é usado para renderizar na tela toda atualização identificada pela instrução de &lt;code&gt;draw&lt;/code&gt; (&lt;code&gt;DXYN&lt;/code&gt;) que veremos adiante.&lt;/p&gt;

&lt;p&gt;No chip-8, os pixels são valores booleanos, &lt;code&gt;0&lt;/code&gt; ou &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;on&lt;/code&gt; ou &lt;code&gt;off&lt;/code&gt;, &lt;code&gt;0x0&lt;/code&gt; ou &lt;code&gt;0x1&lt;/code&gt;. Os displays eram monocromáticos (preto e branco), porém, quando chegar a hora você vai poder usar diferentes cores para os pixels. O display começa com todos os pixels off.&lt;/p&gt;

&lt;p&gt;Em desenvolvimento você pode, em vez de escrever na tela usando alguma &lt;em&gt;engine&lt;/em&gt; de gráficos, escrever no &lt;code&gt;STDOUT&lt;/code&gt; para conferir se o programa está desenhado da maneira correta.&lt;/p&gt;

&lt;h3&gt;
  
  
  Teclado
&lt;/h3&gt;

&lt;p&gt;O teclado do COSMAC VIP tem 16 teclas, por isso é chamado de &lt;em&gt;hex keypad&lt;/em&gt;. Não vamos construir o teclado, mas precisamos saber que ele existe e que algumas instruções do chip-8 esperam por uma tecla a ser pressionada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxjeg25nzyfn1nlowc6zc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxjeg25nzyfn1nlowc6zc.png" alt="teclado com 16 teclas do COSMAC VIP"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao construir seu chip-8 você não precisa seguir esse layout. O layout que usei é como mostrado abaixo:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

╔═══╦═══╦═══╦═══╗
║ 1 ║ 2 ║ 3 ║ 4 ║
╠═══╬═══╬═══╬═══╣
║ Q ║ W ║ E ║ R ║
╠═══╬═══╬═══╬═══╣
║ A ║ S ║ D ║ F ║
╠═══╬═══╬═══╬═══╣
║ Z ║ X ║ C ║ V ║
╚═══╩═══╩═══╩═══╝


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Se o seu teclado não é no layout &lt;code&gt;QWERTY&lt;/code&gt;, tudo bem, você pode mapear outras teclas ou até deixar isso configurável.&lt;/p&gt;

&lt;h3&gt;
  
  
  Stack
&lt;/h3&gt;

&lt;p&gt;A stack é uma área de memória relacionada a sub-rotinas que podem ser chamadas durante a execução da ROM.&lt;br&gt;
Como o próprio nome já diz, isso é uma stack. Se a sua linguagem de programação suporta o uso de stacks, use-a para maior legibilidade.&lt;/p&gt;

&lt;p&gt;Os valores guardados nessa stack são de 16 bits.&lt;/p&gt;

&lt;p&gt;Em Rust, os vectors possuem as funções &lt;code&gt;push&lt;/code&gt; e &lt;code&gt;pop&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;u16&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0xF&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;stack&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Existe uma limitação no chip-8 de 12 a 16 endereços de memória, porém, não precisamos nos preocupar com isso, deixando o tamanho da stack ilimitada.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fontes
&lt;/h3&gt;

&lt;p&gt;O chip-8 vem com fontes pré-determinadas. Isso significa que existe na memória do chip-8 sprites que são basicamente números e letras no intervalo hexadecimal: &lt;code&gt;0&lt;/code&gt; a &lt;code&gt;F&lt;/code&gt;. Cada uma dessas representações tem 4 pixels de largura e 5 pixels de altura.&lt;/p&gt;

&lt;p&gt;Guarde essas fontes em uma área de memória antes do &lt;code&gt;0x200&lt;/code&gt; que falamos acima. Por algum motivo ficou popular gravar a partir do &lt;code&gt;0x50&lt;/code&gt;. É possível definir e carregar as fontes usando um código similar ao debaixo:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;FONTS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;80&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 0&lt;/span&gt;
    &lt;span class="mi"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x60&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x70&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 1&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 2&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 3&lt;/span&gt;
    &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 4&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 5&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 6&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x20&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x40&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 7&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 8&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 9&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// A&lt;/span&gt;
    &lt;span class="mi"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// B&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// C&lt;/span&gt;
    &lt;span class="mi"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x90&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xE0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// D&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// E&lt;/span&gt;
    &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0xF0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0x80&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// F&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;FONTS&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.enumerate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="py"&gt;.memory&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0x50&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;n&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Existe uma instrução (&lt;code&gt;FX29&lt;/code&gt;) que guarda o endereço da fonte a ser desenhada. Então, a instrução de desenhar vai usar esse valor de memória para escrever a fonte na tela.&lt;/p&gt;

&lt;h3&gt;
  
  
  Timers
&lt;/h3&gt;

&lt;p&gt;O chip-8 conta com 2 timers (temporizadores): &lt;strong&gt;delay timer&lt;/strong&gt; e &lt;strong&gt;sound timer&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ambos timers são decrementados numa taxa de &lt;code&gt;60khz&lt;/code&gt; até chegar a zero. Ao chegar em zero, cada um terá seu evento interrompido e a contagem recomeça.&lt;/p&gt;

&lt;p&gt;O delay timer é usado para sincronizar eventos.&lt;br&gt;
O sound timer vai tocar um som (&lt;em&gt;beep&lt;/em&gt;) enquanto o valor for maior que zero.&lt;/p&gt;

&lt;p&gt;Os dois timers podem ser inicializados como um inteiro de 8 bits sem sinal (&lt;code&gt;u8&lt;/code&gt;).&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.delay_timer&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.delay_timer&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sound_timer&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.sound_timer&lt;/span&gt; &lt;span class="o"&gt;-=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;No próximo post vamos abordar, em detalhes, a codificação do chip-8 usando a linguagem Rust, entendendo como decodificar as instruções e executar as operações.&lt;/p&gt;

</description>
      <category>chip8</category>
      <category>emuladores</category>
    </item>
  </channel>
</rss>
