Reverse String in Java DSA: Two Pointers That Swap in Place
-
Last Updated: July 26, 2026
-
By: javahandson
-
Series
Learn Java in a easy way
Learn Reverse String in Java DSA step by step. Move from recursion to an in-place two-pointer swap with dry runs, full code, and complexity comparisons for interviews.
Reverse String in Java is the eighth problem in this series. It follows Valid Palindrome, and it uses the same two-pointer habit. Here you flip a character array end to end, without building a new one.
The task sounds tiny. You get an array of characters. You must reverse its order, so the last character comes first.
One rule makes it interesting. You cannot return a fresh array. You have to flip the characters inside the same array you were handed.
We build the answer in three steps. Recursion swaps the ends and then calls itself on the middle. An extra-array version copies characters backwards into a helper. The final two-pointer swap flips the array in a single sweep, with no extra memory.
Every approach here comes with a full dry run. We trace the same word through each one, so you can watch the extra memory disappear step by step.
Let us fix the rules before we write any code. A clear problem statement now saves you from silent bugs later.
So the word hello turns into olleh. The first character swaps with the last, and the array reads backwards when you are done.
Notice one easy point. A single character, or an empty array, is already reversed. There is nothing to swap, so you leave it as it is.
The problem could be solved by building a new array. But it asks for something stricter.
This rule is the whole point of the exercise. It pushes you toward the neat two-pointer trick instead of an easy copy.
The word hello is short but not trivial. That keeps the trace honest.
A two-letter word hides the middle case. A five-letter word shows you what the pointers really do around the centre.
| 💡 Interview Insight Interviewers often ask what happens to the middle character in an odd-length array. The two-pointer loop simply stops before it, so the centre stays put. Saying this out loud shows you thought about odd and even lengths, not just one case. |
Two small ideas carry this whole problem. Both come back often in later array questions, so learn them well.
A swap trades the contents of two slots. You cannot just copy one onto the other, or you lose a value.
Think of two cups of juice. To swap them, you pour one into a spare cup first. Skip that spare cup and the two juices mix into one.
Here is the idea that makes this problem click. You keep two pointers walking toward each other.
One pointer starts on the far left. It marks the first slot to swap. We call it left.
The other pointer starts on the far right. It marks the last slot to swap. We call it right.
Think of two hands on a row of books. One hand starts at each end. They swap the outer books, then step inward together, until they meet at the middle.
The first idea many people reach for is recursion. Swap the two ends, then ask the method to reverse the smaller middle part. Each call peels off one pair from the outside.
It reverses the array correctly and in place. But every call adds a layer to the call stack, so it quietly uses extra memory. Still, it is a clear way to see the pairs being swapped.
reverse(s):
call helper(s, left = 0, right = last index)
helper(s, left, right):
if left >= right: // base case: nothing left to swap
return
swap s[left] and s[right] // swap this outer pair
helper(s, left + 1, right - 1) // recurse on the inner partLet us read this slowly, one block at a time. The plan is to swap the outermost pair, then hand the smaller inside to another copy of the same method. Think of peeling an onion, one layer of skin from each side at a time.
Start at the top. The reverse method does almost nothing itself. It just calls a helper, handing it two starting positions: left at 0 and right at the last index.
Now look at the helper. It carries three things: the array, a left position, and a right position. Everything interesting happens here.
The very first line of the helper is the base case. It asks a stop question before doing any work.
Why does this stop question come first? Because without it, the method would call itself forever. The base case is the brake that halts the recursion once the pointers meet in the middle.
If the base case is not hit, we do the real work. We swap the character at left with the character at right. That places one outer pair in its final reversed spot.
Then comes the recursive call, and this is the clever part. The method calls itself, but with left moved one step inward and right moved one step inward.
Picture it on hello. The first call swaps h and o. A second call swaps e and l around the middle. Then the third call finds left and right have met, so it stops. The array is now reversed.
So the whole plan reads as one repeating move: swap the outer pair, then ask a smaller version of yourself to reverse what is left inside.
public class ReverseStringRecursive {
public static void reverseString(char[] s) {
helper(s, 0, s.length - 1);
}
private static void helper(char[] s, int left, int right) {
if (left >= right) {
return;
}
char temp = s[left];
s[left] = s[right];
s[right] = temp;
helper(s, left + 1, right - 1);
}
public static void main(String[] args) {
char[] s = { 'h', 'e', 'l', 'l', 'o' };
reverseString(s);
System.out.println(new String(s)); // olleh
}
}Each part here has one clear job. Let us walk through them slowly.
One detail deserves a second look. We split the work across two methods, and that is on purpose.
This code reads cleanly and shows the pairs plainly. That is its strength. The weakness is the stack of calls, which we remove later.
Let us trace s = [‘h’, ‘e’, ‘l’, ‘l’, ‘o’]. Watch each call swap one outer pair, then hand the middle onward.
| Call | left | right | left >= right? | Swap done | array after |
|---|---|---|---|---|---|
| 1 | 0 | 4 | No | swap s[0] and s[4] | o e l l h |
| 2 | 1 | 3 | No | swap s[1] and s[3] | o l l e h |
| 3 | 2 | 2 | Yes | none, base case | o l l e h |
Call three finds left equal to right, so it stops without swapping. The middle letter l never moves, which is exactly right for an odd length.
Three calls finished the whole thing. Let us break that down.
Now look at the hidden cost. Each call sits on the call stack until the ones below it finish.
Recursion swaps each pair once, so the work is linear in the array length.
Our five characters stacked three calls. Grow the input to a million characters and the stack can grow to half a million frames. That memory cost is the pain point we solve next.
The recursion works, but the call stack bothers us. So here is a flatter idea that many beginners find easy to picture. Make a second array, and copy the characters into it in reverse order.
Then copy that reversed helper back into the original array. It uses a plain loop instead of recursion, so there is no growing call stack. The trade is a second array of the same size.
n = length of s
copy = new array of size n
for i from 0 to n-1: // fill copy in reverse
copy[i] = s[n - 1 - i]
for i from 0 to n-1: // pour copy back into s
s[i] = copy[i]This time there is no recursion, so read it as two plain loops. The idea is to write the reversed word onto a fresh sheet, then copy that sheet back over the original. Think of rewriting a word backwards on scrap paper, then tracing it back over the top.
First, look at the two setup lines. We store the length in n, so we do not keep asking for it. Then we make a second array called copy, the same size as s.
Now the first loop, which is the heart of the trick. It walks a counter i from the start to the end of copy. For each i, it pulls one character from the far side of the original.
So as i counts up, the read position counts down. The first slot of copy gets the last character of s. The second slot gets the second to last. By the end, copy holds the whole word backwards.
Let us picture it on hello. When i is 0, copy[0] takes s[4], which is o. When i is 1, copy[1] takes s[3], which is l. Keep going and copy becomes o, l, l, e, h. That is the reversed word sitting in the helper.
After the first loop, the answer exists, but it lives in copy, not in s. The caller only ever looks at s. So we are not done yet.
That is what the second loop fixes. It walks i across every slot again and copies each character from copy straight back into s.
So the plan is two simple loops: fill a helper array backwards, then pour that helper back into the original.
public class ReverseStringExtra {
public static void reverseString(char[] s) {
int n = s.length;
char[] copy = new char[n];
for (int i = 0; i < n; i++) {
copy[i] = s[n - 1 - i];
}
for (int i = 0; i < n; i++) {
s[i] = copy[i];
}
}
public static void main(String[] args) {
char[] s = { 'h', 'e', 'l', 'l', 'o' };
reverseString(s);
System.out.println(new String(s)); // olleh
}
}The shape is simple, so let us focus on why each piece exists.
Compare this with the recursion. The growing call stack is gone, replaced by two flat loops.
Same word. Watch copy fill from the back of s during the first loop.
| Step | i | reads s[n-1-i] | character | copy after this step |
|---|---|---|---|---|
| 1 | 0 | s[4] | o | o |
| 2 | 1 | s[3] | l | o l |
| 3 | 2 | s[2] | l | o l l |
| 4 | 3 | s[1] | e | o l l e |
| 5 | 4 | s[0] | h | o l l e h |
After the first loop, copy holds o, l, l, e, h. The second loop then pours those five characters back into s, leaving s reversed.
Five steps filled the helper, then a copy-back finished it. Let us look closely.
Notice the memory story. We built one whole second array to hold the answer.
| 💡 Interview Insight A sharp follow-up asks whether you even need the second loop. If your language lets you return a new array, you could stop after building copy. But this problem demands in-place editing, so the copy-back is what makes the change visible to the caller. |
Both versions above use extra memory, a call stack or a second array. So what is left to improve?
We can reverse the array in one flat loop, with no extra array and no recursion. We keep two pointers, one at each end, and swap the characters they point at. Then we step both pointers inward and repeat.
Think of it as folding the array onto itself. The outer pair swaps first, then the next pair, until the two pointers meet in the middle.
left = 0
right = last index
while left < right:
swap s[left] and s[right] // swap this outer pair
left = left + 1 // step left inward
right = right - 1 // step right inwardThis is the tightest version, so let us take it gently. There is one loop, no recursion, and no helper array. Everything happens on the original array. The magic is a pair of pointers that close in from the two ends.
Before the loop, we set left to 0 and right to the last index. So left sits on the first character and right sits on the last character.
Now the while loop drives everything. It keeps running as long as left is still to the left of right. The moment they meet or cross, the loop stops.
Inside the loop, we do just one action: swap the character at left with the character at right. That puts one outer pair into its reversed place.
Right after the swap, we move both pointers. We push left one step to the right, and pull right one step to the left. That points them at the next inner pair.
Here is the part beginners should sit with. The two pointers always stay balanced around the centre. Left climbs up while right climbs down, so they cover equal distance from each side.
Let us walk it on hello. At the start left is 0 and right is 4, so we swap h and o. Then left becomes 1 and right becomes 3, so we swap e and l. Now left becomes 2 and right becomes 2, so left is not less than right, and the loop stops. The middle l never moved.
One last worry: what about an even-length array, with no single middle? Then left and right cross without landing on the same slot, and the loop still stops correctly. Every character has a partner, so the balanced pointers handle both cases.
So the plan is one clean sweep: swap the outer pair, step both pointers inward, and repeat until they meet.
public class ReverseStringTwoPointer {
public static void reverseString(char[] s) {
int left = 0;
int right = s.length - 1;
while (left < right) {
char temp = s[left];
s[left] = s[right];
s[right] = temp;
left++;
right--;
}
}
public static void main(String[] args) {
char[] s = { 'h', 'e', 'l', 'l', 'o' };
reverseString(s);
System.out.println(new String(s)); // olleh
}
}This version does the most thinking in the fewest lines, so let us go gently.
Two details separate this from the earlier versions.
Because each pair is swapped exactly once, one sweep is enough. That single idea is what makes this solution shine.
Now we trace [‘h’, ‘e’, ‘l’, ‘l’, ‘o’] one final time. Watch the pointers swap and close inward.
| Step | left | right | left < right? | Swap done | array after |
|---|---|---|---|---|---|
| 1 | 0 | 4 | Yes | swap s[0] and s[4] | o e l l h |
| 2 | 1 | 3 | Yes | swap s[1] and s[3] | o l l e h |
| 3 | 2 | 2 | No | loop stops | o l l e h |
Legend: left scans from the start, right scans from the end, swapping each outer pair.
Three steps end the whole thing. Let us look at each one closely.
Step 1 is worth studying. The swap did two useful things at once.
There is a quiet promise hidden in this loop. Every pair gets swapped exactly once, and the middle of an odd array is simply left alone. You never need a second pass to tidy up.
Same word, same answer, three very different amounts of work and memory.
| Approach | Passes over array | Extra memory used |
|---|---|---|
| Recursion | One, split across calls | A call stack up to n/2 deep |
| Extra array | Two flat loops | A second array of size n |
| Two-pointer swap | One flat loop | Two int pointers |
The gap grows with size. On five characters the difference looks small. On five million it becomes the difference between a huge stack or copy and barely any extra memory at all.
| 💡 Interview Insight Expect this question: why not use recursion, since it is elegant? Because deep recursion risks a stack overflow on very long arrays. The two-pointer loop uses constant extra space, so it stays safe no matter how long the input grows. |
Tables are precise, but a sketch often lands faster. Here is the same two-pointer swap drawn by hand.

