151. Reverse Words in a String
Problem Description
You are given a string s
that contains words separated by spaces. Your task is to reverse the order of these words and return them as a single string.
A word is defined as any sequence of characters that doesn't contain spaces. Words in the input string are separated by one or more spaces.
The key requirements are:
- Extract all words from the input string
- Reverse their order (last word becomes first, second-to-last becomes second, etc.)
- Join them back together with exactly one space between each word
- Remove any extra spaces (leading, trailing, or multiple spaces between words)
For example:
- Input:
" hello world "
β Output:"world hello"
- Input:
"the sky is blue"
β Output:"blue is sky the"
- Input:
" Bob Loves Alice "
β Output:"Alice Loves Bob"
The solution uses two pointers to identify word boundaries:
- Pointer
i
skips over spaces to find the start of each word - Pointer
j
moves from the start of a word to find where it ends (next space or end of string) - Each identified word
s[i:j]
is added to a list - After collecting all words, the list is reversed using
words[::-1]
- Finally, the reversed words are joined with single spaces using
" ".join()
This approach handles all edge cases including multiple consecutive spaces, leading/trailing spaces, and ensures the output has clean formatting with single spaces between words.
Intuition
The most straightforward way to reverse words is to first extract all the words, then reverse their order. Since we need to handle irregular spacing (multiple spaces, leading/trailing spaces), we can't simply use split()
and join()
blindly - we need more control over the parsing process.
The key insight is that we need to identify where each word starts and ends. We can think of this as a state machine with two states:
- In a space region - we're currently looking at spaces and need to skip them
- In a word region - we're currently inside a word and need to capture it
This naturally leads to a two-pointer approach:
- Use pointer
i
to track our current position in the string - When we encounter spaces, keep moving
i
forward until we find a non-space character (or reach the end) - When we find the start of a word at position
i
, use another pointerj
to find where this word ends - Extract the substring
s[i:j]
as a complete word
By collecting words in order as we scan from left to right, we get a list like ["the", "sky", "is", "blue"]
. To reverse the word order, we can simply reverse this list to get ["blue", "is", "sky", "the"]
using Python's slice notation words[::-1]
.
The final step is reconstruction. Since we've already extracted clean words without any spaces, joining them with a single space " ".join()
automatically gives us the properly formatted output without any extra spaces.
This approach is intuitive because it breaks down the problem into three clear phases: parse (extract words), transform (reverse order), and reconstruct (join with single spaces).
Solution Approach
The solution implements a two-pointer technique to parse the string and extract words while handling irregular spacing. Let's walk through the implementation step by step:
1. Initialize data structures:
- Create an empty list
words
to store extracted words - Set two pointers:
i = 0
(current position) andn = len(s)
(string length)
2. Main parsing loop:
The outer while i < n
loop continues until we've processed the entire string.
3. Skip spaces:
while i < n and s[i] == " ": i += 1
This inner loop moves pointer i
forward through any consecutive spaces. It handles leading spaces, trailing spaces, and multiple spaces between words.
4. Extract word:
if i < n: # Check if we haven't reached the end j = i # Mark the start of the word while j < n and s[j] != " ": j += 1 # Find the end of the word words.append(s[i:j]) # Extract and store the word i = j # Move i to where j stopped
Once we find a non-space character at position i
, we:
- Use pointer
j
starting fromi
to find where the word ends - The word spans from index
i
toj-1
(sinces[j]
is either a space or we've reached the end) - Extract the substring
s[i:j]
and add it to our words list - Update
i
toj
to continue processing from where we left off
5. Reverse and join:
return " ".join(words[::-1])
words[::-1]
creates a reversed copy of the words list using Python's slice notation" ".join()
concatenates all words with exactly one space between them
Time Complexity: O(n)
where n
is the length of the input string. We traverse the string once to extract words, and the join operation is also linear.
Space Complexity: O(n)
for storing the words list and the output string.
This approach elegantly handles all edge cases: strings with only spaces return an empty string, and any combination of leading, trailing, or multiple spaces between words is properly normalized to single spaces in the output.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's trace through the solution with the input string " the sky is blue "
.
Initial Setup:
s = " the sky is blue "
words = []
(empty list to collect words)i = 0
,n = 21
(string length)
First Iteration:
- Skip leading spaces:
i
moves from 0 β 1 β 2 (skipping two spaces) - Found non-space at position 2 ('t')
- Set
j = 2
, find word end:j
moves 2 β 3 β 4 β 5 (stops at space) - Extract word:
s[2:5] = "the"
, add to words:words = ["the"]
- Update
i = 5
Second Iteration:
- Skip space at position 5:
i
moves 5 β 6 - Found non-space at position 6 ('s')
- Set
j = 6
, find word end:j
moves 6 β 7 β 8 β 9 (stops at space) - Extract word:
s[6:9] = "sky"
, add to words:words = ["the", "sky"]
- Update
i = 9
Third Iteration:
- Skip spaces at positions 9-10:
i
moves 9 β 10 β 11 - Found non-space at position 11 ('i')
- Set
j = 11
, find word end:j
moves 11 β 12 β 13 (stops at space) - Extract word:
s[11:13] = "is"
, add to words:words = ["the", "sky", "is"]
- Update
i = 13
Fourth Iteration:
- Skip spaces at positions 13-15:
i
moves 13 β 14 β 15 β 16 - Found non-space at position 16 ('b')
- Set
j = 16
, find word end:j
moves 16 β 17 β 18 β 19 β 20 (stops at space) - Extract word:
s[16:20] = "blue"
, add to words:words = ["the", "sky", "is", "blue"]
- Update
i = 20
Fifth Iteration:
- Skip trailing space:
i
moves 20 β 21 - Now
i = 21 = n
, exit main loop
Final Steps:
- Reverse the words list:
words[::-1] = ["blue", "is", "sky", "the"]
- Join with single spaces:
" ".join(["blue", "is", "sky", "the"]) = "blue is sky the"
Output: "blue is sky the"
Notice how the solution naturally handles the irregular spacing (2 leading spaces, 1 space after "the", 2 spaces after "sky", 3 spaces after "is", and 1 trailing space) and produces a clean output with exactly one space between words.
Solution Implementation
1class Solution:
2 def reverseWords(self, s: str) -> str:
3 """
4 Reverse the order of words in a string.
5
6 Args:
7 s: Input string containing words separated by spaces
8
9 Returns:
10 String with words in reversed order, separated by single spaces
11 """
12 words = []
13 i = 0
14 n = len(s)
15
16 # Iterate through the string to extract words
17 while i < n:
18 # Skip leading spaces
19 while i < n and s[i] == " ":
20 i += 1
21
22 # Check if we've reached a word
23 if i < n:
24 # Mark the start of the word
25 word_start = i
26
27 # Find the end of the current word
28 while i < n and s[i] != " ":
29 i += 1
30
31 # Extract and store the word
32 words.append(s[word_start:i])
33
34 # Reverse the list of words and join them with single spaces
35 return " ".join(words[::-1])
36
1class Solution {
2 public String reverseWords(String s) {
3 // List to store individual words extracted from the string
4 List<String> wordList = new ArrayList<>();
5 int length = s.length();
6
7 // Iterate through the entire string
8 for (int index = 0; index < length;) {
9 // Skip leading spaces
10 while (index < length && s.charAt(index) == ' ') {
11 index++;
12 }
13
14 // If we haven't reached the end of string after skipping spaces
15 if (index < length) {
16 // Build the current word
17 StringBuilder currentWord = new StringBuilder();
18 int wordStartIndex = index;
19
20 // Keep appending characters until we hit a space or end of string
21 while (wordStartIndex < length && s.charAt(wordStartIndex) != ' ') {
22 currentWord.append(s.charAt(wordStartIndex));
23 wordStartIndex++;
24 }
25
26 // Add the completed word to our list
27 wordList.add(currentWord.toString());
28
29 // Move the main index to where we stopped
30 index = wordStartIndex;
31 }
32 }
33
34 // Reverse the order of words in the list
35 Collections.reverse(wordList);
36
37 // Join all words with a single space and return
38 return String.join(" ", wordList);
39 }
40}
41
1class Solution {
2public:
3 string reverseWords(string s) {
4 int readIndex = 0; // Index for reading characters from original string
5 int writeIndex = 0; // Index for writing characters to compressed string
6 int stringLength = s.size();
7
8 // Process the entire string
9 while (readIndex < stringLength) {
10 // Skip leading spaces for current word
11 while (readIndex < stringLength && s[readIndex] == ' ') {
12 ++readIndex;
13 }
14
15 // Process a word if we haven't reached the end
16 if (readIndex < stringLength) {
17 // Add a space separator between words (except before the first word)
18 if (writeIndex != 0) {
19 s[writeIndex++] = ' ';
20 }
21
22 // Mark the start position of current word
23 int wordStartIndex = readIndex;
24
25 // Copy the current word to the write position
26 while (wordStartIndex < stringLength && s[wordStartIndex] != ' ') {
27 s[writeIndex++] = s[wordStartIndex++];
28 }
29
30 // Reverse the current word in-place
31 // Calculate word boundaries: start = writeIndex - wordLength, end = writeIndex
32 int wordLength = wordStartIndex - readIndex;
33 reverse(s.begin() + writeIndex - wordLength, s.begin() + writeIndex);
34
35 // Move read pointer to the next position after current word
36 readIndex = wordStartIndex;
37 }
38 }
39
40 // Remove extra characters after compression
41 s.erase(s.begin() + writeIndex, s.end());
42
43 // Reverse the entire string to get the final result
44 reverse(s.begin(), s.end());
45
46 return s;
47 }
48};
49
1/**
2 * Reverses the order of words in a string while handling multiple spaces
3 * @param s - Input string containing words separated by spaces
4 * @returns String with words in reversed order, separated by single spaces
5 */
6function reverseWords(s: string): string {
7 // Array to store individual words extracted from the string
8 const words: string[] = [];
9
10 // Get the total length of the input string
11 const stringLength: number = s.length;
12
13 // Index pointer to traverse through the string
14 let currentIndex: number = 0;
15
16 // Process the entire string character by character
17 while (currentIndex < stringLength) {
18 // Skip leading spaces before each word
19 while (currentIndex < stringLength && s[currentIndex] === ' ') {
20 currentIndex++;
21 }
22
23 // Check if we haven't reached the end after skipping spaces
24 if (currentIndex < stringLength) {
25 // Mark the start of the current word
26 let wordEndIndex: number = currentIndex;
27
28 // Find the end of the current word (until we hit a space or string end)
29 while (wordEndIndex < stringLength && s[wordEndIndex] !== ' ') {
30 wordEndIndex++;
31 }
32
33 // Extract the word and add it to the words array
34 words.push(s.slice(currentIndex, wordEndIndex));
35
36 // Move the current index to continue processing after this word
37 currentIndex = wordEndIndex;
38 }
39 }
40
41 // Reverse the array of words and join them with single spaces
42 return words.reverse().join(' ');
43}
44
Time and Space Complexity
Time Complexity: O(n)
The algorithm traverses the string once using two pointers (i
and j
). The outer while loop moves through the entire string, and for each position, we either:
- Skip spaces:
while i < n and s[i] == " "
- Extract a word:
while j < n and s[j] != " "
Each character in the string is visited at most twice (once by i
and once by j
), making the traversal O(n)
. The string slicing operation s[i:j]
takes O(k)
time where k
is the length of the word, but since the total length of all words combined cannot exceed n
, the overall slicing operations sum to O(n)
. The final " ".join(words[::-1])
operation takes O(n)
time (O(n)
to reverse the list and O(n)
to join the words). Therefore, the total time complexity is O(n) + O(n) + O(n) = O(n)
.
Space Complexity: O(n)
The algorithm uses additional space for:
- The
words
list which stores all the words from the input string. In the worst case (when the string has no extra spaces), this stores approximatelyn
characters worth of data:O(n)
- The reversed list
words[::-1]
creates a new list:O(n)
- The final joined string which is the return value:
O(n)
Therefore, the total space complexity is O(n)
.
Common Pitfalls
1. Using split()
without arguments vs. split(" ")
with space
A frequent mistake is using split(" ")
with an explicit space character instead of split()
without arguments.
Incorrect approach:
def reverseWords(self, s: str) -> str:
words = s.split(" ") # Wrong!
return " ".join(words[::-1])
Why it fails:
split(" ")
splits on single spaces only, creating empty strings when multiple spaces are encountered- For input
" hello world "
, it produces:['', '', 'hello', '', '', 'world', '', '']
- The output becomes
" world hello "
instead of"world hello"
Correct approach:
def reverseWords(self, s: str) -> str:
words = s.split() # Correct!
return " ".join(words[::-1])
Why it works:
split()
without arguments splits on any whitespace and automatically removes empty strings- It handles multiple spaces, tabs, newlines, and strips leading/trailing whitespace
- For the same input, it produces:
['hello', 'world']
2. Forgetting to update the pointer after finding a word
When manually parsing with two pointers, forgetting to update the main pointer can cause infinite loops.
Incorrect approach:
while i < n: # Skip spaces while i < n and s[i] == " ": i += 1 if i < n: j = i while j < n and s[j] != " ": j += 1 words.append(s[i:j]) # Forgot to update i = j, causing infinite loop!
Solution: Always update the main pointer after extracting a word:
words.append(s[i:j]) i = j # Critical: move i to where j stopped
3. Not checking bounds before extracting words
Attempting to extract a word without verifying that we haven't reached the string's end.
Incorrect approach:
while i < n: # Skip spaces while i < n and s[i] == " ": i += 1 # Dangerous: i might equal n after skipping spaces j = i while j < n and s[j] != " ": # s[i] could be out of bounds j += 1 words.append(s[i:j]) # Might append empty string
Solution: Add a bounds check before processing a word:
if i < n: # Ensure we have characters left to process j = i while j < n and s[j] != " ": j += 1 words.append(s[i:j])
4. Using string concatenation instead of list for building result
Building the result by concatenating strings is inefficient and error-prone.
Inefficient approach:
result = ""
for i in range(len(words) - 1, -1, -1):
if result:
result += " " + words[i] # O(nΒ²) due to string immutability
else:
result = words[i]
Better approach:
return " ".join(words[::-1]) # O(n) and cleaner
The join method is more efficient because it calculates the total size needed and allocates memory once, rather than creating new string objects repeatedly.
Which of the following array represent a max heap?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!