LeetCode 443. 压缩字符串


文章目录

摘要

这道题围绕的是一个非常常见但也非常容易写乱的逻辑:在原地压缩字符数组。我们要把连续的字符统计次数,然后按照"字符 + 次数"重新写回原数组。难点不是算法,而是各种边界情况,比如次数大于 9、数组最后一段如何处理、怎么做到原地覆盖等等。

为了让整个过程更容易理解,我把解法拆成了两个指针的移动逻辑,并准备了一个可运行的 Swift Demo,你可以直接在 Xcode 或 Swift Playground 中跑起来验证。

描述

题目要求我们对一个字符数组做压缩,规则是遍历数组中 连续重复的字符,然后:

  • 如果某个字符只出现一次,就直接写回该字符;
  • 如果字符重复出现,比如 5 次,就写回 "字符" + "5"
  • 并且压缩后的结果要覆盖原数组,不能开新的数组。

最终输出的是压缩后数组的"有效长度",数组后面剩余的字符可以忽略。

举个例子:

txt 复制代码
输入: ["a","a","b","b","c","c","c"]
压缩后: ["a","2","b","2","c","3"]
返回 6

看起来简单,但操作过程中涉及大量"边界处理"和指针移动,一不小心就容易写乱。

3# 题解答案

最常见的实现方式是:

  • read 指针遍历原数组;
  • write 指针把压缩后的内容写回;
  • 在处理一段连续字符后,把字符和计数按顺序写入。

整体就是一个标准的 "双指针" + "分段处理" 的问题。

题解代码分析(含可运行 Demo)

下面是一个可以直接运行的 Swift 代码示例,其中包含 main 方法(适合 Swift Playground)。

swift 复制代码
import Foundation

class Solution {
    func compress(_ chars: inout [Character]) -> Int {
        var write = 0      // 写指针,指向压缩后要写入的位置
        var read = 0       // 读指针,遍历字符数组
        
        while read < chars.count {
            let currentChar = chars[read]
            var count = 0
            
            // 统计当前字符的连续重复次数
            while read < chars.count && chars[read] == currentChar {
                read += 1
                count += 1
            }
            
            // 写入字符
            chars[write] = currentChar
            write += 1
            
            // 写入次数(如果次数大于1)
            if count > 1 {
                for digit in String(count) {
                    chars[write] = digit
                    write += 1
                }
            }
        }
        
        return write
    }
}


// MARK: - Demo 测试代码
func runDemo() {
    var chars1: [Character] = ["a","a","b","b","c","c","c"]
    var chars2: [Character] = ["a"]
    var chars3: [Character] = ["a","b","b","b","b","b","b","b","b","b","b","b","b"]
    
    let sol = Solution()
    
    print("原数组 1:", chars1)
    let len1 = sol.compress(&chars1)
    print("压缩后:", Array(chars1.prefix(len1)), "长度:", len1)
    print()
    
    print("原数组 2:", chars2)
    let len2 = sol.compress(&chars2)
    print("压缩后:", Array(chars2.prefix(len2)), "长度:", len2)
    print()
    
    print("原数组 3:", chars3)
    let len3 = sol.compress(&chars3)
    print("压缩后:", Array(chars3.prefix(len3)), "长度:", len3)
}

runDemo()

示例测试及结果

运行上面的 Demo,可以看到如下输出(我在这里帮你整理成清晰的形式):

示例 1

输入:

txt 复制代码
["a","a","b","b","c","c","c"]

输出:

txt 复制代码
["a","2","b","2","c","3"]
长度 = 6

示例 2

输入:

txt 复制代码
["a"]

输出:

txt 复制代码
["a"]
长度 = 1

示例 3

输入:

txt 复制代码
["a","b","b","b","b","b","b","b","b","b","b","b","b"]

输出:

txt 复制代码
["a","b","1","2"]
长度 = 4

这里 "b" 重复了 12 次,所以变成 "b12"

与实际场景结合

这类"压缩字符串"的操作在真实开发中比想象中常见,比如:

1. 日志压缩

很多移动端或物联网设备会生成大量日志,但带宽有限,所以会对重复内容压缩传输。类似 "AAAAABB""A5B2",就是很常见的一种日志优化方式。

2. 数据同步传输优化

例如某些设备上传传感器数据,如果连续数据相同,可以大幅减少传输量,降低服务器压力。

3. UI 文本处理

比如聊天 App 中用户输入 "hhhhhhh" 时,可以处理成 "h7" 存储,降低消息体积。

我们在做实际业务时,经常会需要对连续数据分段统计重复值,这题就是个非常小巧但很典型的数据压缩基础模型。

时间复杂度

整个算法通过 read 指针遍历了一遍数组,不会有重叠或回退。

  • 时间复杂度:O(n)
    每个字符最多被读一次、写一次。

空间复杂度

  • 空间复杂度:O(1)
    压缩结果直接写回原数组,未开辟额外空间(输出不计入空间消耗)。

总结

这道题虽然逻辑不难,但对"指针移动"、"边界处理"、"次数拆分"为字符等细节要求很高,容易写出 Bug。掌握之后会对处理类似 "分段统计" 的问题非常有帮助。

核心记住三点:

  1. 用 read 统计,用 write 写回原数组
  2. 次数需要拆成字符写入,例如 12 → '1', '2'
  3. 最后返回 write 的位置作为结果长度
相关推荐
海石1 小时前
1500分的题目,确实有实力,不过还是我略胜一筹
算法·leetcode
海石1 小时前
【记忆化搜索】条条大路通AC,走好适合你的那一条,走到后再考虑走得快
算法·leetcode
咖啡星人k6 小时前
从Vibe Coding到Prompt Engineering:AI编程的新技能树
面试·职场和发展·ai编程
学iOS很快乐10 小时前
Pinterest Staff SDE 五轮面试全过,加拿大 TC 500K CAD,附 OA 真题和每轮考点
面试·职场和发展
Purple Coder13 小时前
基于电机控制的MatlabSenier模型
职场和发展
tachibana215 小时前
hot100 排序链表(148)
java·数据结构·算法·leetcode·链表
不能跑的代码不是好代码16 小时前
二叉树从基础概念到LeetCode实战
算法·leetcode
凯瑟琳.奥古斯特16 小时前
二分查找解力扣1011最优运载能力
开发语言·c++·算法·leetcode·职场和发展
YuK.W1 天前
Leetcode100: 70.爬楼梯、118.杨辉三角、198.打家劫舍
java·算法·leetcode
旖-旎1 天前
《LeetCode 64 最小路径和 || LeetCode 174 地下城游戏》
c++·算法·leetcode·动态规划