LeetCode-Go 题解 1313. Decompress Run-Length Encoded List:行程长度编码数组解压的 Go 实现
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
本题解围绕 LeetCode 第 1313 题「解压行程长度编码列表」展开,对应本仓库 leetcode/1313.Decompress-Run-Length-Encoded-List/README.md 及其配套源码。文章将完整讲解行程长度编码(Run-Length Encoding,RLE)的解压规则、题目约束与两个示例,深入剖析仓库中 Go 解法的核心思路与复杂度,并结合同目录测试文件说明如何在本仓库中验证实现。读完本文,你将掌握这类「按频次展开数组」题目的标准解压套路,并能独立写出可复用的 Go 实现。
题目背景:什么是行程长度编码(RLE)
行程长度编码是一种简单的无损压缩方式,核心思想是把「连续出现的相同值」记录为「出现次数 + 值」的形式。本题给出的压缩列表nums正是这种编码的线性存储:数组中元素按相邻成对排列,每对[freq, val]表示解压后应有freq个值为val的元素。
具体对应关系为:
[freq, val] = [nums[2*i], nums[2*i+1]] (i >= 0)也就是说,偶数下标(nums[0]、nums[2]、nums[4]…)存放的是频次freq,紧随其后的奇数下标(nums[1]、nums[3]、nums[5]…)存放的是对应的值val。解压时,只需从左到右依次把每个val复制freq份,最后拼接所有子列表即可。
题目要求与约束
输入:一个整数列表nums,表示经过行程长度编码压缩的列表。
输出:解压后的列表。
题目给出的两个示例:
示例 1
Input: nums = [1,2,3,4] Output: [2,4,4,4]解释:第一对[1,2]表示freq = 1、val = 2,生成子列表[2];第二对[3,4]表示freq = 3、val = 4,生成子列表[4,4,4];拼接[2] + [4,4,4]得到[2,4,4,4]。
示例 2
Input: nums = [1,1,2,3] Output: [1,3,3]解释:第一对[1,1]生成[1],第二对[2,3]生成[3,3],拼接得到[1,3,3]。
约束条件
2 <= nums.length <= 100:输入数组长度最小为 2,最大为 100;nums.length % 2 == 0:数组长度一定是偶数,保证每对[freq, val]都能完整配组;1 <= nums[i] <= 100:所有元素均为正整数,频次与值都落在 1 到 100 之间。
从约束可以看出,本题规模很小(输入长度不超过 100,单个频次不超过 100),即使使用最简单的双层循环也能轻松通过,是典型的入门级模拟题。
解题思路:按奇偶下标成对展开
原文档给出的解题思路非常清晰:下标从 0 开始,奇数位下标对应的元素是前一个(偶数位)下标元素应重复的次数,把该值重复append对应次数即可,最终输出解压后的数组。
用自然语言描述算法流程:
- 初始化一个空的结果切片
res; - 用步长为 2 的循环遍历
nums,每次取出当前偶数下标i作为频次下标; - 内层循环执行
nums[i]次,每次把nums[i+1]追加到res末尾; - 遍历结束后返回
res。
这种做法的关键在于:利用数组下标奇偶性天然切分出[freq, val]配对,外层循环i += 2保证了每一对只处理一次,内层循环负责把val按频次复制。整个过程无需额外状态变量,也无需处理长度边界——因为题目保证nums.length为偶数。
仓库源码实现
本仓库在 leetcode/1313.Decompress-Run-Length-Encoded-List/1313. Decompress Run-Length Encoded List.go 中给出了与文档一致的具体实现:
package leetcode func decompressRLElist(nums []int) []int { res := []int{} for i := 0; i < len(nums); i += 2 { for j := 0; j < nums[i]; j++ { res = append(res, nums[i+1]) } } return res }逐行解读这段代码:
res := []int{}:声明空切片存放解压结果;for i := 0; i < len(nums); i += 2:外层循环以步长 2 扫描,i始终指向每对元素中的频次位置;for j := 0; j < nums[i]; j++:内层循环执行freq(即nums[i])次;res = append(res, nums[i+1]):每次把该对的值nums[i+1]追加进结果;return res:返回完整解压列表。
需要注意,函数名decompressRLElist使用小写开头,是包内私有函数。由于本仓库每个题解目录都是一个独立的package leetcode包(见 go.mod 的模块定义),该函数仅在当前题解包内可见,测试文件与它处于同一包内,可以直接调用。
复杂度分析
时间复杂度:外层循环遍历n/2对元素(n为nums长度),内层循环总执行次数等于所有频次之和,即sum(nums[2*i]),而这恰好就是解压后结果数组的长度L。因此整体时间复杂度为O(n + L),其中n是输入长度,L是输出长度。受约束限制,n <= 100、L <= 100 × 50 = 5000,规模极小。
空间复杂度:除返回的结果切片res外,只使用了i、j两个循环变量,额外空间为O(1)。结果切片所占空间属于输出本身,通常不计入额外空间复杂度。
测试用例与验证方式
同目录下的 leetcode/1313.Decompress-Run-Length-Encoded-List/1313. Decompress Run-Length Encoded List_test.go 为本题提供了表驱动风格的测试:
package leetcode import ( "fmt" "testing" ) type question1313 struct { para1313 ans1313 } // para 是参数 // one 代表第一个参数 type para1313 struct { nums []int } // ans 是答案 // one 代表第一个答案 type ans1313 struct { one []int } func Test_Problem1313(t *testing.T) { qs := []question1313{ { para1313{[]int{1, 2, 3, 4}}, ans1313{[]int{2, 4, 4, 4}}, }, { para1313{[]int{1, 1, 2, 3}}, ans1313{[]int{1, 3, 3}}, }, { para1313{[]int{}}, ans1313{[]int{}}, }, } fmt.Printf("------------------------Leetcode Problem 1313------------------------\n") for _, q := range qs { _, p := q.ans1313, q.para1313 fmt.Printf("【input】:%v ", p) fmt.Printf("【output】:%v \n", decompressRLElist(p.nums)) } fmt.Printf("\n\n\n") }从测试源码可以观察到三个细节:
- 覆盖两个官方示例:用例 1 与用例 2 与题目示例完全一致,验证基本逻辑;
- 额外覆盖空输入:测试还包含
nums = []的场景。虽然该用例超出了题目约束(约束要求nums.length >= 2),但它恰好可以验证实现的防御性——外层循环条件i < len(nums)在空数组下直接不成立,函数返回空切片,不会越界或 panic; - 表驱动组织方式:通过
para1313(参数)与ans1313(期望答案)两个结构体成对组织用例,这是本仓库各题解测试的统一风格,便于后续继续追加新用例。
需要说明的是,该测试目前以打印输入输出为主(使用fmt.Printf),并未通过t.Error之类的断言函数做严格比对,运行后可从终端输出人工核对【output】与期望结果是否一致。这与本仓库其余题解的测试风格保持一致。
在仓库根目录运行以下命令即可单独执行本题测试:
go test -v ./leetcode/1313.Decompress-Run-Length-Encoded-List/ -run Test_Problem1313若想对整个仓库运行覆盖测试并生成 coverage.txt,可执行仓库根目录提供的脚本:
./gotest.sh该脚本使用go test -covermode=atomic -coverprofile=coverage.txt ./leetcode/...一次性为所有题解包生成合法的覆盖率文件,项目描述中「100% test coverage」的工程目标正是通过这套流程来保障的。
边界情况与进阶优化
边界情况一:最小合法输入。当nums = [1, x]时,只有一对元素,函数只执行一次内层循环,返回[x],符合预期。
边界情况二:全部为最小频次。当所有偶数位都为 1 时(如[1,a,1,b]),内层循环每对只执行一次,结果就是所有奇数位值按原顺序排列的列表。
进阶优化:预分配切片容量。原实现从空切片开始反复append,Go 的切片扩容机制会在容量不足时重新分配并拷贝底层数组。由于解压后总长度可预先算出(即所有偶数下标元素之和),可以先统计总长度再一次性分配:
package leetcode func decompressRLElistWithCap(nums []int) []int { total := 0 for i := 0; i < len(nums); i += 2 { total += nums[i] } res := make([]int, 0, total) for i := 0; i < len(nums); i += 2 { for j := 0; j < nums[i]; j++ { res = append(res, nums[i+1]) } } return res }该变体将空间分配次数降为 1 次,避免扩容拷贝开销。不过在本题的约束规模下(输出最长 5000 个元素),两种写法性能差异微乎其微,仓库保留的是代码最简洁的双层循环版本,便于读者聚焦算法本身。
小结
LeetCode 1313 是一道以行程长度编码为背景的模拟题,核心只有一步:把数组按相邻两元素[freq, val]成组切分,再按频次展开拼接。本仓库 leetcode/1313.Decompress-Run-Length-Encoded-List/ 目录下的 题解文档、实现代码 与 测试文件 三件套构成了完整的解题闭环,其中双层循环 + 步长 2 的遍历模式值得作为模板记忆,可平滑迁移到其他「按组处理数组元素」的题目(如按对、按块解压等场景)中。
【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考