<?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: Jackson S</title>
    <description>The latest articles on DEV Community by Jackson S (@jackson_s_33a25dbcc9fc24e).</description>
    <link>https://dev.to/jackson_s_33a25dbcc9fc24e</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2608608%2F2b92c63b-fe57-468e-9bc5-6e9a23178bf6.jpg</url>
      <title>DEV Community: Jackson S</title>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jackson_s_33a25dbcc9fc24e"/>
    <language>en</language>
    <item>
      <title>The 4 Caching Layers in Next.js App Router, in One Table</title>
      <dc:creator>Jackson S</dc:creator>
      <pubDate>Sat, 17 Oct 2026 06:44:07 +0000</pubDate>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e/the-4-caching-layers-in-nextjs-app-router-in-one-table-38pn</link>
      <guid>https://dev.to/jackson_s_33a25dbcc9fc24e/the-4-caching-layers-in-nextjs-app-router-in-one-table-38pn</guid>
      <description>&lt;p&gt;If you have ever asked "why is my Next.js page still showing old data?", the answer is almost always one of four caches. They live in different places, last for different lengths of time, and are cleared in different ways. Mixing them up is also one of the most common Next.js interview mistakes.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What it stores&lt;/th&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;How long&lt;/th&gt;
&lt;th&gt;How to invalidate&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Request Memoization&lt;/td&gt;
&lt;td&gt;Results of identical &lt;code&gt;fetch&lt;/code&gt; / &lt;code&gt;React.cache&lt;/code&gt; calls during one render&lt;/td&gt;
&lt;td&gt;Server, in memory&lt;/td&gt;
&lt;td&gt;One request&lt;/td&gt;
&lt;td&gt;Nothing to do: it ends with the request&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Cache&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;fetch&lt;/code&gt; responses&lt;/td&gt;
&lt;td&gt;Server, persistent&lt;/td&gt;
&lt;td&gt;Across requests (and, depending on your host, across deploys)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;revalidateTag&lt;/code&gt;, &lt;code&gt;revalidatePath&lt;/code&gt;, time-based &lt;code&gt;revalidate&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full Route Cache&lt;/td&gt;
&lt;td&gt;Rendered HTML + RSC payload of static routes&lt;/td&gt;
&lt;td&gt;Server&lt;/td&gt;
&lt;td&gt;Until revalidated or redeployed&lt;/td&gt;
&lt;td&gt;Revalidating the data it depends on; dynamic APIs opt a route out&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Router Cache&lt;/td&gt;
&lt;td&gt;RSC payloads of visited and prefetched routes&lt;/td&gt;
&lt;td&gt;Browser, in memory&lt;/td&gt;
&lt;td&gt;The user's session, with time limits&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;router.refresh()&lt;/code&gt;, Server Actions that revalidate&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  How they work together
&lt;/h2&gt;

&lt;p&gt;When a request hits a static route, Next.js first checks the &lt;strong&gt;Full Route Cache&lt;/strong&gt;. If the page is there, it skips rendering entirely.&lt;/p&gt;

&lt;p&gt;If not, it renders the Server Components. During that render, repeated identical &lt;code&gt;fetch&lt;/code&gt; calls are deduplicated by &lt;strong&gt;Request Memoization&lt;/strong&gt;, and each &lt;code&gt;fetch&lt;/code&gt; checks the &lt;strong&gt;Data Cache&lt;/strong&gt; before going to the network.&lt;/p&gt;

&lt;p&gt;When your data changes, &lt;code&gt;revalidateTag()&lt;/code&gt; purges the matching Data Cache entries, and the routes built from them are re-rendered on the next request.&lt;/p&gt;

&lt;p&gt;On the client, the &lt;strong&gt;Router Cache&lt;/strong&gt; keeps the RSC payloads of pages the user has already visited or prefetched, so back/forward and repeat navigation do not refetch them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three mistakes that come up in interviews
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Confusing the Router Cache with the Full Route Cache.&lt;/strong&gt; One lives in the browser, the other on the server. Clearing one does not clear the other.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expecting &lt;code&gt;revalidateTag()&lt;/code&gt; to update an already-open tab instantly.&lt;/strong&gt; The server cache is fresh, but the open tab still has its Router Cache until the user navigates or you call &lt;code&gt;router.refresh()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assuming the Data Cache is wiped on every deploy.&lt;/strong&gt; Depending on where you host, it can survive deploys, so stale data after a release is not always a build problem.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  A version note
&lt;/h2&gt;

