easyleetcodeNot Started

Reverse Vowels of a String

A curated StudyFlow practice problem with comprehensive editorial and multi-language solutions.

Solve on leetcode
Lesson

Problem Statement

Given a string s, reverse only all the vowels in the string and return it.

The vowels are 'a', 'e', 'i', 'o', and 'u', and they can appear in both lower and upper cases, more than once.

Example 1 · Example:

Input: s = "IceCreAm"
Output: "AceCreIm"
Explanation: The vowels in s are [I, e, e, A]. On reversing the vowels, s becomes "AceCreIm".

Example 2 · Example:

Input: s = "leetcode"
Output: "leotcede"
Explanation: The vowels in s are [e, e, o, e]. On reversing the vowels, s becomes "leotcede".

Constraints:

  • 1 <= s.length <= 3 * 105
  • s consist of printable ASCII characters.

What You'll Learn

Key principles and algorithmic techniques you will master from this problem:

  • Apply two-pointer converging technique with selective predicates.
  • Handle case sensitivity for vowels.
  • Avoid unnecessary memory allocations.

Pattern Recognition Trigger

How to instantly recognize this algorithmic pattern when encountering unseen interview questions:

Signals to Look For:

  • Reversing a specific subset of characters within a string while preserving positions of others.
  • Symmetric inward scanning with predicate filter.

Algorithmic Decision Tree:

  • Two Pointers Converging
  • Selective Predicate Swapping

Connected Patterns:

Reverse StringValid PalindromeReverse Only Letters

1. Extract, Reverse, and Re-insert

Intuition

First pass: collect all vowels into a vector vowels. Second pass: traverse s, whenever a vowel is encountered, pop the last vowel from vowels and replace the character in s.

When to apply: Basic baseline comparison.

Algorithm

  1. Initialize vowels = [].
  2. For c in s: if isVowel(c): vowels.push(c).
  3. Initialize idx = vowels.length - 1.
  4. Create mutable string res.
  5. For i from 0 to s.length - 1:
  6. If isVowel(s[i]): res[i] = vowels[idx--].
  7. Else: res[i] = s[i].
  8. Return res.

Extract, Reverse, and Re-insert Trace

2 of 2 questions left today
Step 1 / 6
Trace timeline17% explored
InputINPUT nums = [h, e, l, l, o]
Current operationSTEP 1 OF 6
Observation: Scan string and collect all vowels ('a','e','i','o','u' case-insensitive)
Visual state
Values and pointers at this exact step
array
S
h
e
l
l
o
0
1
2
3
4
Variables at this step
Vowels
["e","o"]
Count
2
Check / comparison
Pass 1: Scan s = "hello" and extract all vowels in order: ['e', 'o'].
Decision / next transition
Extracted vowels list has length 2.
Why this step matters

Observation: Scan string and collect all vowels ('a','e','i','o','u' case-insensitive).

Decision: Extracted array is ['e', 'o'].

Invariant: Original vowel order captured.

Step 1 / 6
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3#include <vector>
4#include <unordered_set>
5
6class Solution {
7 bool isVowel(char c) {
8 c = tolower(c);
9 return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
10 }
11public:
12 std::string reverseVowels(std::string s) {
13 std::vector<char> vowels;
14 for (char c : s) {
15 if (isVowel(c)) vowels.push_back(c);
16 }
17
18 int idx = vowels.size() - 1;
19 for (char& c : s) {
20 if (isVowel(c)) {
21 c = vowels[idx--];
22 }
23 }
24 return s;
25 }
26};
27
28int main() {
29 Solution sol;
30 std::cout << sol.reverseVowels("IceCreAm") << std::endl; // AceCreIm
31 std::cout << sol.reverseVowels("leetcode") << std::endl; // leotcede
32 return 0;
33}

Time & Space Complexity

  • Time complexity: O(N) — Two passes over string; stores up to N vowels in array.
  • Space complexity: O(N)

Why Brute Force Doesn't Scale:

Two pointers skip consonants and swap vowels in-place.

Zero auxiliary memory overhead.


2. Two Pointers with Lookup Array

Intuition

Create a lookup table isVowel of size 128 (covering all ASCII characters). Initialize left = 0, right = s.length - 1. While left < right: advance left if !isVowel[s[left]], decrement right if !isVowel[s[right]]. If both are vowels, swap and advance both.

When to apply: Fast lookup table variant.

Algorithm

  1. Initialize boolean vowelMap[128] = false with true for a,e,i,o,u,A,E,I,O,U.
  2. Convert s to mutable array.
  3. Initialize left = 0, right = n - 1.
  4. While left < right:
  5. While left < right && !vowelMap[arr[left]]: left++.
  6. While left < right && !vowelMap[arr[right]]: right--.
  7. If left < right: swap(arr[left++], arr[right--]).
  8. Return joined string.

Two Pointers with Boolean Lookup Array Trace

2 of 2 questions left today
Step 1 / 6
Trace timeline17% explored
InputINPUT nums = [h, e, l, l, o]
Current operationSTEP 1 OF 6
Observation: Fast lookup table for vowels
Visual state
Values and pointers at this exact step
array
S
h
e
l
l
o
0
↑ L=0
1
2
3
4
↑ R=4
Variables at this step
L
1
R
4
Char L
'e'
Char R
'o'
Check / comparison
Pointers start at L=0 ('h') and R=4 ('o').
Decision / next transition
Advance L until vowel, decrement R until vowel.
Why this step matters

