<?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: Ganesh Nellaiappan Rajan</title>
    <description>The latest articles on DEV Community by Ganesh Nellaiappan Rajan (@ganesh_rajan).</description>
    <link>https://dev.to/ganesh_rajan</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%2F4014784%2F8bdcfe3e-df00-4f10-9089-55c2cc49f93e.jpg</url>
      <title>DEV Community: Ganesh Nellaiappan Rajan</title>
      <link>https://dev.to/ganesh_rajan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/ganesh_rajan"/>
    <language>en</language>
    <item>
      <title>80. Remove Duplicates from Sorted Array II</title>
      <dc:creator>Ganesh Nellaiappan Rajan</dc:creator>
      <pubDate>Sat, 04 Jul 2026 15:25:40 +0000</pubDate>
      <link>https://dev.to/ganesh_rajan/80-remove-duplicates-from-sorted-array-ii-2j2j</link>
      <guid>https://dev.to/ganesh_rajan/80-remove-duplicates-from-sorted-array-ii-2j2j</guid>
      <description>&lt;p&gt;Read the problem description here.&lt;br&gt;
&lt;/p&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body flex items-center justify-between"&gt;
        &lt;a href="https://leetcode.com/problems/remove-duplicates-from-sorted-array-ii/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;leetcode.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;This is a medium-range problem, and it genuinely pushed me to the edge. Even when I had a working solution in front of me, I couldn't reason about it. Then suddenly it clicked, and everything made perfect sense. I'm sharing my perspective partly to solidify my own understanding, and partly to help anyone else who is stuck. Let's jump in.&lt;/p&gt;

&lt;p&gt;Here is our example array:&lt;/p&gt;

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

&lt;p&gt;Your goal is to make it look like this:&lt;/p&gt;

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

&lt;p&gt;You don't care what sits after the valid elements.&lt;/p&gt;




&lt;p&gt;This problem is a variant of deduplicating a sorted array in-place, so make sure you understand that one first. The twist here: each value is allowed to appear &lt;strong&gt;at most twice&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;Point No 1:&lt;/strong&gt; The first two elements are always valid, no matter what they are. So the real processing starts at index 2.&lt;/p&gt;

&lt;p&gt;We use two pointers: a &lt;strong&gt;readPointer&lt;/strong&gt; that scans the array one value at a time, and a &lt;strong&gt;writePointer&lt;/strong&gt; that tracks the next position in the clean array we are building. The readPointer always moves forward. The writePointer moves only after a successful write.&lt;/p&gt;

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

&lt;p&gt;Both pointers start at index 2, because we already know the first two elements are valid.&lt;/p&gt;

&lt;h2&gt;
  
  
  The key question
&lt;/h2&gt;

&lt;p&gt;Before every write, you ask the exact same question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Can I write the readPointer's value into the writePointer's position?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you can answer this correctly, you have solved the problem. So how do you answer it?&lt;/p&gt;

&lt;p&gt;Ask what would make the array &lt;em&gt;bad&lt;/em&gt;. Since each value may appear at most twice, a bad array is one where &lt;code&gt;array[writePointer] === array[writePointer - 2]&lt;/code&gt; — three copies in a row. (If no duplicates were allowed at all, you would compare against &lt;code&gt;writePointer - 1&lt;/code&gt; instead. The "how many duplicates" rule directly decides how far back you look.)&lt;/p&gt;

&lt;p&gt;So the answer is one comparison: &lt;strong&gt;the value you are about to write must differ from the value at &lt;code&gt;writePointer - 2&lt;/code&gt;.&lt;/strong&gt; If it differs, write it. If not, skip it.&lt;/p&gt;

&lt;p&gt;Every step below asks this same question. Watch the pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Walking through it
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;readPointer = 2, writePointer = 2.&lt;/strong&gt; The value to write is 1. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 2 − 2 = index 0, holding the value 0. 1 ≠ 0, so the write is legal — write 1 at position 2. (The array looks unchanged, since we wrote the same value back, but the mechanism is working.) Both pointers move forward.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;readPointer = 3, writePointer = 3.&lt;/strong&gt; The value to write is 1. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 3 − 2 = index 1, holding the value 0. 1 ≠ 0, write is legal — write 1 at position 3. Both pointers move.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;readPointer = 4, writePointer = 4.&lt;/strong&gt; The value to write is 1. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 4 − 2 = index 2, holding the value 1. &lt;strong&gt;1 === 1, equal!&lt;/strong&gt; Writing would create three 1s in a row — a bad array. So we don't write.&lt;/p&gt;

