题目描述

✅ 345. 反转字符串中的元音字母

image-20260928224203652

题意分析

只将字符串中所有元音的出现次序反转,其他字符仍留在原下标。元音包括 a、e、i、o、u 及对应大写形式;大小写是字符自身的一部分,移动时原样保留,不统一转换。

返回新的字符串。题目输入是可打印 ASCII 字符,数字、标点以及其他字母都不能当作元音;没有元音或只有一个元音时,结果与输入相同。

解法:左右指针交换元音

核心思路

[!blue]

如果只看元音组成的序列,反转就是交换第一与最后一个、第二与倒数第二个。无需先提取这条序列,可以直接在原位置上用左右指针寻找下一对需要交换的元音。

左指针向右跳过非元音,右指针向左跳过非元音,停下时分别指向最左与最右尚未处理的元音。只要它们仍是两个不同位置,就交换这两个字符,再同时向内推进。

每轮结束后,两个指针外侧的元音都已处于最终顺序,后面不会再碰到;非元音从未参与写入,因此位置不变。指针相遇或交错时,最多剩下一个居中的元音,无需移动,整个反转就完成了。

输入字符串不可变,Java 先转成 char[]、Go 先转成字节切片,在可写缓冲区交换后再构造字符串。每次寻找都先检查 left < right,避免整段没有元音时越过边界。

解题步骤

  1. 复制到可修改字符缓冲区,将两个指针放在首尾。
  2. 左指针跳过非元音,右指针也跳过非元音,两次寻找都受 left < right 限制。
  3. 两指针仍未相遇时交换它们指向的元音,再向内移动。
  4. 不再有需要交换的元音对时,将缓冲区转回字符串返回。

代码实现

class Solution {
    // 左右指针分别向中间寻找下一个元音,两个指针都停在元音上时再交换。
    public String reverseVowels(String s) {
        char[] chars = s.toCharArray();
        int left = 0;
        int right = chars.length - 1;

        // 两端之外的元音已就位,非元音位置始终不改动
        while (left < right) {
            while (left < right && !isVowel(chars[left])) {
                left++;
            }

            while (left < right && !isVowel(chars[right])) {
                right--;
            }

            if (left < right) {
                char swapValue = chars[left];

                chars[left] = chars[right];
                chars[right] = swapValue;
            }

            left++;
            right--;
        }

        return new String(chars);
    }

    private boolean isVowel(char ch) {
        return "aeiouAEIOU".indexOf(ch) >= 0;
    }
}
func reverseVowels(s string) string {
    // 左右指针分别向中间寻找下一个元音,两个指针都停在元音上时再交换。
    chars := []byte(s)
    left, right := 0, len(chars)-1

    // 两端之外的元音已就位,非元音位置始终不改动
    for left < right {
        for left < right && !isVowel(chars[left]) {
            left++
        }
        for left < right && !isVowel(chars[right]) {
            right--
        }

        if left < right {
            chars[left], chars[right] = chars[right], chars[left]
        }
        left++
        right--
    }

    return string(chars)
}

func isVowel(ch byte) bool {
    switch ch {
    case 'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U':
        return true
    default:
        return false
    }
}

复杂度分析

  • 时间复杂度:$O(n)$,每个指针单向移动。
  • 空间复杂度:$O(n)$,字符缓冲与结果转换。

关键点总结

[!green]

  • 交换发生在元音位置之间,其他位置不动。
  • 大小写字符各自保留原字符,不做统一转小写。

易错点总结

[!yellow]

  • 只识别小写会遗漏大写元音;为便于判断而直接把输出全部转小写,也会改变原字符。
  • 元音仅为指定的十个字符,不能把标点、数字等所有非辅音都当作元音。
  • 跳过非元音时也要检查指针边界,不能只在最外层检查。
  • 只能交换找到的元音位置,直接反转整个区间会移动本应保持原位的其他字符。

相似题目

题目 难度 关联与区别
344. 反转字符串 简单 同样由两端交换,本题先跳过非元音,只对符合条件的位置配对。
917. 仅仅反转字母 简单 同样仅反转某类字符,原题保留非字母位置,本题保留非元音位置。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2023/91955856
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!