Observation: Fast lookup table for vowels. s[0]='h' is not a vowel -> advance L -> 1.

Decision: s[1]='e' is a vowel. s[4]='o' is a vowel.

Invariant: Both pointers locate vowels.

Step 1 / 6
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3
4class Solution {
5public:
6 std::string reverseVowels(std::string s) {
7 bool isVowel[128] = {false};
8 for (char c : "aeiouAEIOU") if (c) isVowel[(unsigned char)c] = true;
9
10 int left = 0, right = (int)s.size() - 1;
11 while (left < right) {
12 while (left < right && !isVowel[(unsigned char)s[left]]) left++;
13 while (left < right && !isVowel[(unsigned char)s[right]]) right--;
14
15 if (left < right) {
16 std::swap(s[left++], s[right--]);
17 }
18 }
19 return s;
20 }
21};
22
23int main() {
24 Solution sol;
25 std::cout << sol.reverseVowels("IceCreAm") << std::endl; // AceCreIm
26 return 0;
27}

Time & Space Complexity

  • Time complexity: O(N) — Each character is inspected at most twice.
  • Space complexity: O(N) for mutable string characters

3. Two Pointers Converging In-Place (Optimal)

Intuition

Initialize left = 0, right = s.length - 1. While left < right: 1. Advance left while left < right and s[left] is not a vowel. 2. Decrement right while left < right and s[right] is not a vowel. 3. Swap s[left] and s[right]. 4. Advance left++ and decrement right--. Return modified string.

When to apply: The standard gold standard solution.

Algorithm

  1. Initialize left = 0, right = s.length - 1.
  2. While left < right:
  3. While left < right && !isVowel(s[left]): left++.
  4. While left < right && !isVowel(s[right]): right--.
  5. If left < right: swap(s[left++], s[right--]).
  6. Return s.

Two-Pointer Inward Vowel Swap Convergence

2 of 2 questions left today
Step 1 / 6
Trace timeline17% explored
InputINPUT nums = [i, c, e, c, r, e, a, m]
Current operationSTEP 1 OF 6
Observation: s[0] = 'i' is a vowel
Visual state
Values and pointers at this exact step
array
S
i
c
e
c
r
e
a
m
0
↑ L=0 ('i')
1
2
3
4
5
6
↑ R=6 ('a')
7
Variables at this step
L Pointer
0 ('i')
R Pointer
6 ('a')
Action
Swap 'i' <-> 'a'
Check / comparison
s[0]='i' is vowel. s[7]='m' is consonant (R moves to 6 'a', vowel).
Decision / next transition
Both L and R point to vowels. Swap s[0] ('i') with s[6] ('a').
Why this step matters

Observation: s[0] = 'i' is a vowel. s[7] = 'm' is a consonant, so R decrements to index 6 ('a'), which is a vowel.

Decision: Both pointers point to vowels: swap s[0] (i) and s[6] (a).

Invariant: Outer-most vowel pair is swapped.

Step 1 / 6
C++ SOLUTIONCLEAN & OPTIMIZED
1#include <iostream>
2#include <string>
3
4class Solution {
5 bool isVowel(char c) {
6 char lower = tolower(c);
7 return lower == 'a' || lower == 'e' || lower == 'i' || lower == 'o' || lower == 'u';
8 }
9public:
10 std::string reverseVowels(std::string s) {
11 int left = 0, right = (int)s.size() - 1;
12 while (left < right) {
13 while (left < right && !isVowel(s[left])) left++;
14 while (left < right && !isVowel(s[right])) right--;
15
16 if (left < right) {
17 std::swap(s[left++], s[right--]);
18 }
19 }
20 return s;
21 }
22};
23
24int main() {
25 Solution sol;
26 std::cout << sol.reverseVowels("IceCreAm") << std::endl; // AceCreIm
27 std::cout << sol.reverseVowels("leetcode") << std::endl; // leotcede
28 return 0;
29}

Time & Space Complexity

  • Time complexity: O(N) strictly — Exact single pass with zero auxiliary dynamic memory allocations.
  • Space complexity: O(1) auxiliary space

Approach Comparison Matrix

Side-by-side comparison of time, space, and algorithmic trade-offs across each approach:

ApproachTimeSpaceCore IdeaTrade-off / Bottleneck
1.Extract Vowels and Re-insertO(N)O(N)Extracts all vowels into a list, reverses list, and writes back.Two passes and extra vector allocation.
2.Two Pointers In-Place Swapping (Optimal)OptimalO(N)O(1) extra spaceSingle pass, zero extra allocations beyond mutable string representation.Inner loop bounds checking (l < r).

Common Pitfalls & Crucial Edge Cases

Pitfall 1:

Not checking uppercase vowels.

Pitfall 2:

Omitting left < right guard inside the inner vowel search loops.

Crucial Edge Cases to Validate:

  • String with no vowels (s = rhythm): left advances to right, returns rhythm untouched.
  • String with all vowels (s = aeiou): Reverses string completely to uoiea.

Interview Tips & Best Practices

How to communicate your thought process and address follow-ups in a live technical interview:

  1. Mention handling both upper and lower case vowels explicitly.
  2. Show the in-place two-pointer swap pattern.

Quick Comprehension Check

Test your intuitive understanding of the core concept in 30 seconds:

1.Why must both uppercase and lowercase vowels be handled in the vowel check predicate?