力扣-6-Z 字形变换
其实叫“N 字形变换”更形象
第一版代码,在二维数组中模拟打印过程,但是时间和空间效率都很差,都是 n2
string convert(string s, int numRows) {
int len = s.size();
// 如果只有一行或者只有一列则直接输出
if (numRows == 1 || numRows >= len) return s;
// 计算每个周期占用的字符数量
int cycle = numRows + numRows - 2;
int column = (1 + numRows - 2) * len / cycle + len % cycle;
vector<string> maps(numRows, string(column, 0));
for (int x = 0, y = 0, k = 0; k < len; k++) {
maps[x][y] = s[k];
if (k % cycle + 1 < numRows) x++;
else {
x--;
y++;
}
}
string res;
for (string str : maps) for (char ch : str) if (ch)res += ch;
return res;
}
思路:模拟
准备一个二维数组,两次遍历,第一次模拟打印过程,第二次拼接输出结果
整个图形其实能看做是打印一个个 V 形单元:先从上往下打印 numRows 个,然后斜着往右上角打印 numRows -2 个
既然是模拟,那么关键在于控制这两个阶段。如图示,把左上角看作是二维数组的第一个位置 [0][0],x 代表行索引,y 代表列索引
每个单元打印过程:
- 第一个阶段:从上往下,x 行索引递增,列索引 y 不变
- 第二个截断:行索引 x 递减,列索引 y 递增
最关键也是最困难的,如何控制这两个状态的切换?
折点发生在:每个单元打印到第 numRows 个元素时

边界问题:
- 根据代码分析,当 numRows = 1,每单元字符数算出来 cellCharNum = 0,所以需要单独处理
- column 的计算,行数 = 单元数 * 每单元宽度,单元数默认向下取整,手动 +1 保证不越界
其他问题:Java 中 char 存在默认值,改用包装类,在拼接结果时判断位置是否填充元素
public String convert(String s, int numRows) {
if (numRows == 1) return s;
int len = s.length();
int cellCharNum = 2 * numRows - 2;
int column = (len / cellCharNum + 1) * (numRows - 1);
Character[][] print = new Character[numRows][column];
int index = 0, x = 0, y = 0;
while (index < len) {
print[x][y] = s.charAt(index);
if (index % cellCharNum < numRows - 1) {
x++;
} else {
y++;
x--;
}
index++;
}
StringBuilder result = new StringBuilder();
for (int i = 0; i < numRows; i++) {
for (int j = 0; j < column; j++) {
if (print[i][j] != null) {
result.append(print[i][j]);
}
}
}
return result.toString();
}
改成静态二维布尔数组标记,内存减少了一点,但是耗时增加了不少,意义不大

思路:直接算位置
public String convert(String s, int numRows) {
if (numRows == 1) return s;
int cellNum = 2 * numRows - 2;
StringBuilder res = new StringBuilder(s.length());
for (int i = 0; i < numRows; i++) {
int j = i;
while (j < s.length()) {
res.append(s.charAt(j));
if (0 < i && i < numRows - 1) {
int index = 2 * (numRows - 1 - i) + j;
if (index < s.length()) {
res.append(s.charAt(index));
}
}
j += cellNum;
}
}
return res.toString();
}
优化点:
j%cellNum直接取 i,去掉取模操作- 初始化设置 builder 长度
时间复杂度:O(N),因为虽然是两层循环,但是之访问了s.length()个位置
空间复杂度:O(1),除返回结果外,没有额外空间

浙公网安备 33010602011771号