news 2026/9/10 10:24:24

LeetCode-Go 第 27 题 Remove Element:原地删除数组元素的交换双指针解法与全量测试验证

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LeetCode-Go 第 27 题 Remove Element:原地删除数组元素的交换双指针解法与全量测试验证

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个元素。

原始文档给出的两个示例需要完整保留,它们是理解评判机制的关键:

示例 1

Given nums = [3,2,2,3], val = 3, Your function should return length = 2, with the first two elements of nums being 2. It doesn't matter what you leave beyond the returned length.

示例 2

Given 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 doesn't 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] != 0

func 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 }

实现采用交换式双指针,各部分职责如下:

  1. 空数组短路(L4-L6):len(nums) == 0时直接返回 0。虽然对空切片循环体一次都不会执行、结果也正确,但显式短路使边界意图清晰,也对应测试中的空数组用例。
  2. 双指针分工(L7):j标记"下一块非 val 元素应写入的位置"(即当前已保留元素区间的右边界),i负责扫描整个数组。j永远不大于i,因此只需一趟扫描。
  3. 交换归位(L9-L14):当nums[i] != val时,说明该元素要保留。若i != j,则将nums[i]nums[j]交换——这一步把"要保留的元素"前移到j位置,同时把"要删除的元素"(原来的nums[j])后移到了扫描指针i之后,即文档所说的"将删除的元素移动到数组后面的空间内"。随后j++收缩保留区间。
  4. 返回新长度(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、0,j=1[2,3,2,3]
321nums[2]==2交换 2、1,j=2[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):除ij两个指针外没有额外数据结构,满足题目 O(1) 额外内存的约束。

测试用例与边界覆盖

配套测试文件为 leetcode/0027.Remove-Element/27. Remove Element_test.go,采用本仓库统一的"参数结构体 + 答案结构体 + 用例切片"风格:para27{one []int, two int}承载输入(数组与 val),ans27{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-Go,Go 版本 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 -covermode=atomic -coverprofile=coverage.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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 10:21:40

LlamaIndex 集成 Astra DB 向量存储:AstraDBVectorStore 完整实战指南

LlamaIndex 集成 Astra DB 向量存储&#xff1a;AstraDBVectorStore 完整实战指南 【免费下载链接】llama_index LlamaIndex is the leading document agent and OCR platform 项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index 导读 本文围绕 LlamaIndex…

作者头像 李华
网站建设 2026/9/10 10:21:32

C++ Qt开发Android门禁系统:跨平台硬件桥接实战

简介&#xff1a;本资源是一套完整的C Qt与Android跨平台智能门禁系统毕业设计源码&#xff0c;面向计算机、软件工程及物联网方向的本科生与初阶开发者&#xff0c;解决毕业设计中多端协同开发、生物识别集成与安防系统落地等典型难题。压缩包共740个文件&#xff0c;涵盖427个…

作者头像 李华
网站建设 2026/9/10 10:18:00

CANN/GE模型加载文件接口

aclmdlLoadFromFile 【免费下载链接】ge GE&#xff08;Graph Engine&#xff09;是面向昇腾的图编译器和执行器&#xff0c;提供了计算图优化、多流并行、内存复用和模型下沉等技术手段&#xff0c;加速模型执行效率&#xff0c;减少模型内存占用。 GE 提供对 PyTorch、Tensor…

作者头像 李华
网站建设 2026/9/10 10:14:44

汽配老板的SKU宇宙:一台车一万个件

汽配老板的SKU宇宙&#xff1a;一台车一万个件 一位汽配店主的规模困惑&#xff1a; 「外行以为卖汽车配件就是机油脚垫&#xff0c;内行知道这是个SKU宇宙&#xff1a;一款车型一套适配&#xff0c;一个保险杠左边右边不一样&#xff0c;年款改一次全部重来。我一个车型就一万…

作者头像 李华