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

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

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

Reverse String in Java DSA made simple. Learn recursion, an extra-array version, and the optimal in-place two-pointer swap with dry runs and clean code.

1. Introduction

Reverse String in Java is a classic warm-up for two-pointer thinking. You get an array of characters, and you flip it end to end. The catch is that you must do it in place, inside the same array.

The task looks tiny, yet it teaches a habit you will reuse everywhere. Instead of building a fresh array, you walk two pointers toward the middle and swap as you go.

We build the answer in three steps. Recursion swaps the outer pair, then calls itself on the inside. A helper-array version copies characters backward into a second array. The final two-pointer swap does the whole job in one flat loop with no extra array.

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’].

Notice one easy trap. The middle character of an odd-length word never moves. Only the pairs around it trade places.

2.1 Why In-Place Matters Here

In place means you edit the array the caller already holds. You do not build a copy and hand that back.

  • It uses only a few helper variables, never a second full array.
  • So the extra memory stays tiny, even for a very long array.
  • That is the exact win the final approach delivers.
💡 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 two values without losing either one. You always need a temporary holder.

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

3.2 The Two-Pointer Idea

Here is the idea that makes this problem click. You keep two pointers walking toward each other through the same array.

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

4. Approach 1: Recursion

The first idea many people reach for is recursion. Swap the two outer characters, then ask a smaller copy of the method to reverse the inside.

It reads elegantly, but it grows a call stack. Still, it is a good warm-up, and it proves you understand the goal.

4.1 Pseudocode

reverse(s):
    call helper(s, lower = 0, higher = last index)
 
helper(s, lower, higher):
    if lower >= higher:            // base case: nothing left to swap
        return
    swap s[lower] and s[higher]    // swap this outer pair
    helper(s, lower + 1, higher - 1)   // recurse on the inner part

