ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LeetCode 1385题解:数组距离值的二分查找优化

LeetCode 1385题解:数组距离值的二分查找优化 1. 题目解析与问题定义LeetCode 1385题Find the Distance Value Between Two Arrays要求我们计算两个数组间的距离值。题目描述如下给定两个整数数组arr1和arr2以及一个整数d我们需要统计arr1中满足对于arr2中的每一个元素arr1[i]与arr2[j]的绝对差都大于d的元素个数。这个距离值的定义有点反直觉——它不是计算两个数组之间的某种总体距离而是统计arr1中那些远离arr2所有元素的个体数量。换句话说我们需要找出arr1中那些孤独的元素它们与arr2中的每个成员都保持至少d1的距离。2. 暴力解法与优化思路2.1 直观的暴力解法最直接的思路是双层循环遍历对于arr1中的每个元素x检查arr2中的每个元素y如果存在任意| x - y | d则x不满足条件否则计数器加1这种解法时间复杂度为O(n*m)其中n和m分别是arr1和arr2的长度。在LeetCode的测试用例中这种解法虽然能通过但显然不是最优的。def findTheDistanceValue(arr1, arr2, d): count 0 for x in arr1: valid True for y in arr2: if abs(x - y) d: valid False break if valid: count 1 return count2.2 排序二分查找优化观察到题目中的距离定义实际上是在寻找arr1中那些不在任何arr2元素的[d-left, dright]区间内的元素。这提示我们可以先对arr2排序然后对于每个arr1的元素x用二分查找检查arr2中是否存在元素落在[x-d, xd]范围内。这种解法的时间复杂度为排序arr2: O(m log m)对于每个arr1元素进行二分查找: O(n log m) 总复杂度为O((m n) log m)明显优于暴力解法。3. 二分查找实现细节3.1 排序预处理首先需要对arr2进行排序这是二分查找的前提。Python中使用内置的sorted()函数即可arr2_sorted sorted(arr2)3.2 区间检查的实现对于每个arr1的元素x我们需要检查[x-d, xd]区间是否与arr2有任何交集。这可以通过bisect模块高效实现import bisect def is_valid(x, arr2_sorted, d): left x - d right x d # 找到第一个left的位置 low bisect.bisect_left(arr2_sorted, left) # 如果这个位置存在且right说明有交集 if low len(arr2_sorted) and arr2_sorted[low] right: return False return True3.3 完整优化解法结合上述思路完整的优化解法如下import bisect def findTheDistanceValue(arr1, arr2, d): arr2_sorted sorted(arr2) count 0 for x in arr1: left x - d right x d # 找到第一个left的位置 low bisect.bisect_left(arr2_sorted, left) # 检查这个位置是否在范围内 if low len(arr2_sorted) or arr2_sorted[low] right: count 1 return count4. 边界条件与测试用例4.1 常见边界情况空数组如果arr1或arr2为空结果应该是0或len(arr1)单元素数组测试最小输入规模所有元素都满足/都不满足测试极端情况d0相当于要求arr1元素不在arr2中大d值可能导致所有元素都满足4.2 示例测试用例# 示例1 arr1 [4,5,8] arr2 [10,9,1,8] d 2 # 输出2 # 示例2 arr1 [1,4,2,3] arr2 [-4,-3,6,10,20,30] d 3 # 输出2 # 示例3 arr1 [2,1,100,3] arr2 [-5,-2,10,-3,7] d 6 # 输出15. 算法复杂度分析5.1 时间复杂度暴力解法O(n*m)优化解法排序arr2O(m log m)n次二分查找O(n log m)总计O((m n) log m)当n和m都较大时(如1e4)优化解法比暴力解法快约1000倍。5.2 空间复杂度暴力解法O(1)优化解法O(m) (存储排序后的arr2)6. 实际编码中的注意事项二分查找的实现Python的bisect模块已经提供了高效的实现比自己手写更可靠。注意bisect_left和bisect_right的区别。区间检查的边界在检查[x-d, xd]区间时要注意数组越界问题。bisect_left可能返回len(arr2)这时表示所有元素都小于目标值。排序稳定性对于有重复元素的arr2排序不影响结果因为只要有任意一个元素落在区间内就不满足条件。大数处理虽然题目中的数字范围不大但在实际工程中要考虑整数溢出问题特别是当x和d都很大时xd可能溢出。7. 性能优化实战7.1 提前终止优化在暴力解法中一旦发现arr1的某个元素x不满足条件可以立即终止内层循环for x in arr1: for y in arr2: if abs(x - y) d: break # 提前终止 else: count 17.2 使用集合优化如果d0问题简化为统计arr1中不在arr2中的元素个数这时可以使用集合if d 0: set2 set(arr2) return sum(1 for x in arr1 if x not in set2)虽然对于一般情况集合不适用但这是特殊情况的优化。8. 同类问题扩展8.1 变种问题1最近距离如果题目改为求arr1中每个元素到arr2的最近距离然后统计这些距离大于d的元素个数解法类似但需要找到每个x在arr2中的最近元素。8.2 变种问题2距离和如果要求计算arr1和arr2所有元素对的距离和则需要完全不同的解法可能涉及前缀和等技巧。8.3 实际应用场景这种距离值计算在以下场景有应用数据去重识别与现有数据集足够不同的新数据异常检测找出远离正常值范围的数据点推荐系统避免推荐与用户不喜欢内容太相似的物品9. 不同语言的实现差异9.1 C实现C中可以使用STL的sort和lower_bound#include algorithm #include vector int findTheDistanceValue(std::vectorint arr1, std::vectorint arr2, int d) { std::sort(arr2.begin(), arr2.end()); int count 0; for (int x : arr1) { auto it std::lower_bound(arr2.begin(), arr2.end(), x - d); if (it arr2.end() || *it x d) { count; } } return count; }9.2 Java实现Java中使用Arrays.sort和Arrays.binarySearchimport java.util.Arrays; public int findTheDistanceValue(int[] arr1, int[] arr2, int d) { Arrays.sort(arr2); int count 0; for (int x : arr1) { int left x - d; int right x d; int idx Arrays.binarySearch(arr2, left); if (idx 0) idx -idx - 1; if (idx arr2.length || arr2[idx] right) { count; } } return count; }10. 刷题经验分享理解题意是关键这道题的距离值定义有点特别一定要仔细阅读题目描述和示例。从暴力解法开始即使知道暴力解法不够高效先实现它确保理解正确再考虑优化。排序是常见优化手段当问题涉及比较或范围查询时排序往往能打开优化之门。二分查找的变种bisect_left等函数有多种用途熟练掌握它们能解决许多范围查询问题。测试边界情况特别是空数组、单元素数组、极大/极小值等情况容易忽略但很重要。在实际面试中面试官可能会先问暴力解法然后要求优化。因此即使你直接想到优化解法也应该能够解释暴力解法的思路和缺点。
返回列表