345. Reverse Vowels of a String
Problem Description
You are given a string s
. Your task is to reverse only the vowels in the string while keeping all other characters in their original positions.
The vowels are the letters 'a'
, 'e'
, 'i'
, 'o'
, and 'u'
. These vowels can appear in both lowercase and uppercase forms ('A'
, 'E'
, 'I'
, 'O'
, 'U'
), and they may appear multiple times throughout the string.
To clarify what "reversing only the vowels" means:
- First, identify all positions in the string where vowels appear
- Then, reverse the order of just those vowels
- Keep all consonants and other characters in their original positions
For example:
- If the input is
"hello"
, the vowels are'e'
at position 1 and'o'
at position 4 - After reversing only the vowels, we get
"holle"
(the'o'
and'e'
have swapped positions) - The consonants
'h'
,'l'
,'l'
remain in their original positions
The function should return the modified string with only the vowels reversed.
Intuition
When we need to reverse only specific elements (vowels) while keeping others (consonants) in place, we can think about how we would do this manually. If we were to reverse vowels on paper, we would:
- Find the first vowel from the left
- Find the first vowel from the right
- Swap these two vowels
- Continue this process moving inward
This naturally leads us to a two-pointer approach. We can use one pointer starting from the beginning of the string and another from the end, moving them toward each other.
The key insight is that we only care about swapping vowels with other vowels. So when our left pointer encounters a consonant, we skip it and move forward. Similarly, when our right pointer encounters a consonant, we skip it and move backward. Only when both pointers are pointing at vowels do we perform a swap.
Why does this work? Because reversing means the first vowel should go to the last vowel's position, the second vowel should go to the second-to-last vowel's position, and so on. By using two pointers moving toward each other and swapping whenever both point to vowels, we achieve exactly this pattern.
The process continues until the two pointers meet or cross each other, at which point all vowels have been properly reversed. Since we're only swapping vowels with vowels and leaving consonants untouched, all non-vowel characters remain in their original positions as required.
Solution Approach
The solution implements the two-pointer technique we identified in our intuition. Here's how the algorithm works step by step:
Initial Setup:
- Create a string
vowels = "aeiouAEIOU"
containing all vowel characters (both lowercase and uppercase) for quick lookup - Initialize two pointers:
i = 0
(left pointer) andj = len(s) - 1
(right pointer) - Convert the string to a list
cs = list(s)
since strings are immutable in Python and we need to swap characters
Main Algorithm:
The algorithm uses a while loop that continues as long as i < j
:
-
Find the next vowel from the left:
while i < j and cs[i] not in vowels: i += 1
This inner loop moves the left pointer
i
forward until it finds a vowel or meets the right pointer. -
Find the next vowel from the right:
while i < j and cs[j] not in vowels: j -= 1
This inner loop moves the right pointer
j
backward until it finds a vowel or meets the left pointer. -
Swap the vowels:
if i < j: cs[i], cs[j] = cs[j], cs[i] i, j = i + 1, j - 1
After both pointers are positioned at vowels (and haven't crossed), we swap the characters at positions
i
andj
. Then we move both pointers inward (i
moves right,j
moves left) to continue the process.
Final Step:
- Convert the list back to a string using
"".join(cs)
and return it
The time complexity is O(n)
where n
is the length of the string, as each character is visited at most twice (once by each pointer). The space complexity is O(n)
for storing the character list, though the algorithm itself only uses O(1)
extra space for the pointers and variables.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution using the string s = "IceCreAm"
.
Initial Setup:
- Convert string to list:
cs = ['I', 'c', 'e', 'C', 'r', 'e', 'A', 'm']
- Set pointers:
i = 0
(at 'I'),j = 7
(at 'm') - Vowels to check:
"aeiouAEIOU"
Step 1:
- Left pointer
i = 0
is at 'I' - this is a vowel, so stop - Right pointer
j = 7
is at 'm' - not a vowel, move left j = 6
is at 'A' - this is a vowel, so stop- Both pointers at vowels: swap 'I' and 'A'
- Result:
['A', 'c', 'e', 'C', 'r', 'e', 'I', 'm']
- Move pointers:
i = 1
,j = 5
Step 2:
- Left pointer
i = 1
is at 'c' - not a vowel, move right i = 2
is at 'e' - this is a vowel, so stop- Right pointer
j = 5
is at 'e' - this is a vowel, so stop - Both pointers at vowels: swap 'e' and 'e' (no visible change)
- Result:
['A', 'c', 'e', 'C', 'r', 'e', 'I', 'm']
- Move pointers:
i = 3
,j = 4
Step 3:
- Left pointer
i = 3
is at 'C' - not a vowel, move right i = 4
is at 'r' - not a vowel, move right- Now
i = 5
which is not less thanj = 4
, so stop
Final Result:
- Convert list back to string:
"AceCreIm"
- The vowels 'I', 'e', 'e', 'A' from the original string have been reversed to 'A', 'e', 'e', 'I'
- All consonants ('c', 'C', 'r', 'm') remain in their original positions
Solution Implementation
1class Solution:
2 def reverseVowels(self, s: str) -> str:
3 """
4 Reverses only the vowels in a string.
5
6 Args:
7 s: Input string to process
8
9 Returns:
10 String with vowels reversed while other characters remain in place
11 """
12 # Define all vowels (both lowercase and uppercase)
13 vowels = "aeiouAEIOU"
14
15 # Initialize two pointers at the start and end of the string
16 left = 0
17 right = len(s) - 1
18
19 # Convert string to list for in-place modification
20 char_list = list(s)
21
22 # Use two-pointer technique to find and swap vowels
23 while left < right:
24 # Move left pointer forward until a vowel is found
25 while left < right and char_list[left] not in vowels:
26 left += 1
27
28 # Move right pointer backward until a vowel is found
29 while left < right and char_list[right] not in vowels:
30 right -= 1
31
32 # Swap vowels if both pointers are at valid positions
33 if left < right:
34 char_list[left], char_list[right] = char_list[right], char_list[left]
35 left += 1
36 right -= 1
37
38 # Convert list back to string and return
39 return "".join(char_list)
40
1class Solution {
2 public String reverseVowels(String s) {
3 // Create a boolean array to mark vowels (using ASCII values as indices)
4 // Size 128 covers all standard ASCII characters
5 boolean[] isVowel = new boolean[128];
6
7 // Mark all vowels (both lowercase and uppercase) as true in the array
8 for (char vowel : "aeiouAEIOU".toCharArray()) {
9 isVowel[vowel] = true;
10 }
11
12 // Convert string to character array for in-place modification
13 char[] characters = s.toCharArray();
14
15 // Initialize two pointers: left starts at beginning, right starts at end
16 int left = 0;
17 int right = characters.length - 1;
18
19 // Continue until the two pointers meet
20 while (left < right) {
21 // Move left pointer forward until a vowel is found
22 while (left < right && !isVowel[characters[left]]) {
23 left++;
24 }
25
26 // Move right pointer backward until a vowel is found
27 while (left < right && !isVowel[characters[right]]) {
28 right--;
29 }
30
31 // If pointers haven't crossed, swap the vowels
32 if (left < right) {
33 char temp = characters[left];
34 characters[left] = characters[right];
35 characters[right] = temp;
36
37 // Move both pointers inward for next iteration
38 left++;
39 right--;
40 }
41 }
42
43 // Convert character array back to string and return
44 return String.valueOf(characters);
45 }
46}
47
1class Solution {
2public:
3 string reverseVowels(string s) {
4 // Create a lookup table for vowels (ASCII table size)
5 // Using array for O(1) lookup time
6 bool isVowel[128];
7 memset(isVowel, false, sizeof(isVowel));
8
9 // Mark all vowels (both lowercase and uppercase) in the lookup table
10 for (char c : "aeiouAEIOU") {
11 isVowel[c] = true;
12 }
13
14 // Initialize two pointers: left starting from beginning, right from end
15 int left = 0;
16 int right = s.size() - 1;
17
18 // Use two-pointer technique to swap vowels
19 while (left < right) {
20 // Move left pointer forward until a vowel is found
21 while (left < right && !isVowel[s[left]]) {
22 ++left;
23 }
24
25 // Move right pointer backward until a vowel is found
26 while (left < right && !isVowel[s[right]]) {
27 --right;
28 }
29
30 // If both pointers point to vowels, swap them and move both pointers
31 if (left < right) {
32 swap(s[left], s[right]);
33 ++left;
34 --right;
35 }
36 }
37
38 return s;
39 }
40};
41
1/**
2 * Reverses only the vowels in a given string
3 * @param s - The input string to process
4 * @returns A new string with vowels reversed in their positions
5 */
6function reverseVowels(s: string): string {
7 // Create a set of vowels for O(1) lookup time
8 const vowelSet: Set<string> = new Set(['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U']);
9
10 // Convert string to character array for in-place swapping
11 const charArray: string[] = s.split('');
12
13 // Initialize two pointers: left starting from beginning, right from end
14 let leftPointer: number = 0;
15 let rightPointer: number = charArray.length - 1;
16
17 // Continue until the two pointers meet
18 while (leftPointer < rightPointer) {
19 // Move left pointer forward until a vowel is found
20 while (leftPointer < rightPointer && !vowelSet.has(charArray[leftPointer])) {
21 leftPointer++;
22 }
23
24 // Move right pointer backward until a vowel is found
25 while (leftPointer < rightPointer && !vowelSet.has(charArray[rightPointer])) {
26 rightPointer--;
27 }
28
29 // Swap the vowels at left and right positions
30 if (leftPointer < rightPointer) {
31 [charArray[leftPointer], charArray[rightPointer]] = [charArray[rightPointer], charArray[leftPointer]];
32 leftPointer++;
33 rightPointer--;
34 }
35 }
36
37 // Join the character array back into a string
38 return charArray.join('');
39}
40
Time and Space Complexity
Time Complexity: O(n)
, where n
is the length of the string s
. The algorithm uses two pointers that traverse the string from both ends toward the middle. Each character is visited at most twice - once by each pointer during the scanning phase. The inner while loops collectively examine each character at most once as the pointers move inward, and the swap operation takes constant time. Therefore, the overall time complexity is linear.
Space Complexity: O(n)
, where n
is the length of the string. The algorithm converts the input string to a list of characters (cs = list(s)
), which requires O(n)
additional space. The vowels string is a constant with fixed size, contributing O(1)
space. The two pointer variables i
and j
also use O(1)
space. Therefore, the dominant space requirement is the character list, making the total space complexity O(n)
.
Note: The reference answer mentions O(|ฮฃ|)
where ฮฃ
is the size of the character set, which would apply if we were using a set or hash table to store all possible vowels dynamically. However, in this implementation, the vowels are stored as a fixed string constant, and the main space consumption comes from converting the string to a mutable list.
Common Pitfalls
1. Forgetting to Handle Both Uppercase and Lowercase Vowels
One of the most common mistakes is only checking for lowercase vowels ('a', 'e', 'i', 'o', 'u'
) and forgetting that vowels can also appear in uppercase ('A', 'E', 'I', 'O', 'U'
).
Incorrect Implementation:
vowels = "aeiou" # Missing uppercase vowels!
Consequence:
If the input is "Hello"
, the uppercase 'H'
won't be recognized as a consonant issue, but more importantly, if we have "HEllo"
, the 'E'
won't be recognized as a vowel and won't be swapped.
Solution: Always include both cases in your vowel set:
vowels = "aeiouAEIOU"
# Or alternatively:
vowels = set("aeiouAEIOU") # Using a set for O(1) lookup
2. Attempting to Modify String Directly
Strings in Python are immutable, so trying to swap characters directly in the string will result in an error.
Incorrect Implementation:
def reverseVowels(self, s: str) -> str:
left, right = 0, len(s) - 1
while left < right:
# This will cause TypeError: 'str' object does not support item assignment
s[left], s[right] = s[right], s[left]
Solution: Convert the string to a mutable data structure (list) first:
char_list = list(s)
# Perform swaps on char_list
# Then convert back: return "".join(char_list)
3. Missing the Pointer Movement After Swap
After swapping two vowels, forgetting to move both pointers can lead to an infinite loop or incorrect results.
Incorrect Implementation:
while left < right: while left < right and char_list[left] not in vowels: left += 1 while left < right and char_list[right] not in vowels: right -= 1 if left < right: char_list[left], char_list[right] = char_list[right], char_list[left] # Forgot to move pointers! This creates an infinite loop
Solution: Always remember to advance both pointers after a successful swap:
if left < right: char_list[left], char_list[right] = char_list[right], char_list[left] left += 1 right -= 1
4. Incorrect Loop Boundary Checks
Not properly checking left < right
in all necessary places can lead to index out of bounds errors or incorrect swaps.
Incorrect Implementation:
while left < right: # Missing boundary check in inner loops while char_list[left] not in vowels: # Can go out of bounds! left += 1 while char_list[right] not in vowels: # Can go out of bounds! right -= 1
Solution: Include boundary checks in all loop conditions:
while left < right and char_list[left] not in vowels: left += 1 while left < right and char_list[right] not in vowels: right -= 1
What data structure does Breadth-first search typically uses to store intermediate states?
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!