&lt;p&gt;Defaults changed between Next.js 14 and 15. For example, &lt;code&gt;fetch&lt;/code&gt; requests are no longer cached by default in 15. Check which version you are on before trusting any caching article, including this one.&lt;/p&gt;




&lt;p&gt;I wrote a longer, interactive version of this (with code, a model interview answer and the common traps) as part of a free Next.js interview prep track I built: &lt;a href="https://faangprep.com/nextjs/caching-invalidation/the-four-distinct-cache-layers/" rel="noopener noreferrer"&gt;The Four Caching Layers in Next.js&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>nextjs</category>
      <category>react</category>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Kadane's algorithm: two lines in the wrong order return 0 for [-3, -1, -2]</title>
      <dc:creator>Jackson S</dc:creator>
      <pubDate>Mon, 12 Oct 2026 04:30:00 +0000</pubDate>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e/kadanes-algorithm-two-lines-in-the-wrong-order-return-0-for-3-1-2-fhb</link>
      <guid>https://dev.to/jackson_s_33a25dbcc9fc24e/kadanes-algorithm-two-lines-in-the-wrong-order-return-0-for-3-1-2-fhb</guid>
      <description>&lt;p&gt;Maximum subarray (LeetCode 53) is one of the most common interview warm-ups, and Kadane's algorithm solves it in one pass and constant space. It's also easy to write a version that passes the example in the problem statement and fails on an array of all-negative numbers. The difference is the order of two lines.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea
&lt;/h2&gt;

&lt;p&gt;Walk the array while keeping a running sum of the subarray that ends at the current element. If that running sum ever drops below zero, throw it away: any later subarray is better off starting fresh than carrying a negative prefix. Along the way, remember the best sum you've seen.&lt;/p&gt;

&lt;h2&gt;
  
  
  The version that works
&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;def&lt;/span&gt; &lt;span class="nf"&gt;max_subarray&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;current&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;best&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;x&lt;/span&gt; &lt;span class="ow"&gt;in&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;current&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
        &lt;span class="n"&gt;best&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;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# 1. record the best
&lt;/span&gt;        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&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="p"&gt;:&lt;/span&gt;             &lt;span class="c1"&gt;# 2. then discard a negative run
&lt;/span&gt;            &lt;span class="n"&gt;current&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;return&lt;/span&gt; &lt;span class="n"&gt;best&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On &lt;code&gt;[-2, 1, -3, 4, -1, 2, 1, -5, 4]&lt;/code&gt; it returns &lt;code&gt;6&lt;/code&gt;, from &lt;code&gt;[4, -1, 2, 1]&lt;/code&gt;. On &lt;code&gt;[-3, -1, -2]&lt;/code&gt; it returns &lt;code&gt;-1&lt;/code&gt;, which is correct: the best subarray is the single element &lt;code&gt;-1&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Two ways to break it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Reset before recording:&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="ow"&gt;in&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;current&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current&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="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;current&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;best&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;best&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;       &lt;span class="c1"&gt;# sees 0, never the negative value
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Start best at 0:&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="n"&gt;current&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;best&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="mi"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both return &lt;code&gt;6&lt;/code&gt; on the textbook example. Both return &lt;code&gt;0&lt;/code&gt; on &lt;code&gt;[-3, -1, -2]&lt;/code&gt; and on &lt;code&gt;[-5]&lt;/code&gt;. There's no empty subarray to take, so 0 isn't an answer. Interviewers like all-negative input as a follow-up for exactly this reason.&lt;/p&gt;

