LeetCode-day6-Z 字形变换(中等)

问题描述

  • 将一个给定字符串根据给定的行数,以从上往下、从左到右进行 Z 字形排列。
  • 比如输入字符串为 "LEETCODEISHIRING" 行数为 3 时,排列如下
   L   C   I   R
   E T O E S I I G
   E   D   H   N
  • 之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"LCIRETOESIIGEDHN"。

示例

  输入: s = "LEETCODEISHIRING", numRows = 3
  输出: "LCIRETOESIIGEDHN"

  输入: s = "LEETCODEISHIRING", numRows = 4
  输出: "LDREOEIIECIHNTSG"
  解释:
        L     D     R
        E   O E   I I
        E C   I H   N
        T     S     G

题解

思路一

  • 按照传入的行数生成同样长度的集合,集合中的每个元素同样定义成集合,用于存储当前行所有的字节
  • 遍历传入的字符串,按照传入的指定行数将字符串排列成指定的Z字型,遍历的同时将字节按照其所在行插入对应的下标的集合中
  • 最后顺序遍历集合中的每个集合将字节拼成字符串返回
public static String convert(String s, int numRows) {

    if (numRows <= 1) {
        return s;
    }
    ArrayList<ArrayList<Character>> list = new ArrayList<>();
    for (int i = 0; i < numRows; i++) {
        list.add(new ArrayList<>());
    }
    int index = 0, step = 1;
    for (int i = 0; i < s.length(); i++) {
        list.get(index).add(s.charAt(i));
        if (index == numRows - 1) {
            index = numRows - 2;
            step = -1;
        } else if (index == 0) {
            index = 1;
            step = 1;
        } else {
            index += step;
        }
    }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numRows; i++) {
        list.get(i).forEach(sb::append);
    }
    return sb.toString();
}
  • 上例中的list中可以直接存放StringBuilder,在每次遍历的时候直接将字节拼接在StringBuilder中,可以避免最后的遍历拼接,提高效率。代码如下:
public static String convert(String s, int numRows) {
  if (numRows <= 1) {
          return s;
  }
  ArrayList<StringBuilder> list = new ArrayList<>();
  for (int i = 0; i < numRows; i++) {
      list.add(new StringBuilder());
  }
  int index = 0, step = 1;
  for (int i = 0; i < s.length(); i++) {
      list.get(index).append(s.charAt(i));
      if (index == numRows - 1) {
          index = numRows - 2;
          step = -1;
      } else if (index == 0) {
          index = 1;
          step = 1;
      } else {
          index += step;
      }
  }
  StringBuilder sb = new StringBuilder();
  list.forEach(sb::append);
  return sb.toString();
}
  • 时间复杂度 O(n)
  • 空间复杂度 O(n)

思路二

  • 观察生成的Z字型的结构不难发现第一层和最后一层的每两个字符在原字符串中的下标的差值是固定的,即是最大步长 2 * (numRows - 1)
  • 其他层的每三个字符之间在原字符串中的下标的差值的和也是固定的,同样是最大步长2 * (numRows - 1)
  • 而每一层的第一个字节在原字符串的下标即为当前层的层数-1
  • 相应的每一层的第一个步长即为 maxStep - 2 * (n - 1) 最后一层除外。其中n为当前的层数,maxStep为最大步长
  • 遍历每一层按照步长选取指定下标的字节进行拼接,因为除第一层和最后一层外每次遍历都需要变换步长的大小,且指定初始化步长的大小,所以在每次遍历完成之后需要初始化下一次遍历时的初始步长
  • 又因为如果不是第一层和最后一层每次遍历都会改变步长,所以初始化步长的值变为2 * (n - 1), 其中n为当前层数
public static String convert(String s, int numRows) {
    if (numRows <= 1) {
        return s;
    }
    // 最大步长
    int maxStep = 2 * (numRows - 1);
    // 当前步长
    int step = maxStep;
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < numRows; i++) {
        for (int j = i; j < s.length(); j += step) {
            sb.append(s.charAt(j));
            // 如果不是最大步长,需要每次变化步长大小
            step = step != maxStep ? maxStep - step : maxStep;
        }
        step = 2 * (i + 1);

    }
    return sb.toString();
}
  • 时间复杂度 O(n)
  • 空间复杂度 O(1)

LeetCode题目地址

猜你喜欢

转载自www.cnblogs.com/lastpriest/p/12923243.html