Reverse String in Java DSA: Two Pointers That Swap in Place

  • Last Updated: July 26, 2026
  • By: javahandson
  • Series
img

Reverse String in Java DSA: Two Pointers That Swap in Place

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.

1. Introduction

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.

2. Understanding the Problem

Let us fix the rules before we write any code. A clear problem statement now saves you from silent bugs later.

  • You get an array of characters, for example [‘h’, ‘e’, ‘l’, ‘l’, ‘o’].
  • Reverse the order of those characters.
  • Do it in place, so no second array is allowed.
  • You do not return anything. The caller reads the same array afterwards.
  • For that input the array becomes [‘o’, ‘l’, ‘l’, ‘e’, ‘h’].

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.

2.1 Why In-Place Matters Here

The problem could be solved by building a new array. But it asks for something stricter.

  • In place means you edit the array the caller already holds.
  • You use only a few helper variables, never a second full array.
  • So the extra memory stays tiny, even for a very long array.

This rule is the whole point of the exercise. It pushes you toward the neat two-pointer trick instead of an easy copy.

2.2 Why This Example Word

The word hello is short but not trivial. That keeps the trace honest.

  • It has five characters, so there is a clear middle letter.
  • That middle letter stays in place, which surprises some beginners.
  • The two l characters look the same, so we watch positions, not values.

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.

3. Concepts You Need Here

Two small ideas carry this whole problem. Both come back often in later array questions, so learn them well.

3.1 Swapping Two Values

A swap trades the contents of two slots. You cannot just copy one onto the other, or you lose a value.

  • First you save one value in a temporary variable called temp.
  • Then you copy the second value into the first slot.
  • Finally you copy the saved temp into the second slot.

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.

3.2 The Two-Pointer Idea

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.

  • The left pointer moves forward, from the start toward the middle.
  • The right pointer moves backward, from the end toward the middle.
  • They meet or cross in the centre, and that is when the work is done.

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.

4. Approach 1: Recursion

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.

4.1 Pseudocode

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 part

4.2 Reading the Pseudocode

Let 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.

  • The question is: has left reached or passed right?
  • When left is greater than or equal to right, there is no pair left to swap.
  • So the method returns at once, and this branch of the recursion ends.

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.

  • So the next call works on a slightly smaller slice of the array.
  • Its own outer pair is the pair we have not swapped yet.
  • Each call shrinks the slice, until left and right finally meet.

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.

4.3 Java Code

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
    }
}

4.4 Reading the Java Code

Each part here has one clear job. Let us walk through them slowly.

  • Line 3 is the public method the caller uses, taking just the array.
  • Inside it, line 4 kicks off the work by calling helper with left as 0 and right as the last index.
  • Next, line 7 declares the helper, which carries the array plus the two positions.
  • Lines 8 to 10 hold the base case, returning early once left meets or passes right.
  • Lines 11 to 13 swap the characters at left and right using temp.
  • Line 14 calls helper again, moving both positions one step inward.

One detail deserves a second look. We split the work across two methods, and that is on purpose.

  • The public method on line 3 keeps a simple shape, matching what the problem asks for.
  • The helper on line 7 carries the extra positions that recursion needs.
  • So the caller never has to pass in left and right themselves.

This code reads cleanly and shows the pairs plainly. That is its strength. The weakness is the stack of calls, which we remove later.

4.5 Dry Run of the Recursion

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.

4.6 Reading the Dry Run

Three calls finished the whole thing. Let us break that down.

  • Call one swapped the outer pair h and o, the two far ends.
  • Then call two swapped the next pair e and l, just inside the ends.
  • Finally call three hit the base case at the centre, so it swapped nothing.

Now look at the hidden cost. Each call sits on the call stack until the ones below it finish.

  • For five characters we stacked three calls at the deepest point.
  • For a long array that stack grows with the array length.
  • That stack is the extra memory the final approach avoids.

4.7 Time and Space Cost

Recursion swaps each pair once, so the work is linear in the array length.

  • Time is O(n), because each character is touched once across all calls.
  • Space is O(n), since the call stack can grow as deep as half the array.

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.

5. Approach 2: Copy Backwards into a Helper Array

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.

5.1 Pseudocode

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]

5.2 Reading the Pseudocode

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.

  • The read position is n minus 1 minus i, which is the mirror of i.
  • When i is 0, that read position is the last index of s.
  • When i is 1, it is the second to last index, and so on.

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.

  • Every pass moves one character from the helper into the original.
  • It repeats for all n slots, so nothing is left behind.
  • When it finishes, s itself holds the reversed word.

So the plan is two simple loops: fill a helper array backwards, then pour that helper back into the original.

5.3 Java Code

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
    }
}

5.4 Reading the Java Code

