Reverse String: Two-Pointer Swap In-Place
LeetCode 344, Reverse String, is about as clean as a coding problem gets. You are given an array of characters s, and your job is to reverse it in place. No return value. The function modifies s directly.
The catch is the constraint: you must do it with O(1) extra memory. That means you cannot create a reversed copy and overwrite the original. You have to do the work inside the array itself.
For the example input ["h","e","l","l","o"], the expected output is ["o","l","l","e","h"].
["h","e","l","l","o"] with left at index 0 and right at index 4. The two pointers will walk toward each other, swapping characters as they go.The approach
Two pointers are all you need. Place left at index 0 and right at the last index. Swap the characters at both positions, then move left one step right and right one step left. Keep going until the pointers meet or cross.
Each swap puts one character from the front into its correct final position and one character from the back into its correct final position simultaneously. By the time the pointers meet in the middle, every character has been placed correctly.
The loop condition is left < right. The moment the two pointers are at the same index (odd-length array) or have crossed (even-length array), the work is done. The middle element of an odd-length array never needs to move.
Python solution
def reverse_string(s):
left, right = 0, len(s) - 1
while left < right:
s[left], s[right] = s[right], s[left]
left += 1
right -= 1
Python's tuple unpacking makes the swap a single line. No temporary variable needed. The whole function is five lines including the signature.
Step-by-step walkthrough
Here is what happens for ["h","e","l","l","o"], traced swap by swap.
Step 1: left=0, right=4. Swap "h" and "o".
After swap: ["o","e","l","l","h"]. Move left to 1, right to 3.
Step 2: left=1, right=3. Swap "e" and "l".
After swap: ["o","l","l","e","h"]. Move left to 2, right to 2.
Step 3: left=2, right=2. Pointers meet. Done.
left is no longer less than right. The array is fully reversed: ["o","l","l","e","h"].
Three iterations of the loop and you are done. The total number of swaps is always floor(n / 2), where n is the length of the array.
Complexity
| Complexity | |
|---|---|
| Time | O(n) |
| Space | O(1) |
Every element is visited at most once. The left pointer moves right and the right pointer moves left, each covering half the array. That is O(n) total work. The only extra memory used is the two pointer variables, so space is O(1).
Building blocks
Reverse String is the purest expression of the opposite-end convergence pattern. Two pointers start at the extremes of a sequence, do some work, and walk toward the center. The same skeleton appears in:
- Valid Palindrome -- instead of swapping, you compare the characters at both pointers to check equality
- Two Sum II -- you compare the sum at both pointers against a target to decide which pointer to move
- Container With Most Water -- you move the pointer on the shorter side to maximize area
- 3Sum -- the inner loop is a converging two-pointer search on a sorted subarray
Reverse String is the easiest version because the decision is always the same: swap and advance both pointers. There is no conditional logic about which pointer to move. That is what makes it a good starting point for building the muscle memory around this pattern.
It also has a natural connection to palindrome problems. A string is a palindrome if reversing it produces the same string. The two-pointer check in Valid Palindrome is essentially a read-only version of this exact swap loop -- you compare instead of swap, but the pointer movement is identical.
Edge cases
Empty array. s = []. The initial condition left < right is 0 < -1, which is false. The loop never runs. The array is returned as-is. Correct.
Single character. s = ["a"]. left = 0, right = 0. The condition 0 < 0 is false. No swaps. Correct.
Even length. ["a","b","c","d"]. The pointers meet at the boundary between index 1 and 2 (left becomes 2, right becomes 1, loop exits). Two swaps total.
Odd length. ["a","b","c","d","e"]. The pointers meet at index 2. Two swaps total. The middle character "c" is never touched, which is correct -- it stays in the center.
In all cases the loop condition handles everything. You do not need special cases in the code.
From understanding to recall
Reading this solution takes under a minute. You can follow every line. But the real question is whether you can reproduce it from scratch a week from now, without hints, without looking anything up.
The Two Pointers pattern is small enough to internalize completely -- two variables, one loop, one condition. The swap itself is a single Python line. But there are still details that slip away: initializing right to len(s) - 1 rather than len(s), using left < right rather than left <= right, advancing both pointers every iteration.
Spaced repetition surfaces these problems at the right intervals, before the memory fully fades. Instead of re-reading the solution after forgetting it, you recall it actively, which is what actually builds long-term retention.
Related posts
- Valid Palindrome -- reads the same two-pointer loop, but compares instead of swaps
- Reverse Vowels of a String -- same in-place swap idea, with a filter step added
- The Two Pointers Pattern -- the full guide to opposite-end convergence and its variations