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

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

时间:2023-10-24 16:04:30浏览次数:40  
标签:std nums vSumIndex C++ vRet int 源码 llSum 短子

题目

给你一个整数数组 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;
 }
 };

扩展阅读

相关下载

想高屋建瓴的学习算法,请下载《闻缺陷则喜算法册》doc版

鄙人想对大家说的话

闻缺陷则喜是一个美好的愿望,早发现问题,早修改问题,给老板节约钱。

墨家名称的来源:有所得以墨记之。

如果程序是一条龙,那算法就是他的是睛

测试环境

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

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


标签:std,nums,vSumIndex,C++,vRet,int,源码,llSum,短子
From: https://blog.51cto.com/u_15724537/8005200

相关文章

  • C++前缀和算法:构造乘积矩阵
    题目给你一个下标从0开始、大小为n*m的二维整数矩阵grid,定义一个下标从0开始、大小为n*m的的二维矩阵p。如果满足以下条件,则称p为grid的乘积矩阵:对于每个元素p[i][j],它的值等于除了grid[i][j]外所有元素的乘积。乘积对12345取余数。返回grid的乘积......
  • C++算法:二叉树的序列化与反序列化
    #题目序列化是将一个数据结构或者对象转换为连续的比特位的操作,进而可以将转换后的数据存储在一个文件或者内存中,同时也可以通过网络传输到另一个计算机环境,采取相反方式重构得到原数据。请设计一个算法来实现二叉树的序列化与反序列化。这里不限定你的序列/反序列化算法执行逻......
  • C++算法前缀和的应用:得分最高的最小轮调的原理、源码及测试用例
    题目给你一个数组nums,我们可以将它按一个非负整数k进行轮调,这样可以使数组变为[nums[k],nums[k+1],…nums[nums.length-1],nums[0],nums[1],…,nums[k-1]]的形式。此后,任何值小于或等于其索引的项都可以记作一分。例如,数组为nums=[2,4,1,3,0],我们按k=2进行......
  • C++算法:数据流的中位数
    题目中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。例如arr=[2,3,4]的中位数是3。例如arr=[2,3]的中位数是(2+3)/2=2.5。实现MedianFinder类:MedianFinder()初始化MedianFinder对象。voidaddNum(int......
  • C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
    分割数组的最大值二分过些天整理基础知识题目给定一个非负整数数组nums和一个整数m,你需要将这个数组分成m个非空的连续子数组。设计一个算法使得这m个子数组各自和的最大值最小。示例1:输入:nums=[7,2,5,10,8],m=2输出:18解释:一共有四种方法将nums分割为2个......
  • C++算法:给表达式添加运算符
    题目给定一个仅包含数字0-9的字符串num和一个目标值整数target,在num的数字之间添加二元运算符(不是一元)+、-或*,返回所有能够得到target的表达式。注意,返回表达式中的操作数不应该包含前导零。示例1:输入:num=“123”,target=6输出:[“1+2+3”,“123......
  • C++数位算法:数字1的个数
    题目给定一个整数n,计算所有小于等于n的非负整数中数字1出现的个数。示例1:输入:n=13输出:6示例2:输入:n=0输出:0提示:0<=n<=1092023年1月版classSolution{public:intcountDigitOne(intn){intiNum=0;intiMul=1;for(inti=0;i<9;i++){......
  • C++桶排序算法的应用:存在重复元素 III
    题目给你一个整数数组nums和两个整数indexDiff和valueDiff。找出满足下述条件的下标对(i,j):i!=j,abs(i-j)<=indexDiffabs(nums[i]-nums[j])<=valueDiff如果存在,返回true;否则,返回false。示例1:输入:nums=[1,2,3,1],indexDiff=3,valueDiff=0输出......
  • C++前缀和算法应用:矩形区域不超过 K 的最大数值和
    题目给你一个mxn的矩阵matrix和一个整数k,找出并返回矩阵内部矩形区域的不超过k的最大数值和。题目数据保证总会存在一个数值和不超过k的矩形区域。示例1:输入:matrix=[[1,0,1],[0,-2,3]],k=2输出:2解释:蓝色边框圈出来的矩形区域[[0,1],[-2,3]]的数值和是......
  • 时间复杂度O(40n*n)的C++算法:修改图中的边权
    1.12.1.题目给你一个n个节点的无向带权连通图,节点编号为0到n-1,再给你一个整数数组edges,其中edges[i]=[ai,bi,wi]表示节点ai和bi之间有一条边权为wi的边。部分边的边权为-1(wi=-1),其他边的边权都为正数(wi>0)。你需要将所有边权为-1的边都修改为范......