&lt;p&gt;The rule to remember: &lt;strong&gt;&lt;code&gt;best&lt;/code&gt; has to see &lt;code&gt;current&lt;/code&gt; before it's reset&lt;/strong&gt;, and &lt;code&gt;best&lt;/code&gt; has to start below any possible answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  When Kadane isn't the right tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maximum product subarray (LeetCode 152).&lt;/strong&gt; Two negatives multiply to a positive, so a negative running product can become the best one later. Discarding it is wrong; track both the running maximum and minimum product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Circular arrays (LeetCode 918).&lt;/strong&gt; The best subarray may wrap around. Take the larger of the ordinary maximum and &lt;code&gt;total - minimum subarray&lt;/code&gt;, unless every element is negative.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A length limit.&lt;/strong&gt; With "at most k elements", the reset rule no longer holds. Use prefix sums with a monotonic deque.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Seeing the two variables move
&lt;/h2&gt;

&lt;p&gt;Most of Kadane's algorithm is in how &lt;code&gt;current&lt;/code&gt; and &lt;code&gt;best&lt;/code&gt; interact, and it's easier to see than to read. I built a &lt;a href="https://faangprep.com/dsa/arrays/kadanes-algorithm/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=dsa_kadane" rel="noopener noreferrer"&gt;free step-by-step Kadane's algorithm visualizer&lt;/a&gt;. You advance one element at a time and watch the running sum reset, with the matching code line highlighted in Python, Java, C++, JavaScript, TypeScript or C. Try an all-negative array and predict &lt;code&gt;best&lt;/code&gt; before each step.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: FaangPrep is my site. This lesson and the other introductory lessons open with no account; the other visualizers are free with an account, and the pattern courses are paid. This post was drafted with AI assistance and reviewed before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>interview</category>
      <category>beginners</category>
    </item>
    <item>
      <title>x &amp; (x - 1) and three other bit tricks, and the one that lies about negative numbers</title>
      <dc:creator>Jackson S</dc:creator>
      <pubDate>Fri, 09 Oct 2026 04:30:00 +0000</pubDate>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e/x-x-1-and-three-other-bit-tricks-and-the-one-that-lies-about-negative-numbers-30i7</link>
      <guid>https://dev.to/jackson_s_33a25dbcc9fc24e/x-x-1-and-three-other-bit-tricks-and-the-one-that-lies-about-negative-numbers-30i7</guid>
      <description>&lt;p&gt;Bit manipulation questions look like trivia until you see that almost all of them come from four facts. Here they are, why each one works, and the shortcut that gives the wrong answer on negative numbers.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. &lt;code&gt;x &amp;amp; 1&lt;/code&gt; tells you if a number is odd
&lt;/h2&gt;

&lt;p&gt;The lowest bit is the 1s column. It's 1 for every odd number and 0 for every even one, so &lt;code&gt;x &amp;amp; 1&lt;/code&gt; is the parity without a division.&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;is_odd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nf"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. &lt;code&gt;x &amp;amp; (x - 1)&lt;/code&gt; clears the lowest set bit
&lt;/h2&gt;

&lt;p&gt;Subtracting 1 flips the lowest 1 bit to 0 and every 0 below it to 1. ANDing with the original keeps everything above that bit and zeroes the rest:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;x         = 1011 0100
x - 1     = 1011 0011
x &amp;amp; (x-1) = 1011 0000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two classic questions fall straight out of it:&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;is_power_of_two&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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;x&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="ow"&gt;and&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&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="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;   &lt;span class="c1"&gt;# a power of two has exactly one set bit
&lt;/span&gt;
&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;count_set_bits&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;                     &lt;span class="c1"&gt;# LeetCode 191; loops once per set bit
&lt;/span&gt;    &lt;span class="n"&gt;count&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;while&lt;/span&gt; &lt;span class="n"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;=&lt;/span&gt; &lt;span class="n"&gt;x&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;count&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;count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;x &amp;gt; 0&lt;/code&gt; guard matters. Without it, &lt;code&gt;0&lt;/code&gt; passes the check, because &lt;code&gt;0 &amp;amp; -1&lt;/code&gt; is &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. &lt;code&gt;x ^ x == 0&lt;/code&gt; finds the element without a pair
&lt;/h2&gt;