The shape is simple, so let us focus on why each piece exists.

  • Line 4 saves the array length in n, so the loops read cleanly.
  • Line 5 creates the helper array copy, the same size as s.
  • The loop on lines 7 to 9 fills copy in reverse, reading from the far side of s.
  • Line 8 does the mirror read, taking s at position n minus 1 minus i.
  • The loop on lines 11 to 13 pours copy back into s, slot by slot.

Compare this with the recursion. The growing call stack is gone, replaced by two flat loops.

  • Recursion asked, can a smaller copy of me reverse the inner part?
  • This version flips it and asks, where does each character land in a fresh array?
  • That question needs a second array, which is the memory we still pay for.

5.5 Dry Run of the Extra-Array Approach

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.

5.6 Reading the Dry Run

Five steps filled the helper, then a copy-back finished it. Let us look closely.

  • Step 1 read the last character o and placed it first in copy.
  • Steps 2 to 5 kept reading backwards, building o, l, l, e, h.
  • The second loop then copied that reversed word back into s.

Notice the memory story. We built one whole second array to hold the answer.

  • The copy array is as long as the input, doubling the memory used.
  • For five characters that is fine, but long inputs pay for a full second array.
  • The problem asked us to avoid exactly this, which is why we improve it.
💡 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.

6. Approach 3: The Two-Pointer In-Place Swap

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.

6.1 Pseudocode

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 inward

6.2 Reading the Pseudocode

This 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.

  • So on the first pass, the first and last characters trade places.
  • On the next pass, the second and second-to-last trade places.
  • Each pass fixes one more pair, working from the outside in.

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.

6.3 Java Code

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
    }
}

6.4 Reading the Java Code Line by Line

This version does the most thinking in the fewest lines, so let us go gently.

  • Line 4 sets left to 0, the first slot to swap.
  • Line 5 sets right to the last index, the length minus one.
  • The while loop on line 7 runs while left stays to the left of right.
  • Lines 8 to 10 swap the characters at left and right using temp.
  • Lines 11 and 12 step left forward and right backward after the swap.

Two details separate this from the earlier versions.

  • There is no second array, so the extra memory drops to a couple of variables.
  • There is no recursion, so nothing piles up on the call stack.

Because each pair is swapped exactly once, one sweep is enough. That single idea is what makes this solution shine.

6.5 Dry Run of the Two-Pointer Swap

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.

6.6 Reading the Dry Run

Three steps end the whole thing. Let us look at each one closely.

  • Step 1 swapped the outer pair h and o, so the ends traded places.
  • Then step 2 swapped the inner pair e and l, closing the pointers further.
  • Finally step 3 found left equal to right, so the loop stopped before the centre.

Step 1 is worth studying. The swap did two useful things at once.

  • It moved o to the front, its final reversed position.
  • At the same time it carried h to the back, also its final spot.
  • So every swap places two characters, not just one.

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.

6.7 Comparing the Three Traces

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.

7. The Dry Run on Paper

Tables are precise, but a sketch often lands faster. Here is the same two-pointer swap drawn by hand.

Pen and paper dry run of the Reverse String two-pointer approach in Java DSA

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.

  • A gold box marks the first swap, where the two ends trade places.
  • The green box marks the moment the pointers cross and the loop ends.
  • Down at the bottom, the finished array reads E, D, O, C.

Some people read pictures faster than tables. Use whichever version clicks for you, or sketch your own on paper while you follow the code.

8. Comparing the Three Approaches

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.

9. Common Mistakes and Edge Cases

A few small traps catch beginners here. Keep them in mind.

  • Skipping the temp variable is the most common slip. Then one character overwrites the other and both slots end up the same.
  • Using left <= right instead of left < right makes an odd array swap the middle with itself, which wastes a step.
  • An empty array should stay empty. The loop never runs, which is correct.
  • A single-character array should stay unchanged, since left already equals right.
  • A two-character array should just swap once, then stop.

Run those last three cases through your code before you say you are finished. They catch more bugs than any normal input will.

10. Interview Questions

Q: How do you reverse a string in place in Java?

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.

Q: Why use two pointers instead of recursion to reverse a string?

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.

Q: What happens to the middle character when reversing an odd-length string?

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.

Q: What is the time and space complexity of reversing a string in Java?

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.

11. Conclusion

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.

12. Further Reading

  • LeetCode Reverse String problem — practice the original problem and submit your own solution.
  • Oracle Java arrays tutorial — the official guide to declaring and using arrays like the char array
  • Previous in this series: Valid Palindrome in Java — check a string from both ends with two pointers.
  • Next in this series: 3Sum in Java — find triplets that sum to zero using sorting and two pointers: https://javahandson.com/3sum-in-java/

Leave a Comment