多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

LeetCode 335 路径交叉(Self Crossing)题解:O(1) 空间的一趟扫描相交判定算法

LeetCode 335 路径交叉(Self Crossing)题解:O(1) 空间的一趟扫描相交判定算法 LeetCode 335 路径交叉Self Crossing题解O(1) 空间的一趟扫描相交判定算法【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址: https://gitcode.com/gh_mirrors/le/leetcode导读本文基于《力扣加加》LeetCode 题解仓库leetcode中的 335.self-crossing 题解 展开深入讲解 LeetCode 335「路径交叉」Self Crossing这道 Hard 难度几何题给定一组长度数组机器人按北、西、南、东逆时针交替行走如何用一趟扫描、O(1) 空间判断路径是否发生自相交。读完本文你将掌握圈形路径自相交的三种几何判定条件、为何只需观察最近五段路径的核心洞察以及滚动数组思想在空间优化中的实际应用。题目背景与题意题目编号LeetCode 335路径交叉 / Self Crossing被收录于本仓库 hard 难度题单 中。题目地址LeetCode 官方题库本题最初收录于力扣中国站。题目描述给定一个含有 n 个正数的数组 x。从点 (0,0) 开始先向北移动 x[0] 米然后向西移动 x[1] 米向南移动 x[2] 米向东移动 x[3] 米持续移动。也就是说每次移动后你的方位会发生逆时针变化。编写一个 O(1) 空间复杂度的一趟扫描算法判断你所经过的路径是否相交。示例示例 1┌───┐ │ │ └───┼── │输入:[2,1,1,2]输出:true示例 2┌──────┐ │ │ │ │ └────────────输入:[1,2,3,4]输出:false示例 3┌───┐ │ │ └───┼输入:[1,1,1,1]输出:true从示例可以看出路径是一圈一圈向内收缩或向外扩张的螺旋只有当某一段与之前某一段交叉或触碰时才会相交。示例 3 中[1,1,1,1]最后一段恰好触碰到第一段的端点因此也判定为相交true。前置知识滚动数组Rolling Array原题解文档将滚动数组列为本道题的前置知识。所谓滚动数组是指当状态转移只依赖最近若干个状态、而与更早的历史状态无关时不再用完整数组保存全部状态而是用固定数量的几个变量滚动覆盖旧值从而把空间复杂度从 O(N) 降到 O(1)。本仓库的 动态规划专题thinkings/dynamic-programming.md 以爬楼梯为例给出了经典说明因为f(n)只与前两个状态f(n-1)、f(n-2)有关所以只需两个变量a、b交替更新即可这就是滚动数组的雏形。其本质是太远的层用不到了就可以直接抹去。本题正是这种思想的几何版本——判断最新一段路径是否相交不必与之前所有段逐一比较只需要与最近的少数几段比较即可这就是滚动数组思路在几何问题上的落地。思路从 O(B) 朴素解法到 O(1) 滚动优化朴素做法动态障碍物集合空间 O(B)符合直觉的做法是 O(N) 时间和 O(B) 空间复杂度的算法其中 B 为障碍物的个数也就是行走过程中经过的坐标点的个数。这种做法与仓库中另一道题 874. 模拟行走机器人874.walking-robot-simulation 的思路基本一致机器人在网格上逐步行走把已经踩过的坐标点记录为障碍物每走一步判断下一步是否落在已有障碍物上若是则说明路径发生了自相交。区别在于874 题的障碍物集合是题目预先给定的而本题的障碍物是遍历过程中动态生成的——每遇到一个新坐标点就将其标记为 obstacle。随着算法进行obstacles 集合逐渐增大最终会膨胀到 O(B) 的空间开销这显然不满足题目 O(1) 空间的要求。关键洞察不相交只有两种形态经过仔细观察可以发现如果路径一直不相交从大范围来看只有两种情况我们画的圈不断增大向外扩张的螺旋我们画的圈不断减少向内收缩的螺旋。一旦形态在这两种之间发生切换比如本来在扩张却突然大幅内收或本来在收缩却突然外扩就极有可能触发相交。这个观察把逐点比较的笨办法转化为仅关注相邻几段长度关系的局部判定。核心洞察只需考虑最近的五段顺着上面的观察会发现画最新一笔的时候并不需要把之前画的所有线段都拿来比较只需要考虑最近的几个线段即可。原题解指出实际只需要最近的五个线段。理由非常巧妙对最新一段而言如果它可能与更早的某一段相交那么一旦与之相交则必然也一定会与红色标记最近若干段部分相交。换句话说与老线段相交是被最近几段相交的必要不充分条件——相交发生的最早时刻一定落在最近几段的覆盖范围内因此检查最近五段就足以覆盖所有相交可能性。旋转不变性方向无关画的方向也是不需要考虑的。例如当前画的方向是从左到右和从上到下对于是否相交的判定没有任何区别——把整幅图顺时针旋转 90 度相交关系完全不变。方向只是一个坐标系选择问题判定条件只与长度之间的相对关系有关因此可以统一按同一套比较逻辑处理四个方向的移动。相交的三种情形与判定条件当仔细观察后会发现相交的情况其实只有以下三种原题解文档特别注明图有误第一种和第二种在换个角度看后是同一种情况文字解释和代码已更正因此下面以文字条件为准设当前遍历到第 i 个元素i 从 0 开始计数情形一经典夹断相交条件x[i] x[i - 2] and x[i - 1] x[i - 3]含义最新一段的长度不小于两段之前那条与之平行的线段的长度同时中间一段又足够短使得最新一段直接压到了更早的边上形成相交。情形二端点精确触碰条件i 3 and x[i - 1] x[i - 3] and x[i] x[i - 4] x[i - 2]含义最新一段的端点恰好落在四段之前那条线段的端点上属于刚好碰上的相交示例 3 的[1,1,1,1]即属此类形成└───┼的形态。情形三较复杂的环绕相交条件i 4且同时满足x[i] x[i - 4] x[i - 2]、x[i - 1] x[i - 3] - x[i - 5]、x[i - 1] x[i - 3]、x[i - 2] x[i - 4]、x[i - 3] x[i - 5]含义这是一组更深的缠绕条件需要同时用到最近五段i 到 i-5的长度关系才能判定也正是为什么需要看五段的原因所在。其余情况则不相交。注意循环从i 3开始少于四条线段n 4时路径不可能发生自相交可以直接返回false。关键点解析一定要画图辅助这类几何判定题文字条件很难直接想象建议按示例把螺旋画出来标注每段的序号对照三个条件逐一验证。O(1) 空间有固定套路常见的有两种——直接修改原数组把历史信息就地存储滚动数组当前状态并不是和之前所有状态有关而仅和某几个有关。本题采用的是滚动数组判定条件只访问x[i]到x[i-5]六个下标即只依赖最近六段长度。如果你了解动态规划的滚动数组优化可参考本仓库 thinkings/dynamic-programming.md 中的滚动数组优化章节就能理解这里的做法如出一辙。难点在于如何确定当前状态和哪几个历史状态有关——对这道题来说画图是打开思路的最好方式。面试时先说出 O(B) 的朴素思路也不失为一个帮助自己冷静分析问题、再逐步优化到 O(1) 的可行策略。代码实现Python3原题解给出的 Python3 实现如下已补充注释便于理解class Solution: def isSelfCrossing(self, x: List[int]) - bool: n len(x) # 少于四条线段不可能相交 if n 4: return False for i in range(3, n): # 情形一最新一段与两段前的平行段夹断相交 if x[i] x[i - 2] and x[i - 1] x[i - 3]: return True # 情形二端点与四段前的线段端点精确触碰 if i 3 and x[i - 1] x[i - 3] and x[i] x[i - 4] x[i - 2]: return True # 情形三较复杂的环绕相交需要最近五段 if i 4 and x[i] x[i - 4] x[i - 2] and x[i - 1] x[i - 3] - x[i - 5] \ and x[i - 1] x[i - 3] and x[i - 2] x[i - 4] and x[i - 3] x[i - 5]: return True return False代码要点一趟for循环完成全部判定无需额外存储结构三个判定分支分别对应三种相交情形命中即返回true通过下标i-2, i-3, i-4, i-5直接读取历史长度正是滚动数组式的最近状态访问模式原题解同时感谢了社区成员指出的代码重复判断问题因此最终版分支条件互斥、无冗余判断。注本题解文档标注的代码支持语言为 Python3仓库中暂未收录该题的 JavaScript 等其他语言实现如需验证算法正确性可将上述代码直接在 Python3 环境中对三组示例输入运行。复杂度分析其中 N 为数组长度时间复杂度O(N)一趟扫描完成判定空间复杂度O(1)只使用常数个额外变量不随 N 增长。相关题目与仓库指引朴素思路对照874. 模拟行走机器人874.walking-robot-simulation——同样的障碍物集合模型但障碍物由题目预给定空间开销天然是 O(B)可对比体会本题动态障碍物与空间优化的差异。滚动数组理论基础thinkings/dynamic-programming.md 滚动数组优化章节——解释了当前状态只与最近若干状态相关时如何进行空间压缩。题目难度归属本题位于 hard 难度题单 中。仓库索引本题解同时被收录于仓库总 README.md 题解索引、SUMMARY.md 目录与 introduction.md 文章列表读者可在这些索引中按题号快速检索到本文对应章节。【免费下载链接】leetcodeLeetCode Solutions: A Record of My Problem Solving Journey.( leetcode题解记录自己的leetcode解题之路。)项目地址: https://gitcode.com/gh_mirrors/le/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表