1. Problem Statement
The Reverse Words in a String problem asks you to take an input string s and reverse the order of its words.
A word is defined as a sequence of non-space characters. The input string may contain leading or trailing spaces, or multiple spaces between two words. The returned string must:
- Have the words in reverse order.
- Be separated by exactly one space.
- Contain no leading or trailing spaces.
2. Examples
Example 1
Input: s = "the sky is blue" Output: "blue is sky the"
Example 2
Input: s = " hello world " Output: "world hello" Explanation: The reversed string should not contain leading or trailing spaces.
Example 3
Input: s = "a good example" Output: "example good a" Explanation: Multiple spaces between "good" and "example" are reduced to a single space in the reversed string.
3. Brute Force Approach
Intuition
The simplest way to solve this is to extract all the words from the string, put them into a list, reverse the list, and then join them back together into a single string separated by spaces.
Data Structures Used
std::stringstream: To automatically tokenize the string and handle multiple spaces.std::vector<std::string>: To store the extracted words.
Algorithm
- Create a
stringstream from the input string s. - Extract words one by one using the
>> operator, which automatically skips multiple spaces. - Push each word into a
vector. - Reverse the
vector. - Iterate through the reversed vector and append each word to a
result string, adding a space between them. - Return the
result (make sure to trim the final trailing space).
Step-by-step Dry Run
Input: " hello world "
- Stream Extraction:
- Extract 1:
"hello" -> Push to vector. - Extract 2:
"world" -> Push to vector.
- Vector state:
["hello", "world"] - Reverse vector:
["world", "hello"] - Join: Result string becomes
"world ", then "world hello ". Trim last space -> "world hello".
#include <string>
#include <vector>
#include <sstream>
using namespace std;
class SolutionBrute {
public:
string reverseWords(string s) {
stringstream ss(s);
string word;
vector<string> words;
while (ss >> word) {
words.push_back(word);
}
string result = "";
for (int i = words.size() - 1; i >= 0; i--) {
result += words[i];
if (i != 0) result += " ";
}
return result;
}
};
Time Complexity
- O(N) where N is the length of the string. Iterating through the string to extract words takes linear time. Reversing and joining also takes linear time.
Space Complexity
- O(N) extra space to store the parsed words in the vector.
Why this approach is inefficient
While clean and fast enough for most practical applications, it uses O(N) extra memory. In an interview, the interviewer will ask: "Can you optimize the space complexity?" (Especially since C++ strings are mutable, unlike Java or Python strings).
4. Better Approach
Intuition
Instead of using a vector and reversing it, we can use a Stack. A stack follows the LIFO (Last-In-First-Out) principle, which is exactly what we need to reverse the order of elements. We read the string word by word, push each word onto the stack, and then pop them off to build the final string.
Data Structures Used
std::stack<std::string>: To naturally reverse the order of the words.
Algorithm
- Traverse the string, collecting characters into a temporary
word variable. - When a space is encountered (and
word is not empty), push word to the stack and clear the word variable. - Push the last remaining word to the stack.
- Pop words from the stack one by one, appending them to a
result string separated by a space. - Pop the last trailing space.
Detailed Dry Run
Input: "a good example"
- Traverse: find
"a", hit space -> Stack: ["a"] - Traverse: find
"good", hit spaces -> Stack: ["a", "good"] - Traverse: find
"example", end -> Stack: ["a", "good", "example"] - Pop 1:
"example" -> Result: "example " - Pop 2:
"good" -> Result: "example good " - Pop 3:
"a" -> Result: "example good a " - Trim last space ->
"example good a"
C++ Implementation
#include <string>
#include <stack>
using namespace std;
class SolutionBetter {
public:
string reverseWords(string s) {
stack<string> st;
string word = "";
for (char c : s) {
if (c == ' ') {
if (!word.empty()) {
st.push(word);
word = "";
}
} else {
word += c;
}
}
if (!word.empty()) st.push(word);
string result = "";
while (!st.empty()) {
result += st.top();
st.pop();
if (!st.empty()) result += " ";
}
return result;
}
};
Time Complexity
- O(N) as we iterate through the string once to build the stack, and once to empty it.
Space Complexity
- O(N) to hold the strings inside the stack.
Advantages over brute force
It removes the need to use std::reverse. The LIFO property handles the reversal organically.
Remaining bottlenecks
We are still allocating O(N) extra memory for the stack and new string. In C++, strings are essentially arrays of characters that can be modified in-place.
5. Optimal Approach
The Optimal approach solves this in O(1) auxiliary space using Two Pointers and In-place Reversal.
Core Intuition
If you reverse the entire string, the words will end up in the correct reversed order, but each individual word will be spelled backward. To fix this, you simply traverse the newly reversed string and reverse each individual word back to normal.
Finally, we can use two pointers to compact the string, shifting characters left to overwrite any duplicate or leading spaces, just like the "Remove Element" array problem.
Example: "the sky"
- Reverse whole string:
"yks eht" - Reverse each word:
"sky the"
Why Two Pointers are used
Since there might be multiple spaces, we need a read pointer (i) to scan the original text and a write pointer (right) to place the valid characters at the front of the string.
Explain every data structure before coding
std::string s: Passed by value from LeetCode (so we modify the copy), but we operate entirely within this allocated memory block. No external arrays or stacks.left, right: Integers acting as pointers to mark the boundaries of a word.i: Integer pointer to traverse the string and skip spaces.
6. Algorithm
- Reverse the entire string using
std::reverse(s.begin(), s.end()). - Initialize pointers:
left = 0, right = 0, and i = 0. - Loop
i through the string to find words:- Skip Spaces: While
s[i] == ' ', increment i. - Add Word Spacing: If
right > 0 (meaning this is not the first word), add a single space s[right] = ' ' and increment right. - Mark Word Start: Set
left = right. - Copy Word: While
i < length and s[i] != ' ', copy characters using s[right++] = s[i++]. - Reverse Individual Word: Reverse the characters between
left and right.
- Resize: Since we shifted words to the front and removed extra spaces, the valid string ends at index
right. Call s.resize(right) to chop off the garbage characters at the end.
7. Complete C++ Code
#include <string>
#include <algorithm>
using namespace std;
class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(), s.end());
int n = s.size();
int left = 0, right = 0;
int i = 0;
while (i < n) {
while (i < n && s[i] == ' ') i++;
if (i == n) break;
if (right > 0) s[right++] = ' ';
left = right;
while (i < n && s[i] != ' ') {
s[right++] = s[i++];
}
reverse(s.begin() + left, s.begin() + right);
}
s.resize(right);
return s;
}
};
8. Dry Run
Input: s = " a good "
Step 1: Reverse Entire String
s = " doog a "
Step 2: Traverse, Shift, and Reverse Words
Initialize left=0, right=0, i=0.
| State / Action |
String State |
Right Pointer |
Left Pointer |
i |
Explanation |
| Character is ' '. Skip. |
" doog a " |
0 |
0 |
i=0 |
Initial space skipped. |
| Found 'd'. Not first word? No (right==0). |
" doog a " |
0 |
0 |
i=1 |
Start copying first word. |
| Copy word "doog" to front. |
"doogog a " |
4 |
0 |
i=1..4 |
Word placed at beginning. |
| Reverse word (0ā4) |
"goodog a " |
4 |
0 |
i=4 |
Fix letters inside word. |
| Spaces. Skip. |
"goodog a " |
4 |
0 |
i=5..6 |
Skip multiple spaces. |
| Found 'a'. Not first word? Yes (right=4). Add space. |
"good g a " |
5 |
0 |
i=7 |
Insert space before next word. |
| Copy word "a". left=5 |
"good a a " |
6 |
5 |
i=7 |
Word copied after space. |
| Reverse word (5ā6) |
"good a a " |
6 |
5 |
i=7 |
Single letter word stays same. |
| Spaces. Skip. End of string. |
"good a a " |
6 |
5 |
i=8..9 |
Reached end of input. |
Step 3: Resize
right is 6. s.resize(6) chops off indices 6 onwards. Final Output: "good a"
9. Complexity Analysis
| Operation |
Time Complexity |
Space Complexity |
| Reverse entire string |
O(N) |
O(1) |
| Shift/Copy characters |
O(N) |
O(1) |
| Reverse individual words |
O(N) |
O(1) |
| Total |
O(N) |
O(1) auxiliary |
Why it is O(1) Space: We do not allocate any new strings, arrays, or stacks. We only use three integer variables (i, left, right) and manipulate the existing memory array s in place
Visualisation
11. Interview Tips
- C++ specific advantage: If an interviewer asks you this question, mention immediately: "Since strings are mutable in C++, we can solve this in O(1) extra space. In Java or Python, strings are immutable, so an O(N) array allocation is mandatory." Interviewers love this language-specific insight.
- Common mistakes: Forgetting to place exactly one space between words during the two-pointer compaction, or forgetting to
resize() the string at the end resulting in garbage characters. - Why split the reversal? Trying to reverse words while also shifting them left simultaneously is prone to off-by-one errors. Doing a global reverse first makes the logic linear and much easier to code without bugs under interview pressure.
code link:
https://leetcode.com/problems/reverse-words-in-a-string/description/
video reference: