一、题目描述
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
P A H N A P L S I I G Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
string convert(string s, int numRows);
示例 1:
输入:s = "PAYPALISHIRING", numRows = 3 输出:"PAHNAPLSIIGYIR"
示例 2:
输入:s = "PAYPALISHIRING", numRows = 4 输出:"PINALSIGYAHRPI" 解释: P I N A L S I G Y A H R P I
示例 3:
输入:s = "A", numRows = 1 输出:"A"
提示:
1 <= s.length <= 1000
s
由英文字母(小写和大写)、','
和'.'
组成1 <= numRows <= 1000
二、解题思路
-
初始化:首先,我们需要确定是否需要进行 Z 字形排列。如果
numRows
等于 1 或者字符串长度小于等于numRows
,那么直接返回原字符串,因为这种情况下不需要进行任何变换。 -
创建辅助数组:创建一个二维数组(或列表的列表),其大小为
numRows
,用于存储每一行的字符。这个数组的每一行将代表 Z 字形排列中的一行。 -
填充辅助数组:遍历输入字符串
s
,根据当前的行数和 Z 字形排列的规律,将字符填充到辅助数组的相应位置。在 Z 字形排列中,第一行和最后一行的字符是直接从上到下或从下到上填充的。中间的行则需要交替填充,即先从左到右,然后从右到左。 -
构建结果字符串:在填充完辅助数组后,我们需要将每一行的字符连接起来,形成最终的 Z 字形排列字符串。这可以通过遍历辅助数组的每一行,并将它们按顺序连接起来实现。
-
返回结果:最后,返回构建好的 Z 字形排列字符串。
三、具体代码
class Solution {
public String convert(String s, int numRows) {
if (numRows == 1) {
return s;
}
StringBuilder result = new StringBuilder();
int n = s.length();
char[][] rows = new char[numRows][];
for (int i = 0; i < numRows; i++) {
rows[i] = new char[n];
}
int curRow = 0;
boolean goingDown = false;
for (int i = 0; i < n; i++) {
rows[curRow][i] = s.charAt(i);
if (curRow == 0) {
goingDown = true;
} else if (curRow == numRows - 1) {
goingDown = false;
}
if (goingDown) {
curRow++;
} else {
curRow--;
}
}
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < n; j++) {
if (rows[i][j] != 0) {
result.append(rows[i][j]);
}
}
}
return result.toString();
}
}
四、时间复杂度和空间复杂度
代码的时间复杂度是 O(n)
,空间复杂度是 O(n)
。
1. 时间复杂度
- 首先,我们需要遍历整个字符串
s
,这需要O(n)
的时间,其中n
是字符串的长度。 - 然后,我们需要填充二维数组
rows
,这个过程中,每个字符都会被放置到数组中的一个位置,这同样需要O(n)
的时间。 - 最后,我们需要将二维数组中的字符连接成一个新的字符串,这涉及到遍历每一行,这需要
O(n)
的时间。
由于这些操作都是线性的,并且它们都是独立的,所以总的时间复杂度是 O(n) + O(n) + O(n) = O(n)
。
2. 空间复杂度
- 我们创建了一个大小为
numRows
的二维数组rows
,每个元素是一个长度为n
的字符数组。这需要O(numRows * n)
的空间。 - 我们还有一个
StringBuilder
对象result
,它在最坏的情况下可能需要存储整个字符串,所以需要O(n)
的空间。
由于 numRows
是一个常数(在这个问题中,它不会随着输入的增长而增长),所以空间复杂度可以简化为 O(n)
。
五、总结知识点
-
条件判断:使用
if
语句来判断numRows
的值,以决定是否需要进行 Z 字形变换。 -
循环控制:使用
for
循环来遍历字符串s
和填充二维数组rows
。 -
数组操作:创建二维数组
rows
并初始化,以及在循环中对数组进行读写操作。 -
布尔逻辑:使用布尔变量
goingDown
来控制curRow
的增减,实现 Z 字形排列的上下移动。 -
字符串构建:使用
StringBuilder
类来构建最终的字符串结果,这是一个比使用String
类更高效的字符串处理方式,尤其是在需要多次修改字符串内容时。 -
空间复杂度优化:虽然代码中创建了一个大小为
numRows * n
的二维数组,但实际上这个数组并没有被完全使用。在 Z 字形变换中,只有numRows
行和n
列中的部分元素会被填充。这个知识点涉及到空间复杂度的优化,可以通过只创建实际需要的行来减少空间消耗。 -
时间复杂度分析:理解代码执行过程中的时间消耗,包括遍历字符串、填充数组和构建结果字符串的时间。
-
算法效率:这段代码展示了解决特定问题所需的基本算法逻辑,同时也体现了在实际编程中如何权衡时间和空间的效率。
-
代码可读性:代码的组织结构清晰,变量命名直观,有助于理解算法的逻辑流程。
-
边界条件处理:代码正确处理了当
numRows
为 1 或字符串长度小于等于numRows
的边界条件,直接返回原字符串。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。