面试经典150题[022]:Z 字形变换(LeetCode 6)

Z 字形变换(LeetCode 6)

题目链接:Z 字形变换(LeetCode 6)

难度:中等

1. 题目描述

给定一个字符串 s 和行数 numRows,按照 Z 字形从上到下、从左到右排列字符串,然后逐行读取生成新字符串。

  • 字符串长度 1 <= s.length <= 1000,由英文字母(大小写)、',' 和 '.' 组成。
  • 行数 1 <= numRows <= 1000

示例:

复制代码
输入: s = "PAYPALISHIRING", numRows = 3
输出: "PAHNAPLSIIGYIR"
解释:
P   A   H   N
A P L S I I G
Y   I   R

输入: s = "PAYPALISHIRING", numRows = 4
输出: "PINALSIGYAHRPI"
解释:
P     I    N
A   L S  I G
Y A   H R
P     I

输入: s = "A", numRows = 1
输出: "A"

2. 问题分析

2.1 规律

  • Z 字形排列是一个周期性模式:
    • 每个周期包含 numRows 个字符向下排列,再斜向上排列到第一行。
    • 周期长度为 2 * numRows - 2(向下 numRows 步,向上 numRows - 2 步)。
    • 特殊情况:
      • numRows = 1 时,直接返回原字符串。
      • numRows = 2 时,周期为 2,交替填充两行。
  • 最终输出需要按行读取,行内字符按顺序拼接。
  • 核心问题:如何确定每个字符在 Z 字形中的位置,并按行输出?

2.2 解法思路

方法 1:按行分配(直观模拟)
  • 模拟 Z 字形排列的过程:
    • 遍历字符串 s,按照 Z 字形的路径分配字符到对应行。
    • 使用一个数组(或字符串列表)存储每行的字符。
    • 跟踪当前行和移动方向(向下或向上)。
    • 最后按行拼接结果。
  • 优点:直观,易于理解;空间复杂度较高。
方法 2:按行访问(数学规律)
  • 直接计算每个字符在 Z 字形中的行号,优化空间复杂度。
  • 每个周期的字符位置有规律:
    • i 行的字符索引可以通过周期公式计算。
    • 第一行和最后行的字符索引间隔为 2 * numRows - 2
    • 中间行的字符有两个来源:周期内的向下和斜向上。
    • 向下的字符坐标:当前周期起始点+当前行号。
    • 斜向上的字符坐标:下一个周期的起始点-当前行号。
  • 优点:空间复杂度 O(1),仅需输出字符串的空间。
  • 本题采用 方法 2,因为它更高效且空间复杂度低。

2.3 示例

s = "PAYPALISHIRING", numRows = 4 为例:

复制代码
P     I    N
A   L S  I G
Y A   H R
P     I
  • 周期长度 = 2 * numRows - 2 = 6
  • 第 0 行:P (0), I (6), N (12) → 索引间隔 6。
  • 第 1 行:A (1), L (4), S (7), I (10) → 索引规律为周期内和周期间。
  • 第 2 行:Y (2), A (3), H (8), R (9)。
  • 第 3 行:P (5), I (11)。
  • 按行拼接:PINALSIGYAHRPI

3. 代码实现

Python

python 复制代码
class Solution:
    def convert(self, s: str, numRows: int) -> str:
        if numRows == 1 or numRows >= len(s):
            return s
        
        result = []
        n = len(s)
        cycle = 2 * numRows - 2
        
        for i in range(numRows):
            for j in range(0, n, cycle):
                # 第一行和最后行
                if i == 0 or i == numRows - 1:
                    if j + i < n:
                        result.append(s[j + i])
                # 中间行
                else:
                    # 周期内的字符
                    if j + i < n:
                        result.append(s[j + i])
                    # 周期间的字符(斜向上)
                    if j + cycle - i < n:
                        result.append(s[j + cycle - i])
        
        return ''.join(result)

C++

cpp 复制代码
class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows == 1 || numRows >= s.length()) {
            return s;
        }
        
        string result;
        int n = s.length();
        int cycle = 2 * numRows - 2;
        
        for (int i = 0; i < numRows; ++i) {
            for (int j = 0; j < n; j += cycle) {
                // 第一行和最后行
                if (i == 0 || i == numRows - 1) {
                    if (j + i < n) {
                        result += s[j + i];
                    }
                }
                // 中间行
                else {
                    // 周期内的字符
                    if (j + i < n) {
                        result += s[j + i];
                    }
                    // 周期间的字符(斜向上)
                    if (j + cycle - i < n) {
                        result += s[j + cycle - i];
                    }
                }
            }
        }
        
        return result;
    }
};

4. 复杂度分析

  • 时间复杂度 :O(n),其中 n 是字符串 s 的长度。每个字符最多被访问一次。
  • 空间复杂度
    • Python:O(n),用于存储结果字符串(result 列表)。
    • C++:O(1),不计输出字符串的空间(result 直接追加)。

5. 总结

  • Z 字形变换 :核心是理解周期性规律,周期长度为 2 * numRows - 2
  • 按行访问方法利用数学规律,高效且空间优化。
  • 特殊情况(numRows = 1numRows >= n)需提前处理。
  • 可扩展到变体问题,如动态行数或不同排列方式。
相关推荐
KaifuZeng10 小时前
通信与接口协议面试五、RS422
嵌入式硬件·面试·通信与接口
Frostnova丶10 小时前
(12)LeetCode 76. 最小覆盖子串
算法·leetcode·职场和发展
旧曲重听111 小时前
为什么现在 RAG 越少越少提及了
数据库·程序人生·职场和发展·agent
wabs66611 小时前
关于动态规划【力扣583.两个字符串的删除操作的思考】
算法·leetcode·动态规划
SamDeepThinking11 小时前
组合还是继承?我在项目里主要看这两个判断
java·后端·面试
wear工程师12 小时前
setState 到底是不是异步的?这题从 React 16 问到 18,标准答案早就变了
react.js·面试
_Doubletful12 小时前
妙用位运算:解构汉明距离至100%(提供分析与多解)
c语言·算法·leetcode
凯瑟琳.奥古斯特12 小时前
力扣1013三等分解法与C++实现
开发语言·c++·算法·leetcode·职场和发展
凯瑟琳.奥古斯特13 小时前
力扣1012数位DP解法详解
开发语言·c++·算法·leetcode·职场和发展
SamDeepThinking14 小时前
为什么很多人学了Java,还是没真正理解类和对象?
java·后端·面试