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 的位置作为结果长度
相关推荐
练习时长一年17 小时前
LeetCode热题100(分割等和子集)
算法·leetcode·职场和发展
52Hz11817 小时前
力扣148.排序链表
leetcode
iAkuya18 小时前
(leetcode)力扣100 46二叉树展开为链表(递归||迭代||右子树的前置节点)
windows·leetcode·链表
程序员-King.19 小时前
day151—双端队列—找树左下角的值(LeetCode-513)
算法·leetcode·二叉树·双端队列·队列
苦藤新鸡19 小时前
15 .数组右移动k个单位
算法·leetcode·动态规划·力扣
氷泠19 小时前
路径总和系列(LeetCode 112 & 113 & 437 & 666)
leetcode·前缀和·深度优先·路径总和
橘颂TA20 小时前
【剑斩OFFER】算法的暴力美学——力扣 130 题:被围绕的区域
算法·leetcode·职场和发展·结构与算法
一分之二~20 小时前
回溯算法--解数独
开发语言·数据结构·c++·算法·leetcode
程序员-King.21 小时前
day154—回溯—分割回文串(LeetCode-131)
算法·leetcode·深度优先·回溯
程序员-King.21 小时前
day155—回溯—组合(LeetCode-77)
算法·leetcode·回溯