一次搞懂:方向控制
dir+ 为什么一定要用StringBuilder
一、题目到底在干嘛?
题目要求我们把一个字符串,按照 Z 字形的方式排列,然后按行读出来。
示例一
输入:s = "PAYPALISHIRING", numRows = 3
Z 字形排列:
P A H N
A P L S I I G
Y I R
输出:"PAHNAPLSIIGYIR"
👉 不是画 Z,而是“上下折返”写字符
二、核心思路
这道题的本质其实就三件事:
- 用 多个容器 保存每一行的字符
- 用一个变量
row表示当前写到哪一行 - 用一个方向变量
dir控制“向下 / 向上”
三、为什么一定要用 StringBuilder?
这是这道题最容易被忽略、但最重要的点。
❌ 错误直觉:直接用 String 拼接
rows[row] += c;
看起来很舒服,但实际上:
String在 Java 中是 不可变的- 每次
+都会创建新对象 - 字符一多,性能直接炸裂
👉 时间复杂度会退化成 O(n²)
✅ 正解:使用 StringBuilder
rows.get(row).append(c);
StringBuilder 的好处:
- 可变字符串
append()是原地追加- 均摊时间复杂度 O(1)
- 非线程安全 → 更快(刚好适合这道题)
📌 一句话总结:
只要是“循环里拼字符串”,100% 用
StringBuilder
四、dir 是干嘛的?为什么它这么巧妙?
dir 的作用
👉 控制字符是“往下走”还是“往上走”
int dir = 1; // 1 表示向下,-1 表示向上
什么时候换方向?
- 到了 第一行 → 只能往下
- 到了 最后一行 → 只能往上
if (row == 0) dir = 1;
if (row == numRows - 1) dir = -1;
行号怎么走?
row += dir;
💡 这一步非常优雅:
- 不用 if-else 判断上下
- 一个
dir变量搞定所有方向逻辑
五、完整 Java 解法
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1 || s.length() <= numRows) {
return s;
}
List<StringBuilder> rows = new ArrayList<>();
for (int i = 0; i < numRows; i++) {
rows.add(new StringBuilder());
}
int row = 0;
int dir = 1; // 1 表示向下,-1 表示向上
for (char c : s.toCharArray()) {
rows.get(row).append(c);
if (row == 0) dir = 1;
if (row == numRows - 1) dir = -1;
row += dir;
}
StringBuilder res = new StringBuilder();
for (StringBuilder sb : rows) {
res.append(sb);
}
return res.toString();
}
}
六、用一个小例子走一遍流程
输入
s = "ABCDEF", numRows = 3
行变化过程
| 字符 | row | dir | 行内容 |
|---|---|---|---|
| A | 0 | ↓ | A |
| B | 1 | ↓ | B |
| C | 2 | ↑ | C |
| D | 1 | ↑ | BD |
| E | 0 | ↓ | AE |
| F | 1 | ↓ | BDF |
最终结果
AE + BDF + C = "AEBDFC"
👉 一步不跳,新手也能看懂。
七、时间 & 空间复杂度
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |
八、这道题真正考察的是什么?
- ✅ 是否理解 字符串不可变
- ✅ 是否会使用 StringBuilder
- ✅ 是否能用一个变量(
dir)优雅控制方向 - ❌ 考的不是算法难度,而是代码设计能力
九、总结一句话版
Z 字形变换本质不是“画 Z”,
而是方向控制 + 高效字符串拼接。
dir让逻辑更优雅,StringBuilder让性能更可靠。