开启我的LeetCode刷题日记:6. Z 字形变换

89 阅读2分钟

Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情

编程世界总是离不了算法

最近在看框架源码时,会有很多算法的实现逻辑,有时候会感到吃力

于是决定蹭着假期,加强算法和数据结构相关的知识

那怎么提升呢?

其实我知道算法这东西没有捷径,多写多练才能提升,于是我开启我的LeetCode刷题之旅

第一阶段目标是:200道,每天12

为了不乱,本系列文章目录分为三部分:

  1. 今日题目:xxx
  2. 我的思路
  3. 代码实现

今天题目:2016. 增量元素之间的最大差值

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

我的思路

两种解法,第一种比较好理解,第二种比较精简效率也更高但是理解起来有点麻烦\

第一种思路就是通过两个变量,一个记录当前的前进方向是向上还是向下,另一个记录当前在第几行的索引,循环往复就行

代码实现

/**
 * @param {string} s
 * @param {number} numRows
 * @return {string}
 */
var convert = function (s, numRows) {
  if (numRows === 1) return s // 如果只有一行直接返回就行了
  let arr = new Array(numRows).fill('') // 创建一个数组,用来存储每一行的结果
  let down = true // 定义方向是否向下
  let row = 0 // 定义当前行数,从 0 开始向下循环
  for (let i = 0; i < s.length; i++) { // 遍历 s
    arr[row] += s[i] // 拼接当前字母到arr中对应的位置上
    if (down) { // 如果当前向下,行数加一,下个字符添加到下一行,否则相反,不需要考虑边界
      row++
    } else {
      row--
    }
    if (row === numRows - 1) { // 在这统一处理越界情况
      down = false // 如果触碰到最后一行,转向上
    } else if (row === 0) {
      down = true // 如果触碰到第一行,转向下
    }
  }
  return arr.join('') // 直接拼接数组返回就行了
};



第二种思路

我们观察一下第一行的所有值和最后一行的所有值,发现一个规律,

(1,7,13)(4, 10) 都是按 6 递增,行数有 4 行 + z 的向上方向会经过除了第一行和最后一行的所有行,所以 6 的获取来源为 numRows + (numRows - 2), 按照数学方法可以简化为 numRows * 2 - 2 按照上面的规律遍历所有字母,展开会变成

这时候我们可以发现,第[0]行和第[numRows-1] 行已经是最终的结果了,也就是目标的最上最下两行已经完成了

其他行则不完整分为了两个部分,第[numRows-1 - 1]行,也就是倒数第二行+第[numsRows-1 + 1]行,也就是溢出的第一行逐位按照顺序拼接在一起就是最终需求的结果,其它行同上

于是统计规律,当 i % n < numRows的时候,按照图中也就是前面 4 行的,直接放进数组存起来就可以了,也就是arr[i % n] += s[i]

但是当 i % n >= numRows的时候,也就是 2 图中蓝色的线的部分我们需要围绕最后一行做个翻转,再放到数组中,也就是 最后一行的索引 - (当前溢出的行应该在的索引 - 最后一行的索引) , numRows - 1 - (i % n - (numRows - 1)),按照图 1 第一行数字 5为例 , 3 - (4 - 3) = 2, 6 为例 3 - (5 - 3) = 1, 7 为例, 6 % 6 < 6,直接走 4 的逻辑 优化6 的公式 numRows - 1 - (i % n - (numRows - 1)) = numRows - 1 - i % n + (numRows - 1) = numRows - 1 - i % n + numRows - 1 = numRows + numRows - 1 - 1 - i%n = numRows * 2 - 2 - i % n

使用三元优化if判断,简化为i % n < numRows ? arr[i % n] += s[i] : arr[numRows * 2 - 2 - i % n] += s[i]

拼接返回就可以了

代码实现

var convert = function (s, numRows) {
  if (numRows === 1) return s // 首先还是,只有一行直接返回
  let arr = new Array(numRows).fill('') // 创建一个数组,存储每行
  let n = numRows * 2 - 2 // 1. 
  for (let i = 0; i < s.length; i++) {
    i % n < numRows ? arr[i % n] += s[i] : arr[numRows * 2 - 2 - i % n] += s[i] // 8.
  }
  return arr.join('') // 拼接返回
};

总结

实现方式其实有很多,这里仅供参考~

由于刚开始刷题,也不知道从哪里刷好,如果前辈们有好的建议,希望不吝赐教,感谢🌹