问题描述
假设给定一个数组 [1,2,3,...,n]。现在随机洗牌。什么是最好的方法(就时间和空间复杂度而言,在混洗数组中找到唯一连续增加的子序列的数量?
例如,a=[1,6,5,4,7,8,9] 有 3 个这样的子序列: [1,4],[6,9],[5]。 ([5] 微不足道地满足定义)
提前致谢。
编辑:我想到的一种方法是,每当我们遇到一个新元素时,检查它是否是前一个元素的 +1,如果不是,则将其放入一个新数组中。但是当有很多这样的数组时,检查下一个元素是否是任何现有数组的最后一个元素的 +1 需要更长的时间。所以空间复杂度是 n,但在最坏情况下花费的时间 a=[n,n-1,n-2,1] 是 1 + 2 + 3 +...+ n-1=O( n^2)。但可能有更好的方法。
编辑 2:抱歉,我刚刚意识到能够输出这些子序列的显式形式也很重要(如给出的示例所示)。所以有两种最好的复杂情况,一种用于计算数字,另一种用于输出显式形式。
解决方法
您可以通过计算连续的开始次数来计算连续的次数。如果您以前从未见过 x
,则值 x - 1
是连续开始。在您的示例中,这是 1、6 和 5。需要 O(n) 时间和空间。
a = [1,2,3,6,5,4,7,8,9]
streaks = 0
seen = set()
for x in a:
if x - 1 not in seen:
streaks += 1
seen.add(x)
print(streaks)
输出,GNU bash:
3
如果允许对 a
进行修改,则可以是 O(1) 空间(例如,记住 x
通过否定 a[x-1]
看到的)。
a = [1,9]
streaks = 0
for x in map(abs,a):
if a[x-2] > 0:
streaks += 1
a[x-1] *= -1
print(streaks)
输出,Try it online!:
3
收集条纹、时间和空间 O(n) 的版本:
a = [1,9]
streaks = {}
for x in a:
streak = streaks.pop(x - 1,[])
streak.append(x)
streaks[x] = streak
print(*streaks.values())
输出,Try it online!:
[5] [1,4] [6,9]
,
我想出了这个(在 Java 中):
public int countSubSequences(int[] array){
int numberOfSubSequences = 0;
for (int i = 0; i < array.length-1; i++) {
if(array[i]>array[i+1]){
numberOfSubSequences++;
}
}
numberOfSubSequences++;
return numberOfSubSequences;
}
我只是遍历数组并检查数组中的下一个元素是否小于我的当前元素。如果是这种情况,当前元素是子序列的结尾,我们可以增加计数。在返回之前我们也需要自增,因为数组的结尾也是最后一个子序列的结尾。
时间复杂度是线性的,所以 O=(n)
和空间复杂度应该是常数 O=(1)
,因为我们不需要记住使用这种方法的子序列来计算它们。