6、Z字形变换

博客围绕Java字符串处理展开,分析了String、StringBuilder和StringBuffer的区别。包括运行速度上StringBuilder最快,String最慢;线程安全方面,String和StringBuilder不安全,StringBuffer安全。还给出了适用场景,最后对比了代码执行结果与官方题解的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

在这里插入图片描述

问题分析

分析题目,这是字符串处理的简单模拟。需要注意的地方是:String,StringBuilder以及StringBuffer的区别。

  1. 运行速度快慢:StringBuilder > StringBuffer > String

这是因为String是字符串常量,每次追加字符串时,都需要创建新的对象,原来的对象需要JVM的垃圾回收机制来处理,增加了时间和内存消耗;而StringBuilder、StringBuffer是字符串变量,追加字符串时是在原有对象的内存的基础上扩展,没有创建和回收的过程,相较于String提高了性能。

  1. 线程是否安全:String(线程不安全)、StringBuilder(线程不安全)、StringBuffer(线程安全)

StringBuilder和StringBuffer的使用几乎完全相同,唯一的区别就是线程的安全性不同。正是由于StringBuffer保证线程安全(支持同步锁),所以性能要比StringBuilder稍差一些。

  1. 总结:
  1. String:适合操作少量的数据时使用
  2. StringBuffer:适用于单线程下在字符缓冲区进行大量操作的情况
  3. StringBuffer:适用多线程下在字符缓冲区进行大量操作的情况
  • 时间复杂度:O( n ),其中 n 表示s的长度。
  • 空间复杂度:O( n )。

Java代码


class Solution {
    public ListNode addTwoNumbers(ListNode l1, ListNode l2) {//特殊情况
        if (numRows == 1) {
            return s;
        }

        //HashMap保存每一行的String
        HashMap<Integer, StringBuilder> map = new HashMap<Integer, StringBuilder>();
        //转向标志
        boolean type = false; //false代表向下
        int rows = 0;

        //遍历扫描原String
        for (int i = 0; i < s.length(); i++) {
            char str = s.charAt(i);
            if(map.containsKey(rows)){
                //在每一行的尾端添加游标所指的字符
                map.put(rows, map.get(rows).append(str));
            }else {
                map.put(rows, new StringBuilder(String.valueOf(str)));
            }

            //移动游标,转向
            if (type){
                //向上
                rows -= 1;
                if (rows == 0){
                    type = false;
                }
            }else {
                //向下
                rows += 1;
                if (rows == numRows-1){
                    type = true;
                }
            }
        }

        //输出处理
        StringBuilder result = new StringBuilder();
        for (int i = 0; i < Math.min(numRows, s.length()); i++) {
            result.append(map.get(i));
        }

        return result.toString();
    }
}

结果分析

以上代码的执行结果:

执行时间内存消耗
53ms49.4MB

在这里插入图片描述
我的思路和官方题解的思路是一样的,但是时间和内存的消耗差距不小,原因有待研究。以下是官方题解:

执行时间内存消耗
29ms45.1MB

在这里插入图片描述

class Solution {
    public String convert(String s, int numRows) {
        if (numRows == 1) return s;

        List<StringBuilder> rows = new ArrayList<>();
        for (int i = 0; i < Math.min(numRows, s.length()); i++)
            rows.add(new StringBuilder());

        int curRow = 0;
        boolean goingDown = false;

        for (char c : s.toCharArray()) {
            rows.get(curRow).append(c);
            if (curRow == 0 || curRow == numRows - 1) goingDown = !goingDown;
            curRow += goingDown ? 1 : -1;
        }

        StringBuilder ret = new StringBuilder();
        for (StringBuilder row : rows) ret.append(row);
        return ret.toString();
    }    
    
}
### Z字形变换算法实现与解释 #### 算法概述 Z字形变换是一种特殊的字符串排列方式,其核心在于按照特定的行数 `numRows` 将字符串重新排列成一种类似于“Z”形状的形式。最终目标是从这种排列中按照行优先的方式读取出新的字符串。 对于输入字符串 `s` 和行数 `numRows` 的约束条件为: - $1 \leq |s| \leq 1000$ - $1 \leq numRows \leq 1000$ 当 `numRows = 1` 或者字符串长度小于等于 `numRows` 时,可以直接返回原字符串[^3]。 --- #### 算法逻辑 该问题可以通过模拟字符分配的过程来解决。具体步骤如下: 1. **初始化存储结构** 创建一个列表(数组),其中每个元素代表一行的内容。初始为空字符串。 2. **遍历字符串并分配字符** 使用一个变量记录当前字符应放置在哪一行,并通过方向标志控制上下移动的趋势。如果到达顶部或底部,则改变方向。 3. **拼接结果** 遍历完成后,将每一行的结果依次连接起来形成最终字符串。 这种方法的时间复杂度为 $O(n)$,空间复杂度也为 $O(n)$,因为需要额外的空间存储每行的数据[^4]。 --- #### Python 实现代码 以下是基于上述逻辑的 Python 实现代码: ```python def convert(s: str, numRows: int) -> str: if numRows == 1 or len(s) <= numRows: # 特殊情况处理 return s rows = [''] * numRows # 初始化每行为空字符串 current_row = 0 # 当前行索引 going_down = False # 方向标志,默认向下 for char in s: rows[current_row] += char # 将字符加入对应行 # 判断是否达到边界并调整方向 if current_row == 0 or current_row == numRows - 1: going_down = not going_down # 更新当前行号 current_row += 1 if going_down else -1 return ''.join(rows) # 合并所有行得到结果 ``` --- #### 示例解析 假设输入字符串为 `"LEETCODEISHIRING"`,行数为 `3`,则执行过程如下: 1. 初始化三行数据:`['', '', '']` 2. 开始遍历字符串并将字符逐一放入对应的行中: - `'L'` 放入第 0 行 → `[L, , ]` - `'E'` 放入第 1 行 → `[L, E, ]` - `'E'` 放入第 2 行 → `[L, E, E]` - `'T'` 变换方向放回第 1 行 → `[L, ET, E]` ... 3. 最终各行列状态为: ``` Row 0: LCIRE Row 1: TOESII Row 2: GEDHN ``` 4. 拼接结果为 `"LCIRETOESIIGEDHN"` --- #### 边界情况讨论 1. 如果 `numRows = 1`,无需任何操作直接返回原始字符串。 2. 如果字符串长度小于等于 `numRows`,同样不需要特殊处理,只需简单分隔即可[^5]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值