在解决 “两数之和” 问题的过程中,我尝试了不同的思路,从有序数组的双指针法到无序数组的暴力解法和哈希表解法,每种方法都有其特点和局限。以下是对这些尝试的详细记录与分析。
一、有序版本:双指针法的局限
最初,我考虑先将数组排序,再使用双指针寻找目标和,代码如下:
var twoSum = function(nums, target) {
nums.sort((a,b)=>a-b) // 将数组升序排序
let left=0,right=nums.length-1; // 初始化左右指针
while(left<right){
let s=nums[left]+nums[right] // 计算两指针指向元素的和
if(s==target){
return [left+1,right+1]; // 假设返回排序后的下标(+1是无意义的尝试)
}
s>target?left++:right--; // 和大于目标值则左移右指针,否则右移左指针
}
};
问题分析:
- 下标混乱:这是最关键的问题。题目要求返回原始数组中元素的下标,但排序会改变元素的原始位置,导致双指针找到的
left和right是排序后数组的下标,与原始数组无关。 - 无意义的 + 1 操作:代码中返回
[left+1, right+1]是无效的,即使不考虑原始下标,排序后的下标本身也无需偏移,这一操作源于对问题的误解。 - 适用场景局限:双指针法更适合 “返回元素值” 而非 “返回原始下标” 的场景,例如在排序数组中寻找和为目标值的两个数(不要求下标)时,该思路才有效。
二、无序版本:暴力解法的效率问题
由于需要保留原始下标,我尝试了直接遍历所有可能的元素对,即暴力解法:
var twoSum = function(nums, target) {
for(let i=0;;i++){ // 外层循环遍历第一个元素
for(let j=i+1;j<nums.length;j++){ // 内层循环遍历后续元素(避免重复)
if(nums[i]+nums[j] === target){
return [i,j]; // 返回原始下标
}
}
}
};
问题分析:
- 时间复杂度过高:暴力解法使用双层循环,时间复杂度为
O(n²)。当数组长度n较大(如接近10^4)时,会超出时间限制,导致执行超时。 - 循环条件不严谨:外层循环
for(let i=0;;i++)缺少终止条件,虽然题目保证有唯一解,但规范的写法应添加i < nums.length的限制,避免逻辑漏洞。
三、无序版本:哈希表解法的优化
为了解决暴力解法的效率问题,我采用了哈希表(Map)来优化查找过程,代码如下:
var twoSum = function(nums, target) {
const map = new Map(); // 存储元素值与原始下标的映射
for(let j=0; j < nums.length; j++){ // 遍历数组
const x = nums[j]; // 当前元素值
const complement = target - x; // 需要寻找的另一个元素值
if(map.has(complement)){ // 若哈希表中存在目标元素
return [map.get(complement), j]; // 返回两个元素的原始下标
}
map.set(x, j); // 若不存在,将当前元素存入哈希表
}
};
优势分析:
- 时间复杂度优化:通过哈希表的
O(1)查找效率,将时间复杂度降至O(n),只需遍历一次数组即可完成查找。 - 保留原始下标:哈希表存储的是元素值与原始下标的映射,因此能准确返回题目要求的原始下标。
- 避免重复使用元素:由于哈希表中存储的是当前元素之前的元素,因此不会出现同一元素被重复使用的情况,符合题目要求。