是否有一种有效的就地算法可以根据给定条件将数组分成左右?
Is there an efficient in-place algorithm to separate an array into left and right by a given condition?
将一个数组分成一对数组是很常见的,一侧通过而另一侧失败。基本算法是这样的:
function partition(list, cond) {
let left = [];
let right = [];
for (let item of list) {
if (cond(item)) {
left.push(item);
} else {
right.push(item);
}
}
return [left, right];
}
在我的例子中,这个条件有点特殊 - 它只是一个等长的位集,其中对于每个位置,set = left,unset = right:
function partition(list, bitset) {
let left = [];
let right = [];
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
left.push(list[i]);
} else {
right.push(list[i]);
}
}
return [left, right];
}
事情是,我想就地执行此操作,并且只有位集中相应位在左侧 true
的位,在右侧 false
位的位同一个数组。 (条目确实需要保留它们的顺序 - 这很重要。否则,它会很明显。)这是迄今为止我想出的最有效的版本:
// The return value is the start offset for the rejects
function partition(list, cond) {
let right = [];
let rightStart = 0;
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
list[rightStart++] = list[i];
} else {
right.push(list[i]);
}
}
for (let i = 0, j = rightStart; i < list.length; i++) {
list[j] = right[i];
}
return right_start;
}
是否可以仅用 O(1) space 时间来完成此操作,同时仍保持 O(n) 时间,如果是这样,该算法将是什么长什么样子?
您正在寻找的算法称为稳定分区。在常量 space 中,它具有 O(n log n)
时间复杂度。参见示例 here(它使用 C++,但在这种情况下无关紧要)。
实现可能类似于
stable_partition(begin, end, predicate)
if (end - begin == 0)
return begin
if (end - begin == 1)
return predicate(*begin)? begin: end
mid = begin + (end - begin)/2
left_partition_point = stable_partition(begin, mid, predicate)
right_partition_point = stable_partition(mid, end, predicate)
return rotate(left_partition_point, mid, right_partition_point)
其中 rotate
returns 最左边的元素所在的位置。
论文...
Katajainen, J., Pasanen, T. 稳定最小 space 线性时间分区。 BIT 32, 580–585 (1992).
https://doi.org/10.1007/BF01994842
... 提出了一种在 O(n) 时间和 O(1) 额外时间内实现稳定就地分区的算法 space.
将一个数组分成一对数组是很常见的,一侧通过而另一侧失败。基本算法是这样的:
function partition(list, cond) {
let left = [];
let right = [];
for (let item of list) {
if (cond(item)) {
left.push(item);
} else {
right.push(item);
}
}
return [left, right];
}
在我的例子中,这个条件有点特殊 - 它只是一个等长的位集,其中对于每个位置,set = left,unset = right:
function partition(list, bitset) {
let left = [];
let right = [];
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
left.push(list[i]);
} else {
right.push(list[i]);
}
}
return [left, right];
}
事情是,我想就地执行此操作,并且只有位集中相应位在左侧 true
的位,在右侧 false
位的位同一个数组。 (条目确实需要保留它们的顺序 - 这很重要。否则,它会很明显。)这是迄今为止我想出的最有效的版本:
// The return value is the start offset for the rejects
function partition(list, cond) {
let right = [];
let rightStart = 0;
for (let i = 0; i < list.length; i++) {
if ((bitset[i >>> 5] & 1 << (i & 31)) !== 0) {
list[rightStart++] = list[i];
} else {
right.push(list[i]);
}
}
for (let i = 0, j = rightStart; i < list.length; i++) {
list[j] = right[i];
}
return right_start;
}
是否可以仅用 O(1) space 时间来完成此操作,同时仍保持 O(n) 时间,如果是这样,该算法将是什么长什么样子?
您正在寻找的算法称为稳定分区。在常量 space 中,它具有 O(n log n)
时间复杂度。参见示例 here(它使用 C++,但在这种情况下无关紧要)。
实现可能类似于
stable_partition(begin, end, predicate)
if (end - begin == 0)
return begin
if (end - begin == 1)
return predicate(*begin)? begin: end
mid = begin + (end - begin)/2
left_partition_point = stable_partition(begin, mid, predicate)
right_partition_point = stable_partition(mid, end, predicate)
return rotate(left_partition_point, mid, right_partition_point)
其中 rotate
returns 最左边的元素所在的位置。
论文...
Katajainen, J., Pasanen, T. 稳定最小 space 线性时间分区。 BIT 32, 580–585 (1992).
https://doi.org/10.1007/BF01994842
... 提出了一种在 O(n) 时间和 O(1) 额外时间内实现稳定就地分区的算法 space.