【前端JavaScript】两数之和问题的多种解法尝试与分析

61 阅读3分钟

在解决 “两数之和” 问题的过程中,我尝试了不同的思路,从有序数组的双指针法到无序数组的暴力解法和哈希表解法,每种方法都有其特点和局限。以下是对这些尝试的详细记录与分析。

一、有序版本:双指针法的局限

最初,我考虑先将数组排序,再使用双指针寻找目标和,代码如下:

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--;  // 和大于目标值则左移右指针,否则右移左指针
    }
};

问题分析:

  1. 下标混乱:这是最关键的问题。题目要求返回原始数组中元素的下标,但排序会改变元素的原始位置,导致双指针找到的leftright是排序后数组的下标,与原始数组无关。
  2. 无意义的 + 1 操作:代码中返回[left+1, right+1]是无效的,即使不考虑原始下标,排序后的下标本身也无需偏移,这一操作源于对问题的误解。
  3. 适用场景局限:双指针法更适合 “返回元素值” 而非 “返回原始下标” 的场景,例如在排序数组中寻找和为目标值的两个数(不要求下标)时,该思路才有效。

二、无序版本:暴力解法的效率问题

由于需要保留原始下标,我尝试了直接遍历所有可能的元素对,即暴力解法:

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];  // 返回原始下标
            }
        }
    }
};

问题分析:

  1. 时间复杂度过高:暴力解法使用双层循环,时间复杂度为O(n²)。当数组长度n较大(如接近10^4)时,会超出时间限制,导致执行超时。
  2. 循环条件不严谨:外层循环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);  // 若不存在,将当前元素存入哈希表
    }
};

优势分析:

  1. 时间复杂度优化:通过哈希表的O(1)查找效率,将时间复杂度降至O(n),只需遍历一次数组即可完成查找。
  2. 保留原始下标:哈希表存储的是元素值与原始下标的映射,因此能准确返回题目要求的原始下标。
  3. 避免重复使用元素:由于哈希表中存储的是当前元素之前的元素,因此不会出现同一元素被重复使用的情况,符合题目要求。