欢迎来到李耶的频道【LeetCode面试题】。
Z 字形变换
题目
将一个给定字符串 s 根据给定的行数 numRows,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING",行数为 3 时,排列如下:
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左到右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。
请你实现这个将字符串进行指定行数变换的函数。
输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"
输入:s = "PAYPALISHIRING", numRows = 4
输出:"PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
输入:s = "A", numRows = 1
输出:"A"
解法一:按行模拟(顺序遍历)
思路 :用 numRows 个字符串数组来存储每行的字符。遍历原字符串,用 currentRow 和 direction(向下或向上)控制当前字符应该放到哪一行。当到达顶部或底部时改变方向。
javascript
function convert(s, numRows) {
if (numRows === 1 || s.length <= numRows) return s;
const rows = new Array(numRows).fill('');
let currentRow = 0;
let goingDown = false;
for (const char of s) {
rows[currentRow] += char;
if (currentRow === 0 || currentRow === numRows - 1) {
goingDown = !goingDown;
}
currentRow += goingDown ? 1 : -1;
}
return rows.join('');
}
- 时间复杂度 / 空间复杂度:O(n) / O(n)
- 优势:思路直观,代码简洁,容易理解,是面试中最推荐的写法
解法二:按行访问(数学规律法)
思路 :直接按行顺序访问原字符串中的字符。对于第 0 行和第 numRows-1 行,字符间隔为 cycleLen = 2 * numRows - 2;对于中间行,需要额外访问斜线上的字符,间隔为 cycleLen - 2 * i。
javascript
function convert(s, numRows) {
if (numRows === 1 || s.length <= numRows) return s;
const n = s.length;
const cycleLen = 2 * numRows - 2;
let result = '';
for (let i = 0; i < numRows; i++) {
for (let j = i; j < n; j += cycleLen) {
result += s[j];
// 中间行还需要添加斜线上的字符
if (i !== 0 && i !== numRows - 1) {
const nextIndex = j + cycleLen - 2 * i;
if (nextIndex < n) {
result += s[nextIndex];
}
}
}
}
return result;
}
- 时间复杂度 / 空间复杂度:O(n) / O(1)(不计输出字符串)
- 优势:空间效率更高,但逻辑稍复杂,面试中可作为进阶解法展示
解法对比
| 解法 | 时间 / 空间复杂度 | 优势 | 推荐指数 |
|---|---|---|---|
| 按行模拟 | O(n) / O(n) | 直观易懂,不易出错 | ⭐⭐⭐⭐⭐ |
| 按行访问 | O(n) / O(1) | 空间更优,体现数学思维 | ⭐⭐⭐⭐ |
扩展题
- 对角线遍历 :给定一个
m x n的矩阵,按照对角线顺序遍历并返回结果。 - 螺旋矩阵 :给定一个
m x n的矩阵,按照螺旋顺序返回所有元素。 - 字符串的排列 :给定两个字符串
s1和s2,判断s2是否包含s1的排列。
"善弈者,通盘无妙手。" ------ 《棋经十三篇》
关注李耶,每天一道面试题,一起卷起来 🔥