&lt;p&gt;Here is the interesting part. When you cannot write, only the readPointer moves. The writePointer stays, patiently holding its spot, because that position is still where the next valid value must go.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;readPointer = 5, writePointer = 4.&lt;/strong&gt; The value is again 1. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is still 4 − 2 = index 2, holding 1. Equal again. Skip. We have now skipped two 1s back to back and the writePointer hasn't budged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;readPointer = 6, writePointer = 4.&lt;/strong&gt; The value to write is 2. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 4 − 2 = index 2, holding 1. 2 ≠ 1, write is legal — write 2 at position 4.&lt;/p&gt;

&lt;p&gt;This is the moment the array visibly changes. The third 1 at index 4 gets overwritten by 2:&lt;/p&gt;

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

&lt;p&gt;The two pointers have now separated. From here on, the readPointer scouts ahead while the writePointer trails behind, building the clean array.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;readPointer = 7, writePointer = 5.&lt;/strong&gt; The value to write is 3. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 5 − 2 = index 3, holding 1. 3 ≠ 1, write is legal — write 3 at position 5.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;readPointer = 8, writePointer = 6.&lt;/strong&gt; The value to write is 3. Look at &lt;code&gt;writePointer - 2&lt;/code&gt;, which is 6 − 2 = index 4, holding 2. 3 ≠ 2, write is legal — write 3 at position 6. Note that this second 3 is allowed because the comparison happens against the &lt;em&gt;written&lt;/em&gt; region, not the original array. Two positions back in the clean part is 2, so the invariant automatically permits exactly one duplicate 3.&lt;/p&gt;

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

&lt;p&gt;The readPointer has reached the end. We are done. The writePointer is 7, and that is the answer — the length of the valid array. The first 7 elements are &lt;code&gt;[0, 0, 1, 1, 2, 3, 3]&lt;/code&gt;, exactly what we wanted. Whatever garbage sits after index 6, we don't care.&lt;/p&gt;




&lt;h2&gt;
  
  
  The one question, generalized
&lt;/h2&gt;

&lt;p&gt;Every iteration asks the same thing:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"Is the value I am reading different from the value at writePointer − 2?"&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If yes, write it and move both pointers. If no, move only the readPointer. That's the entire algorithm — one comparison doing all the heavy lifting, guaranteeing no value ever appears a third time.&lt;/p&gt;

&lt;p&gt;And if the problem ever changes to "allow at most k duplicates", you change the question to &lt;code&gt;writePointer − k&lt;/code&gt;. Nothing else changes. That is the sign of a good invariant.&lt;/p&gt;

&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;removeDuplicatesSortedArrayII&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;[]):&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;readPointer&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="nx"&gt;writePointer&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="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(;&lt;/span&gt; &lt;span class="nx"&gt;readPointer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;readPointer&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// can I write this value in the writePointer's position?&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;readPointer&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;readPointer&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;writePointer&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="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;writePointer&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;readPointer&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;writePointer&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;The &lt;code&gt;readPointer &amp;lt; 2&lt;/code&gt; guard is Point No 1 from earlier: the first two elements are always valid, so they are written unconditionally. From index 2 onwards, the question kicks in. One pass, O(n) time, O(1) space.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned the hard way
&lt;/h2&gt;

&lt;p&gt;My first attempt tracked duplicates with a counter and tried to jump the writePointer around based on how many duplicates I had seen. It failed in two places: it destroyed the second valid copy whenever a new value appeared, and it never flushed trailing duplicates when the array ended mid-run. On paper, my brain was silently doing the right thing — but the code wasn't asking the right question.&lt;/p&gt;

&lt;p&gt;The moment I reframed it as &lt;em&gt;"can I write this value here, and how do I answer that?"&lt;/em&gt;, everything collapsed into a single comparison. If you are stuck on this problem, don't memorize the solution. Ask the question.&lt;/p&gt;

</description>
      <category>datastructures</category>
      <category>algorithms</category>
      <category>programming</category>
      <category>leetcode</category>
    </item>
  </channel>
</rss>
