Two Heads for Two Arrays
Each pointer walks a different array. A first while advances both while they are valid and decides what to take; two trailing while loops drain whichever array still has leftovers. This 3-loop template handles merges and comparisons cleanly, and the fill direction (forward or backward) is chosen to avoid clobbering data still being read.
Three While Loops
88. Merge Sorted Array
We are given two sorted arrays nums1 (with capacity for m + n elements, but only the first m are valid) and nums2 (with n valid elements). The goal is to merge them in-place into nums1 in non-decreasing order.
Why fill from the back?
The tail of nums1 is free space - those n trailing slots are placeholders. If we filled from the front we would immediately overwrite valid elements in nums1 before we had a chance to compare them. Starting from position m + n - 1 and working left gives us a write position that is always beyond the read positions of both arrays, so no element is ever clobbered.
The classic 3-while template:
- Pointer
astarts at the last valid index ofnums1(m - 1), pointerbat the last index ofnums2(n - 1), andinsert_hereat the last slot (m + n - 1). - First while - both arrays still have elements: compare
nums1[a]andnums2[b], write the larger one toinsert_here, then advance the corresponding source pointer andinsert_hereleft by one. - Second while - only
nums1elements remain: copy them into position. (In practice these are already in the right place, but the loop handles the case correctly.) - Third while - only
nums2elements remain: copy them. This is the critical mop-up: ifnums2exhausts slower thannums1, the remainingnums2values must be written into the front ofnums1.
At the end every element from both arrays occupies exactly one slot in nums1, sorted in place with no extra memory.
- Time
- O(M+N)
MandNare the number of valid elements innums1andnums2respectively. Every element is visited exactly once across the three while loops.- Space
- O(1)
- The merge is done in-place using only the existing capacity of
nums1. No auxiliary array is allocated.
1768. Merge Strings Alternately
We interleave two strings character by character: take one from word1, one from word2, one from word1, and so on. If one string runs out before the other, the remaining characters of the longer string are appended in order.
Parallel walk:
- Pointers
aandbstart at the beginning ofword1andword2respectively. - The first while loop advances both pointers in lock-step: append
word1[a]thenword2[b]to the result list, then increment both. This loop runs exactlymin(m, n)times. - The second while handles leftover characters in
word1whenword1is longer. - The third while handles leftover characters in
word2whenword2is longer.
Result:
"".join(ans)collapses the list into the final merged string. Using a list and joining at the end avoids repeated string concatenation, keeping the overall work linear.
The 3-while structure mirrors the classic merge pattern: process the shared prefix together, then drain whichever tail remains.
- Time
- O(M+N)
MandNare the lengths ofword1andword2. Every character from both strings is visited exactly once.- Space
- O(M+N)
- The result list
ansaccumulates allM + Ncharacters before the final join.
165. Compare Version Numbers
A version string is a sequence of non-negative integer revisions separated by dots (e.g. "1.01.0"). Two versions are compared revision by revision from left to right; if all compared revisions are equal, trailing missing revisions are treated as 0.
Parsing each revision on the fly:
- Pointers
aandbwalkversion1andversion2character by character. - An inner loop accumulates digits into an integer (
v1orv2) until it hits a dot or the end of the string. After each inner loop the outer pointer is advanced past the dot witha += 1(orb += 1), so the next outer iteration starts at the first character of the next revision. - Leading zeros are automatically collapsed because we multiply into a running integer (
v1 = v1 * 10 + int(char)).
Comparison:
- After extracting both current revisions, compare
v1andv2. Return1or-1immediately if they differ. - If they are equal, continue to the next revision pair.
Trailing revisions:
- When one string is exhausted first, the remaining revisions of the other string must still be compared against
0. The two cleanup while loops drain the remaining characters ofversion1andversion2intov1andv2, then the final three-way comparison handles the result.
Key insight: version "1.0" equals "1.0.0" because any unspecified trailing revision defaults to zero, and both cleanup loops leave v1 = v2 = 0 when the shorter string was already empty at that point.
- Time
- O(M+N)
MandNare the lengths ofversion1andversion2. Each character is visited exactly once.- Space
- O(1)
- Only a fixed number of integer variables are used; no extra strings or arrays are allocated.
Miscellaneous
392. Is Subsequence
A string s is a subsequence of t if every character of s can be found in t in the same relative order, with possibly other characters in between. We do not need to find every possible alignment - the greedy approach works: always match s[a] against the earliest available character in t.
Two-pointer walk:
- Pointer
atracks our position ins(the pattern); pointerbtracks our position int(the text). badvances on every iteration - we always move forward throught.aadvances only whens[a] == t[b], recording a successful match.
Why greedy is correct:
- Suppose we skip a matching position in
thoping for a later one. That can never help: using the earliest match leaves the most remaining characters intavailable for the rest ofs. Taking the first opportunity is always at least as good as taking a later one.
Result:
- After the loop,
a == mmeans every character ofswas matched in order - returnTrue. - If
bexhauststwhilea < m, some characters ofswere never matched - returnFalse. - An empty
sis trivially a subsequence of anyt: the loop body never executes anda == 0 == m.
- Time
- O(N)
Nis the length oft. Pointerbadvances on every step, so the loop runs at mostNiterations.M(length ofs) is bounded byNin the worst case.- Space
- O(1)
- Only two integer pointers and the two length variables are used.
844. Backspace String Compare
Given two strings where # represents a backspace key, we need to determine whether the two strings produce the same result after all backspaces are applied. The O(1)-space approach avoids materializing the processed strings by scanning both from right to left and skipping characters that would have been deleted.
Key insight - scan from the right:
- Processing left-to-right is hard because a
#deletes the character before it - you would need to look backward. Scanning right-to-left is natural: a#tells us to skip the next non-#character we encounter, which is the one immediately to the left.
Skip counters back_a and back_b:
- When we see a
#ats[a], we incrementback_aand move left. This schedules one deletion. - When
back_a > 0and the current character is not#, we decrementback_aand move left - this character has been "deleted" by a pending backspace. - The same logic applies independently to
tviabandback_b.
Comparison step:
- Once both pointers have cleared their pending skips and both point to a surviving character, we compare
s[a]andt[b]. If they differ, the strings are not equal - returnFalseimmediately. - If they match, advance both pointers and continue.
Termination:
- The loop continues as long as at least one pointer is still in range. After the loop, both arrays must be fully exhausted (
a < 0 and b < 0) for equality. If one still has surviving characters left, they cannot match the empty other side.
- Time
- O(M+N)
MandNare the lengths ofsandt. Each pointer moves strictly left on every branch, so the total iterations are bounded byM + N.- Space
- O(1)
- Only two pointers and two skip counters are used. No processed string is ever stored.