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。