不能躺平起来嗨~

337 阅读5分钟

在leetcode上做了一道算法题,关于前缀和map优化的。记录一下。

先来看看原题型:
给你一个整数数组nums和一个整数k,请你统计并返回该数组中和为k的子数组的个数。(子数组是数组中元素的连续非空序列)

先来看看什么是子数组
比如一个数组为[1,2,3],则它的子数组为[1],[2],[1,2],[3],[2,3],[1,2,3]。可能有的人想的思路是[1],[2],[3],[1,2],[2,3],[1,2,3]。

其实前一种思路有从前往后只需要一遍循环的既视感,即:连续子数组的个数等于(i为数组下标)以i = 0结尾的子数组个数 + 以i = 1结尾的子数组个数 + 以i = 2 结尾的子数组个数 + ... + 以i = 数组长度- 1结尾的子数组的个数。即P[1,2,3] = [1] + ([2], [1,2]) + ([3], [2,3], [1,2,3]) = 1 + 2 + ... + n = (1 + n) / 2。

再来看看什么是前缀和
可以简单理解为数列的前n项的和,例如:[1,2,3,4,5,6],它的前缀和就是[1,2,3,10,15,21],所以sum[i] = sum[i - 1] + nums[i],我们可以在前缀和前加个0,即[0,1,2,3,10,15,21],避免i-1为负数的尴尬。

所以计算子数组个数也带有前缀和的思路,代码如下:

const countsub = (nums) => {
    let count = 0; // 统计子数组和
    let pre = 0; // 记录在当前第几个位置上往前数有几个子数组,保留前一个位置满足条件子数组个数
    for (let i = 0; i < nums.length; i++) {
        pre++;
        count += pre;
    }
    return count;
}

还有变种题:求一个数组相邻差为1的连续子数组的总个数?

思路:先来理解上道题目的pre,表示记录当前位置的前个位置有几个子数组,如:[1,2,3],当i = 2,即到了3这个位置,它的子数组为[3],[2,3],[1,2,3];是i = 1的时候子数组的个数 + 1,所以pre++。pre保留了前一个满足子数组个数条件基础上 + 1个。一定要

再来看看当前题目,相邻数组差为一。

举个例子,如:数组为[1,2,3],到了i = 1,即到了2的位置,满足条件的数组有[2], [1,2];

到了i = 2,即到了3的位置,满足的数组是在[3],[2,3],[1,2,3];

可以理解为,只要是nums[i] - nums[i - 1] === 1满足了,这里为3-2 = 1满足,就可以复用i = 1时候满足条件的pre,即[2,3]和[1,2,3],要不然只有它自己[3],即pre = 1。

写下代码:

function countsub = (nums) => {
    let count = 1;
    let pre = 1;
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] - nums[i - 1] === 1) {
            pre++;
        } else {
            pre = 1;
        }
        count += pre;
    }
    return count;
}

变种题:求出不大于k的子数组的个数?

思路:这道题的意思是子数组里每个数值都小于等于k。

如下代码:

function countsub = (nums, k) => {
    let count = 0; 
    let pre = 0; 
    for (let i = 0; i < nums.length; i++) {
        if (nums[i] <= k) {
            pre += 1; 
        } else { 
            pre = 0; 
        } count += pre; 
    }
    return count;
}

再回来看看这道题:给你一个整数数组nums和一个整数k,请你统计并返回该数组中和为k的子数组的个数。

思路:我们先循环获取前缀和的数组。数组中和为k的子数组,例如:数组为[1,2,3],k为3。我们循环得到前缀和数组为sumArr = [1,3,6],为了好取值,可以改为[0,1,3,6],满足子数组和为3,只要满足sumArr[i] - sumArr[j] = 3。

优化点:我们将每一步得到的和保留在一个map中,这样sumArr[i] - k的值如果在map中有值,就把它的value值取出。

如下代码:

const subarraySum = (nums, k) => {
    let count; // 记录子数组的个数 
    let pre; // 记录前缀和 
    let m = new Map(); // map的k值为前缀和里的每一个pre,value值为这个pre出现的个数,比如[0,0,0],key为0,value需要叠加上去,为4 
    m.set(0,1); // 因为自身满足等于k值也可以,所以默认map加入个0; 
    for (let i = 0; i < nums.length; i++) {
        pre += nums[i];
        if (m.get(pre - k)) {
            count += m.get(pre - k); 
        }
        if (m.get(pre)) {
            m.set(pre, m.get(pre) + 1); 
        } else {
            m.set(pre, 1);
        }
     }
     return count;
   }

其他随笔:

社会好像是一个大舞台,时常感觉身边有很多人非常厉害,必要时可以侃侃而谈,落落大方。但是当深入其中,你会发现,很多人所表现出来的出彩和光芒其实是自我包装和他人的滤镜,在深入交流时发现其可能也只是一知半解,没有人能做到十全十美。
根据艾宾浩斯遗忘理论,人的知识如果不重复训练会随着时间的流逝慢慢消散,最终留下的可能只是很小的影响最深刻的一部分,当然这一部分已经足够面对大部分的场景。大部分优秀的人也只是在反复的练习。无他,但手熟尔。要知道,优秀的人可能一生都在自我完善,自我学习。
形成的观点和认知要么是身边人的影响,亦或是书籍及其他渠道。当你处在一个身边的并不是十分优秀的环境下,过滤不必要的输入,多去从经典书籍中汲取知识是十分有比要的,人和人差异可能就是日积月累而来。
不需要觉得自己不可以,深入某一领域,肯花时间和思考,并且保持输出,你完全可以比大部分的人优秀,变成你的专业及特长,一定要坚信时间的力量!如果想练习表达,每天下班的时候带上耳机,自我口述,你会发现一个月后你已经不再害怕表达。
一定要学会包装和展现你自己。有些时候,可能你并没有擅长某一块领域,但是又能如何,大家可能都不是非常了解。只要你能获得了实践的机会,一段时间后,你就是可以成为这个领域的专家。这个时代从来不缺人,但缺少的是愿意主动去尝试,不害怕失败,勇于挑战、勇于承担后果的人。

以上仅为日常的自我反省和自我审视,如果有不当之处,望指出。

希望大家都能喜乐平安,赚大钱~