C++ 当所有的值都是非负值时,我们真的能避免额外的空间吗?
这个问题是我不久前提出的后续问题: 我们已经得到了一个整数数组和另一个数字k,我们需要找到其和等于k的连续子数组的总数。例如,对于输入:C++ 当所有的值都是非负值时,我们真的能避免额外的空间吗?,c++,algorithm,memory,space-complexity,C++,Algorithm,Memory,Space Complexity,这个问题是我不久前提出的后续问题: 我们已经得到了一个整数数组和另一个数字k,我们需要找到其和等于k的连续子数组的总数。例如,对于输入:[1,1,1]和k=2,预期输出为2 在报告中说: PS:顺便说一句,如果所有值都是非负的,则有更好的算法。它不需要额外的内存 虽然当时我没怎么想,但现在我对它很好奇。嗯,我们需要额外的内存。如果所有的输入值都是非负的,我们的运行(前缀)和将继续增加,因此,当然,我们不需要一个无序映射来存储特定和的频率。但是,我们仍然需要额外的内存(可能是一个无序集)来存储正在
[1,1,1]
和k=2
,预期输出为2
在报告中说:
PS:顺便说一句,如果所有值都是非负的,则有更好的算法。它不需要额外的内存
虽然当时我没怎么想,但现在我对它很好奇。嗯,我们需要额外的内存。如果所有的输入值都是非负的,我们的运行(前缀)和将继续增加,因此,当然,我们不需要一个无序映射来存储特定和的频率。但是,我们仍然需要额外的内存(可能是一个无序集
)来存储正在运行的(前缀)和。这显然与所说的相矛盾
有人能确认一下我们是否真的需要额外的内存,或者它是否可以避免
谢谢 我认为这个算法可以使用O(1)
空间
我们维护两个指向当前子序列开始和结束的指针,以及当前子序列的总和。最初,两个指针都指向array[0]
,并且总和显然设置为array[0]
向前移动结束指针(从而将子序列向右扩展),并按其指向的值增加总和,直到总和超过k
。然后向前移动开始指针(从而从左侧收缩子序列),并减小和,直到和低于k
。继续执行此操作,直到结束指针到达数组的末尾。跟踪求和的精确次数k
让我们从一个稍微简单的问题开始:所有值都是正的(没有零)。在这种情况下,子阵列可以重叠,但不能相互包含
即:arr=21512
,Sum=8
2 1 5 1 1 5 1 2
|---|
|-----|
|-----|
|---|
但这种情况永远不会发生:
* * * * * * *
|-------|
|---|
考虑到这一点,有一种算法不需要额外的空间(嗯..O(1)
space),并且具有O(n)
时间复杂度。ideea应具有左右索引,指示当前序列和当前序列的总和
- 如果总和为
k
递增计数器,则向前推进左
和右
- 如果总和小于
k
,则前进right
- else advance<代码>左侧
现在,如果有零,区间可以互相包含,但前提是零位于区间的边距上 要适应非负数,请执行以下操作: 执行上述操作,但以下情况除外:
- 向左推进
时跳过零
- 如果总和为
:k
- 在
的右侧计算连续的零,比如说right
zero\u right\u count
- 在
的左侧连续计算零。比如说左侧
zero\u left\u count
- 不要像以前那样增加计数,而是将计数器增加:
(零左计数+1)*(零右计数+1)
- 在
... 7 0 0 5 1 2 0 0 0 9 ...
^ ^
left right
这里有两个零在左边,三个零在右边。这使得(2+1)*(3+1)=12个序列的和8
如下:
5 1 2
5 1 2 0
5 1 2 0 0
5 1 2 0 0 0
0 5 1 2
0 5 1 2 0
0 5 1 2 0 0
0 5 1 2 0 0 0
0 0 5 1 2
0 0 5 1 2 0
0 0 5 1 2 0 0
0 0 5 1 2 0 0 0
因为这些值仅仅是非负的,而不是严格意义上的正的,所以您需要解释零。@EricPostChil我们将此作为练习留给读者。我记得我以前见过这个问题,所以这一定是重复的。有人能找到它吗?