【LeetCode】6. Z 字形变换
解题思路
Z字形变换的核心在于方向模拟和行索引的动态调整。通过维护一个方向数组控制行号的增减,实现字符的逐行填充。具体步骤如下:
- 边界处理:当行数为1或字符串为空时直接返回原字符串。
 - 方向控制:使用方向数组
direction = {1, -1}模拟字符填充时的上下移动(向下时行号递增,向上时行号递减)。 - 行填充:遍历字符串,将每个字符添加到对应的行数组中,并根据边界条件调整方向。
 - 结果合并:将所有行的字符按顺序拼接成最终结果。
 
关键代码解析
func convert(s string, numRows int) string { if numRows == 1 || len(s) <= numRows { return s // 边界条件直接返回 } // 初始化行存储数组 rows := make([][]byte, numRows) row, direction := 0, 1 // 初始方向向下(行号递增) for i := 0; i < len(s); i++ { rows[row] = append(rows[row], s[i]) // 方向反转条件(到达顶部或底部) if row == 0 { direction = 1 // 向下移动 } else if row == numRows-1 { direction = -1 // 向上移动 } row += direction // 更新行号 } // 合并所有行的字符 result := make([]byte, 0) for _, r := range rows { result = append(result, r...) } return string(result) }
复杂度分析
| 指标 | 值 | 说明 | 
|---|---|---|
| 时间复杂度 | O(n) | 单次遍历字符串,合并操作线性时间 | 
| 空间复杂度 | O(n) | 存储所有字符(与原字符串等长) | 
示例验证
func main() { // 示例1 s1 := "PAYPALISHIRING" fmt.Println(convert(s1, 3)) // 输出: "PAHNAPLSIIGYIR" // 示例2 fmt.Println(convert(s1, 4)) // 输出: "PINALSIGYAHRPI" // 示例3 s2 := "A" fmt.Println(convert(s2, 1)) // 输出: "A"
核心逻辑说明
- 
方向控制机制
- 初始方向为向下(
direction=1),行号递增。 - 当行号到达底部(
row=numRows-1)时,方向改为向上(direction=-1)。 - 当行号到达顶部(
row=0)时,方向恢复向下。 
 - 初始方向为向下(
 - 
字符填充过程
以输入s="PAYPALISHIRING", numRows=3为例:P→ 行0,方向向下。A→ 行1,继续向下。Y→ 行2(到达底部),方向改为向上。P→ 行1,继续向上。- 最终各行数据:
["PAHN", "APLSIIG", "YIR"]。 
 - 
边界处理优化
- 当 
numRows=1时直接返回原字符串,避免无效计算。 - 使用 
len(s) <= numRows提前终止无意义的Z字形变换(如示例3)。 
 - 当 
 
扩展讨论
对比其他解法
| 方法 | 优点 | 缺点 | 适用场景 | 
|---|---|---|---|
| 方向模拟法 | 逻辑直观,代码简洁 | 需要存储所有行数据 | 常规开发场景 | 
| 数学规律法 | 无需存储中间数据(直接计算) | 需处理复杂模运算 | 内存敏感场景 | 
数学规律法的实现思路
若采用数学规律法,需根据字符索引 
i 计算其所在行:周期 = 2*numRows - 2 行号 = i % 周期 (当行号 < numRows) = numRows-2 - (i % 周期 - numRows + 1) (否则)
但该方法的实现复杂度较高,需处理大量边界条件。
通过方向模拟法,代码简洁且易于维护,是解决Z字形变换问题的推荐方案。该方案完美覆盖所有测试用例,且适用于大规模输入场景。
                    
                
                
            
        
浙公网安备 33010602011771号