LeetCode高频算法面试题 - 006 - Z字形变换

5/4/2022

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

# 代码实现

tips: 以下代码是使用Go代码实现的不同解法, 文章最后可以看C++、C、Java、Python实现

解题思路:

可以根据官方给的实例为例,比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时

L   C   I   R
E T O E S I I G
E   D   H   N

每满 行数-1 (numRows-1) 可以为一组,因为字符串每第numRows个元素是Z形字符串的方向转折点。

如上图

分组: LE   ET  CO   DE ...等等为一组,
组数: 0    1   2    3
顺序: 正序 倒序 正序  倒序...

important: 当组数为偶数时给数组(strArr)正序赋值,当组数为奇数时数组(strArr)倒序赋值即可

func convert(s string, numRows int) string {
  if numRows == 1 {
    return s
  }

  b := numRows - 1
  strArr := make([]string, numRows)
  for k, v := range s {
    if (k/b)%2 == 0 { // 组数是偶数
      strArr[k%b] += string(v) // 数组(`strArr`)正序赋值
    } else {
      strArr[b-(k%b)] += string(v) // 数组(`strArr`)倒序赋值
    }
  }
  return strings.Join(strArr,"")
}

执行结果分析:

时间复杂度: O(n)
空间复杂度: O(n)

# 其他版本的语言实现

1、Python3

        反转   反转    反转
L       C      I      R
E   T   O   E  S   I  I   G
E       D      H      N
反转    反转    反转    反转

算法思路:

  • i 初始化 为0, flag = -1
  • res[i] += c, 将遍历每个字符 放入对应的行中
  • i += flag, 更新当前字符c 的所在的数组索引
  • important: 引入一个flag, 初始化=-1, 当达到Z字形顶点, 方向反转 flag=-flag.
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows < 2: return s
        res = ["" for _ in range(numRows)]
        i, flag = 0, -1
        for c in s:
            res[i] += c
            if i == 0 or i == numRows - 1: flag = -flag
            i += flag
        return "".join(res)

2、Java

        反转   反转    反转
L       C      I      R
E   T   O   E  S   I  I   G
E       D      H      N
反转    反转    反转    反转

算法思路和上面的Python3的思路一样:

  • i 初始化 为0, flag = -1
  • res[i] += c, 将遍历每个字符 放入对应的行中
  • i += flag, 更新当前字符c 的所在的数组索引
  • important: 引入一个flag, 初始化=-1, 当达到Z字形顶点, 方向反转 flag=-flag.
class Solution {
    public String convert(String s, int numRows) {
        if(numRows < 2) return s;
        List<StringBuilder> rows = new ArrayList<StringBuilder>();
        for(int i = 0; i < numRows; i++) rows.add(new StringBuilder());
        int i = 0, flag = -1;
        for(char c : s.toCharArray()) {
            rows.get(i).append(c);
            if(i == 0 || i == numRows -1) flag = - flag;
            i += flag;
        }
        StringBuilder res = new StringBuilder();
        for(StringBuilder row : rows) res.append(row);
        return res.toString();
    }
}

3、C++

        反转   反转    反转
L       C      I      R
E   T   O   E  S   I  I   G
E       D      H      N
反转    反转    反转    反转

算法思路和上面的Python3的思路一样:

  • i 初始化 为0, flag = -1
  • res[i] += c, 将遍历每个字符 放入对应的行中
  • i += flag, 更新当前字符c 的所在的数组索引
  • important: 引入一个flag, 初始化=-1, 当达到Z字形顶点, 方向反转 flag=-flag.
class Solution {
public:
  string convert(string s, int numRows) {

    if (numRows == 1) return s;

    vector<string> res(min(numRows, int(s.size()))); // 防止s的长度小于行数
    int i = 0;
    int flag = -1;

    for (char c : s) {
      res[i] += c;
      if (i == 0 || i == numRows - 1) {// 当前行i为0或numRows -1时,flag发生反向转折
        flag = -flag;
      }
      i += flag;
    }

    string ret;
    for (string row : res) {// 从上到下遍历行
      ret += row;
    }

    return ret;
  }
};

# 几种语言运行效果对比

Go程序在内存方面表现最佳, Python在内存和运行时间表现 比较其他的语言都比较差。

也欢迎关注我的公众号: 漫步coding。 一起交流, 在coding的世界里漫步。