&lt;p&gt;XOR is its own inverse, and order doesn't matter. XOR every element of an array where each value appears twice except one, and the pairs cancel:&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;functools&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nb"&gt;reduce&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;operator&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;xor&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;single_number&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="c1"&gt;# LeetCode 136
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;xor&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="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4. Shifts multiply and divide by powers of two, mostly
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;x &amp;lt;&amp;lt; k&lt;/code&gt; multiplies by 2^k and &lt;code&gt;x &amp;gt;&amp;gt; k&lt;/code&gt; divides by 2^k. For non-negative numbers that's exactly true. For negative numbers the right shift &lt;strong&gt;rounds down&lt;/strong&gt;, while integer division in Java, C and C++ &lt;strong&gt;rounds toward zero&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Expression&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;-7 &amp;gt;&amp;gt; 1&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-7 / 2&lt;/code&gt; in Java, C, C++&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-3&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;-7 // 2&lt;/code&gt; in Python&lt;/td&gt;
&lt;td&gt;&lt;code&gt;-4&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So replacing &lt;code&gt;/ 2&lt;/code&gt; with &lt;code&gt;&amp;gt;&amp;gt; 1&lt;/code&gt; "for speed" changes the answer on negative input in Java and C. Compilers already make that optimisation where it's safe, so writing it by hand saves nothing. In C, shifting a negative number right is technically implementation-defined; every mainstream compiler gives &lt;code&gt;-4&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recognising a bit manipulation question
&lt;/h2&gt;

&lt;p&gt;The problem statement usually gives it away. Look for "single number", "number of 1 bits", "power of two", "subsets of a small set" (a bitmask of length n enumerates all 2ⁿ subsets), or a hard O(1) space limit on a counting problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching the columns
&lt;/h2&gt;

&lt;p&gt;Bitwise operators are easiest to understand as a column-by-column rule, and that's easier to see than to read. I built a &lt;a href="https://faangprep.com/dsa/math/bit-manipulation/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=dsa_bits" rel="noopener noreferrer"&gt;free bit manipulation visualizer&lt;/a&gt; that shows AND, OR, XOR and shifts on the binary representation step by step, with the code in Python, Java, C++, JavaScript, TypeScript and C.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: FaangPrep is my site. This lesson and the other introductory lessons open with no account; the other visualizers are free with an account, and the pattern courses are paid. This post was drafted with AI assistance and reviewed before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>interview</category>
      <category>beginners</category>
    </item>
    <item>
      <title>BFS has one line people get wrong: where you mark a node visited</title>
      <dc:creator>Jackson S</dc:creator>
      <pubDate>Wed, 07 Oct 2026 04:30:00 +0000</pubDate>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e/bfs-has-one-line-people-get-wrong-where-you-mark-a-node-visited-3gc1</link>
      <guid>https://dev.to/jackson_s_33a25dbcc9fc24e/bfs-has-one-line-people-get-wrong-where-you-mark-a-node-visited-3gc1</guid>
      <description>&lt;p&gt;Breadth-first search is usually the second graph algorithm anyone learns, and it looks almost too simple: a queue, a loop, a visited set. Most BFS bugs I see come down to one decision in that loop: &lt;strong&gt;when you mark a node as visited&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The idea, without code
&lt;/h2&gt;

&lt;p&gt;Drop a stone in a pond. The first ripple reaches everything one step away, the second ripple everything two steps away, and so on. BFS explores a graph the same way. It visits every neighbour of the start node, then every neighbour of those, one layer at a time.&lt;/p&gt;

&lt;p&gt;The queue is what keeps the layers in order. It is first-in, first-out, so every node from layer 1 is processed before any node from layer 2 is even looked at. That ordering is also why BFS finds the &lt;strong&gt;shortest path in an unweighted graph&lt;/strong&gt;: the first time it reaches a node is along the fewest possible edges.&lt;/p&gt;

&lt;h2&gt;
  
  
  The standard version
&lt;/h2&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;graph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;visited&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;          &lt;span class="c1"&gt;# mark the start as soon as it is queued
&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;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;order&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;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;order&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;node&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;neighbour&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;neighbour&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;visited&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
                &lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;neighbour&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;# mark on ENQUEUE
