leetcode 713. 乘积小于K的子数组(滑动窗口或二分)

本文探讨了寻找数组中乘积小于特定阈值的连续子数组个数的问题,提供了两种解决方案:一种是使用二分查找结合前缀和的方法,另一种是通过动态调整窗口大小来实现。文章详细介绍了每种方法的实现思路和代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给定一个正整数数组 nums。

找出该数组内乘积小于 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的子数组。

需要注意到数组内每一个数字都是正数,所以随着子数组的中数字个数增多,乘积也是单调递增的,这是这个题的关键。

找到这个单调递增的关系之后,有两种办法解决这个问题。

方法一:

首先是二分+前缀和,前缀和如不知请自行点击,利用前缀和可以在O(1)时间内求得一个子数组的乘积,那么对于数组的每一位i,我们可以从后向前找到最长的以i位为结尾子数组使得该子数组的乘积小于k。

注意到随着数组变长乘积单增,那么用一下二分枚举中点加上前缀和就能解决问题。

不过还有一点,这里前缀和保存的是乘积,容易爆掉long int的范围,为了避免这一点,可以取对数将乘积变成加法运算。

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if (k == 0) return 0;
        double logk = Math.log(k);
        double[] prefix = new double[nums.length + 1];
        for (int i = 0; i < nums.length; i++) {
            prefix[i+1] = prefix[i] + Math.log(nums[i]);
        }

        int ans = 0;
        for (int i = 0; i < prefix.length; i++) {
            int lo = i + 1, hi = prefix.length;
            while (lo < hi) {
                int mi = lo + (hi - lo) / 2;
                if (prefix[mi] < prefix[i] + logk - 1e-9) lo = mi + 1;
                else hi = mi;
            }
            ans += lo - i - 1;
        }
        return ans;
    }
}


代码原地址

方法二:

注意到上文说的单增的性质,假如[L,R]区间的子数组乘积>=k,那么无论如何扩大R,得到的子数组都不可能满足题意要得到满足题意的子数组只能扩大L,想到这点很关键。

那么我们要维护一个last[i]数组,last[i]代表从第i位开始满足题意的子数组最远可以到达哪一位。

从L=1开始,让R不断扩大,直到子数组乘积>=k,那么就得到了last[1],然后让L++,再去判断子数组乘积决定是否要移动R,注意R不可能缩小,以此类推就能得到所有的last[i]。

得到last数组之后,累加每一个(last[i]-i)就是答案,这个也很显然,画画图就明白。

class Solution {
    public int numSubarrayProductLessThanK(int[] nums, int k) {
        int len = nums.length;
        int sum = 1;
        int last = -1;
        int ans = 0;
        for(int i=0;i<len;i++)
        {
            if(i>0)
                sum = sum / nums[i-1];
            while(sum<k&&last<len)
            {
                last++;
                if(last>=len)
                    break;
                sum = sum * nums[last];
            }
            ans = ans + Math.max(last-i,0);
        }
        return ans;
    }
}
<think>嗯,用户问的是能不能用滑动窗口算法来找最小路径。这个问题我得仔细想想。首先,我得回忆一下滑动窗口算法通常用在什么地方。滑动窗口一般用于处理数组字符串中的子数组、子字符串问题,比如找最长无重复子串、子数组和满足特定条件的情况。它的核心思想是通过调整窗口的左右边界来减少重复计算,优化时间复杂度。 那最小路径问题通常出现在图论者动态规划中,比如Dijkstra算法者A*算法,用于在加权图中找到两点之间的最短路径。滑动窗口和这些算法之间有什么联系吗?可能用户是混淆了不同的问题类型。 不过,有没有例外的情况呢?比如说,在某种特定条件下的路径问题,可能可以转化为滑动窗口的问题。比如,如果路径是在一维数组上,比如在数组中找子数组,使得其和最小,这时候滑动窗口可能适用。但这种情况其实属于子数组问题,而不是传统的图论中的路径问题。比如,当问题涉及连续子数组的最小和,滑动窗口可能有用,但需要注意窗口移动的条件,比如当所有元素都是正数时,可能无法直接用滑动窗口,因为需要更复杂的条件判断。 者,用户可能误解了滑动窗口的适用范围,把窗口的概念应用到了路径遍历中。比如,在网格中寻找路径,每次移动窗口来覆盖可能的路径方向。不过这样的话,滑动窗口可能并不适用,因为路径通常是多方向的,而滑动窗口一般处理线性结构。 需要明确的是,滑动窗口主要用于解决子数组/子字符串问题,而最短路径算法处理的是图结构中的最优路径。两者的应用场景不同。因此,一般情况下,滑动窗口并不适用于寻找图网格中的最小路径。不过,如果问题被特定地限制,比如一维路径者某些变形,可能会有例外,但这种情况比较少见。 另外,可能用户提到的“最小路径”指的是某种特定场景,比如在滑动窗口过程中记录某些路径信息,但需要具体问题具体分析。比如,在动态规划中,如果状态转移可以分解为窗口内的操作,可能有类似滑动窗口的优化,但这已经不是传统意义上的滑动窗口算法了。 总结的话,应该告诉用户滑动窗口通常不用于传统的最小路径问题,但在特定子数组问题中可能有应用。需要根据具体问题来判断,并推荐正确的算法如Dijkstra、BFS动态规划。</think>可以分步骤回答如下: --- **关于“滑动窗口算法是否可以用于寻找最小路径”的问题,需要结合具体场景分析:** --- ### **一、滑动窗口算法的核心适用场景** 滑动窗口算法主要用于解决**线性结构(数组、字符串)上的子区间问题**,典型应用包括: 1. 寻找满足条件的**最长/最短子数组**(如:和≥target的最短子数组) 2. 统计满足条件的**子区间个数**(如:乘积小于k的子数组数量) 3. 处理**固定长度窗口的滑动计算**(如:长度为k的子数组最大平均值) **关键特征**:问题需要围绕**连续的子区间**展开,且可通过调整窗口边界避免重复计算。 --- ### **二、传统最小路径问题的常用算法** 最小路径问题通常出现在**图网格结构**中,常用算法包括: 1. **无权图最短路径**:广度优先搜索(BFS) 2. **带权图最短路径**:Dijkstra算法、Bellman-Ford算法 3. **网格动态规划**:如最小路径和问题(LeetCode 64) **关键特征**:需要处理**多方向移动****非连续跳跃**的路径选择。 --- ### **三、滑动窗口与最小路径的关联性** #### **1. 不适用传统图/网格路径** - 滑动窗口无法处理图结构中节点的**非连续访问**和多方向移动(如上、下、左、右)。 - 反例:在网格中从左上角到右下角找最小路径和(需动态规划而非滑动窗口)。 #### **2. 特殊场景下的变形应用** 若问题被限制为**一维线性路径且路径需连续**,可能间接关联滑动窗口: - **示例**:在数组中找到长度为`k`的子数组,使其和最小。 - 解法:滑动窗口计算窗口内和,维护最小值。 - 代码片段: ```python def min_subarray_sum(nums, k): window_sum = sum(nums[:k]) min_sum = window_sum for i in range(k, len(nums)): window_sum += nums[i] - nums[i - k] min_sum = min(min_sum, window_sum) return min_sum ``` --- ### **四、结论** 1. **传统最小路径问题**(图、网格):❌ 不适用滑动窗口,需用BFS、Dijkstra动态规划。 2. **一维连续子数组问题**:✅ 可通过滑动窗口优化,如固定长度的最小和子数组。 --- **建议**:根据具体问题类型选择合适的算法。若需进一步分析,请提供更多问题细节!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值