跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书AI学习GitHub 精选镜像AI工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava算法

Java 模拟算法实战:五道经典题解

本文通过五道 LeetCode 经典题目深入讲解 Java 模拟算法。涵盖替换问号、提莫攻击、Z 字形变换、外观数列及数青蛙五个案例。重点分析了边界条件处理、时间重叠计算、数学规律推导、双指针迭代以及状态机计数等核心技巧。代码经过规范化整理,附带详细思路解析与复杂度分析,适合希望提升算法细节把控能力的开发者参考。

DevStack发布于 2026/3/21更新于 2026/10/2292 浏览
Java 模拟算法实战:五道经典题解

Java 模拟算法实战

模拟算法的核心在于严格按照题目描述进行步骤操作。这类问题通常逻辑直观,但容易在边界条件和细节处理上出错。下面通过五道经典题目,梳理一下模拟算法的常见套路和注意事项。

替换所有的问号

题目核心 将字符串中的 ? 替换为小写字母,要求替换后的字符不能与相邻字符重复。

思路解析 遍历字符串,遇到 ? 时尝试从 a 到 z 寻找一个不与左右邻居冲突的字符。这里有两个关键点:一是边界情况(首尾字符),二是确保找到的字符唯一且合法。

class Solution {
    public String modifyString(String s) {
        int n = s.length();
        char[] chars = s.toCharArray();
        
        for (int i = 0; i < n; i++) {
            if (chars[i] == '?') {
                // 尝试 a-z 找到合适的字符
                for (char ch = 'a'; ch <= 'z'; ch++) {
                    // 检查左邻居和右邻居
                    boolean leftOk = (i == 0 || chars[i - 1] != ch);
                    boolean rightOk = (i == n - 1 || chars[i + 1] != ch);
                    
                    if (leftOk && rightOk) {
                        chars[i] = ch;
                        break;
                    }
                }
            }
        }
        return new String(chars);
    }
}
  • 时间复杂度:O(n),每个位置最多尝试 26 次。
  • 空间复杂度:O(n),用于存储字符数组。

提莫攻击

题目核心 计算中毒总时长。技能有持续时间,如果新技能在旧技能结束前释放,则重叠部分只算一次。

思路解析 关键在于判断两次攻击的时间间隔。如果间隔大于等于持续时间,说明上次攻击完全生效;否则,上次攻击被中断,实际贡献时间为间隔差值。最后别忘了加上最后一次攻击的完整持续时间。

