LeetCode6.Z字形变换
难度:中等
目录
官方解法1-构造Z字型数组模拟
案例分析
案例一:
s = "PAYPALISHIRING", numRows = 3,当该案例构造成数组时,得到以下的图形
案例二:
s = "PAYPALISHIRING", numRows = 4当该案例构造成数组时,得到以下的图形
规律探索
- 1.通过上面两个案例可得,我们可以将一列和一条斜线做作为一个周期t, t = r + r - -2 = 2r - 2
- 2.通过观察,一个周期占用了r - 1列
- 3.假设,一共有n个元素,那么,n个元素一共有n / t 个周期,一共占用 c = n / t * (r - 1)列
- 但是,由于可能最后几个元素不满一个周期,上面的等式明显是有问题的,只会取整,即会漏掉不满一个周期的那几个元素,要想解决这个问题,需将等式转换成 c = (n + t - 1)/t * (r - 1)
原因:
- 如果有这么一个场景,其一列能放六个元素,问13个元素需要放几列
- 如果按照上面的思想去做这道题,t = 6,n = 13, c = n / t = 2,结果为2
- 但是,显然,我们需要三行才可以承载所有的元素,所以,我们需要加上5即 18 / 6 = 3,就是三行
- 同理上面
代码实现
class Solution {
public String convert(String s, int numRows) {
var r = numRows;
var t = 2 * r - 2;
var n = s.length();
if (r >= n || r == 1) {
return s;
}
var c = (n + t - 1)/t * (r - 1);
var strBul = new StringBuilder();
var matrix = new char[r][c];
for (int i = 0,x = 0,y = 0; i < n; i++) {
matrix[x][y] = s.charAt(i);
if (i % t < r - 1) {
x++;
}else {
x--;
y++;
}
}
for (int i = 0; i < r; i++) {
for (int j = 0; j < c; j++) {
if (matrix[i][j] != 0) {
strBul.append(matrix[i][j]);
}
}
}
return strBul.toString();
}
}
代码分析:
- 1.第一个判断
- 1.1r >= n时,就是所需要的行大于等于元素个数n,就是一整列的情况,不需要构造Z,直接返回
- 1.2r == 1时,就是所需要的行等于1,就是一整行的情况,不需要构造Z,直接返回
- 2.声明matrix二维数组用于构造Z字形与strBul拼接字符串
- 3.for循环
- 3.1变量的定义与初始化
- 3.1.1 'i'用于遍历字符串,表示第i + 1个元素的下标,并初始化为0
- 3.1.2 'x,y'分别表示二维数组matrix中的行与列,并初始化为0(我们从最左上角开始构造Z)
- 3.2追加
- 3.3if else详解
- 3.1变量的定义与初始化
- 4.最后将横向遍历二维数组,不是0的都拼接上去即可
问题
- 这种解法会产生大量的冗余空间,这些冗余空间使得代码效率变低
官方解法二-压缩上述二维数组
思路分析
- 我们不直接构造上述的二维数组,转而我们构造多个StringBuilder去完成上述的代码
代码实现
class Solution {
public String convert(String s, int numRows) {
var res = new StringBuilder();
var r = numRows;
var t = 2 * r - 2;
var tBul = new StringBuilder[r];
var n = s.length();
for (int i = 0; i < r; i++) {
tBul[i] = new StringBuilder();
}
if (r >= n || r == 1) {
return s;
}
for (int i = 0,x = 0; i < n; i++) {
tBul[x].append(s.charAt(i));
if (i % t < r - 1) {
x++;
}else {
x--;
}
}
for (var item : tBul) {
res.append(item);
}
return res.toString();
}
}
代码分析
- 1.注意,在构造StringBuilder数组的时候不要用Arrays工具类里面的fill方法,因为这样会导致所有的下标都指向同一个StringBuilder对象
- 2.for循环
- 2.1因为每一个都是StringBuilder,所以y的创建就没有必要了
- 2.2根据方法一的思想,让x移动即可,注意,如果拼接,它相当于y++,是等效的
- 3.将所有的StringBuilder拼接起来即可
问题
- 还是用到了额外的空间,能否直接拼接呢?
官方解法三-直接构造法
算法思想
- 根据方法一所给的图像,利用里面隐藏的元素下标直接拼接,无需构造
代码实现
class Solution {
public String convert(String s, int numRows) {
var res = new StringBuilder();
var r = numRows;
var t = 2 * r - 2;
var n = s.length();
if (r == 1 || r >= n) {
return s;
}
for (int i = 0; i < r; i++) {
for (int j = 0; j + i < n; j += t) {
res.append(s.charAt(j + i));
if (i < r - 1 && i > 0 && j + t - i < n) {
res.append(s.charAt(j + t - i));
}
}
}
return res.toString();
}
}
代码分析
结论
这道算法题更多的是推导,也没有说具体的用什么有名字的算法,刷刷刷,刷着刷着就会了,我来总结一下的几点
1.方法一中,[t,r,c,n]概念的深化理解
2.方法一的代码实现
3.方法三的代码实现
最后,当我们发现某个算法示例呈周期性变化的时候,都可以借鉴此算法