算法42,模拟算法,模拟z字形变换

LeetCode 6. Z 字形变换 · 总结

一、题目在问什么

把字符串 s 按 Z 字形(从上往下、再从下往上斜着回摆)填入 numRows 行,然后按行从左到右逐行读出,返回读出的结果。

复制代码
输入:  s = "PAYPALISHIRING", numRows = 3
P   A   H   N
A P L S I I G
Y   I   R
输出: "PAHNAPLSIIGYIR"     // PAHN + APLSIIG + YIR

关键认知 :这题根本不用真的"画"出 Z 字阵列。它只问"最终读出来的顺序",所以本质是找下标规律,把字符直接按读出顺序收集起来。


二、核心规律:一个周期 cycle

概念 公式 含义
周期长度 cycle = 2 × numRows − 2 一个"竖列向下 + 斜列向上"单元包含的字符数
第 i 行竖列字符 j = i, i+cycle, i+2cycle ... 每行的"骨架",等差出现
第 i 行斜列字符 k = j + cycle − 2i 夹在两个竖列字符之间的那一个

为什么斜列偏移是 cycle − 2i :第 i 行的竖列字符到自己那一行"峰值"还有 numRows−1−i 步,回摆经过 numRows−1−i 步回到同行,两者相加即 2(numRows−1−i) = cycle − 2i。

只有中间行有斜列字符​ ------ 首行和末行每个周期只出现一次,这是最容易漏的分支判断。


三、两种解法

① 公式法(推荐,找规律直取)

复制代码
class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1 || s.length() <= numRows) return s;
        int n = s.length(), cycle = 2 * numRows - 2;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < numRows; i++) {
            for (int j = i; j < n; j += cycle) {
                sb.append(s.charAt(j));                        // 竖列
                int k = j + cycle - 2 * i;                     // 斜列
                if (i != 0 && i != numRows - 1 && k < n) {
                    sb.append(s.charAt(k));
                }
            }
        }
        return sb.toString();
    }
}

② 模拟法(更不容易写错,行数方向法)

复制代码
class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1) return s;
        StringBuilder[] rows = new StringBuilder[numRows];
        for (int i = 0; i < numRows; i++) rows[i] = new StringBuilder();
        int cur = 0, step = 1;                     // 当前行 + 行进方向
        for (char c : s.toCharArray()) {
            rows[cur].append(c);
            if (cur == 0) step = 1;                // 触顶→向下
            else if (cur == numRows - 1) step = -1;// 触底→向上
            cur += step;
        }
        StringBuilder sb = new StringBuilder();
        for (StringBuilder r : rows) sb.append(r);
        return sb.toString();
    }
}

模拟法用 step 在上下边界翻转方向,天然规避了斜列下标算错的问题,口算不熟时优先用它;公式法更省内存,是面试时更想听到的"找规律"思路。


四、三个必踩的坑

# 坑 触发后果
1 numRows == 1 不特判 cycle = 0 → j += 0 死循环(本题头号 WA/TLE 原因)
2 斜列写成 s.charAt(j) 输出 PAHNAALLIIGGYIR:字符重复 + 丢字符 + 长度变长
3 漏判 i != 0 && i != numRows−1,或漏 k < n 首尾行被复制 / 越界 StringIndexOutOfBoundsException

验证用例 :("PAYPALISHIRING", 3) → PAHNAPLSIIGYIR;("PAYPALISHIRING", 4) → PINALSIGYAHRPI;("AB", 1) → AB;("ABC", 2) → ACB。上面三个坑会在这几个用例上立刻暴露(已用 3000 组随机用例与真实 Z 字模拟比对,公式法全部一致)。


五、复杂度与迁移价值

  • 时间 O(n):每个字符恰好被访问一次。
  • 空间 O(n):结果串本身;模拟法额外 O(numRows) 行缓冲。
  • 可迁移的思维 :凡是"按某种规则重排再读出"的题(如螺旋矩阵、对角线遍历),先别急着建二维数组模拟,先算周期和下标通项------能推出公式就 O(1) 额外空间,推不出再用方向模拟兜底。

一句话记住:cycle = 2×numRows−2,竖列 j,斜列 j + cycle − 2i,首尾行没有斜列,一行行数时必先判 numRows == 1。

相关推荐
CQU_JIAKE几秒前
9.11【A】
算法
All for pursuit.33 分钟前
【分治-3】347.前K个高频元素
数据结构·c++·算法·leetcode
索尔~古德曼1 小时前
CF(1400-1400&1300-1500)错题集
算法
All for pursuit.2 小时前
【二分查找-2】34.在排序数组中查找元素的第一个和最后一个位置
数据结构·c++·算法·leetcode
泯泷2 小时前
为什么 AI 会"失忆"?——读懂 Agent 的记忆系统(一)
人工智能·算法·agent
需要8264 小时前
分布式 ID 生成:雪花算法、号段模式与时钟回拨
分布式·算法
垆边人似月.4 小时前
华为机考题:字符串分隔
算法·华为
CQU_JIAKE4 小时前
9.4【A】
算法
CQU_JIAKE4 小时前
9.13[A]
算法
懿路向前4 小时前
【唤醒实战笔记】2026-09-29 | 智能体提示词迭代史(v1 → v8)
linux·笔记·算法