Z 字形变换(LeetCode 6)
题目链接:Z 字形变换(LeetCode 6)
难度:中等
1. 题目描述
给定一个字符串 s
和行数 numRows
,按照 Z 字形从上到下、从左到右排列字符串,然后逐行读取生成新字符串。
- 字符串长度
1 <= s.length <= 1000
,由英文字母(大小写)、',' 和 '.' 组成。 - 行数
1 <= numRows <= 1000
。
示例:
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"
解释:
P A H N
A P L S I I G
Y I R
输入: s = "PAYPALISHIRING", numRows = 4
输出: "PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
输入: s = "A", numRows = 1
输出: "A"
2. 问题分析
2.1 规律
- Z 字形排列是一个周期性模式:
- 每个周期包含
numRows
个字符向下排列,再斜向上排列到第一行。 - 周期长度为
2 * numRows - 2
(向下numRows
步,向上numRows - 2
步)。 - 特殊情况:
- 当
numRows = 1
时,直接返回原字符串。 - 当
numRows = 2
时,周期为 2,交替填充两行。
- 当
- 每个周期包含
- 最终输出需要按行读取,行内字符按顺序拼接。
- 核心问题:如何确定每个字符在 Z 字形中的位置,并按行输出?
2.2 解法思路
方法 1:按行分配(直观模拟)
- 模拟 Z 字形排列的过程:
- 遍历字符串
s
,按照 Z 字形的路径分配字符到对应行。 - 使用一个数组(或字符串列表)存储每行的字符。
- 跟踪当前行和移动方向(向下或向上)。
- 最后按行拼接结果。
- 遍历字符串
- 优点:直观,易于理解;空间复杂度较高。
方法 2:按行访问(数学规律)
- 直接计算每个字符在 Z 字形中的行号,优化空间复杂度。
- 每个周期的字符位置有规律:
- 第
i
行的字符索引可以通过周期公式计算。 - 第一行和最后行的字符索引间隔为
2 * numRows - 2
。 - 中间行的字符有两个来源:周期内的向下和斜向上。
- 向下的字符坐标:当前周期起始点+当前行号。
- 斜向上的字符坐标:下一个周期的起始点-当前行号。
- 第
- 优点:空间复杂度 O(1),仅需输出字符串的空间。
- 本题采用 方法 2,因为它更高效且空间复杂度低。
2.3 示例
以 s = "PAYPALISHIRING", numRows = 4
为例:
P I N
A L S I G
Y A H R
P I
- 周期长度 =
2 * numRows - 2 = 6
。 - 第 0 行:
P
(0),I
(6),N
(12) → 索引间隔 6。 - 第 1 行:
A
(1),L
(4),S
(7),I
(10) → 索引规律为周期内和周期间。 - 第 2 行:
Y
(2),A
(3),H
(8),R
(9)。 - 第 3 行:
P
(5),I
(11)。 - 按行拼接:
PINALSIGYAHRPI
。
3. 代码实现
Python
python
class Solution:
def convert(self, s: str, numRows: int) -> str:
if numRows == 1 or numRows >= len(s):
return s
result = []
n = len(s)
cycle = 2 * numRows - 2
for i in range(numRows):
for j in range(0, n, cycle):
# 第一行和最后行
if i == 0 or i == numRows - 1:
if j + i < n:
result.append(s[j + i])
# 中间行
else:
# 周期内的字符
if j + i < n:
result.append(s[j + i])
# 周期间的字符(斜向上)
if j + cycle - i < n:
result.append(s[j + cycle - i])
return ''.join(result)
C++
cpp
class Solution {
public:
string convert(string s, int numRows) {
if (numRows == 1 || numRows >= s.length()) {
return s;
}
string result;
int n = s.length();
int cycle = 2 * numRows - 2;
for (int i = 0; i < numRows; ++i) {
for (int j = 0; j < n; j += cycle) {
// 第一行和最后行
if (i == 0 || i == numRows - 1) {
if (j + i < n) {
result += s[j + i];
}
}
// 中间行
else {
// 周期内的字符
if (j + i < n) {
result += s[j + i];
}
// 周期间的字符(斜向上)
if (j + cycle - i < n) {
result += s[j + cycle - i];
}
}
}
}
return result;
}
};
4. 复杂度分析
- 时间复杂度 :O(n),其中
n
是字符串s
的长度。每个字符最多被访问一次。 - 空间复杂度 :
- Python:O(n),用于存储结果字符串(
result
列表)。 - C++:O(1),不计输出字符串的空间(
result
直接追加)。
- Python:O(n),用于存储结果字符串(
5. 总结
- Z 字形变换 :核心是理解周期性规律,周期长度为
2 * numRows - 2
。 - 按行访问方法利用数学规律,高效且空间优化。
- 特殊情况(
numRows = 1
或numRows >= n
)需提前处理。 - 可扩展到变体问题,如动态行数或不同排列方式。