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 的位置作为结果长度
相关推荐
a努力。1 天前
字节Java面试被问:系统限流的实现方式
java·开发语言·后端·面试·职场和发展·golang
Binky6781 天前
力扣--回溯篇(2)
算法·leetcode·职场和发展
东东的脑洞1 天前
【面试突击】Redis 主从复制核心面试知识点
redis·面试·职场和发展
2401_841495641 天前
【LeetCode刷题】打家劫舍
数据结构·python·算法·leetcode·动态规划·数组·传统dp数组
努力学算法的蒟蒻1 天前
day45(12.26)——leetcode面试经典150
算法·leetcode·面试
闻缺陷则喜何志丹1 天前
【离线查询 前缀和 二分查找 栈】P12271 [蓝桥杯 2024 国 Python B] 括号与字母|普及+
c++·算法·前缀和·蓝桥杯·二分查找··离线查询
我命由我123451 天前
Photoshop - Photoshop 工具栏(43)标尺工具
学习·ui·职场和发展·求职招聘·职场发展·学习方法·photoshop
LYFlied1 天前
【每日算法】LeetCode 300. 最长递增子序列
前端·数据结构·算法·leetcode·职场和发展
闲看云起1 天前
LeetCode-day2:字母异位词分组分析
算法·leetcode·职场和发展
熬夜敲代码的小N1 天前
2026 职场生存白皮书:Gemini Pro 实战使用指南
人工智能·python·ai·职场和发展