【力扣每日一题】713. 乘积小于 K 的子数组

本文通过一道编程题,探讨了如何使用滑动窗口优化算法来解决寻找乘积小于特定值的连续子数组数量的问题。作者对比了自己最初的O(n^2)解决方案和官方的O(n)解决方案,强调了考虑相邻循环间关系以减少冗余计算的重要性,从而提高算法效率。
题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回子数组内所有元素的乘积严格小于 k 的连续子数组的数目。

示例 1:

输入: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 的子数组。

示例 2:

输入:nums = [1,2,3], k = 0
输出:0

提示:

  • 1 <= nums.length <= 3 * 10^4
  • 1 <= nums[i] <= 1000
  • 0 <= k <= 10^6

有了上次的经验,这次的连续子数组项目我是游刃有余啊,直接两层for循环走起,每一个元素开头的情况都遍历一遍,只要乘积小于k,就一直往后加,直到大于k为止,本质上是一种滑动窗口的思路。但时间复杂度达到了O(n2),耗时比较久。

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int ans = 0;
        for(int i=0;i<nums.length;i++){
            //定义每一次的乘积结果
            int product = 1;
            for(int j=i;j<nums.length;j++){
                product *= nums[j];
                //如果不是严格小于k就跳出循环
                if(!(product<k))break;                
                ans++;
            }
        }
        return ans;
    }
}

官方对于这一滑动窗口的思路给出了另一种解法,以子数组的右端点为标准,去判断左端点是否满足,这样左端点不会每次都从0开始遍历,而是从上一次的位置开始,这就是利用了每一轮循环对下一轮循环的帮助,在时间复杂度上会大大减少,只有O(n),当然这里从右往左以左端点为标准,判断右端点也是一样。

官方
class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int n = nums.length, ret = 0;
        int prod = 1, j = n-1;
        for (int i = n-1; i >= 0; i--) {
            prod *= nums[i];
            while (i <= j && prod >= k) {
                prod /= nums[j];
                j--;
            }
            ret += j - i + 1;
        }
        return ret;
    }
}

今天的题目,一是弥补了我上次周赛的遗憾,我用自己的思路做出来了;二是再一次提醒我,想要提高效率,缩短运行时间,必须要尽可能地减少冗余的计算过程。

像我自己的那个思路里,每一次循环都是独立的,都是从一个位置开始,一直到结尾,我并没有注意每一次循环之间的关系;

而官方的解法中,将相邻两次的循环关联起来,每一次循环都为下一次循环缩小了遍历范围,省去了不必要的运行时间,提高了效率,整个计算过程就连成一个整体,十分美妙。这种境界我还需要慢慢探索,不断磨炼啊。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值