LeetCode 和至少为K的最短子数组

版权声明:本文为博主原创文章,博客地址:https://blog.csdn.net/qq_41855420,未经博主允许不得转载。 https://blog.csdn.net/qq_41855420/article/details/91128600

返回 A 的最短的非空连续子数组的长度,该子数组的和至少为 K 。

如果没有和至少为 K 的非空子数组,返回 -1 。

示例 1:

输入:A = [1], K = 1
输出:1

示例 2:

输入:A = [1,2], K = 4
输出:-1

示例 3:

输入:A = [2,-1,2], K = 3
输出:3

提示:

1 <= A.length <= 50000
-10 ^ 5 <= A[i] <= 10 ^ 5
1 <= K <= 10 ^ 9

\color{blue}思路分析: 这道题测试数据量比较大,所以使用蛮力法的都会超时。下面的代码是来自评论区大神的O(n)算法。
思路有点像滑动窗口,即使用两个指针分别作为窗口的left、right边界,如果窗口中的sum小于K,则一直往前移动right,当sum >= K时,这时我们又考虑缩小窗口(右移left),这样就会得到一个结果,用这个结果去更新minRes。
不过这里有点特殊的是当A[i] <= 0,这种负值对于子数组的和超过K不起任何作用,因此我们需要将它尽可能从子数组中剔除,特别是当A[i] << 0时,可能造成A[left] + A[left + 1] + … + A[i] <= 0,也就是说A[i]远小于0,造成[left, i]这一段都是子数组中的无用元素。

class Solution {
public:
	int shortestSubarray(vector<int>& A, int K) {
		int len = A.size(), minRes = INT_MAX;//minRes记录最短子数组
        int sum = 0, left = 0;//sum记录当前窗口中的元素和,left左边界
        //right为右边界
        for (int right = 0; right < len; ++right) {
            if (A[right] >= K) {
                //如果单个元素超过了K,则最优解必定是1
                return 1;
            }
            sum += A[right];//放入滑动窗口
            if (sum < 1) {
                //如果窗口sum小于1,则说明窗口中的元素都是在做无用功,因为我们需要找到大于K的最短子数组
                sum = 0;//清空窗口
                left = right + 1;//移动左边界,方便下一个窗口的形成
                continue;
            }
            //特殊操作,如果当前右边界附近出现一连串的负值,则把他们累加转到他们之前的正值上
            //这样处理并不会影响窗口求和,蛋式在缩小左边界时候将会起到巨大的作用,比如A[i] << 0 造成A[left] + A[left + 1] + .. + A[i] <= 0
            for (int j = right - 1; A[j + 1] < 0; --j) {
                A[j] += A[j + 1];//将负值转移到它前一个元素上
                A[j + 1] = 0;//自己清空
            }
            if (sum >= K) {
                //窗口sum不小于K,这时我们尝试右移左边界left,尽量减小窗口大小
                while (sum - A[left] >= K || A[left] <= 0) {
                    sum -= A[left++];
                }
                minRes = min(minRes, right - left + 1);//right - left + 1表示窗口大小
            }
        }
        return minRes == INT_MAX ? -1 : minRes;
	}
};

在这里插入图片描述
这个算法最精华的部分就是当前窗口右边界附近出现一连串的负值,把他们累加转到他们之前的正值上,这样下次这一段出现在某个窗口的左边界附近的时候可以快速剔除。

猜你喜欢

转载自blog.csdn.net/qq_41855420/article/details/91128600