每日算法 -【Swift 算法】Z 字形变换(Zigzag Conversion)详解与实现

Swift | Z 字形变换(Zigzag Conversion)详解与实现

题目描述

给定一个字符串 s 和一个行数 numRows,请按照从上往下、再从下往上的“Z”字形排列这个字符串,并按行输出最终结果。例如:

输入:

s = "PAYPALISHIRING"
numRows = 3

Z 字形排列如下:

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

输出字符串为:“PAHNAPLSIIGYIR”


题目理解与思路解析

目标:将字符串按 Z 字形排列后,再逐行读取,拼成新的字符串。

Z字形结构特点

  • 每个“Z”形周期包含:竖向向下 + 斜向向上(不含两端)
  • 举例,numRows = 3,Z 周期宽度为 4下(3)+ 斜上(1) = 4
  • 举例,numRows = 4,Z 周期宽度为 6下(4)+ 斜上(2) = 6

因此,我们只需按照这个顺序依次将字符“填”到对应的行中,最后逐行拼接输出即可。


Swift 实现

func convert(_ s: String, _ numRows: Int) -> String {
    // 边界情况:如果只有一行,直接返回原字符串
    if numRows == 1 || numRows >= s.count {
        return s
    }

    // 创建 numRows 个空字符串用于收集每一行的字符
    var rows = Array(repeating: "", count: numRows)

    var currentRow = 0       // 当前字符所在的行
    var goingDown = false    // 控制方向是否向下

    // 遍历字符串中的每个字符
    for char in s {
        // 将字符添加到当前行
        rows[currentRow] += String(char)

        // 如果到达最顶部或最底部,则改变方向
        if currentRow == 0 || currentRow == numRows - 1 {
            goingDown.toggle()
        }

        // 根据方向决定下一行
        currentRow += goingDown ? 1 : -1
    }

    // 拼接所有行的字符
    return rows.joined()
}

复杂度分析

✅ 时间复杂度:O(n)

  • n 为字符串 s 的长度。
  • 每个字符都被访问一次,插入到对应的行中。

✅ 空间复杂度:O(n)

  • 存储中间的行数组 rows,最多 n 个字符分布在其中。

☑️可选实现方式

方法二:按照周期直接计算索引(按坐标模拟)

  • 每一个 Z 字形周期长度为 cycleLen = 2 * numRows - 2
  • 可以根据当前字符的索引 i,通过 i % cycleLen 判断它属于哪一行。
func convert(_ s: String, _ numRows: Int) -> String {
    if numRows == 1 || numRows >= s.count { return s }

    var rows = Array(repeating: "", count: numRows)
    let cycleLen = 2 * numRows - 2

    for (i, char) in s.enumerated() {
        let pos = i % cycleLen
        let row = pos < numRows ? pos : cycleLen - pos
        rows[row] += String(char)
    }

    return rows.joined()
}

优点

  • 不用维护方向变量 goingDown,逻辑更数学化,适合喜欢用索引推理的同学。

总结

方法 易读性 实现难度 性能 推荐场景
按行构建 ✅ 高 ⭐⭐ ✅ 优 新手友好,适合面试
索引模拟周期 ⭐ 中 ⭐⭐⭐ ✅ 优 偏数学推理,适合进阶

如果你想打印字符矩阵,那么自取

func printZMatrix(_ s: String, _ numRows: Int) {
    if numRows == 1 || numRows >= s.count {
        print(s)
        return
    }

    let cycleLen = 2 * numRows - 2
    let numCols = (s.count / cycleLen + 1) * (numRows - 1)
    var matrix = Array(repeating: Array(repeating: " ", count: numCols), count: numRows)

    var row = 0, col = 0
    var index = 0
    let chars = Array(s)

    while index < chars.count {
        // 向下
        for r in 0..<numRows where index < chars.count {
            matrix[r][col] = String(chars[index])
            index += 1
        }
        // 向右上
        for r in stride(from: numRows - 2, through: 1, by: -1) where index < chars.count {
            col += 1
            matrix[r][col] = String(chars[index])
            index += 1
        }
        col += 1
    }

    // 打印矩阵
    for row in matrix {
        print(row.joined())
    }
}

示例调用:

printZMatrix("PAYPALISHIRING", 4)

输出示例:

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

你可能感兴趣的:(算法,算法,swift,ssh)