当前位置: 首页 > news >正文

【LeetCode 每日一题】2785. 将字符串中的元音字母排序

Problem: 2785. 将字符串中的元音字母排序

文章目录

  • 整体思路
  • 完整代码
  • 时空复杂度
    • 时间复杂度:O(N)
    • 空间复杂度:O(N)

整体思路

这段代码的目的是对一个给定的字符串 S 中的所有元音字母进行排序,并保持所有辅音字母的位置不变。排序是基于元音字母的ASCII码值进行的,即升序排序。

该算法采用了一种非常高效的 “提取-排序-放回” 的策略,并利用了 计数排序 (Counting Sort) 的思想来对元音字母进行排序。

其核心逻辑步骤如下:

  1. 第一步:识别并提取元音信息

    • 算法首先定义了一个包含所有大小写元音字母的 Set,以便于进行 O(1) 的快速查找。
    • 然后,它遍历输入的字符串 S(已转换为字符数组 s 以便修改)。
    • 在遍历过程中,它做了两件重要的事情:
      a. 记录元音位置:每当遇到一个元音字母,就将其在原字符串中的索引 i 存入一个列表 idx
      b. 统计元音频率:使用一个大小为 128 的整型数组 vowelASC(可以覆盖所有基本ASCII字符)来充当一个,统计每种元音字母出现的次数。例如,遇到字符 ‘a’,则 vowelASC['a']++
  2. 第二步:隐式的排序过程

    • 在第一步完成后,我们有了所有元音字母的种类和数量(存储在 vowelASC 中),以及它们应该被放回的位置(存储在 idx 中)。
    • 接下来,代码通过一个 for 循环,从 i = 0 遍历到 127,这实际上是在按ASCII码的顺序隐式地遍历所有可能的字符。
    • 这个循环天然地保证了我们是先处理ASCII码小的字符(如 ‘A’),再处理大的字符(如 ‘e’)。这就是计数排序的核心思想。
  3. 第三步:按序放回元音

    • 在上述的 for 循环中,对于每个字符 c = (char) i
      • 它会检查 vowelASC[i] 的值,即字符 c 出现了多少次。
      • 使用一个 while 循环,将字符 c 按照其出现次数,依次放回到元音字母原来所在的位置上。
      • s[idx.get(t++)] = c;: idx 列表保证了我们总是按从左到右的顺序填充元音位置。t 是一个指针,用于追踪下一个可用的元音位置。
    • 例如,循环首先会处理 ‘A’。假设 vowelASC['A'] 是 2,那么代码会把 s[idx.get(0)]s[idx.get(1)] 都设置为 ‘A’。然后 t 变为 2。接着处理 ‘E’,以此类推。
  4. 返回结果

    • 当所有元音都按序放回后,字符数组 s 中就包含了最终的结果。
    • 最后,通过 new String(s) 将其转换回字符串并返回。

完整代码

import java.util.ArrayList;
import java.util.List;
import java.util.Set;class Solution {/*** 对字符串中的元音字母按ASCII值升序排序,保持辅音字母位置不变。* @param S 输入的字符串* @return 排序元音后的新字符串*/public String sortVowels(String S) {int n = S.length();// 使用 Set 提供 O(1) 的元音查找Set<Character> vowels = Set.of('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U');// idx: 存储原字符串中所有元音字母的索引List<Integer> idx = new ArrayList<>();// vowelASC: 充当计数排序的桶,统计每种元音的频率int[] vowelASC = new int[128];// 将字符串转为字符数组,以便原地修改char[] s = S.toCharArray();// 步骤 1: 遍历字符串,提取元音信息for (int i = 0; i < n; i++) {char c = s[i];if (vowels.contains(c)) {// 记录元音的位置idx.add(i);// 统计元音的频率vowelASC[c]++;}}// t: 指针,用于在 idx 列表中定位下一个要填充的元音位置int t = 0;// 步骤 2 & 3: 隐式排序并按序放回元音// 遍历所有可能的ASCII字符 (0-127)for (int i = 0; i < 128; i++) {char c = (char) i;// 检查字符 c (作为一个元音) 出现了多少次while (vowelASC[i]-- > 0) {// 将字符 c 放入下一个可用的元音位置s[idx.get(t++)] = c;}}// 将修改后的字符数组转换回字符串并返回return new String(s);}
}

时空复杂度

时间复杂度:O(N)

  1. 提取元音信息:第一个 for 循环遍历整个字符串一次,长度为 N。内部的 vowels.contains() 是 O(1),idx.add() 是均摊O(1),数组访问是 O(1)。因此,这部分的时间复杂度是 O(N)
  2. 排序与放回
    • 第二个 for 循环的迭代次数是一个固定的常数 128。
    • 内部的 while 循环的总执行次数,等于字符串中元音字母的总数。设元音字母数量为 V ( V <= N )。
    • while 循环体内的操作 idx.get() 和数组赋值都是 O(1) 的。
    • 因此,这部分的总时间复杂度是 O(128 + V),可以简化为 O(V)
  3. 字符串转换new String(s) 的时间复杂度是 O(N)。

综合分析
总的时间复杂度是 O(N) + O(V) + O(N)。由于 V <= N,所以最终的时间复杂度为 O(N)

空间复杂度:O(N)

  1. 主要存储开销
    • Set<Character> vowels: 存储10个元音,占用 O(1) 的常数空间。
    • List<Integer> idx: 在最坏的情况下(例如,字符串全由元音组成),这个列表需要存储 N 个索引。因此,其空间复杂度为 O(N)
    • int[] vowelASC: 大小是固定的128,占用 O(1) 的常数空间。
    • char[] s: S.toCharArray() 创建了字符串的一个副本,占用了 O(N) 的空间。

综合分析
算法所需的额外空间主要由 idx 列表和 s 字符数组决定。因此,总的空间复杂度为 O(N)

http://www.dtcms.com/a/392094.html

相关文章:

  • 游戏开发中的友好提示,错误信息,异常描述等数据管理的必要性
  • 总线协议(Bus Protocol)如何支持总线错误条件?
  • simuilink 中的引用模型(reference model)的作用? 它和子系统的区别? 如何生成引用模型?
  • HTML+JS实现table表格和鼠标移入移出效果
  • windows11用Qt6自带的mingw编译OSGEarth(自用记录)
  • 仓颉编程语言青少年基础教程:泛型(Generic)和区间(Range)类型
  • 原码反码补码------相关理解
  • 【Python】字典
  • 玩转deepseek之海报生成器
  • C++强制类型转换和I/O流深度解析
  • Transformer 和 MoE
  • Python基础 7》数据类型_元组(Tuple)
  • AI大模型入门第四篇:借助RAG实现精准用例自动生成!
  • leetcode 198 打家劫舍问题,两个dp数组->一个dp数组
  • 嵌入式ARM架构学习8——串口
  • Motion-sensor基础应用
  • 今日行情明日机会——20250919
  • 跟着Carl学算法--动态规划【7】
  • T拓扑结构的特性
  • 第一章 开发工具与平台介绍
  • 线上环境出了个问题:Young GC看起来很正常,但Full GC每天发生20多次,每次都让CPU飙得很高。你会怎么去排查和解决?
  • Linux系统多线程总结
  • 【PyTorch】单对象分割
  • 1.3 状态机
  • 软件测试之自动化测试概念篇(沉淀中)
  • 二分答案:砍树
  • 串口通信简介
  • 模运算(Modular Arithmetic)的性质
  • 破解“双高“电网难题,进入全场景构网新时代
  • 企业实训|AI技术在职能办公领域的应用场景及规划——某央企汽车集团