算法42,模拟算法,模拟z字形变换

LeetCode 6. Z 字形变换 · 总结

一、题目在问什么

把字符串 s 按 Z 字形(从上往下、再从下往上斜着回摆)填入 numRows 行,然后按行从左到右逐行读出,返回读出的结果。

复制代码
输入:  s = "PAYPALISHIRING", numRows = 3
P   A   H   N
A P L S I I G
Y   I   R
输出: "PAHNAPLSIIGYIR"     // PAHN + APLSIIG + YIR

关键认知 :这题根本不用真的"画"出 Z 字阵列。它只问"最终读出来的顺序",所以本质是找下标规律,把字符直接按读出顺序收集起来。


二、核心规律:一个周期 cycle

概念 公式 含义
周期长度 cycle = 2 × numRows − 2 一个"竖列向下 + 斜列向上"单元包含的字符数
第 i 行竖列字符 j = i, i+cycle, i+2cycle ... 每行的"骨架",等差出现
第 i 行斜列字符 k = j + cycle − 2i 夹在两个竖列字符之间的那一个

为什么斜列偏移是 cycle − 2i :第 i 行的竖列字符到自己那一行"峰值"还有 numRows−1−i 步,回摆经过 numRows−1−i 步回到同行,两者相加即 2(numRows−1−i) = cycle − 2i。

只有中间行有斜列字符​ ------ 首行和末行每个周期只出现一次,这是最容易漏的分支判断。


三、两种解法

① 公式法(推荐,找规律直取)

复制代码
class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1 || s.length() <= numRows) return s;
        int n = s.length(), cycle = 2 * numRows - 2;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < numRows; i++) {
            for (int j = i; j < n; j += cycle) {
                sb.append(s.charAt(j));                        // 竖列
                int k = j + cycle - 2 * i;                     // 斜列
                if (i != 0 && i != numRows - 1 && k < n) {
                    sb.append(s.charAt(k));
                }
            }
        }
        return sb.toString();
    }
}

② 模拟法(更不容易写错,行数方向法)

复制代码
class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1) return s;
        StringBuilder[] rows = new StringBuilder[numRows];
        for (int i = 0; i < numRows; i++) rows[i] = new StringBuilder();
        int cur = 0, step = 1;                     // 当前行 + 行进方向
        for (char c : s.toCharArray()) {
            rows[cur].append(c);
            if (cur == 0) step = 1;                // 触顶→向下
            else if (cur == numRows - 1) step = -1;// 触底→向上
            cur += step;
        }
        StringBuilder sb = new StringBuilder();
        for (StringBuilder r : rows) sb.append(r);
        return sb.toString();
    }
}

模拟法用 step 在上下边界翻转方向,天然规避了斜列下标算错的问题,口算不熟时优先用它;公式法更省内存,是面试时更想听到的"找规律"思路。


四、三个必踩的坑

# 坑 触发后果
1 numRows == 1 不特判 cycle = 0 → j += 0 死循环(本题头号 WA/TLE 原因)
2 斜列写成 s.charAt(j) 输出 PAHNAALLIIGGYIR:字符重复 + 丢字符 + 长度变长
3 漏判 i != 0 && i != numRows−1,或漏 k < n 首尾行被复制 / 越界 StringIndexOutOfBoundsException

验证用例 :("PAYPALISHIRING", 3) → PAHNAPLSIIGYIR;("PAYPALISHIRING", 4) → PINALSIGYAHRPI;("AB", 1) → AB;("ABC", 2) → ACB。上面三个坑会在这几个用例上立刻暴露(已用 3000 组随机用例与真实 Z 字模拟比对,公式法全部一致)。


五、复杂度与迁移价值

  • 时间 O(n):每个字符恰好被访问一次。
  • 空间 O(n):结果串本身;模拟法额外 O(numRows) 行缓冲。
  • 可迁移的思维 :凡是"按某种规则重排再读出"的题(如螺旋矩阵、对角线遍历),先别急着建二维数组模拟,先算周期和下标通项------能推出公式就 O(1) 额外空间,推不出再用方向模拟兜底。

一句话记住:cycle = 2×numRows−2,竖列 j,斜列 j + cycle − 2i,首尾行没有斜列,一行行数时必先判 numRows == 1。

相关推荐
lisw054 小时前
Dayhoff的PAM替换矩阵理论评介!
线性代数·算法·矩阵
一起学安全技术5 小时前
拆大模型 03|注意力的一句话版本:不用论文讲清 Attention
深度学习·算法·语言模型·transformer
Delightrs6 小时前
Rust LeetCode238 算法题解
开发语言·算法·rust
自信150413057597 小时前
重生之从0开始学习c++之——vector-中篇
c++·学习·算法
GoAI18 小时前
DeepSeek Harness Memory (一)主流记忆插件总结
后端·算法·面试
Pniubi18 小时前
力扣54.螺旋矩阵
算法·leetcode·矩阵
choumou_M21 小时前
算法_1:由浅入深学习快速排序QuickSort
学习·算法·排序算法
Coder_Shenshen21 小时前
罗克韦尔ACD工程归档离线变量树解析
算法·c#
心仪久了会沦陷1 天前
C语言数据结构入门——树与二叉树详解:概念、堆实现与链式遍历
c语言·数据结构·算法