&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;neighbour&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;order&lt;/span&gt;

&lt;span class="n"&gt;graph&lt;/span&gt; &lt;span class="o"&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;A&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;B&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;C&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;B&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;A&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;D&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;E&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;C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;A&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;E&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;D&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;B&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;E&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&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;B&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;C&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&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;graph&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="c1"&gt;# ['A', 'B', 'C', 'D', 'E']
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The bug: marking on dequeue
&lt;/h2&gt;

&lt;p&gt;Here is the version that looks equivalent:&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;bfs_buggy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;visited&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;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;start&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;order&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;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;node&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;continue&lt;/span&gt;
        &lt;span class="n"&gt;visited&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&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="c1"&gt;# mark on DEQUEUE
&lt;/span&gt;        &lt;span class="n"&gt;order&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;node&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;neighbour&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;neighbour&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;visited&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;neighbour&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;order&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It returns the same order, so tests pass. Now trace the queue on the graph above.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;A&lt;/code&gt; is dequeued. &lt;code&gt;B&lt;/code&gt; and &lt;code&gt;C&lt;/code&gt; are enqueued. Queue: &lt;code&gt;B, C&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;B&lt;/code&gt; is dequeued. &lt;code&gt;D&lt;/code&gt; and &lt;code&gt;E&lt;/code&gt; are enqueued. Queue: &lt;code&gt;C, D, E&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;C&lt;/code&gt; is dequeued. &lt;code&gt;E&lt;/code&gt; isn't visited &lt;em&gt;yet&lt;/em&gt;, because it's still waiting in the queue, so it's enqueued &lt;strong&gt;again&lt;/strong&gt;. Queue: &lt;code&gt;D, E, E&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;E&lt;/code&gt; is now in the queue twice. On this graph that costs one wasted loop. On a dense graph a node can be queued once for every edge leading into it. The queue grows toward O(E) instead of O(V), and on a large grid problem that's the difference between passing and a memory or time-limit failure.&lt;/p&gt;

&lt;p&gt;The fix is the first version: &lt;strong&gt;mark a node visited the moment you put it in the queue&lt;/strong&gt;, not when you take it out. At that point you've already committed to visiting it, and nothing else should queue it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shortest path is the same loop with a distance map
&lt;/h2&gt;

&lt;p&gt;Once the visited set is right, shortest path in an unweighted graph is a small change. Store each node's distance when you first enqueue it:&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;shortest_path_length&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;graph&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;dist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;start&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="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;start&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;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;node&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;target&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;dist&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;neighbour&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;graph&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="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;neighbour&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;dist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="c1"&gt;# dist doubles as the visited set
&lt;/span&gt;                &lt;span class="n"&gt;dist&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;neighbour&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;dist&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="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&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;neighbour&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;Because the first time BFS reaches a node is along the fewest edges, that distance is final the moment it's written.&lt;/p&gt;

&lt;h2&gt;
  
  
  When BFS is the wrong tool
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Weighted edges.&lt;/strong&gt; "Fewest edges" is no longer "cheapest path". Use Dijkstra's algorithm.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Very wide graphs.&lt;/strong&gt; BFS holds an entire layer in memory. If a layer can be enormous and you only need to know whether a path exists, depth-first search uses less memory.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The quick recognition rule for interviews: if a problem asks for the &lt;em&gt;shortest path&lt;/em&gt;, the &lt;em&gt;fewest steps&lt;/em&gt; or the &lt;em&gt;nearest&lt;/em&gt; something, and every step costs the same, it's almost always BFS.&lt;/p&gt;

&lt;h2&gt;
  
  
  Watching it run
&lt;/h2&gt;