class Solution {
    public int findPoisonedDuration(int[] timeSeries, int duration) {
        if (timeSeries.length == 0) return 0;
        
        int totalDuration = 0;
        for (int i = 1; i < timeSeries.length; i++) {
            int diff = timeSeries[i] - timeSeries[i - 1];
            // 取间隔和持续时间的较小值
            totalDuration += Math.min(diff, duration);
        }
        // 加上最后一次攻击的完整持续时间
        return totalDuration + duration;
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

Z 字形变换

题目核心 将字符串按 Z 字形排列后,按行读取生成新字符串。

思路解析 直接模拟二维矩阵会消耗过多空间。观察规律发现,字符是按周期循环的。周期长度为 2 * numRows - 2。第一行和最后一行每隔一个周期出现一次,中间行每个周期会出现两次(一次向下,一次向上)。

注意当行数小于 2 时,直接返回原字符串即可。

class Solution {
    public String convert(String s, int numRows) {
        if (numRows < 2) return s;
        
        int n = s.length();
        int cycleLen = 2 * numRows - 2;
        StringBuilder sb = new StringBuilder();
        
        // 第一行
        for (int i = 0; i < n; i += cycleLen) {
            sb.append(s.charAt(i));
        }
        
        // 中间行
        for (int k = 1; k < numRows - 1; k++) {
            for (int i = k, j = cycleLen - k; i < n || j < n; i += cycleLen, j += cycleLen) {
                if (i < n) sb.append(s.charAt(i));
                if (j < n) sb.append(s.charAt(j));
            }
        }
        
        // 最后一行
        for (int i = numRows - 1; i < n; i += cycleLen) {
            sb.append(s.charAt(i));
        }
        
        return sb.toString();
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

外观数列

题目核心 第 n 行是对第 n-1 行的描述(读法)。例如 "1" -> "11" -> "21"。

思路解析 这是一个典型的迭代过程。使用双指针扫描当前字符串,统计连续相同字符的个数,然后拼接'个数 + 字符'得到下一行。直到达到第 n 行。

class Solution {
    public String countAndSay(int n) {
        String current = "1";
        
        for (int i = 1; i < n; i++) {
            StringBuilder next = new StringBuilder();
            int len = current.length();
            int left = 0, right = 0;
            
            while (right < len) {
                // 移动右指针直到字符不同
                while (right < len && current.charAt(left) == current.charAt(right)) {
                    right++;
                }
                // 记录个数和字符
                next.append(right - left).append(current.charAt(left));
                left = right;
            }
            current = next.toString();
        }
        return current;
    }
}

数青蛙

题目核心 给定一串 "croak" 混合字符串,求最少需要多少只青蛙才能完成这些叫声。如果字符串不合法(如缺少前驱字符或结尾不完整),返回 -1。

思路解析 这可以看作是一个状态机问题。每只青蛙的状态由它当前叫出的字母决定。我们需要维护五种状态的数量。遍历字符串时,根据当前字符更新状态计数。关键点是:

  1. 遇到 'c' 时,优先复用已经叫完 'k' 的青蛙(减少总数),如果没有则增加新青蛙。
  2. 遇到其他字符时,必须保证前驱状态存在,否则非法。
  3. 遍历结束后,所有非 'k' 状态的计数必须为 0。

这里提供两种实现方式,一种是显式变量控制,另一种是用数组模拟哈希表,后者更通用。

class Solution {
    public int minNumberOfFrogs(String croakOfFrogs) {
        if (croakOfFrogs.length() % 5 != 0) return -1;
        
        // 使用数组模拟状态计数,对应 c, r, o, a, k
        int[] count = new int[5];
        int frogs = 0;
        int maxFrogs = 0;
        
        for (char ch : croakOfFrogs.toCharArray()) {
            int index = getCharIndex(ch);
            if (index == 0) {
                // 开始叫,青蛙数量 +1
                count[0]++;
                frogs++;
                maxFrogs = Math.max(maxFrogs, frogs);
            } else {
                // 检查前驱是否存在
                if (count[index - 1] == 0) return -1;
                count[index - 1]--;
                count[index]++;
                
                if (index == 4) {
                    // 叫完,青蛙数量 -1
                    frogs--;
                }
            }
        }
        
        // 检查是否所有青蛙都叫完了
        return frogs == 0 ? maxFrogs : -1;
    }
    
    private int getCharIndex(char ch) {
        switch (ch) {
            case 'c': return 0;
            case 'r': return 1;
            case 'o': return 2;
            case 'a': return 3;
            case 'k': return 4;
            default: return -1;
        }
    }
}
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)(固定大小的数组)

目录

  1. Java 模拟算法实战
  2. 替换所有的问号
  3. 提莫攻击
  4. Z 字形变换
  5. 外观数列
  6. 数青蛙

更多推荐文章

查看全部
  • C++ gflags 命令行参数解析与 spdlog 日志库实战
  • Python 零基础入门教程:基础语法与核心概念详解
  • 基于 Coze 平台构建企业级 AI 客服机器人的实战指南
  • 单链表核心操作全实现:查找、插入与删除的深度解析
  • 智能体工作流导出导入实战:12345 政务热线分拨示例
  • 国产数据库新机遇:电科金仓融合技术同步全球竞争
  • C++ vector 扩容策略详解:避免频繁内存分配提升效率
  • C++ 智能指针详解:原理、实现与内存管理最佳实践
  • C++ 模板进阶:特化、萃取与可变参数模板
  • 千笔 AI:辅助学术写作的智能工具
  • AI 大模型本地部署指南:使用 Ollama 快速运行
  • 3D Gaussian Splatting 动态场景应用:从 SLAM 到虚拟现实
  • STM32F0 系列 FOC 开源算法源码解析:观测器与电流采样
  • 计算机专业与金融学就业前景及收入对比分析
  • C++ mio 库内存映射文件 IO 使用指南
  • VSCode 中 GitHub Copilot 安装与实战指南
  • AR 健身教练“形随心动”:基于 Rokid CXR-M SDK 的实践落地
  • GPT-4o mini 发布:多模态大模型技术解析与应用实践
  • 基于 IPIDEA API 的 eBay 商品数据 Python 采集实战
  • OpenClaw Skills 原理与实战指南

相关免费在线工具

  • Keycode 信息

    查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

  • Escape 与 Native 编解码

    JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

  • JavaScript / HTML 格式化

    使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

  • JavaScript 压缩与混淆

    Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online