为何array.splice与array.filter实现removeEdge会导致removeVertex结果不同?
问题原因分析
这种差异的核心在于splice和filter操作数组的本质不同,结合removeVertex的遍历逻辑,导致部分边未被正确删除:
1. 两种removeEdge实现的本质区别
splice版本:原地修改数组splice会直接修改原数组的内容和长度,比如执行this.adjacencyList[v2].splice(idx2, 1)时,会直接从原数组中删除元素,数组长度实时缩短。filter版本:返回新数组filter不会修改原数组,而是生成一个过滤后的新数组再赋值给原邻接表变量,原数组在遍历过程中不会被改动。
2. 遍历逻辑与数组修改的冲突
假设你的removeVertex使用了正向for循环(依赖数组实时长度)遍历待删除顶点的邻接表,比如:
removeVertex(vertex) { for (let i = 0; i < this.adjacencyList[vertex].length; i++) { const adjacent = this.adjacencyList[vertex][i]; this.removeEdge(adjacent, vertex); } delete this.adjacencyList[vertex]; }
当调用splice版removeEdge时,会触发双向删除:不仅删除邻接顶点(比如Dallas)的邻接表中的目标顶点(Hong Kong),还会删除目标顶点(Hong Kong)的邻接表中的邻接顶点(Dallas)。这会导致目标顶点的邻接表长度实时缩短,循环条件i < this.adjacencyList[vertex].length提前不满足,部分邻接顶点会被跳过遍历,它们的邻接表自然残留目标顶点。
举个具体流程:
- 初始Hong Kong的邻接表:
['Dallas', 'Tokyo'] - i=0,处理Dallas:调用
splice版removeEdge,删除Dallas邻接表中的Hong Kong,同时删除Hong Kong邻接表中的Dallas,此时Hong Kong的邻接表变为['Tokyo'],长度为1 - i递增到1,此时
i < 1不成立,循环终止,Tokyo未被处理,Tokyo的邻接表仍保留Hong Kong(核心逻辑适用于你遇到的Dallas残留场景)
而filter版removeEdge在处理时,不会修改原邻接表数组(只是赋值新数组),removeVertex的for循环会基于初始数组长度遍历所有邻接顶点,所有对应的边都会被正确删除。
3. 解决方案
- 若坚持用
splice版removeEdge:修改removeVertex的遍历逻辑,遍历邻接表的副本,或者用反向for循环(从后往前遍历,避免数组缩短导致的元素跳过):// 遍历副本 const adjacentVertices = [...this.adjacencyList[vertex]]; adjacentVertices.forEach(adjacent => this.removeEdge(adjacent, vertex)); // 或反向for循环 for (let i = this.adjacencyList[vertex].length - 1; i >= 0; i--) { const adjacent = this.adjacencyList[vertex][i]; this.removeEdge(adjacent, vertex); } - 继续使用
filter版removeEdge:逻辑更简洁,不会出现遍历冲突问题,唯一的小缺点是会创建新数组,但对于图的场景来说影响可以忽略。
内容的提问来源于stack exchange,提问作者R41313IT
相关产品推荐
相关产品推荐

