LeetCode 第6题:Z字形变换

LeetCode 第6题:Z字形变换

题目描述

将一个给定字符串 s 根据给定的行数 numRows ,以从上往下、从左到右进行 Z 字形排列。

比如输入字符串为 "PAYPALISHIRING" 行数为 3 时,排列如下:

P   A   H   N
A P L S I I G
Y   I   R

之后,你的输出需要从左往右逐行读取,产生出一个新的字符串,比如:"PAHNAPLSIIGYIR"。

请你实现这个将字符串进行指定行数变换的函数:

string convert(string s, int numRows);

难度

中等

题目链接

https://leetcode.cn/problems/zigzag-conversion/

示例

示例 1:

输入:s = "PAYPALISHIRING", numRows = 3
输出:"PAHNAPLSIIGYIR"

示例 2:

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

示例 3:

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

提示

  • 1 <= s.length <= 1000
  • s 由英文字母(小写和大写)、',' 和 '.' 组成
  • 1 <= numRows <= 1000

解题思路

方法一:模拟法(使用StringBuilder数组)

通过观察Z字形变换的规律,我们可以发现:

  1. 需要numRows个StringBuilder来存储每行的字符
  2. 字符的移动方向在第一行和最后一行会发生改变
  3. 可以使用一个方向标志来控制字符的移动

关键点:

  1. 特殊情况处理:当numRows=1或字符串长度小于行数时
  2. 使用方向标志goingDown来控制是向下还是向上移动
  3. 使用curRow来追踪当前字符应该放在哪一行

具体步骤:

  1. 创建numRows个StringBuilder
  2. 遍历字符串的每个字符:
    • 将字符添加到当前行
    • 如果到达第一行或最后一行,改变方向
    • 根据方向移动当前行号
  3. 合并所有StringBuilder的结果

时间复杂度:O(n),其中n是字符串长度
空间复杂度:O(n),需要存储所有字符

代码实现

C# 实现

public class Solution {
    public string Convert(string s, int numRows) {
        // 特殊情况处理
        if (numRows == 1 || numRows >= s.Length) {
            return s;
        }
      
        // 创建numRows个StringBuilder来存储每行的字符
        StringBuilder[] rows = new StringBuilder[numRows];
        for (int i = 0; i < numRows; i++) {
            rows[i] = new StringBuilder();
        }
      
        int curRow = 0;          // 当前行号
        bool goingDown = false;  // 方向标志
      
        // 遍历字符串中的每个字符
        foreach (char c in s) {
            rows[curRow].Append(c);
          
            // 在第一行或最后一行时改变方向
            if (curRow == 0 || curRow == numRows - 1) {
                goingDown = !goingDown;
            }
          
            // 根据方向移动当前行号
            curRow += goingDown ? 1 : -1;
        }
      
        // 合并所有行
        StringBuilder result = new StringBuilder();
        foreach (StringBuilder row in rows) {
            result.Append(row);
        }
      
        return result.ToString();
    }
}

优化版本(使用数学公式)

public class Solution {
    public string Convert(string s, int numRows) {
        if (numRows == 1) return s;
      
        StringBuilder result = new StringBuilder();
        int cycleLen = 2 * numRows - 2;
      
        // 逐行遍历
        for (int i = 0; i < numRows; i++) {
            // 遍历每个周期
            for (int j = 0; j + i < s.Length; j += cycleLen) {
                result.Append(s[j + i]); // 添加垂直方向的字符
              
                // 添加斜线方向的字符(除了第一行和最后一行)
                if (i != 0 && i != numRows - 1 && j + cycleLen - i < s.Length) {
                    result.Append(s[j + cycleLen - i]);
                }
            }
        }
      
        return result.ToString();
    }
}

代码详解

基本版本:

  1. 特殊情况处理:
    • numRows = 1 时直接返回原字符串
    • 字符串长度小于行数时也直接返回
  2. StringBuilder数组:
    • 每行一个StringBuilder
    • 用于高效地构建每行的字符串
  3. 方向控制:
    • goingDown标志控制移动方向
    • 在第一行和最后一行时改变方向

优化版本:

  1. 使用周期长度:
    • cycleLen = 2 * numRows - 2
    • 每个完整的Z字形都是一个周期
  2. 数学公式:
    • 垂直方向:j + i
    • 斜线方向:j + cycleLen - i

执行结果

基本版本:

  • 执行用时:84 ms
  • 内存消耗:38.2 MB

优化版本:

  • 执行用时:76 ms
  • 内存消耗:37.9 MB

总结与反思

  1. 这道题的关键是找到字符排列的规律:
    • Z字形实际上是周期性的
    • 每个周期的长度是固定的
  2. 两种解法比较:
    • 模拟法直观易懂
    • 数学公式法效率更高
  3. 优化思路:
    • 使用周期性可以避免方向变换的判断
    • StringBuilder比string拼接效率高

相关题目

  • LeetCode 第38题:外观数列
  • LeetCode 第68题:文本左右对齐
  • LeetCode 第443题:压缩字符串
posted @   旧厂街小江  阅读(7)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示