4.2 Pseudocode Explained

  • The base case checks if lower has reached or passed higher; if so, there is no pair left, so the method returns.
  • Otherwise it swaps the outer pair, the characters at lower and higher.
  • Then it calls itself with lower moved in by one and higher moved in by one, shrinking the slice each time.

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 lower, int higher) {
        if (lower >= higher) {
            return;
        }
        char temp = s[lower];
        s[lower] = s[higher];
        s[higher] = temp;
        helper(s, lower + 1, higher - 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 Java Code Explained

  • 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 lower as 0 and higher 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 lower meets or passes higher.
  • Lines 11 to 13 swap the characters at lower and higher using temp.
  • Finally, line 14 calls helper again, moving both positions one step inward.

4.5 Dry Run of the Recursion

Let us trace s = [h, e, l, l, o]. Watch the slice shrink on every call.

Calllowerhigherlower >= higher?Swap donearray after
104Noswap s[0] and s[4]o e l l h
213Noswap s[1] and s[3]o l l e h
322Yesnone, base caseo l l e h

Legend: lower starts at the front, higher starts at the back, and each call moves both one step toward the centre.

4.6 Reading the Dry Run

Let us walk the whole trace, call by call, and see what each frame of the recursion does.

Call 1: lower = 0, higher = 4.

The first call gets the full array, with lower on the front slot and higher on the back slot. It checks the base case: is lower greater than or equal to higher? Here 0 is well below 4, so the answer is no, and the swap goes ahead.

  • It saves s[0], the h, into temp so nothing is lost.
  • Then it copies s[4], the o, into slot 0.
  • After that it drops temp, the h, into slot 4.

So the two far ends trade places, and the array reads o e l l h. The call then recurses with lower moved in to 1 and higher moved in to 3.

Call 2: lower = 1, higher = 3.

This deeper call works on a smaller slice, the middle three characters. Again it asks whether lower has reached higher. Since 1 is still below 3, it swaps the next pair.

  • The value at slot 1 is e, and the value at slot 3 is l.
  • Using temp, those two swap cleanly, just like before.
  • Now the array reads o l l e h, the fully reversed word.

The word is already correct here, but the recursion does not know that yet. It recurses once more, with lower stepping to 2 and higher stepping to 2.

Call 3: lower = 2, higher = 2.

This is the base case. Both pointers have landed on the same middle index, so lower is now equal to higher. The check lower >= higher is true, and the call returns at once without swapping anything.

  • The middle character l sits alone, so it has no partner to trade with.
  • That single return unwinds the whole stack, and the method finishes.

Now look at the hidden cost. Each call sits on the stack until the one below it returns.

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

4.7 Time and Space Cost

Recursion touches each character once, but every call adds a stack frame. So the time is linear while the space is not constant.

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

5. Approach 2: Copy Backwards into a Helper Array

The next idea drops recursion. You read the original from the back, and you write those characters, in order, into a fresh array.

It is flat and easy to follow. The weakness is that second array, which the problem told us to avoid.

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 Pseudocode Explained

  • The read position n minus 1 minus i is the mirror of i, so when i is 0 it points at the last index.
  • As i grows, that mirror position slides backward through s, one step at a time.
  • The second loop then pours copy back into s so the caller sees the reversed word.

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 Java Code Explained

  • Line 4 saves the array length in n, so the loops read cleanly.
  • Next, 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.
  • Inside it, line 8 does the mirror read, taking s at position n minus 1 minus i.
  • Finally, the loop on lines 11 to 13 pours copy back into s, slot by slot.

5.5 Dry Run of the Extra-Array Approach

Same word. Watch copy fill from the back of s during the first loop.

Stepi (write index)reads s[n-1-i]charactercopy after this step
10s[4]oo
21s[3]lo l
32s[2]lo l l
43s[1]eo l l e
54s[0]ho l l e h

5.6 Reading the Dry Run

Let us go step by step through the first loop, then follow the second loop that copies the result home.

Loop 1, step 1: write index 0.

The write index i starts at 0, and the mirror read position is n minus 1 minus 0, which is 4. So we read s[4], the last character.

  • The character at s[4] is o.
  • We place that o at copy[0], the very front of the helper.
  • After this step copy holds just o.

Loop 1, step 2: write index 1.

Now i is 1, so the mirror position is n minus 1 minus 1, which is 3. We read s[3].

  • The character at s[3] is l, and it lands at copy[1].
  • So copy grows to o l, still building backward from the original.

Loop 1, steps 3 to 5: write indexes 2, 3, 4.

Each later step slides the read position one place further left, filling copy left to right.

  • At i = 2 the mirror is s[2], another l, so copy becomes o l l.
  • At i = 3 the mirror is s[1], the e, so copy becomes o l l e.
  • By i = 4 the mirror is s[0], the h, so copy becomes o l l e h, the finished reversed word.

Loop 2: copy back into s.

The helper now holds the answer, but the caller only ever reads s. So a second flat loop pours copy back into s, slot by slot, from index 0 to the end. After it finishes, s itself reads o l l e h.

Now look at the hidden cost. The helper array is a whole second array sitting in memory.

  • It grows to the same size as the input before we are done.
  • 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

The two versions above both waste memory. Recursion grows the stack, and the helper builds a whole array. We can do better.

Keep one pointer at each end. Swap the pair they point at, then step both inward. When they meet, the array is reversed, and you never touched a second array.

6.1 Pseudocode

lower = 0
higher = last index
 
while lower < higher:
    swap s[lower] and s[higher]   // swap this outer pair
    lower = lower + 1             // step lower inward
    higher = higher - 1           // step higher inward

6.2 Pseudocode Explained

  • The loop runs only while lower stays to the left of higher, so it stops the moment they meet or cross.
  • On each pass it swaps the outer pair, then steps lower forward and higher backward.
  • So each pass fixes one more pair, working steadily from the outside in.

6.3 Java Code

public class ReverseStringTwoPointer {
 
    public static void reverseString(char[] s) {
        int lower = 0;
        int higher = s.length - 1;
 
        while (lower < higher) {
            char temp = s[lower];
            s[lower] = s[higher];
            s[higher] = temp;
            lower++;
            higher--;
        }
    }
 
    public static void main(String[] args) {
        char[] s = { 'h', 'e', 'l', 'l', 'o' };
        reverseString(s);
        System.out.println(new String(s)); // olleh
    }
}

6.4 Java Code Explained

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

6.5 Dry Run of the Two-Pointer Swap

Now we trace the same word one final time. Watch the two pointers close in on the centre.

Steplowerhigherlower < higher?Swap donearray after
104Yesswap s[0] and s[4]o e l l h
213Yesswap s[1] and s[3]o l l e h
322Noloop stopso l l e h

Legend: lower is the front pointer, higher is the back pointer, and both step one place inward after each swap.

6.6 Reading the Dry Run

Let us go step by step and watch the two pointers close in on the centre.

Step 1: lower = 0, higher = 4.

The loop checks its condition first: is lower less than higher? Here 0 is below 4, so the loop body runs and a swap happens.

  • The value at lower is h, and the value at higher is o.
  • temp saves the h, then o copies into slot 0, then the saved h drops into slot 4.
  • So the array becomes o e l l h, with the two ends traded.

This one swap did two useful things at once. It moved o to the front, its final reversed position, and it carried h to the back, also its final spot. After the swap, lower steps up to 1 and higher steps down to 3.

Step 2: lower = 1, higher = 3.

The condition runs again: 1 is still below 3, so we swap the next pair inward.

  • The value at lower is e, and the value at higher is l.
  • They trade through temp, so the array becomes o l l e h.
  • That is the fully reversed word, though the loop has one more check to make.

After this swap, lower steps up to 2 and higher steps down to 2. Both pointers now sit on the same middle index.

Step 3: lower = 2, higher = 2.

The loop checks its condition one last time: is lower less than higher? Now 2 is not less than 2, so the condition is false and the loop stops before doing anything.

  • The middle character l never needed to move, so skipping it is correct.
  • Because every swap placed two characters, two swaps were enough for five slots.

6.7 Comparing the Three Traces

Same word, same answer, three very different amounts of work and memory.

ApproachPasses over arrayExtra memory used
RecursionOne, split across callsA call stack up to n/2 deep
Extra arrayTwo flat loopsA second array of size n
Two-pointer swapOne flat loopTwo int pointers
💡 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 trace drawn by hand.

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 swap and the pointer move. The panel below 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.

8. Comparing the Three Approaches

All three give a correct answer. They just pay different prices for it.

ApproachTimeSpaceNote
RecursionO(n)O(n)Clear, but grows the call stack
Extra arrayO(n)O(n)Flat loops, but copies the array
Two-pointer swapO(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. Both the recursion and the helper array cost O(n) extra memory, while the two-pointer swap carries just two variables.

In an interview, start by naming the simple options. Then explain the memory each one wastes. Finally tighten it into the two-pointer swap, which is the answer they expect.

💡 Interview Insight
If asked to reverse only part of the array, mention that two pointers adapt easily. You just start lower and higher 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 lower <= higher instead of lower < higher 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 lower already equals higher.
  • 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 Java in place?

A: Use two pointers. Put one at the start (lower) and one at the end (higher), swap the characters they point to, then move lower forward and higher backward. Stop when lower is no longer less than higher. This edits the same char array, so it uses O(1) extra space.

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

A: It stays put. When the string has an odd length, lower and higher meet on the middle index, so the loop stops before swapping it with itself. Only the pairs around the centre trade places.

Q: Why is the two-pointer swap better than recursion here?

A: Both run in O(n) time, but recursion adds a stack frame per call, so it costs O(n) extra space and can overflow on very long inputs. The two-pointer loop carries just two integer variables, giving true O(1) space.

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

A: The optimal two-pointer approach is O(n) time and O(1) space. The recursion and extra-array versions are also O(n) time but O(n) space, since they grow a call stack or allocate a 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 stack, and the helper needed a second array of the same size. The two-pointer swap needed just two variables and a single sweep.

So take the pattern, not just the answer. Reach for a second array only when you truly must. The moment you can edit in place, a pair of pointers walking inward often does the whole job.

12. Further Reading

Leave a Comment