首页 > 代码库 > 待字闺中之子序列分析
待字闺中之子序列分析
原题
给定长度为n的整数数列:a0,a1,..,an-1,以及整数S。这个数列会有连续的子序列的整数总和大于S的,求这些数列中,最小的长度。
分析
如果只是像题目这样的描述,没有强调正数,可以采用O(n^2)的方法。具体代码如下:
int subSeqWithNegative(vector<int>& data,int sum) { int i,j,length = data.size(),res = length+1; for (i = 0;i < length;i++)//查找每一个以i开始的,满足条件的最短序列长度 { j = i; int curSum = 0; while(j < length) { curSum += data[j]; if (curSum > sum) { if(j - i + 1 < res)res = j - i + 1; break; } j++; } } if(res == length+1)return -1;//找不到大于sum的序列 return res; }
但是,很多同学在讨论的时候,指出了如果是正数,解法将会有什么样的变化。这个很好。不考虑正负的O(n^2)的方法,这里不详细说了,我们来讨论,当数列中都是正数的情况。
介绍一个利用排序+二分的方法。对于子序列ai...at,子序列和s=ai+...+at=sum[t]-sum[i-1]。sum[t]表示数列a0...at的和。那么,数组sum天然就是递增的,可以进行二分查找。 那么如何进行二分查找呢?对于数组sum,遍历找到第一个k,sum[k]>S,二分查找k前面的某一个j,j是sum[k]-sum[j]>S里最大的一个,则k-j是最小的。依次遍历完数组。 可以得到最小的长度,整体的时间复杂度O(nlogn),空间复杂度为O(n)。 是否有更快的方法呢?从以上两个方法,我们可以有如下的观察:
a0...at>S,则a0...at+1无需再考虑
对于a0...at>S,只需尝试a1...at是否>S,如果大于S,则更新最短长度。
如果不大于S,大于S的只可能是a1...atat+1等。
鉴于以上的观察,我们有如下的算法:设置索引i,j指向第一个整数:
1. ++j,直到sum[j]-sum[i]>S,这里不需要额外保存sum,为了方便说明。记录子序列长度
2. ++i,如果sum[j]-sum[i]>S,更新最小子序列长度。直到sum[j]-sum[i]<=S。
3. ++j,直到sum[j]-sum[i]>S。重复上面的两步,直到数组遍历完毕。
整个算法的时间复杂度为O(n)。下面我们做一个示例数组为{5,1,3,5,10,7,4,9,2,8},S=10,i=j=0开始
1. 当j=3时,和为14>10,则更新最小长度为4
2. 对i进行递增操作,和为9<10,不满足条件。对j进行递增
3. 当i=1,j=4时,和为19>10,长度为4,不更新最小长度。递增i,直到i=3,此时和15>10,更新最小长度为2,
4. 依次类推
最终得到最小长度为2.具体代码如下:
int subSeqWithPositive(vector<int>& data,int sum) { int i = 0 ,j = 0,length = data.size(),curSum = 0,res = length+1; while (j < length) { while (curSum <= sum)//移动后指针 { curSum += data[j++]; if(j >= length)break; } while (curSum > sum)//移动前指针 { if (res > j - i) res = j - i; curSum -= data[i++]; } } return res; }
待字闺中之子序列分析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。