前端算法必刷题系列[74]

245 阅读2分钟

这是我参与更文挑战的第 28 天,活动详情查看 更文挑战

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

139. 和为s的连续正数序列 (findContinuousSequence)

标签

  • 数学
  • 滑动窗口
  • 简单

题目

leetcode 传送门

这里不贴题了,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 传送门

这里不贴题了,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,我看到就通过,加了之后我会尽我所能帮你,但是注意提问方式,建议先看这篇文章:提问的智慧

参考