&lt;p&gt;The buggy version is easier to see than to read. I built a &lt;a href="https://faangprep.com/dsa/structures/breadth-first-search/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=dsa_bfs" rel="noopener noreferrer"&gt;free step-by-step BFS visualizer&lt;/a&gt;. You advance one queue operation at a time, and the matching code line is highlighted in Python, Java, C++, JavaScript, TypeScript or C. Try predicting the queue contents before each step, especially where two nodes share a neighbour.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: FaangPrep is my site. The BFS lesson and the other introductory lessons open with no account; the other visualizers are free with an account, and the pattern courses are paid. This post was drafted with AI assistance and reviewed before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>python</category>
      <category>beginners</category>
      <category>interview</category>
    </item>
    <item>
      <title>How resume keyword matching actually works (and a free tool to check yours)</title>
      <dc:creator>Jackson S</dc:creator>
      <pubDate>Mon, 05 Oct 2026 15:13:38 +0000</pubDate>
      <link>https://dev.to/jackson_s_33a25dbcc9fc24e/how-resume-keyword-matching-actually-works-and-a-free-tool-to-check-yours-4841</link>
      <guid>https://dev.to/jackson_s_33a25dbcc9fc24e/how-resume-keyword-matching-actually-works-and-a-free-tool-to-check-yours-4841</guid>
      <description>&lt;p&gt;Most "your resume will be rejected by the ATS" advice is fear dressed up as fact. Here is what is actually going on, and what you can usefully check before you apply.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a keyword match is
&lt;/h2&gt;

&lt;p&gt;When a recruiter searches their applicant tracking system, they usually search for skills: &lt;code&gt;Kubernetes&lt;/code&gt;, &lt;code&gt;TypeScript&lt;/code&gt;, &lt;code&gt;system design&lt;/code&gt;. Your resume shows up only if those words are in it. Whether a given company ranks or filters on them depends on how &lt;em&gt;that&lt;/em&gt; company set its system up, and no outside tool can know that.&lt;/p&gt;

&lt;p&gt;What you &lt;strong&gt;can&lt;/strong&gt; control is simple: if the job description asks for &lt;code&gt;Kafka&lt;/code&gt; and you have used Kafka, the word &lt;code&gt;Kafka&lt;/code&gt; should be on your resume, spelled the way the posting spells it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Three things that trip people up
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Synonyms you assume count.&lt;/strong&gt; "Built containerised services" does not contain the word &lt;code&gt;Docker&lt;/code&gt;. A human might connect them; a keyword search will not.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Close-but-different terms.&lt;/strong&gt; &lt;code&gt;JavaScript&lt;/code&gt; is not &lt;code&gt;TypeScript&lt;/code&gt;, and &lt;code&gt;accessible UI&lt;/code&gt; is not &lt;code&gt;accessibility&lt;/code&gt;. If the posting uses a specific term and you have the skill, use the term.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repeated domain words.&lt;/strong&gt; If a posting says "payments" five times, it matters to them. It is not a skill keyword, but if you have worked on payments, say so.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to check a resume against a posting
&lt;/h2&gt;

&lt;p&gt;By hand: list every technical term in the job description, then search your resume for each one. That is tedious, so I built a small checker that does it:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://faangprep.com/resume-builder/?utm_source=devto&amp;amp;utm_medium=social&amp;amp;utm_campaign=resume_checker_launch#free-resume-check" rel="noopener noreferrer"&gt;Free resume keyword check&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste your resume text and the job description. It shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the posting's technical keywords your resume already contains;&lt;/li&gt;
&lt;li&gt;the ones it is missing;&lt;/li&gt;
&lt;li&gt;a coverage percentage;&lt;/li&gt;
&lt;li&gt;words the posting repeats that your resume never uses.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs entirely in your browser, so nothing is uploaded, and it needs no sign-up. It is honest about its limits: it matches keywords, and it does not judge layout or writing or predict any company's ATS.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;One rule:&lt;/strong&gt; only add skills you actually have. A keyword you cannot talk about in the interview is worse than a missing one.&lt;/p&gt;

&lt;h2&gt;
  
  
  If you want to go further
&lt;/h2&gt;

&lt;p&gt;The same page has an AI tailoring step, which rewrites a resume against a specific job description. You approve every edit as a diff and see the match before and after. The first run is free with an account. Beyond that it is ₹30 per PDF download, or ₹299 for a month of unlimited use, with no auto-renewal.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclosure: FaangPrep is my site. The keyword check is free with no account. This post was drafted with AI assistance and reviewed before publishing.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>career</category>
      <category>resume</category>
      <category>jobsearch</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
