对于数组[4,3,5,1,2], 我们调用前缀4是NULL,前缀少于4是0; 前缀3为[4],前缀为3为0,因为前缀中没有小于3; 前缀5是[4,3],前缀少于5是2,因为4和3都小于5; 前缀1为[4,3,5],前缀为1为0,因为前缀中没有小于1; 前缀2是[4,3,5,1],前缀少于2是1,因为只有1小于2
因此对于数组[4,3,5,1,2],我们得到[0,0,2,0,1]的无前缀的arrary, 我们可以得到一个O(n)算法来获得无前缀的数组吗?
答案 0 :(得分:1)
由于comparison sort需要O(n)
比较的相同原因,无法在O(n log n)
中完成此操作。可能无前缀数组的数量为n!
,因此您至少需要log2(n!)
位信息才能识别正确的无前缀数组。 log2(n!)
为O(n log n)
,Stirling's approximation。
答案 1 :(得分:1)
假设输入元素总是固定宽度整数,您可以使用基于基数排序的技术来实现线性时间:
在伪代码中......
Def PrefixLess(L, X, n)
if (n == 0)
return;
// setup prefix less for bit n
Count = 0
For I in 1 to |X|
P(I) += Count
If (L(X(I))[n] == 0)
Count++;
// go through subsequence with bit n-1 with bit(n) = 1
Y = []
For I in 1 to |X|
If (L(X(I))[n] == 1)
Y.append(X(I))
PrefixLess(L, Y, n-1)
// go through subsequence on bit n-1 where bit(n) = 0
Y = []
For I in 1 to |X|
If (L(X(I))[n] == 0)
Y.append(X(I))
PrefixLess(L, Y, n-1)
return P
然后执行:
PrefixLess(L, 1..|L|, 32)
答案 2 :(得分:0)
我认为这应该有用,但请仔细检查细节。让我们在原始数组a [i]中调用一个元素,在前缀数组中调用一个元素作为p [i],其中i是各个数组的第i个元素。
所以,假设我们在[i],我们已经计算了p [i]的值。有三种可能的情况。如果a [i] == a [i + 1],则p [i] == p [i + 1]。如果a [i]< a [i + 1],然后p [i + 1]> = p [i] + 1.这使我们得到a [i]>的情况。一个[I + 1]。在这种情况下,我们知道p [i + 1]> = p [i]。
在天真的情况下,我们返回前缀并开始计算小于a [i]的项目。但是,我们可以做得更好。首先,认识到p [i]的最小值为0,最大值为i。接下来看一下索引j的情况,其中i>学家如果a [i]> = a [j],则p [i]> = p [j]。如果a [i]< a [j],然后p [i]< = p [j] + j。因此,我们可以通过p更新p [i] _min和p [i] _max的值来开始倒退。如果p [i] _min等于p [i] _max,那么我们就有了解决方案。
对算法进行包络分析,它具有O(n)最佳案例性能。这是列表已经排序的情况。最糟糕的情况是它被反向排序。然后性能是O(n ^ 2)。平均性能将是O(k * n),其中k是需要回溯多少。我的猜测是随机分布的整数,k会很小。
我也很确定如何针对部分排序数据的情况优化此算法。我会看Timsort对如何做到这一点的一些启发。它使用运行检测来检测部分排序的数据。因此算法的基本思想是通过列表一次并查找数据运行。对于递增的数据运行,您将得到p [i + 1] = p [i] +1的情况。对于下行运行,p [i] = p_run [0]其中p_run是运行中的第一个元素。