算法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。

相关推荐
?abc!1 小时前
(动态规划+DFS)494. 目标和
算法·深度优先·动态规划
信奥卷王1 小时前
[GESP202406 六级] 二叉树(含视频)
数据结构·算法
惊鸿一博1 小时前
Plücker直线_ 用两个三维向量描述一条空间直线
线性代数·算法·直线·几何表示
小道士写程序2 小时前
Rust 中的字符串与切片(String 与 &str)
算法
Tisfy2 小时前
LeetCode 3863.将一个字符串排序的最小操作次数:分类讨论(一次遍历)——简单易懂版
算法·leetcode·字符串·题解·分类讨论
Young Doro2 小时前
从平衡表征到最小充分因果表征:端到端神经网络因果效应估计
算法
0+1112 小时前
算法 --递归
算法·leetcode
打工仔折腾 AI2 小时前
介入式脑机接口的跨尺度解码与闭环调控:从算法落地到后端工程实践
人工智能·后端·python·深度学习·算法·ai agent 实战
凤山老林3 小时前
授权模型只会RBAC?深入 ABAC 授权模型,实现权限判断做成策略引擎
大数据·数据库·算法
海绵宝宝转agent3 小时前
2026-10-21学习笔记
笔记·学习·算法