我打螺丝回来了,每日算法更新继续!!!!
来,题目展示。
将一个给定字符串 s
根据给定的行数 numRows
,以从上往下、从左到右进行 Z 字形排列。
比如输入字符串为 "PAYPALISHIRING"
行数为 3
时,排列如下:
css
P A H N
A P L S I I G
Y I R
之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"
。
请你实现这个将字符串进行指定行数变换的函数:
c
string convert(string s, int numRows);
示例 1:
ini
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"
示例 2:
css
输入: s = "PAYPALISHIRING", numRows = 4
输出: "PINALSIGYAHRPI"
解释:
P I N
A L S I G
Y A H R
P I
示例 3:
ini
输入: s = "A", numRows = 1
输出: "A"
提示:
1 <= s.length <= 1000
s
由英文字母(小写和大写)、','
和'.'
组成1 <= numRows <= 1000
第一次解题
js
/**
* @param {string} s
* @param {number} numRows
* @return {string}
*/
const convert = function (s, numRows) {
// 如果字符串长度小于等于行数,或者行数为1,直接返回原字符串
if (s.length <= numRows || numRows === 1) {
return s;
}
// 创建一个二维数组,表示 Z 字形排列
const zArr = Array.from({ length: numRows }, () => []);
// 初始化当前字符的插入行和插入方向
let index = 0;
let direction = 1;
// 遍历输入字符串
for (let i = 0; i < s.length; i++) {
// 将当前字符插入到对应行
zArr[index].push(s[i]);
// 判断是否到达 Z 字形的上端或下端,更新插入方向
if (index === 0) {
direction = 1; // 如果在第一行,变换为向下方向
} else if (index === numRows - 1) {
direction = -1; // 如果在最后一行,变换为向上方向
}
// 更新当前字符的插入行
index += direction;
}
// 将二维数组展平并拼接成最终结果的字符串
return zArr.flat().join('');
};
解题思路
首先判断输入的参数,如果s
小于numRows
或者numRows
是1
的话直接返回这个字符串就可以了,如果不满足这两个条件之一的话那么继续,先创建一个二维数组来暂存z字形排列的字符串,来看核心 就是index
和direction
,当index
等于0
的时候说明当前插入的在第一个数组,那么此时每个字符就从一个数组到最后一个数组依次插入就好了,当到达最后一个数组也就 numRows-1
的时候,插入顺序就是从倒数第二个数组到第二个数组这样的顺序插入,等这么插插插完之后,就是数组扁平化最后合成字符串返回就完事了。
时间复杂度:
- 数组初始化:
Array.from({ length: numRows }, () => []);
这一部分的时间复杂度是 O(numRows)。 - 遍历字符串:
for (let i = 0; i < s.length; i++)
这一部分的时间复杂度是 O(s.length),其中 s 是输入字符串的长度。 - 展平数组和拼接字符串:
zArr.flat().join('')
这一部分的时间复杂度主要取决于展平数组和拼接字符串的操作,总体来说是 O(s.length)。
因此,整个代码的时间复杂度是 O(s.length)。
空间复杂度:
- 二维数组的空间:
Array.from({ length: numRows }, () => []);
创建了一个 numRows x k 的二维数组,其中 k 是字符串 s 的平均长度。因此,空间复杂度是 O(numRows * k)。 - 其他辅助变量: 除了二维数组之外,还有几个辅助变量(
index
和direction
),这些变量占用常量级别的空间。因此,忽略不计。
综上所述,整个代码的空间复杂度是 O(numRows * k)。
第二种解法
js
const convert = function (s, numRows) {
// 如果字符串长度小于等于行数,或者行数为1,直接返回原字符串
if (s.length <= numRows || numRows === 1) {
return s;
}
// 创建一个数组,表示 Z 字形排列的每一行
const rows = new Array(numRows).fill('');
// 计算 Z 字形排列中每个循环的长度
const n = 2 * numRows - 2;
// 遍历输入字符串
for (let i = 0; i < s.length; i++) {
// 计算当前字符在 Z 字形排列中的位置
const x = i % n;
// 根据当前位置,将字符添加到相应的行
rows[Math.min(x, n - x)] += s[i];
}
// 将每一行的字符拼接成最终结果的字符串
return rows.join("");
};
这种解法的是就是找z字形排列的规律,我画个图大家看看
把这个排列拆解成一个一个的
v
,每一个v
的字符数量是numRows + numRows - 2
也就是2numRows-2
,然后在看看每一个规律,前四个对应着0到3
,也就是 i % (2numRows-2)
, 下来两个分别是2, 1也就是 2numRows-2 - i % (2numRows-2)
这种解法其实我第一次想的就是找规律,但是一直没有找到这个后面的 2numRows-2 - i % (2numRows-2)
这个计算的规律,看了别人写的才想出来,还得是广大的网友。
时间复杂度:
- 字符串遍历:
for (let i = 0; i < s.length; i++)
这一部分的时间复杂度是 O(s.length),其中 s 是输入字符串的长度。
整体上,时间复杂度是 O(s.length)。
空间复杂度:
- 数组初始化:
const rows = new Array(numRows).fill('');
创建了一个长度为 numRows 的数组,这个操作的空间复杂度是 O(numRows)。 - 其他辅助变量: 除了数组之外,只使用了常量级别的辅助变量(如
n
、x
、i
),因此在空间复杂度的分析中可以忽略。
整体上,空间复杂度是 O(numRows)。