
LeetCode-Go 第 27 题 Remove Element原地删除数组元素的交换双指针解法与全量测试验证【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go本篇基于 LeetCode-Go 仓库中 leetcode/0027.Remove-Element/README.md 的题解文档展开完整讲解删除数组中所有等于给定值 val 的元素原地、O(1) 额外空间这一经典题目的题目语义、交换式双指针解法的 Go 源码实现以及配套测试用例如何覆盖空数组、全匹配等边界场景。读完本篇你能掌握原地删除类问题的通用指针框架并能在本仓库中运行该题的测试与覆盖率验证。题目与约束给定一个数组nums和一个数值val将数组中所有等于val的元素删除并返回剩余的元素个数。题目有两点硬约束引自 题目文档原地修改不能为另一个数组分配额外空间必须通过修改输入数组来完成额外内存为 O(1)元素顺序可以改变返回长度之后的数组内容是什么都无所谓评判时只读取前len个元素。原始文档给出的两个示例需要完整保留它们是理解评判机制的关键示例 1Given nums [3,2,2,3], val 3, Your function should return length 2, with the first two elements of nums being 2. It doesnt matter what you leave beyond the returned length.示例 2Given nums [0,1,2,2,3,0,4,2], val 2, Your function should return length 5, with the first five elements of nums containing 0, 1, 3, 0, and 4. Note that the order of those five elements can be arbitrary. It doesnt matter what values are set beyond the returned length.返回值为什么是整数而不是数组原始文档的 Clarification 部分解释了一个常见困惑函数返回的是长度整数但答案却是一个数组。原因是输入数组按引用pass by reference传入函数内对数组的任何修改调用方都能感知调用方借助函数返回的长度打印前len个元素即可// nums is passed in by reference. (i.e., without making a copy) int len removeElement(nums, val); // any modification to nums in your function would be known by the caller. // using the length returned by your function, it prints the first len elements. for (int i 0; i len; i) { print(nums[i]); }因此本题的本质不是真的删除元素而是文档原话所概括的把属于删除对象的元素挪到返回长度之后的空间然后返回实际剩余的元素个数OJ 读取剩余个数的元素进行输出。这一点在 Go 中同样成立——Go 的切片头包含指针函数内通过切片下标写nums[i]会直接修改底层数组调用方可见。解题思路与第 283 题同构README.md 的解题思路部分指出这道题和第 283 题Move Zeroes基本一致——283 题是删除 0这一题是删除给定的val实质相同。本仓库中 leetcode/0283.Move-Zeroes/283. Move Zeroes.go 的实现印证了这一点它与 27 题解法逐行同构只是判定条件固定为nums[i] ! 0func moveZeroes(nums []int) { if len(nums) 0 { return } j : 0 for i : 0; i len(nums); i { if nums[i] ! 0 { if i ! j { nums[i], nums[j] nums[j], nums[i] } j } } }所以 27 题的通用框架可以归纳为把0这个魔法数字参数化为val并额外返回指针j的位置作为新长度。Go 源码实现逐行解析本仓库的解法位于 leetcode/0027.Remove-Element/27. Remove Element.go全文如下package leetcode func removeElement(nums []int, val int) int { if len(nums) 0 { return 0 } j : 0 for i : 0; i len(nums); i { if nums[i] ! val { if i ! j { nums[i], nums[j] nums[j], nums[i] } j } } return j }实现采用交换式双指针各部分职责如下空数组短路L4-L6len(nums) 0时直接返回 0。虽然对空切片循环体一次都不会执行、结果也正确但显式短路使边界意图清晰也对应测试中的空数组用例。双指针分工L7j标记下一块非 val 元素应写入的位置即当前已保留元素区间的右边界i负责扫描整个数组。j永远不大于i因此只需一趟扫描。交换归位L9-L14当nums[i] ! val时说明该元素要保留。若i ! j则将nums[i]与nums[j]交换——这一步把要保留的元素前移到j位置同时把要删除的元素原来的nums[j]后移到了扫描指针i之后即文档所说的将删除的元素移动到数组后面的空间内。随后j收缩保留区间。返回新长度L16循环结束时j恰好等于非 val 元素的总个数返回j即新数组长度。用示例 1 走一遍nums [3,2,2,3], val 3。步骤ij判断动作nums初态00--[3,2,2,3]100nums[0]3跳过[3,2,2,3]210nums[1]2交换 1、0j1[2,3,2,3]321nums[2]2交换 2、1j2[2,2,3,3]432nums[3]3跳过[2,2,3,3]最终返回j 2前两个元素为[2, 2]与题目示例一致示例 2 返回 5前五个元素含 0, 1, 3, 0, 4 的任意顺序交换式写法恰好给出[0,1,3,0,4]的某种归位排列。if i ! j这个判断不是正确性必需的交换相同下标是幂等操作而是避免自己和自己交换的无意义写操作属于实现上的小优化。复杂度时间 O(n)单趟扫描每个元素至多参与一次交换空间 O(1)除i、j两个指针外没有额外数据结构满足题目 O(1) 额外内存的约束。测试用例与边界覆盖配套测试文件为 leetcode/0027.Remove-Element/27. Remove Element_test.go采用本仓库统一的参数结构体 答案结构体 用例切片风格para27{one []int, two int}承载输入数组与 valans27{one int}承载期望长度Test_Problem27遍历用例打印输入与实际输出。共 7 个用例覆盖的边界与典型场景如下输入 numsval期望长度考察点[1, 0, 1]11首尾均为目标值[0, 1, 0, 3, 0, 12]03目标值散布全数组[0, 1, 0, 3, 0, 0, 0, 0, 1, 12]04长数组中目标值成块出现[0, 0, 0, 0, 0]00全部元素都是目标值[1]10单元素数组且就是目标值[0, 1, 2, 2, 3, 0, 4, 2]25题目官方示例 2[]00空数组可以看到测试有意压满了原地删除类问题最容易出错的场景空数组、单元素、全匹配返回 0以及目标值密集分布大量交换发生。在本仓库中运行验证仓库根目录的 go.mod 声明模块为github.com/halfrost/LeetCode-GoGo 版本 1.19。题解与测试同属leetcode包可直接运行该目录的测试go test ./leetcode/0027.Remove-Element/ -run Test_Problem27 -v测试会打印如下格式的输入输出对照见测试文件中的fmt.Printf------------------------Leetcode Problem 27------------------------ 【input】:[1 0 1] 【output】:1 【input】:[0 1 0 3 0 12] 【output】:3 ...仓库还提供了一键生成覆盖率文件的脚本 gotest.sh其核心命令对全部题解包一次性生成合法的coverage.txt脚本注释说明了旧版分包 cat 追加写法会被新版 Codecov 解析器判为 0% 的原因go test -covermodeatomic -coverprofilecoverage.txt ./leetcode/...小结27 题与 283 题同构交换式双指针一趟扫描把保留元素前移、目标值后移返回保留区长度原地删除的评判语义是调用方只读取返回长度之前的元素返回长度之后的内容不做要求Go 切片共享底层数组的特性使这一语义天然成立实现为 O(n) 时间、O(1) 空间i ! j判断仅用于省去自交换本仓库配套 7 个测试用例覆盖空数组、全匹配、单元素等边界可通过go test与 gotest.sh 直接复现验证。【免费下载链接】LeetCode-Go✅ Solutions to LeetCode by Go, 100% test coverage, runtime beats 100% | LeetCode 题解项目地址: https://gitcode.com/GitHub_Trending/le/LeetCode-Go创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考