The array sits along the top with its indices. Each step block on the left shows the pair we swap and where the pointers sit. The panel on the right shows the array after that step.
Follow the colour cues as you read it.
Some people read pictures faster than tables. Use whichever version clicks for you, or sketch your own on paper while you follow the code.
All three give a correct answer. They just pay different prices for it.
| Approach | Time | Space | Note |
|---|---|---|---|
| Recursion | O(n) | O(n) | Clear, but grows the call stack |
| Extra array | O(n) | O(n) | Flat loops, but copies the array |
| Two-pointer swap | O(n) | O(1) | In place, single pass, expected answer |
Notice that all three run in linear time. The real story here is space, not speed. The first two waste memory, while the two-pointer swap carries just two variables.
In an interview, you can mention recursion to show range. Then explain the memory it costs. Finally settle on the two-pointer swap as the clean answer.
That climb from heavier to lean is exactly the story interviewers want to hear. Jumping straight to the optimal answer skips the reasoning they came to observe.
| 💡 Interview Insight If asked to reverse only part of the array, mention that two pointers adapt easily. You just start left and right at the chosen bounds instead of the ends. The same swap loop then reverses only that stretch. |
A few small traps catch beginners here. Keep them in mind.
Run those last three cases through your code before you say you are finished. They catch more bugs than any normal input will.
A: Put one pointer at the start and one at the end of the character array. Swap the two characters they point at, using a temp variable, then move the left pointer forward and the right pointer backward. Repeat until the pointers meet. This reverses the array without any second array.
A: Recursion adds a frame to the call stack for every pair, so a very long array can cause a stack overflow. The two-pointer loop uses only a couple of variables, so it runs in O(1) extra space and stays safe no matter how long the input is.
A: Nothing. The loop condition is left less than right, so it stops just before the two pointers land on the same middle slot. That single centre character is already in its correct place, so it stays put.
A: The two-pointer swap runs in O(n) time because each character is touched once, and O(1) space because it uses only two integer pointers. Recursion and the extra-array method are also O(n) time but O(n) space, from the call stack or the second array.
Reverse String in Java looks like a warm-up, and in a way it is. But the two-pointer, in-place pattern inside it is a real skill.
Our five-character trace showed the payoff clearly. Recursion grew a call stack, and the extra-array version copied the whole word. The two-pointer swap needed just two variables and a single sweep.
So take the pattern, not just the answer. Reach for extra memory only when you truly must. The moment you can swap in place, a left pointer and a right pointer often do the whole job.
That habit turned extra memory into none here. It will do the same for many array and string problems waiting further down the list.