它可以在 O(n) 时间内完成,需要 O(1) 额外内存,通过一次处理一个置换数组的周期。
注意:这种方法比此特定设置所需的要复杂得多(a 和 b 都是 int 数组),但它有一些好处:
考虑最初的例子:
int a[] = {37, 43, 5, 10}; // array to permute
int b[] = { 3, 1, 4, 2}; // new position of each element
数组b表示我们要进行如下的赋值链:
a[1] <-- a[3] <-- a[4] <-- a[2] <-- a[1].
问题是,在最后一次分配中,我们无法再访问a[1](它已经被替换为a[3])。
但是,起始元素的原始值可以保存在一个辅助变量中,以便我们在关闭循环时使用它(保证当我们关闭循环时,我们会准确到达我们已经开始的元素from - 否则某些元素可以通过多种方式访问,ie 我们将有 b[i] = b[j]),对于某些 i != j)。
一般来说,排列可能包含多个循环。处理完一个循环后,我们需要从一个尚未更新的元素开始(即到目前为止它还不是处理过的循环的一部分)。
因此,我们需要知道到目前为止哪些元素没有被处理。
一种可能的方法是临时修改置换向量b 以跟踪更新了哪些元素,例如在更新元素时否定b 中相应位置的值在a。这样做的好处是,最后我们可以遍历b 的所有元素并恢复初始值(通过再次否定所有元素)。
下面是之前想法的实现。
int main() {
int a[] = {11, 22, 33, 44};
int b[] = { 2, 1, 4, 3};
int aux, crtIdx, nxtIdx;
int n = sizeof(a) / sizeof(a[0]);
for (int i = 0; i < n; i++) {
// check whether the i'th element
// was already processed
if (b[i] < 0) {
continue;
}
// start processing of a new cycle;
// backup the first value to aux
aux = a[i];
crtIdx = i;
nxtIdx = b[i] - 1;
// advance along the cycle until we reach
// again the first element
while (nxtIdx != i) {
a[crtIdx] = a[nxtIdx];
// use the b array to mark that the
// element at crtIdx was updated
b[crtIdx] = -b[crtIdx];
crtIdx = nxtIdx;
nxtIdx = b[nxtIdx] - 1;
}
// finalize the cycle using the aux variable
a[crtIdx] = aux;
b[crtIdx] = -b[crtIdx];
}
// restore the original values of b[i]
for (int i = 0; i < n; i++) {
b[i] = -b[i];
}
}
注意:虽然代码包含两个嵌套循环,但时间复杂度为 O(n)。这可以通过考虑每个元素仅更新一次这一事实来看出(如果我们到达一个已经处理过的元素,则外部循环将立即继续)。
我将在这里展示算法执行的主要步骤,使用这个例子:
a = {11, 22, 33, 44}
b = { 2, 1, 4, 3}
第 1 步。
我们查看第一个元素(请参见代码中的 i 的外部 for 循环)。第一个元素不是已处理循环的一部分,因此我们开始处理新循环。为此,我们将这个元素的初始值存储在 aux 中。
a = {11, 22, 33, 44}
b = { 2, 1, 4, 3}
aux = 11
第 2 步。
我们沿着这个循环,更新元素,将它们标记为已更新(通过否定 b 数组中的相应元素),直到我们再次到达第一个元素。
a = {22, 22, 33, 44}
b = {-2, 1, 4, 3}
aux = 11
第 3 步。
我们再次到达循环的第一个元素,并且需要它的初始值来更新循环的最后一个元素。这是我们使用辅助变量的地方。这样第一个循环就处理完了。
a = {22, 11, 33, 44}
b = {-2, -1, 4, 3}
aux = 11
第 4 步。
我们继续外循环(for 超过i)。我们看到第二个元素已经被处理(因为b[1] 是负数),因此我们不会在这里开始一个新的循环。我们继续,并在第三个元素(尚未处理)处开始一个新的循环。
现在我们可以重用相同的aux 变量来备份这个循环的第一个元素(我们不再需要保存第一个循环的值,因为那个循环已经完全解决了)。
a = {22, 11, 33, 44}
b = {-2, -1, 4, 3}
aux = 33
第 5 步。
第二个循环的处理按照前面步骤中描述的类似方式进行,结果是这样的:
a = {22, 11, 44, 33}
b = {-2, -1, -4, -3}
aux = 33
第 6 步。
i 上的循环继续进行,没有找到未处理的元素。
现在,我们知道所有元素都已处理完毕,我们可以对 b 中的每个元素取反,以恢复原始值。
a = {22, 11, 44, 33}
b = { 2, 1, 4, 3}