乘积小于 K 的子数组(滑动窗口)
一.前言
今天奉上的题是来自LeetCode中的一道中等难度的题,但是如果了解滑动窗口的思想,其实这道题也是比较简单的,题目如下:
给你一个整数数组 nums
和一个整数 k
,请你返回子数组内所有元素的乘积严格小于k
的连续子数组的数目。
示例一:
输入:nums = [10,5,2,6], k = 100
输出:8
解释:8 个乘积小于 100 的子数组分别为:[10]、[5]、[2],、[6]、[10,5]、[5,2]、[2,6]、[5,2,6]。
需要注意的是 [10,5,2] 并不是乘积小于 100 的子数组。
示例二:
输入:nums = [1,2,3], k = 0
输出:0
二.思考
像这种从一个数组里面找一些子数组或者子字符串的问题大都可往这方面靠,一般来说都是可以解决的。因此我们大致思路是这样的,同样定义两个指针(一个叫 left,一个叫 i),它们都指向数组的第一个元素。我们用 i 指针来遍历数组,每当 i 指向新的元素时我们都计算 i 指针和 left 指针之间(窗口之间)元素的积并判断一下是否小于 k,若成立则当前子数组就符合条件,用 n 来记录一次。反之,则 i 继续移动下去。
具体的我们看着代码来解释:
1 | class Solution { |
三.再次思考
如果理解了上面这个做法的话,那让我们想想是否还有更优解呢?
在上面这个做法中,我们每开始一次新的查找,都需要将 i 指针和 left 指针重新指向同一个新的元素,并且 addtion 也需要重置为 1,这样看来这其中确实有不妥之处,那让我们来看看官方的的做法吧。LeetCode官方题解:
1 | class Solution { |
思路与分析:整体思想和我们最开始讲的是一样的,只不过在记录子数组个数和重置各元素积这两部进行了优化。
我们着重讲述 while 中有差异的这段,我们可以这么理解:i 指针相当于第一种方法的 left,j 指针相当于之前的 i。这里 while 中有一个 i <= j
条件,这是用来控制 i 指针的范围的,也就是说 i 指针是不能超过 j 指针的,这样就会有一个好处:因为 j < n
,所以 i 就不会超出数组的长度了,也就能避免我们第一种方法还要专门写个if
来判断 i 是否越界,简化了代码。代码进入循环后,也就意味着 prod > k
了,这时我们为了符合条件,需要把 i 指针向后移动,而 prod 值只需要除以nums[i]
就等于此时窗口中元素的值。这和我们第一种方法相比就显得非常优秀了,我们之前的方法需要每次把 prod 值归 1,然后由要经历 prod 等于原来那个值的过程,这就显得有点多余了。