前端算法面试必刷题系列[15]

319 阅读4分钟

这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。

25. 搜索插入位置 (search-insert-position)

标签

  • 二分查找
  • 简单

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给定一个排序数组和一个目标值,在数组中找到目标值,并返回其索引。如果目标值不存在于数组中,返回它将会被按顺序插入的位置。

其实主要就是要求在数组中找到插入 target 元素的位置

你可以假设数组中无重复元素。

基本思路

又是排序数组,可以用二分查找,参照这篇 二分查找

这就是我们上篇说的二分查找变种之一:

在有序数组中找到最后一个比 target 小的元素

步骤

跟上篇二分查找的变种几乎相同。查看二分查找的变种,直接看实现吧。

写法实现

let searchInsert = (nums, target) => {
  let [left, right, pivot] = [0, nums.length-1, 0]
  while (left <= right) {
    pivot = left + Math.floor((right - left) / 2)
    // 由于要找最后一个比target小的元素,所以大于等于 target时,直接把右边界左移
    if (nums[pivot] >= target) {
      right = pivot - 1
    } else {
      // 其实就是在找右边界,发现右边大于等于target时,返回后一位就是结果
      if ((pivot == nums.length-1) || (nums[pivot+1] >= target)) {
        return pivot + 1
      }
      // 否则就继续把左边界右移动就ok了
      left = pivot + 1
    }
  }
  return 0
}

console.log(searchInsert([1,3,5,6], 7))

26. 组合总和 (combination-sum)

标签

  • DFS
  • 回溯
  • 中等

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给你一个数组,里面都是不重复的正数,再给定 target,求出所有为 target 的组合。 元素可以重复使用,但组合不能重复,比如 [2, 2, 3][2, 3, 2] 是重复的组合。

重点分析

  • 这就是要找出通向合法解的Path。直接DFS。不了解可看这篇 深度优先遍历

  • 为了不产生重复组合,我们需要限制下一轮选择的起点,也就是加一个 startIdx,让下轮选择不会选到同层左边的数就行,越往后选选择的数越少。举个例子,我们假设candidates = [2,3,6,7] 看上去是升序的,那么得到的path也应该是有序的 [2,2,3]这种,[2,3,2]就是重复解,因为到3了,其实你的选择下一轮是从3的idx开始,只能选[3,6,7]这3个数,这样就保证不会有重复解。

写法实现

var combinationSum = function(candidates, target) {
  let res = []
  const dfs = (startIdx, curPath, curSum) => {
    // 这种明显超过target直接return
    if (curSum > target) {
      return
    }
    // 等于target时直接进结果数组
    if (curSum === target) {
      res.push(curPath.slice())
    }
    for (let i = startIdx; i < candidates.length; i++) {
      // 去掉这种明显不可能的分支
      if (candidates[i] > target) {
        continue;
      }
      // 本轮选择的数字 candidates[i] 拼到 path 中
      curPath.push(candidates[i])
      // 继续dfs,startIdx = i,下轮就不会选到i左边的数
      dfs(i, curPath, curSum + candidates[i])
      // 撤销选择,回溯上层path,继续尝试选同层右边的数
      curPath.pop()
    }
  }
  // 最开始排列start=0,curPath=[],curSum=0
  dfs(0, [], 0);
  return res
};

let candidates = [2,3,6,7], target = 7
console.log(combinationSum(candidates, target))

27. 组合总和II (combination-sum-ii)

标签

  • DFS
  • 回溯
  • 中等

题目

leetcode 传送门

这里不贴题了,leetcode打开就行,题目大意:

给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的每个数字在每个组合中只能使用一次。

跟上题不同的地方就是,这里不能同一个元素用多次,上题可以重复无限次使用。

基本思路

大写法和上面基本相同。除了以下几点

  • 需要事先把 candidates 数组排序
  • 取数进Path过程如果本轮取数相同直接跳出本轮循环,(下一轮可以取相同),因为是排序好的,相同元素是相邻的
  • dfs递归时 startIdx 取 i + 1,这样可以不取自己

写法实现

// 这里只注释和上面一题的不同部分,其他都是相同思路
var combinationSum2 = function(candidates, target) {
  let res = []
  // 我们需要先把 candidates 排序
  candidates = candidates.sort((a, b) => a - b)
  const dfs = (startIdx, curPath, curSum) => {
    if (curSum > target) {
      return
    }
    if (curSum === target) {
      res.push(curPath.slice())
    }
    for (let i = startIdx; i < candidates.length; i++) {
      if (candidates[i] > target) {
        continue;
      }
      // 如果本轮取数相同直接跳出本轮循环,(下一轮可以取相同),因为是排序好的,相同元素是相邻的
      if (i > startIdx && candidates[i] === candidates[i-1]) {
        continue;
      }
      curPath.push(candidates[i])
      // 继续dfs,startIdx = i + 1,下轮就不会选到包括i在内以及i左边的数
      dfs(i + 1, curPath, curSum + candidates[i])
      curPath.pop()
    }
  }
  dfs(0, [], 0);
  return res
};

let candidates = [10,1,2,7,6,1,5], target = 8
console.log(combinationSum2(candidates, target))

另外向大家着重推荐下这位大哥的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列

今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 搜索我的微信号infinity_9368,可以聊天说地 加我暗号 "天王盖地虎" 下一句的英文,验证消息请发给我 presious tower shock the rever monster,我看到就通过,暗号对不上不加哈,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