力扣-6-Z 字形变换

其实叫“N 字形变换”更形象

第一版代码,在二维数组中模拟打印过程,但是时间和空间效率都很差,都是 n2

string convert(string s, int numRows) {
	int len = s.size();
	// 如果只有一行或者只有一列则直接输出
	if (numRows == 1 || numRows >= len) return s;
	// 计算每个周期占用的字符数量
	int cycle = numRows + numRows - 2;
	int column = (1 + numRows - 2) * len / cycle + len % cycle;

	vector<string> maps(numRows, string(column, 0));
	for (int x = 0, y = 0, k = 0; k < len; k++) {
		maps[x][y] = s[k];
		if (k % cycle + 1 < numRows) x++;
		else {
			x--;
			y++;
		}
	}
	string res;
	for (string str : maps) for (char ch : str) if (ch)res += ch;
	return res;
}

思路:模拟

准备一个二维数组,两次遍历,第一次模拟打印过程,第二次拼接输出结果

整个图形其实能看做是打印一个个 V 形单元:先从上往下打印 numRows 个,然后斜着往右上角打印 numRows -2 个

既然是模拟,那么关键在于控制这两个阶段。如图示,把左上角看作是二维数组的第一个位置 [0][0],x 代表行索引,y 代表列索引

每个单元打印过程:

  • 第一个阶段:从上往下,x 行索引递增,列索引 y 不变
  • 第二个截断:行索引 x 递减,列索引 y 递增

最关键也是最困难的,如何控制这两个状态的切换?

折点发生在:每个单元打印到第 numRows 个元素时

image

边界问题:

  1. 根据代码分析,当 numRows = 1,每单元字符数算出来 cellCharNum = 0,所以需要单独处理
  2. column 的计算,行数 = 单元数 * 每单元宽度,单元数默认向下取整,手动 +1 保证不越界

其他问题:Java 中 char 存在默认值,改用包装类,在拼接结果时判断位置是否填充元素

public String convert(String s, int numRows) {
    if (numRows == 1) return s;
    int len = s.length();
    int cellCharNum = 2 * numRows - 2;
    int column = (len / cellCharNum + 1) * (numRows - 1);
    Character[][] print = new Character[numRows][column];
    int index = 0, x = 0, y = 0;
    while (index < len) {
        print[x][y] = s.charAt(index);
        if (index % cellCharNum < numRows - 1) {
            x++;
        } else {
            y++;
            x--;
        }
        index++;
    }
    StringBuilder result = new StringBuilder();
    for (int i = 0; i < numRows; i++) {
        for (int j = 0; j < column; j++) {
            if (print[i][j] != null) {
                result.append(print[i][j]);
            }
        }
    }
    return result.toString();
}

改成静态二维布尔数组标记,内存减少了一点,但是耗时增加了不少,意义不大

image

思路:直接算位置

public String convert(String s, int numRows) {
    if (numRows == 1) return s;
    int cellNum = 2 * numRows - 2;
    StringBuilder res = new StringBuilder(s.length());
    for (int i = 0; i < numRows; i++) {
        int j = i;
        while (j < s.length()) {
            res.append(s.charAt(j));
            if (0 < i && i < numRows - 1) {
                int index = 2 * (numRows - 1 - i) + j;
                if (index < s.length()) {
                    res.append(s.charAt(index));
                }
            }
            j += cellNum;
        }
    }
    return res.toString();
}

优化点:

  1. j%cellNum 直接取 i,去掉取模操作
  2. 初始化设置 builder 长度

时间复杂度:O(N),因为虽然是两层循环,但是之访问了s.length()个位置
空间复杂度:O(1),除返回结果外,没有额外空间

posted @ 2024-08-03 10:44  YaosGHC  阅读(22)  评论(0)    收藏  举报