双指针

移除元素

function removeElement(nums: number[], val: number): number {
  // 快指针去探查是否存在不等于 val 的元素,慢指针用来记录符合条件的值
  let slow = 0, fast = 0
  while (fast < nums.length) {
    if (nums[fast] !== val) {
      nums[slow] = nums[fast]
      slow++
    }
    fast++
  }
  return slow
};

有序数组的平方

这道题很容易想到要使用双指针解决,但是如果按照原题意求解非递减排序的结果,我们似乎没办法使用双指针解答,因为小的值按道理应该在中间,但是具体在哪里我们不清楚。所以我们可以逆向思维,思考求解递减的结果,然后将其倒序即可得到答案。

function sortedSquares(nums: number[]): number[] {
  let left = 0, right = nums.length - 1
  const res: number[] = []
  while (left <= right) {
    if (Math.abs(nums[left]) >= Math.abs(nums[right])) {
      res.push(nums[left] * nums[left])
      left++
    } else if (Math.abs(nums[left]) < Math.abs(nums[right])) {
      res.push(nums[right] * nums[right])
      right--
    }
  }

  return res.reverse()
}

三数之和

这道题实现的大体方法走向其实是基本确定的,通过排序 + 双指针结合的方式就可以做到。但是这道题的难点并不在于解法,而是具体去重的实现细节。由于都是小细节点不方便解释,我就写到代码注释里去了。

function threeSum(nums: number[]): number[][] {
  nums.sort((a, b) => a - b)

  const res: number[][] = []
  for (let i = 0; i < nums.length; i++) {
    // 剪枝:如果 nums[i] 大于 0,那么后面的数字不可能凑成等于 0 的结果
    if (nums[i] > 0) {
      break
    }

    // a 的去重判断,这里必须要是 nums[i] === nums[i - 1] 而不是 nums[i] === nums[i + 1],因为是与前面一个数进行比较
    if (i > 0 && nums[i] === nums[i - 1]) {
      continue
    }

    let left = i + 1, right = nums.length - 1
    // 严格不等于,因为假如 left 和 right 相等,三个数就变成两个数了
    while (right > left) {
      const leftNum = nums[left], rightNum = nums[right]
      if (nums[i] + leftNum + rightNum > 0) {
        right--
      } else if (nums[i] + leftNum + rightNum < 0) {
        left++
      } else {
        // b 去重
        while (nums[left] === nums[left + 1]) left++
        // c 去重
        while (nums[right] === nums[right - 1]) right--
        // 收集答案
        res.push([leftNum, rightNum, nums[i]])
        left++
        right--
      }
    }
  }

  return res
}

四数之和

这道题的难点主要在于剪枝和去重,其余逻辑与三数之和基本相同。这里要注意只有 nums[i] > 0 的时候才能进行剪枝,因为一个数加上一个负数之后是会越来越小的,必须加一个正数才会变大。二层剪枝的时候,直接把 nums[i] + nums[j] 看成了一个整体,用这个整体来与 target 做比较,原理一样也是必须要加上正数才会变大。

function fourSum(nums: number[], target: number): number[][] {
  nums.sort((a, b) => a - b)

  const res: number[][] = []
  for (let i = 0; i < nums.length; i++) {
    // 一层剪枝
    if (nums[i] > target && nums[i] > 0) {
      break
    }

    // 去重
    if (i > 0 && nums[i] === nums[i - 1]) {
      continue
    }

    for (let j = i + 1; j < nums.length; j++) {
      // 二层剪枝
      if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0) {
        break
      }

      // 去重,注意这里是 j > i + 1
      if (j > i + 1 && nums[j] === nums[j - 1]) {
        continue
      }

      // 复用三数之和逻辑
      let left = j + 1, right = nums.length - 1

      // 严格不等于,因为假如 left 和 right 相等,三个数就变成两个数了
      while (right > left) {
        const leftNum = nums[left], rightNum = nums[right]
        if (nums[i] + nums[j] + leftNum + rightNum > target) {
          right--
        } else if (nums[i] + nums[j] + leftNum + rightNum < target) {
          left++
        } else {
          // b 去重
          while (nums[left] === nums[left + 1]) left++
          // c 去重
          while (nums[right] === nums[right - 1]) right--
          res.push([leftNum, rightNum, nums[i], nums[j]])
          left++
          right--
        }
      }
    }
  }

  return res
}
©2022 Flower-F. All Rights Reserved.