C++前缀和算法应用:和至少为 K 的最短子数组的原理、源码及测试用例

本文涉及的基础知识点

C++算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频

题目

给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。

子数组 是数组中 连续 的一部分。

示例 1:

输入:nums = [1], k = 1

输出:1

示例 2:

输入:nums = [1,2], k = 4

输出:-1

示例 3:

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

输出:3

提示:

1 <= nums.length <= 105

-105 <= nums[i] <= 105

1 <= k <= 10^9

#分析

时间复杂度

枚举子数组的结尾i,时间复杂度O(n),利用二分查找,每次枚举O(logn),故总时间复杂度是O(nlogn)。

细节

llSun是num[0,i]的和,vSumIndex 记录[0,j]之和,j取值[-1,i)。假定j0 < j1,且sum[j0] >= sum[j1],那sum[j0,i]小于sum[j1,i]且j0的长度大于j1,所以j0一定不是备选答案,可淘汰。淘汰后如果j0<j1,则sum[j0]一定小于sum[j1]。也就是前缀和和索引都是按升

序排序。sum-sumold >=k ==> sum-k>=sumold ==>sumold <= sum-k 。淘汰的时候:由于是按升序排序,所以sum[j1]不大于等于sum-k,那么sum[j0]也一定不大于等于sum-k。所以找到一个不符合,就可停止了。

#核心代码

class Solution {

public:

int shortestSubarray(vector& nums, int k) {

m_c = nums.size();

m_vRet.assign(m_c, -1);

vector<pair<long long, int>> vSumIndex = { {0,-1} };

long long llSum = 0;

m_vRet.assign(m_c, INT_MAX);

for (int i = 0; i < m_c; i++)

{

llSum += nums[i];

//sum-sumold >=k ==> sum-k>=sumold ==>sumold <= sum-k

//由于sum和index都是升序,所以可以二分

auto it = std::upper_bound(vSumIndex.begin(), vSumIndex.end(), llSum - k, []( const long long ll,const pair<long, int>& pi)

{

return ll < pi.first;

});

if (vSumIndex.begin() != it)

{

m_vRet[i] = i - std::prev(it)->second;

}

while (vSumIndex.size() && (vSumIndex.back().first >= llSum))

{

vSumIndex.pop_back();

}

vSumIndex.emplace_back(llSum, i);

}

const int iRet = *std::min_element(m_vRet.begin(), m_vRet.end());

return (INT_MAX == iRet) ? -1 : iRet;

}

int m_c;

vector m_vRet;

};

测试用例

m_vRet是多余的,是为了方便排错。

template

void Assert(const vector& v1, const vector& v2)

{

if (v1.size() != v2.size())

{

assert(false);

return;

}

for (int i = 0; i < v1.size(); i++)

{

assert(v1[i] == v2[i]);

}

}

template

void Assert(const T& t1, const T& t2)

{

assert(t1 == t2);

}

class Solution {

public:

int shortestSubarray(vector& nums, int k) {

m_c = nums.size();

m_vRet.assign(m_c, -1);

vector<pair<long long, int>> vSumIndex = { {0,-1} };

long long llSum = 0;

m_vRet.assign(m_c, INT_MAX);

for (int i = 0; i < m_c; i++)

{

llSum += nums[i];

//sum-sumold >=k ==> sum-k>=sumold ==>sumold <= sum-k

//由于sum和index都是升序,所以可以二分

auto it = std::upper_bound(vSumIndex.begin(), vSumIndex.end(), llSum - k, []( const long long ll,const pair<long, int>& pi)

{

return ll < pi.first;

});

if (vSumIndex.begin() != it)

{

m_vRet[i] = i - std::prev(it)->second;

}

while (vSumIndex.size() && (vSumIndex.back().first >= llSum))

{

vSumIndex.pop_back();

}

vSumIndex.emplace_back(llSum, i);

}

const int iRet = *std::min_element(m_vRet.begin(), m_vRet.end());

return (INT_MAX == iRet) ? -1 : iRet;

}

int m_c;

vector m_vRet;

};

错误做法

auto it = std::upper_bound(vSumIndex.begin(), vSumIndex.end(), std::make_pair(llSum - k,0));

我们期望:

返回第一个 first大于llSum-k的值。

实际上,返回第一个符合以下条件之一的迭代器:

一,first大于llSum-k

二,first等于llSum-k,second>0

解决方法:将0换成m_c,这样条件二,就永远不会成立。也可以换成INT_MAX。修改后的代码如下:

auto it = std::upper_bound(vSumIndex.begin(), vSumIndex.end(), std::make_pair(llSum - k,m_c));

2023年3月分的旧版

仅供参考

template

bool Less(const std::pair<Class1, int>& p, Class1 iData)

{

return p.first < iData;

}

template

bool Greater(const std::pair<Class1, int>& p, Class1 iData)

{

return p.first > iData ;

}

class Solution {

public:

int shortestSubarray(vector& nums, int k) {

int iMinLen = INT_MAX;

std::vector<std::pair<long, int>> vQue;

vQue.emplace_back(0, -1);

long long llSum = 0;

for (int i = 0; i < nums.size(); i++)

{

llSum += nums[i];

int iLessEqualNum = std::lower_bound(vQue.begin(), vQue.end(), llSum - k + 1, Less) - vQue.begin();

if (iLessEqualNum > 0 )

{

iMinLen = min(iMinLen, i - vQue[iLessEqualNum - 1].second);

}

while (vQue.size() && (llSum <= vQue.back().first))

{

vQue.pop_back();

}

vQue.emplace_back(llSum, i);

}

return (INT_MAX == iMinLen) ? -1 : iMinLen;

}

};

扩展阅读

视频课程

有效学习:明确的目标 及时的反馈 拉伸区(难度合适),可以先学简单的课程,请移步CSDN学院,听白银讲师(也就是鄙人)的讲解。
https://edu.csdn.net/course/detail/38771

如何你想快

速形成战斗了,为老板分忧,请学习C#入职培训、C++入职培训等课程
https://edu.csdn.net/lecturer/6176

相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版
https://download.csdn.net/download/he_zhidan/88348653

鄙人想对大家说的话
闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。
墨家名称的来源:有所得以墨记之。
如果程序是一条龙,那算法就是他的是睛

测试环境

操作系统:win7 开发环境: VS2019 C++17

或者 操作系统:win10 开发环境: VS2022 C++17

相关推荐
唐诺2 小时前
几种广泛使用的 C++ 编译器
c++·编译器
XH华2 小时前
初识C语言之二维数组(下)
c语言·算法
南宫生2 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
不想当程序猿_3 小时前
【蓝桥杯每日一题】求和——前缀和
算法·前缀和·蓝桥杯
落魄君子3 小时前
GA-BP分类-遗传算法(Genetic Algorithm)和反向传播算法(Backpropagation)
算法·分类·数据挖掘
冷眼看人间恩怨3 小时前
【Qt笔记】QDockWidget控件详解
c++·笔记·qt·qdockwidget
菜鸡中的奋斗鸡→挣扎鸡3 小时前
滑动窗口 + 算法复习
数据结构·算法
红龙创客3 小时前
某狐畅游24校招-C++开发岗笔试(单选题)
开发语言·c++
Lenyiin3 小时前
第146场双周赛:统计符合条件长度为3的子数组数目、统计异或值为给定值的路径数目、判断网格图能否被切割成块、唯一中间众数子序列 Ⅰ
c++·算法·leetcode·周赛·lenyiin
郭wes代码3 小时前
Cmd命令大全(万字详细版)
python·算法·小程序