这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
25. 搜索插入位置 (search-insert-position)
标签
- 二分查找
- 简单
题目
这里不贴题了,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打开就行,题目大意:
给你一个数组,里面都是不重复的正数,再给定 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打开就行,题目大意:
给定一个数组 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,我看到就通过,暗号对不上不加哈,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