【算法-LeetCode】6. Z 字形变换(数组;字符串;Array.reduce)

6. Z 字形变换 - 力扣(LeetCode)

文章起笔:2021年10月26日15:55:21

问题描述及示例

将一个给定字符串 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

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/zigzag-conversion
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

我的题解

这道题的思路还是比较直接的,第一眼看到觉得是不是要用二维数组来存储字符,但是仔细一想,如果 s 的长度较大时,二维数组中就会有大量的空元素,这显然是不大合理的,虽然觉得可行,但是我还是及时转变了思路。把 Z 字形的字符分别标上其在 s 中下标后,我很快发现了规律。

最上面一行和最下面一行的下标值都是 numRows - 1 的倍数。于是我就想着把下标和遍历顺序挂上钩。可以发现,在生成 Z 字形的图案时,填充的方向都是先往下再往上,一直如此循环直至 s 中的字符均被遍历。于是我就想到用一个 isDown 标志量来标识当前应该往下走还是应该往上走。而每次变换方向时,都是在首行或是末行,所以只要在这两个特殊的地方做方向的判断就可以了。

于是乎,大概思路就出来了:

遍历 s,并事先创建一个二维数组 lines,该二维数组用于存储遍历的结果,其第一维的长度就是 numRows,而第二维在开始时就是一个空数组(每个空数组都用来存储对应 Z 字符中对应行的字符)。在遍历的同时,根据当前遍历字符的下标 i 先判断一下当前是往下的还是往上的,然后再将当前字符放入合适的行。

大致图示如下:

在这里插入图片描述
详解请看下方注释:

/**
 * @param {string} s
 * @param {number} numRows
 * @return {string}
 */
var convert = function(s, numRows) {
  // 如果设置行数为1,那么Z形化后的逐行遍历结果和原字符一样,直接返回s即可
  // 如果这里不做判断,就会影响到后面计算remaining
  if (numRows === 1) {
    return s;
  }
  // lines用于存储Z字形的逐行遍历结果,它是一个二维数组,初始时只有对应行数的若干个空数组
  let lines = Array.from({length: numRows}).map(
    () => []
  );
  // isDown是一个标志量,用来判断当前是应该往上走还是该往下走,这是一个关键点
  let isDown = false;
  // position用于标识当前遍历字符应该lines中的哪个数组
  let position = 0;
  // remaining用来存储当前字符在s中的下标对 numRows 取余的结果,
  // 用于判断当前是否该转向,也用于确定position的值,这也是一个关键变量
  let remaining = 0;
  // 开始遍历s字符串
  for(let i = 0; i < s.length; i++) {
    remaining = i % (numRows - 1);
    // 如果remaining不为0,则说明当前字符不在Z字形图案的首行或尾行,也就不需要变换方向
    // 保持原来的值就可以了,否则的话就取原来值的反值
    isDown = remaining ? isDown : !isDown;
    // 根据isDown来判断当前的方向,然后结合remaining余数来确定该将当前字符放到处于哪一行
    // 注意方向不同的话,其确定行数的方式也不一样,从上往下的话就直接从lines的头部开始计算
    // (取remaining即可),从下往上的话,就要注意从lines的尾部开始计算了
    position = isDown ? remaining: numRows - remaining - 1;
    // 将当前字符放到对于的行里
    lines[position].push(s[i]);
  }
  // 注意遍历完成后,lines中存储的是各行的结果,需要将他们按序合并成一行
  lines = lines.reduce((acc, cur) => acc.concat(cur));
  // 最后将lines中单个字符合并成一个字符串
  return lines.join('');
};

提交记录
执行结果:通过
1157 / 1157 个通过测试用例
执行用时:112 ms, 在所有 JavaScript 提交中击败了31.86%的用户
内存消耗:44.5 MB, 在所有 JavaScript 提交中击败了23.67%的用户
时间:2021/10/26 16:16

官方题解

更新:2021年7月29日18:43:21

因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。

更新:2021年10月26日16:17:46

参考:Z 字形变换 - Z 字形变换 - 力扣(LeetCode)

【更新结束】

有关参考

更新:2021年10月26日16:21:03
参考:Array.prototype.reduce() - JavaScript | MDN

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值