给出O(n)算法,该算法将数组S作为输入,然后将S分为三组:负数,零和正数。演示如何在适当的位置实现它,即不分配新内存。你必须保持数字的相对顺序。 例如: {-1,4,0,-2,1,2} ==> {-1,-2,0,4,1,2}
我不确定这样的解决方案是否会退出。我能想到的最好的解决方案是:
解决方案1:使用一个额外的整数数组,然后遍历整个数组得到负数,然后是0,然后是正数。
解决方案2:不要保持数字的相对顺序。然后循环数组两次:
template <typename Type>
void Partion(Type *array, int begin, int end, Type v, int &l, int &r)
{
l = begin;
for (int i=begin; i!=end; ++i)
{
if (array[i] < v)
swap(array[i], array[l++]);
}
r = l;
for (int j=l; j!=end; ++j)
{
if (array[j] == v)
swap(array[j], array[r++]);
}
}
答案 0 :(得分:15)
这是Edsger Dijkstra研究的Dutch national flag problem的一个实例。有趣的是,没有稳定解决这个问题的解决方案在O(n)时间和O(1)空间中运行(或者至少,我最后一次检查文献,没有已知的解决方案问题存在)。
答案 1 :(得分:3)
我不确定这是否有帮助,但是稳定地划分为三个类的要求可以减少到稳定划分为两个类的问题:将负数与非负数分开,然后将正数与非正数分开。如果可以在O(1)空间和O(n)时间内解决两类问题,则可以应用解决方案两次来解决原始问题。
答案 2 :(得分:1)
零是难以区分的,所以我认为你不关心它们是否被交换,甚至只是在最后被覆盖(即我们只是在我们完成将正数和负数移到相反的两端之后将中间部分归零的数组)。
如果您正在查看整数只是更大的关键字的情况,情况可能并非如此 - 您可能希望零保留并稳定分区。但如果没有,这里有两个见解:
首先,您的问题与稳定的二进制分区问题相同。
您的问题的算法当然会执行稳定的二进制分区(只是一个没有零的数组)。相反,如果数组有零,你仍然可以使用二进制分区来完成脏工作:直接扫描数组,用下一个负值交换你遇到的每个零点(跟踪那里你不做的事情) n ^ 2整体扫描),导致
[mixed - ,+] [可能额外的零] [混合0,+]。
然后你做两个二进制分区来获取
[ - ] [+] [0] [+]
并将+值移位以获得所需的结果。
带有二进制分区的AFAIK,您可以选择稳定,就地和O(n)中的任意两个。所以看起来你运气不好,但显然就地O(n * log n)算法被称为使用log(n)暂存空间的O(n)算法。
其次,如果可以保证零的数量至少为f(n),则零可以弥补划痕空间的不足;在时间O(n ^ 2 / f(n))中获得稳定的就地分区很简单。特别是,如果零将至少是数组的某个常量部分,那么只需运行这两个步骤即可获得O(n)运行时间,直到完成为止:
如果零与其他任何一种类型一样丰富,则在执行1然后再执行2然后再执行1之后执行此操作。
答案 3 :(得分:0)
这不能简单地使用自定义比较器执行的任何“稳定排序”,只能检查标志吗?
编辑:
不,那是O(n log n)。
你可以在线性时间做一件事就是减少问题。由于无法对零进行排序(如何区分另一个?),您可以在遍历数组的位置进行传递,跳过零并填入非零值。然后在最后添加正确数量的零。
j=0;
for (i=0;i<N;i++) {
if (A[i]) {
A[j++]=A[i];
}
}
while (j<N) {
A[j++]=0;
}
现在你可以忽略最后一节,问题就是找到一个O(n)算法,用于0左右的稳定分区。不幸的是,stable_partition
函数from the c++ stl只有O(n)比较,但如果没有可用的额外空间,则O(n log n)交换。
然而,这篇文章:"Stable minimum space partitioning in linear time"似乎表明它可能在O(n)中。我不认为我理解得很清楚,可以在这里清楚地总结一下。
如果可行,最后一步是在分区之间插入零,这也是O(n),因为零没有维护顺序。
答案 4 :(得分:0)
C ++库有一个stable_partition
算法,需要 n 比较,O( n log n )在运行时进行交换在的地方。
作为@Ted points out,问题需要此算法的两个应用程序。