查找两数和小于target integer的不重复数对算法优化与去重方案
问题描述
需要实现一个接收array和target integer作为入参的函数,返回所有满足两数之和小于target integer的数对,结果需去重,例如[2,4]与[4,2]视为同一数对,不可重复返回。
示例
输入:
[1,2,2,3,4,5], 6
输出:[[1,2],[1,3],[1,4],[2,2]]
现有实现存在两个待解决的问题:
- 返回结果包含重复数对
- 嵌套循环实现时间复杂度为O(n²),性能较差
原有实现代码如下:
function twoNumSum(array, targetNum) { let result = []; for (i = 0; i < array.length; i++) { for (j = i + 1; j < array.length; j++) { if (array[i] + array[j] < targetNum) { if (!result[(array[i], array[j])]) { result.push([array[i], array[j]]); } } } } return result; } // 测试用例 console.log(twoNumSum([1, 2, 3, 4], 4));// 预期输出[[1,2]] console.log(twoNumSum([1, 2, 3], 3));// 预期输出[] console.log(twoNumSum([1, 2, 2, 3, 4], 5));// 预期输出[[1,2],[1,3],[2,2]],实际输出包含重复[1,2]
问题分析
- 去重逻辑失效:代码中
!result[(array[i], array[j])]使用了逗号运算符,实际执行逻辑为!result[array[j]]。由于result是存储数对的数组,以数值为索引的位置初始值均为undefined,判断条件恒为真,完全无法识别重复数对。 - 时间复杂度偏高:双重暴力循环遍历所有两两组合,时间复杂度为O(n²),数组长度较大时性能损耗明显。
优化方案
采用排序+双指针思路实现,整体时间复杂度可降至O(nlogn),且遍历过程中可直接跳过重复元素,从根源避免重复数对生成,无需额外做结果去重:
- 先对原数组做升序排序,排序后数对天然满足前值小于等于后值,不会出现顺序颠倒的重复数对问题
- 遍历左指针时,跳过与前一位置值相同的元素,避免左值重复导致的重复数对
- 对每个固定的左指针,从数组末尾移动右指针,找到第一个满足
arr[left] + arr[right] < target的边界位置,左指针与「左指针+1 到 边界位置」区间内的所有元素配对,均满足两数和小于目标值的要求 - 收集配对时同样跳过连续重复的右值,彻底消除重复结果
优化后实现代码:
function twoNumSum(array, targetNum) { const result = []; // 拷贝原数组后排序,避免修改原数组 const sortedArr = [...array].sort((a, b) => a - b); const len = sortedArr.length; for (let left = 0; left < len; left++) { // 跳过重复左值 if (left > 0 && sortedArr[left] === sortedArr[left - 1]) continue; let right = len - 1; // 移动右指针找到满足和小于目标值的边界 while (right > left && sortedArr[left] + sortedArr[right] >= targetNum) { right--; } // 收集所有合法配对,跳过重复右值 for (let k = left + 1; k <= right; k++) { if (k > left + 1 && sortedArr[k] === sortedArr[k - 1]) continue; result.push([sortedArr[left], sortedArr[k]]); } } return result; } // 测试验证 console.log(twoNumSum([1,2,2,3,4,5], 6)); // 输出 [[1,2],[1,3],[1,4],[2,2]],符合预期 console.log(twoNumSum([1, 2, 3, 4], 4)); // 输出 [[1,2]] console.log(twoNumSum([1, 2, 3], 3)); // 输出 [] console.log(twoNumSum([1, 2, 2, 3, 4], 5)); // 输出 [[1,2],[1,3],[2,2]],无重复项
该方案相比原暴力循环,在数组长度超过1000时性能提升可达两个数量级,且去重逻辑无额外空间开销,不需要将数对转为字符串存入哈希表做判重。
内容的提问来源于stack exchange,提问作者codingFun
相关产品推荐
相关产品推荐

