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 的位置作为结果长度
相关推荐
ada7_7 小时前
LeetCode(python)——543.二叉树的直径
数据结构·python·算法·leetcode·职场和发展
sprintzer8 小时前
11.26-12.05力扣栈刷题
算法·leetcode·职场和发展
sin_hielo8 小时前
leetcode 3578
数据结构·算法·leetcode
前端小白在前进8 小时前
力扣刷题:无重复字符的最长子串
算法·leetcode·职场和发展
NPE~8 小时前
面试高频——分布式事务详解
分布式·面试·职场和发展·程序员·事务·分布式事务
好易学·数据结构10 小时前
可视化图解算法72:斐波那契数列
数据结构·算法·leetcode·动态规划·力扣·牛客网
CoderYanger11 小时前
动态规划算法-子数组、子串系列(数组中连续的一段):21.乘积最大子数组
开发语言·算法·leetcode·职场和发展·动态规划·1024程序员节
CoderYanger11 小时前
A.每日一题——3432. 统计元素和差值为偶数的分区方案
java·数据结构·算法·leetcode·1024程序员节
努力学算法的蒟蒻12 小时前
day26(12.6)——leetcode面试经典150
算法·leetcode·面试