LeetCode #345 Easy

Reverse Vowels of a String

Reverse Vowels of a String is LeetCode 345 (Easy). Given a string s, reverse only the vowels and return the new string. - The vowels are a, e, i, o and u, in lowercase and uppercase. - Every other character (consonants, digits, spaces, punctuation) stays at its index. s can be 300,000 characters long, so each character should be looked at about once.

Constraints
  • 1 <= s.length <= 3 * 10⁵
  • s consists of printable ASCII characters.
stringtwo-pointers
Open on LeetCode ↗
02

Intuition

Look only at the vowels. Reversing them means the first vowel trades places with the last, the second with the second-to-last, and so on. Two pointers moving inward from both ends produce exactly those pairs, as long as each pointer walks past consonants without stopping. - Left pointer: stops at the next vowel from the front. - Right pointer: stops at the next vowel from the back. - Swap: those two are the outermost vowels not yet placed, so each belongs in the other's slot. Consonants are never written to, so they cannot move.

How to spot this pattern

"Change only the characters that pass a test, in reverse order, and leave the rest where they are" is a converging two-pointer walk where each pointer skips the characters that do not count. Valid Palindrome uses the same skipping walk to compare instead of swap.

03

Approach

Try it first

Before reading on, decide what each pointer should do when it lands on a consonant, and exactly when the two are allowed to swap. Aim for one pass with no second list.

1

Put the ten vowels in a set

Build a set of aeiouAEIOU so that checking one character costs O(1). In Python, copy s into a list of characters first, because a Python string cannot be changed in place.

2

Start a pointer at each end

Set left = 0 and right = len(s) - 1. Every character outside the two pointers is already in its final place, so the loop only needs to run while left < right.

3

Move past one consonant per turn

If the character at left is not a vowel, move left one step right and start the next turn. Otherwise, if the character at right is not a vowel, move right one step left. Only one pointer moves per turn, so neither can skip a vowel or pass the other.

4

Swap when both pointers sit on vowels

When both checks fail, both pointers are on vowels. Swap the two characters and move both pointers one step inward. These were the outermost vowels still unplaced, so after the swap both are final.

5

Why the middle vowel stays put

With an odd number of vowels, one vowel is left unpaired in the middle, and the middle of a reversal does not move. Both pointers reach it, left < right fails, and the loop ends without touching it. Join the list and return it.

04

Reverse Vowels of a String solution in Python | C++ | Java

▶1class Solution:
▶2 def reverseVowels(self, s: str) -> str:
▶3 vowels = set("aeiouAEIOU")
▶4 chars = list(s)
▶5 left, right = 0, len(chars) - 1
▶6 while left < right:
▶7 if chars[left] not in vowels:
▶8 left += 1
▶9 elif chars[right] not in vowels:
▶10 right -= 1
▶11 else:
▶12 chars[left], chars[right] = chars[right], chars[left]
▶13 left += 1
▶14 right -= 1
▶15 return "".join(chars)
I0c1e2C3r4e5A6m7leftrightleft at 0, right at 7
left0
right7
vowels4in the string
One pointer starts at each end. Everything outside the pair of pointers is already final, so the walk only looks between them. This string has 4 vowels.
I0c1e2C3r4e5A6m7leftright'm' is not a vowel → right moves on
left0
right7
Left is parked on the vowel 'I'. The right pointer is on 'm', a non-vowel, so move right one step left and keep left where it is.
A0c1e2C3r4e5I6m7leftrightboth on vowels → swap 'I' and 'A'
left0
right6
Both pointers are on vowels: 'I' at 0 and 'A' at 6. They are the outermost vowels not yet placed, so in the reversed order each takes the other's slot. Both are now final. Then both pointers step inward.
A0c1e2C3r4e5I6m7leftright'c' is not a vowel → left moves on
left1
right5
The left pointer is on 'c', which is not a vowel, so it can never take part in a swap. Move left one step right; the right pointer waits.
A0c1e2C3r4e5I6m7leftrightboth on vowels → swap 'e' and 'e'
left2
right5
Both pointers are on vowels: 'e' at 2 and 'e' at 5. They are the outermost vowels not yet placed, so in the reversed order each takes the other's slot. They are the same letter, so the string looks the same, but the pair is done. Then both pointers step inward.
A0c1e2C3r4e5I6m7leftright'C' is not a vowel → left moves on
left3
right4
The left pointer is on 'C', which is not a vowel, so it can never take part in a swap. Move left one step right; the right pointer waits.
A0c1e2C3r4e5I6m7pointers meet → return "AceCreIm"
result"AceCreIm"
Both pointers reach index 4, so no unswapped vowel is left between them and the loop ends. Every consonant is still at its original index. Return "AceCreIm".
05

Common pitfalls

Leaving the capitals out of the vowel set

✗ Wrong
vowels = set("aeiou")
✓ Right
vowels = set("aeiouAEIOU")

"IceCreAm" should become "AceCreIm". With lowercase vowels only, I and A count as consonants, the two es swap with each other, and the string comes back unchanged.

Inner skip loops with no bound

✗ Wrong
while chars[left] not in vowels:
    left += 1
✓ Right
while left < right and chars[left] not in vowels:
    left += 1

On a string with no vowels, such as "rhythm", the unbounded loop pushes left past the last index and raises IndexError. The one-pointer-per-turn loop on this page avoids the problem because left < right is checked before every move.

Swapping without moving the pointers

✗ Wrong
chars[left], chars[right] = chars[right], chars[left]
✓ Right
chars[left], chars[right] = chars[right], chars[left]
left += 1
right -= 1

Both pointers still sit on vowels after the swap, so the next turn swaps the same pair back, and so on forever. "hello" never returns.

06

Edge cases

The vowels already read the same both ways, e.g. "racecar"

Every swap exchanges two equal letters (a with a), so the output equals the input. That is the correct answer, not a sign that nothing ran.

A single character, e.g. "a"

left and right both start at 0, the loop never runs, and the string is returned as it is.

07

Complexity

Time
O(n)
Space
O(1)
Every turn of the loop moves at least one pointer one step, so there are at most n turns. C++ swaps inside the string it receives. Python's list and Java's char[] are O(n) copies, needed because strings in those languages cannot be changed.
08

Reverse Vowels of a String FAQ

Is y counted as a vowel?

No. The problem lists only a, e, i, o, u and their capitals, so y stays where it is, even in a word like "rhythm" where it sounds like a vowel.

Can you collect the vowels first and write them back?

Yes. Copy the vowels into a list, then walk the string again and replace each vowel with the list's last item, removing it as you go. It is also O(n), but it needs a second list and a second pass; the two pointers need neither.