这是我参与更文挑战的第 28 天,活动详情查看 更文挑战
这个系列没啥花头,就是纯 leetcode 题目拆解分析,不求用骚气的一行或者小众取巧解法,而是用清晰的代码和足够简单的思路帮你理清题意。让你在面试中再也不怕算法笔试。
139. 和为s的连续正数序列 (findContinuousSequence)
标签
- 数学
- 滑动窗口
- 简单
题目
这里不贴题了,leetcode打开就行,题目大意:
输入一个正整数 target
,输出所有和为 target
的连续正整数序列(至少含有两个数)。
序列内的数字由小到大排列,不同序列按照首个数字从小到大排列。
示例 1
输入:target = 9
输出:[[2,3,4],[4,5]]
示例 2
输入:target = 15
输出:[[1,2,3,4,5],[4,5,6],[7,8]]
基本思路
连续正整数序列是一个解的话,我们可以想找一个解只需要找到左右边界就能推导出一个解。
思考用滑动窗口,所以设窗口左右指针来走,期间计算当前窗口的和,跟 target 比较
- 当 curSum === target 时,得一个解
- 当 curSum > target 时,左指针右移减小窗口
- 当 curSum < target 时,右指针右移扩大窗口
那么知道左右指针
,当前和
如何计算比较好呢,答案是 O(1)
复杂度的 等差数列求和公式
比如 left = 1, right = 4
, curSum
其实就是计算 1 + 2 + 3 + 4
还记得高斯小时候那篇课文吗,小高斯老师让他们从 1 加 到 100,然后高斯就 用 (1 + 100) * 50 = 5050
(100是项数,就是一共有100个数相加,除以二是50) 其实就是 首尾相加乘以项数除以二
。这就是等差数列求和公式,本题公差d = 1
,那么当前和计算就是
左右指针值为 left = 1 , right = 4 (1, 2, 3, 4) 一共 4 个数
(1 (首) + 4 (尾) ) * 4 (项数)/ 2 = 10 (1+2+3+4=10)
公差为1时这个项数4 其实也可以简单用 左右指针表示,(4 - 1) + 1 = 4 个数
// 所以代码一行就行,不用循环相加了
let curSum = (left + right) * (right - left + 1) / 2;
那么下面来看下写法
写法实现
var findContinuousSequence = function(target) {
let [res, left, right] = [[], 1, 2]
// 做指针不超过右指针
while (left < right) {
// 等差数列求和公式,分析有推导
let curSum = (left + right) * (right - left + 1) / 2;
// 发现相等时,左边开始加一到有边界数组入结果集
if (curSum === target) {
// 一个解的开始位置用临时变量保存
let startNum = left
// 生成一个以[left, right]为边界的d 为1的等差数列进结果
res.push(
new Array(right - left + 1).fill(0).map(() => startNum++)
);
// 得到一个解,curSum 清空,继续 left++找下一个解
curSum = 0
left++
} else if (curSum < target) {
right++;
} else {
left++
}
}
return res
};
let target = 15
console.log(findContinuousSequence(target))
// 输出:[[1,2,3,4,5],[4,5,6],[7,8]]
140. 两整数之和 (sum-of-two-integers)
标签
- 位运算
- 中等
题目
这里不贴题了,leetcode打开就行,题目大意:
不使用
运算符 +
和 -
,计算两整数 a 、b 之和。
示例 1
输入: a = 1, b = 2
输出: 3
示例 2
输入: a = -2, b = 3
输出: 1
基本思路
不使用运算符 + 和 -, 那我们需要其他方式来替代这两个运算符的功能。
想到位运算
。
这是我这么多篇唯一主要用位运算的一题,平时如果你是写业务代码我这里不建议用位运算,它牺牲了很多可读性,要多考虑新手和团队。除非你写的是底层框架,隐藏很深,暴露简单 interface 对外这个另当别论。
那我们就讲讲一些基础的位运算。
先看位运算中的加法
0 + 0 = 0
0 + 1 = 1
1 + 0 = 1
1 + 1 = 0(进 1)
而异或
运算似乎除了进位都一样
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0
而 5 ^ 6
的计算是按位进行异或
5 = 1 0 1
6 = 1 1 0
^ -----
0 1 1 => 3
console.log(5 ^ 6) // 3
我们同样注意到,他是不会进位的
,所以我们还需要找到需要进位的点
而与运算
满足这个特点,都是 1 才会有进位
5 = 1 0 1
6 = 1 1 0
& -----
1 0 0 => 准确找到了需要进位的点
但这个位置左边一位
才是它的进位的数位
,所以需要把 与运算结果
左移一位
左移运算符 <<
,左移后结果为 (5 & 6) << 1 === 8
最后把这两个结果相加 8 + 3 = 11
即是 5 + 6
的和
使用递归,直到没有需要进位为止(递归结束条件)
写法实现
var getSum = function(a, b) {
// 递归出口
if (b === 0) {
return a;
}
return getSum(a ^ b, (a & b) << 1);
};
let a = 1, b = 2
// let a = -2, b = 3
console.log(getSum(a, b))
另外向大家着重推荐下这个系列的文章,非常深入浅出,对前端进阶的同学非常有作用,墙裂推荐!!!核心概念和算法拆解系列
今天就到这儿,想跟我一起刷题的小伙伴可以加我微信哦 点击此处交个朋友
Or 搜索我的微信号infinity_9368
,可以聊天说地
加我暗号 "天王盖地虎" 下一句的英文
,验证消息请发给我
presious tower shock the rever monster
,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