算法-求和(两数/三数/四数之和)问题

leetcode01:两数之和

解题思路:

1、淳朴解法:双层遍历,O(n^2) 的复杂度,这个复杂度非常容易导致你的算法超时,不推荐。

2、空间换时间,Map 来帮忙

 大家记住一个结论:几乎所有的求和问题,都可以转化为求差问题。 

var twoSum = function(nums, target) {
  let obj = {} //创建一个对象来存储出现过的数字-对应的索引
  for (let i = 0; i < nums.length; i++) {
    const curNum = nums[i]; //当前元素
    const targetNum = target - curNum //几乎所有的求和问题,都可以转化为求差问题
    const targetNumIndex = obj[targetNum]//找到在obj中的索引
    if (targetNumIndex !== undefined) {
      return [targetNumIndex, i] //如果存在, 直接返回[目标元素的索引, 当前索引]
    } else {
      obj[curNum] = i //如果不存在,存入当前的元素和对应的索引
    }
    
  }
};
var twoSum = function(nums, target) {
   const map = new Map();
   for (let i = 0; i < nums.length; i++) {
     const curNum = nums[i];
     const targetNum = target - curNum;
     const targetNumIndex = map.get(targetNum);

     if (targetNumIndex !== undefined) {
       return [targetNumIndex, i];
     } else {
       map.set(curNum, i);
     }
   }
};

leetcode15、三数之和

解题思路:

外层循环遍历指针ℹ️

内层用双指针寻找满足条件的元素

先排序,可以方便去重

var threeSum = function (nums) {
  nums.sort((a, b) => a - b) //排序
  const result = []
  for (let i = 0; i < nums.length-2; i++) { //外层遍历,内层双指针
    const n1 = nums[i];
    if (n1 > 0) break // n1比0大,就不用做了
    if (i - 1 >= 0 && nums[i - 1] == n1) continue //重复的数也跳过

    let left = i + 1 //左指针
    let right = nums.length - 1 //右指针
    while (left < right) {
      let n2 = nums[left],n3 = nums[right];
      if (n1 + n2 + n3 === 0) {
        result.push([n1, n2, n3])
        while (left < right && nums[left] === n2) left++ // 去重,直到指向不一样的数
        while (left < right && nums[right] === n3) right-- //去重, 直到指向不一样的数
      } else if (n1 + n2 + n3 < 0) { //小于0,那么左指针往右移
        left++
      } else { //大于0,那么右指针左移
        right--
      }
    }

  }
  return result
};

leetcode18、四数之和​​​​​​​

解题思路:排序+双指针

参考三数之和,相比多了一层循环

注意事项:

!!!!:一定注意代码中是 先进行了指针的移动 还是 先进行了去重的比较, 对于不同的顺序, 比较的元素是完全不同的。

若先进行了指针的移动, 对于左指针来说, 需要比较的元素就是 当前元素和前面的一个元素

对于 i 和 j 由于每次循环完都先 i++、j++ 了,所以是 先进行了指针的移动再判断重复,所以是 nums[i] === nums[i - 1]


若先进行去重的比较, 那比较的元素就是 当前元素和后面的一个元素, 再进行指针的移动. 对于右指针的情况正好是完全相反的.

while (left < right && nums[left + 1] === nums[left]) left++ // 去重

while (left < right && nums[right - 1] === nums[right]) right-- // 去重

对于 left 和 right ,是 先进行了判断重复再进行指针的移动,所以是两个 while 循环后才 left++, right--

var fourSum = function (nums, target) {
  const len = nums.length
  if (len < 4) return []
  nums.sort((a, b) => a - b) //排序
  const result = []
  for (let i = 0; i < len - 3; i++) {
    if (i > 0 && nums[i] === nums[i - 1]) continue // 去重
    for (let j = i + 1; j < len - 2; j++) {
      if (j > i + 1 && nums[j] === nums[j - 1]) continue // 去重
      let left = j + 1
      let right = len - 1
      while (left < right) {
        if (nums[i] + nums[j] + nums[left] + nums[right] === target) {
          result.push([nums[i], nums[j], nums[left], nums[right]])
          while (left < right && nums[left + 1] === nums[left]) left++ // 去重
          while (left < right && nums[right - 1] === nums[right]) right-- // 去重
          // 找到一个符合条件的了,然后两指针同时收缩
          right--
          left++
        } else if (nums[i] + nums[j] + nums[left] + nums[right] > target) {
          right--
        } else {
          left++
        }
      }

    }
  }
  return result
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值