Editorial
Core insight
Reverse Vowels of a String · Two Pointers
Core Insight for Reverse Vowels of a String
The key intuition is that the first vowel in the string must be swapped with the last vowel, the second vowel with the second-to-last vowel, and so on. We do not need to touch the consonants.
Instead of collecting vowels and re-inserting them, we can identify pairs of vowels to swap dynamically. We establish two markers (pointers): one at the beginning of the string and one at the end.
Visual Description: Imagine the string indices as a fixed horizontal array.
- A
leftpointer initializes at index0. - A
rightpointer initializes at indexN-1. - The
leftpointer advances to the right, skipping consonants, until it lands on a vowel. - Simultaneously, the
rightpointer moves to the left, skipping consonants, until it lands on a vowel. - Once both pointers are stationary on vowels, the characters at these two indices are swapped.
- After the swap, both pointers move one step inward to continue the search.
- This process repeats until
leftcrossesright.
This approach ensures we only visit each character once, achieving a single-pass solution.
Deep dive
Full breakdown
Full reasoning, pitfalls, and implementation flow
LeetCode 345: Reverse Vowels of a String Solution & Explanation
Problem Overview
TL;DR: Use two pointers starting at opposite ends of the string to locate and swap vowels, moving inward until they meet.
The problem asks us to transform a given string s by reversing the order of the vowels present within it, while keeping all non-vowel characters (consonants, numbers, symbols) in their original positions. Vowels are defined as 'a', 'e', 'i', 'o', 'u' and their uppercase counterparts. This is a classic string manipulation task often seen in interviews, and the LeetCode 345 solution requires an efficient traversal method to handle strings up to 300,000 characters.
Brute Force Approach for Reverse Vowels of a String
A naive approach to solving Reverse Vowels of a String involves two distinct passes over the data and additional storage.
- First Pass: Iterate through the entire string from left to right. Whenever a vowel is encountered, store it in a temporary list or stack.
- Reverse: Reverse the temporary list of vowels.
- Second Pass: Iterate through the original string again. If the current character is a vowel, replace it with the next vowel from the reversed list. If it is a consonant, append the original character.
- Reconstruct: Build the final result string from these characters.
Pseudo-code:
function reverseVowels(s):
vowels = []
for char in s:
if isVowel(char):
vowels.push(char)
reverse(vowels)
result = ""
vowelIndex = 0
for char in s:
if isVowel(char):
result.append(vowels[vowelIndex])
vowelIndex++
else:
result.append(char)
return resultComplexity Analysis:
- Time Complexity: , where is the length of the string. We iterate through the string twice.
- Space Complexity: . We store all vowels in a separate list. In the worst case (a string of all vowels), this duplicates the entire string.
Why it fails (or is suboptimal): While this approach does not strictly fail (it usually passes within time limits), it is suboptimal for an interview setting. It requires two passes over the data and explicitly allocates auxiliary space for the vowel list. The optimal solution should aim to perform the operation in a single pass with extra space (logic-wise), modifying the character array in place where the language permits.
Algorithm Strategy: Two Pointer Patterns
- Initialization: Convert the input string into a mutable character array (if the language uses immutable strings like Java or Python). Initialize two pointers:
leftat0andrightats.length - 1. - Define Vowels: Create a set or a helper function to check if a character is a vowel (a, e, i, o, u, A, E, I, O, U) in time.
- Traversal Loop: Enter a
whileloop that continues as long asleft < right.- Find Left Vowel: Increment
leftuntils[left]is a vowel orleft >= right. - Find Right Vowel: Decrement
rightuntils[right]is a vowel orleft >= right. - Swap: If
left < right, swap the characters ats[left]ands[right]. - Advance: Increment
leftand decrementrightto move past the swapped characters.
- Find Left Vowel: Increment
- Finalize: Convert the character array back to a string and return it.
Execution Flow
Consider the input s = "leetcode".
- Start:
left = 0('l'),right = 7('e'). - Move Left:
- 'l' is not a vowel.
leftbecomes 1 ('e'). - 'e' is a vowel. Stop.
- 'l' is not a vowel.
- Move Right:
- 'e' is a vowel. Stop.
- Swap:
left(1) <right(7). Swaps[1]ands[7].- String state: "leotcede" (swapped 'e' with 'e', effectively no change visually, but logically swapped).
- Advance:
leftbecomes 2,rightbecomes 6. - Move Left:
s[2]is 'o' (vowel). Stop.
- Move Right:
s[6]is 'd' (not vowel).rightbecomes 5.s[5]is 'e' (vowel). Stop.
- Swap:
left(2) <right(5). Swaps[2]('o') ands[5]('e').- String state: "leotcede".
- Advance:
leftbecomes 3,rightbecomes 4. - Move Left:
s[3]is 't' (not vowel).leftbecomes 4. - Termination:
left(4) is not strictly less thanright(4). Loop ends. - Return: "leotcede".
Proof of Correctness
The algorithm maintains the invariant that all vowels to the left of the left pointer and all vowels to the right of the right pointer have been correctly placed in their final positions relative to the reversed vowel sequence.
Since left only increments and right only decrements, and they move towards each other, the algorithm is guaranteed to terminate when they meet. At the meeting point, all vowels from the left side have been swapped with their counterparts from the right side. The consonants remain untouched, preserving their original indices. Thus, the output satisfies the problem constraints.
Pattern Reuse Notes
The Two Pointers - String Reversal pattern is highly versatile. Understanding how to manipulate indices from the ends inward applies directly to these problems:
- LeetCode 151: Reverse Words in a String - Uses two pointers to reverse the order of words, often requiring an initial full string reversal.
- LeetCode 344: Reverse String - The most basic form of this pattern, swapping every character from the outside in.
- LeetCode 541: Reverse String II - Applies the reversal logic to specific segments (every characters) of the string.
Feynman coach · Before you peek at the code
Can you explain the core insight in your own words?
If you can explain it before reading code, you’re far more likely to recall it under interview pressure.
Reference Implementation
C++ Solution for LeetCode 345
1#include <string>
2#include <vector>
3
4class Solution {
5public:
6 // Helper function to identify vowels
7 bool isVowel(char c) {
8 c = tolower(c);
9 return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
10 }
11
12 string reverseVowels(string s) {
13 int left = 0;
14 int right = s.length() - 1;
15
16 while (left < right) {
17 // Move left pointer until a vowel is found
18 while (left < right && !isVowel(s[left])) {
19 left++;
20 }
21
22 // Move right pointer until a vowel is found
23 while (left < right && !isVowel(s[right])) {
24 right--;
25 }
26
27 // Swap the vowels
28 if (left < right) {
29 std::swap(s[left], s[right]);
30 left++;
31 right--;
32 }
33 }
34
35 return s;
36 }
37};Java Solution for LeetCode 345
1class Solution {
2 public String reverseVowels(String s) {
3 char[] chars = s.toCharArray();
4 int left = 0;
5 int right = s.length() - 1;
6
7 while (left < right) {
8 // Move left pointer forward until a vowel is found
9 while (left < right && !isVowel(chars[left])) {
10 left++;
11 }
12
13 // Move right pointer backward until a vowel is found
14 while (left < right && !isVowel(chars[right])) {
15 right--;
16 }
17
18 // Swap if pointers haven't crossed
19 if (left < right) {
20 char temp = chars[left];
21 chars[left] = chars[right];
22 chars[right] = temp;
23
24 left++;
25 right--;
26 }
27 }
28
29 return new String(chars);
30 }
31
32 private boolean isVowel(char c) {
33 c = Character.toLowerCase(c);
34 return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
35 }
36}Python Solution for LeetCode 345
1class Solution:
2 def reverseVowels(self, s: str) -> str:
3 # Convert to list because Python strings are immutable
4 s_list = list(s)
5 left = 0
6 right = len(s) - 1
7
8 # Define vowels set for O(1) lookup
9 vowels = set("aeiouAEIOU")
10
11 while left < right:
12 # Move left pointer
13 while left < right and s_list[left] not in vowels:
14 left += 1
15
16 # Move right pointer
17 while left < right and s_list[right] not in vowels:
18 right -= 1
19
20 # Swap
21 if left < right:
22 s_list[left], s_list[right] = s_list[right], s_list[left]
23 left += 1
24 right -= 1
25
26 return "".join(s_list)