Topics › Strings › Medium String Problems
Reverse every word in a string
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given a string s containing words separated by spaces. Return a string with the words in reverse order, joined by single spaces. The input may have leading spaces, trailing spaces, or several spaces between words; none of those may appear in the output. The letters inside each word keep their order.
Constraints: 1 ≤ |s| ≤ 10^4; s contains English letters, digits and spaces, and (in the LeetCode formulation) at least one word. The Java file additionally returns "" for an input that has no words at all.
Example 1
s = "this is an amazing program"
answer = "program amazing an is this"
Example 2
s = " hello world "
answer = "world hello" // leading and trailing spaces are dropped
Example 3
s = "a good example"
answer = "example good a" // a run of spaces becomes one space
Approach 1: Brute force, split, reverse, join
Intuition. The library can do most of the work: split(" ") cuts the string at every space, we throw away the empty pieces that runs of spaces produce, reverse the list and join with a single space. It is the shortest correct program and a fine first answer; the interviewer will then usually ask you to do it by hand.
Algorithm.
- Split
son" "and keep the non-empty pieces in a list. - Reverse the list.
- Join with
" ".
static String bruteForce(String s) {
List<String> words = new ArrayList<>();
for (String w : s.split(" ")) {
if (!w.isEmpty()) words.add(w);
}
Collections.reverse(words);
return String.join(" ", words);
}
Complexity. Time O(n): splitting, reversing and joining each touch every character or word once. Space O(n) for the pieces.
Two details of String.split matter here. Consecutive separators yield empty strings in the middle of the array ("a b" → ["a", "", "b"]), which is why the filter is needed, and trailing empty strings are discarded, so "a " → ["a"]. The empty input gives [""], which the filter removes, and join of an empty list is "".
Approach 2: Better, a stack of words
Intuition. Reversal is what a stack does for free: push the words as you meet them left to right, then pop them, and they come out right to left. Scanning by hand with two nested while loops, one skipping spaces and one consuming a word, replaces split and shows you understand the tokenising. A StringBuilder assembles the result with a space between consecutive words.
Algorithm.
- Set
i = 0. Whilei < n: skip spaces; rememberstart; advance over non-spaces; if the range is non-empty, pushs.substring(start, i). - Pop until the stack is empty, appending each word and a separating space when more words remain.
static String better(String s) {
Deque<String> stack = new ArrayDeque<>();
int n = s.length(), i = 0;
while (i < n) {
while (i < n && s.charAt(i) == ' ') i++; // skip separators
int start = i;
while (i < n && s.charAt(i) != ' ') i++; // consume one word
if (i > start) stack.push(s.substring(start, i));
}
StringBuilder sb = new StringBuilder(n);
while (!stack.isEmpty()) {
sb.append(stack.pop());
if (!stack.isEmpty()) sb.append(' ');
}
return sb.toString();
}
Complexity. Time O(n). Space O(n) for the stack of words.
Approach 3: Optimal, read the words from the right
Intuition. If we want the last word first, start reading at the end of the string. Skip trailing spaces, note where a word ends, walk left to find where it starts, and append it. Repeat until the left end is reached. Nothing is stored except the output, and each character is examined exactly once. In a language with mutable strings this would be in place; in Java the output buffer is the only allocation.
Algorithm.
- Set
j = n - 1. Whilej ≥ 0:- Move
jleft over spaces. Ifj < 0, stop. - Set
end = j; movejleft over non-spaces. The word iss[j+1..end]. - If the buffer is non-empty, append a space; append the word.
- Move
- Return the buffer.
static String optimal(String s) {
StringBuilder sb = new StringBuilder(s.length());
int j = s.length() - 1;
while (j >= 0) {
while (j >= 0 && s.charAt(j) == ' ') j--; // skip separators from the right
if (j < 0) break;
int end = j; // last letter of the word
while (j >= 0 && s.charAt(j) != ' ') j--; // j + 1 is now the first letter
if (sb.length() > 0) sb.append(' ');
sb.append(s, j + 1, end + 1);
}
return sb.toString();
}
Complexity. Time O(n): j only moves left. Space O(n) for the output only, O(1) otherwise.
sb.append(s, j + 1, end + 1) copies the character range directly into the builder without creating an intermediate String, which is why this approach does fewer allocations than the previous two.
Approach 4: The in-place variant, reverse twice
Intuition. The classic C solution works on the character array itself. Reversing the whole string puts the words in the right order but spells every word backwards; reversing each word individually then fixes the spelling. The extra spaces are handled by a preliminary “squeeze” that compacts the array with a write pointer, keeping one space after each word and finally dropping the trailing one. Java strings are immutable, so we copy to a char[] first; beyond that copy the method uses constant extra space.
Algorithm.
- Squeeze: walk a read index
r; copy letters to the write indexw; copy a space only if the previous written character is a letter. Afterwards remove a trailing space. - Reverse
a[0..w-1]. - Walk the cleaned array; at every space and at the end, reverse the word that just finished.
- Return
new String(a, 0, w).
static String optimalInPlace(String s) {
char[] a = s.toCharArray();
int n = a.length;
int w = 0; // 1. squeeze: write index for the cleaned string
for (int r = 0; r < n; r++) {
if (a[r] == ' ') {
if (w > 0 && a[w - 1] != ' ') a[w++] = ' '; // keep exactly one space after a word
} else {
a[w++] = a[r];
}
}
if (w > 0 && a[w - 1] == ' ') w--; // drop the separator left after the last word
reverse(a, 0, w - 1); // 2. reverse the whole cleaned string
int start = 0; // 3. reverse each word back to normal
for (int i = 0; i <= w; i++) {
if (i == w || a[i] == ' ') {
reverse(a, start, i - 1);
start = i + 1;
}
}
return new String(a, 0, w);
}
private static void reverse(char[] a, int i, int j) {
while (i < j) {
char tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
}
}
Dry run on s = " hello world ":
| step | array content (first w chars) |
w |
|---|---|---|
| after squeeze | hello world |
11 |
| after reversing all | dlrow olleh |
11 |
after reversing dlrow |
world olleh |
11 |
after reversing olleh |
world hello |
11 |
Complexity. Time O(n): three linear passes. Space O(n) for the char[] copy, O(1) beyond it.
Edge cases and pitfalls
- Empty string and spaces only.
""and" "both return"". In Approach 3 theif (j < 0) break;after skipping spaces is what prevents appending an empty word; in Approach 4 the squeeze leavesw = 0. - A single word.
"word"and" single "both return the word alone. - Single-letter words.
"F R I E N D S"→"S D N E I R F"; the word loops handle length-one words with no special case. - The trailing separator after squeezing. The squeeze writes a space after every word, including the last one when the input has trailing spaces. The
if (w > 0 && a[w - 1] == ' ') w--;line removes it; forgetting it yields a result with a leading space after the reversal. - Flushing the last word. In step 3 of Approach 4 the loop runs to
i == winclusive so that the final word, which is not followed by a space, is also reversed. - Only
' 'is a separator.trim()andsplit("\\s+")also treat tabs and newlines as whitespace. For this problem that is harmless, but if the specification says “space” precisely, scanning for' 'as the manual approaches do is the faithful choice. - Quadratic string building. Inserting each word at the front of a
StringBuilderwithinsert(0, word)shifts everything already there, givingO(n^2)in the worst case. Append in the correct order instead, as Approach 3 does.
Related problems
- Reverse words in a given string / Palindrome Check is the basic-section row that bundles this exact task with a palindrome check.
- Reverse an array is the two-pointer swap that Approach 4 applies twice.
- Rotate String is another problem where rearranging a string reduces to reversals.
- Largest Odd Number in a String also scans a string from the right to find where to cut.