String Reversal Algorithms Compared
Reversing a string is one of the most common programming exercises — and one of the most deceptively complex. While the basic algorithm is straightforward, the optimal approach varies by language, string encoding, and whether you need in-place mutation. This guide compares the major algorithms and their trade-offs.
Iterative Reversal
The iterative approach swaps characters from both ends, moving toward the center. It runs in O(n) time with O(1) extra space (for mutable strings):
def reverse_iterative(s):
chars = list(s)
left, right = 0, len(chars) - 1
while left < right:
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1
return ''.join(chars)
// In-place reversal of a null-terminated C string
void reverse(char *s) {
int left = 0, right = strlen(s) - 1;
while (left < right) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
}
The iterative method is the most efficient general-purpose algorithm. It makes exactly ⌊n/2⌋ swaps and uses constant additional memory. For languages where strings are immutable (Python, Java, JavaScript), you pay O(n) to create the character array, but the swap logic itself is optimal.
Recursive Reversal
A recursive implementation reverses the outer characters and recurses on the inner substring:
def reverse_recursive(s):
if len(s) <= 1:
return s
return reverse_recursive(s[1:]) + s[0]
While elegant, this approach has serious practical drawbacks:
| Issue | Impact |
|---|---|
| Stack depth | A string of length n requires n stack frames; long strings cause stack overflow |
| Time complexity | Each concatenation creates a new string — O(n²) total in languages with immutable strings |
| Memory | O(n) stack space plus O(n²) intermediate allocations |
Tail-call optimization can mitigate the stack issue in some languages (Scheme, Erlang), but most mainstream languages — including Python, JavaScript, and Java — do not guarantee TCO. Recursive reversal is best treated as an academic exercise rather than a production technique.
Built-In and Functional Approaches
Most modern languages provide a built-in reverse or allow a concise functional expression:
# Python
reversed_s = s[::-1]
// JavaScript
const reversed = s.split('').reverse().join('');
// Rust — reverses in place on a mutable vector of chars
let mut chars: Vec<char> = s.chars().collect();
chars.reverse();
let reversed: String = chars.into_iter().collect();
-- Haskell
reverseString = reverse
Built-in functions delegate to optimized native code, typically using the same two-pointer swap internally. They should be your first choice in production code — they are faster, tested, and more readable than hand-rolled loops.
In-Place vs. Copy Reversal
Whether you can reverse a string in place depends on the language's string representation:
| Language | Mutable? | In-Place Possible? |
|---|---|---|
| C | Yes (char array) | Yes |
| C++ | std::string is mutable | Yes |
| Rust | No (owned String) | Via Vec<char> |
| Python | No | Must create new string |
| Java | No | Must create new string |
| JavaScript | No | Must create new string |
Attempting in-place reversal on an immutable string forces you to convert it to a mutable structure first, which itself costs O(n) space. In these languages, the space advantage of iterative reversal disappears, and built-in methods become the clear winner.
Reversing Words vs. Characters
A related problem is reversing the order of words while preserving each word's characters:
Input: "the sky is blue"
Output: "blue is sky the"
The standard approach: reverse the entire string, then reverse each word individually.
def reverse_words(s):
words = s.split()
return ' '.join(reversed(words))
For in-place C-style solutions, the two-step reverse avoids allocating extra memory:
// Step 1: reverse entire string
reverse(entire_string);
// Step 2: reverse each word individually
for each word in string:
reverse(word);
Key Takeaways
- Iterative two-pointer swap is the most efficient general algorithm: O(n) time, O(1) extra space for mutable strings.
- Recursive reversal is O(n²) in immutable-string languages and risks stack overflow — avoid in production.
- Use built-in reverse functions whenever available; they are optimized and well-tested.
- In-place reversal only saves memory for mutable string types (C, C++); in other languages, all approaches allocate O(n) anyway.
- Word-level reversal is a distinct problem best solved by splitting and rejoining, or by the two-step reverse method.
Try It Yourself
Want to experiment with string reversal across different inputs? Paste any text into the Text Reverser and see the reversed output instantly — including how it handles punctuation, numbers, and multi-line content.