Algorithm 是否可以在O(N)中重新排列阵列?
如果我有一个大小为N的对象数组,并且我有一个范围为1…N的唯一数字数组,是否有任何算法可以按照数字列表指定的顺序重新排列对象数组,并且在O(N)时间内执行此操作 上下文:我正在对大小相当大的对象执行快速排序算法,因此在索引上进行交换比在对象本身上进行交换更快,并且只在最后一次传递中移动对象。我只是想知道我是否可以在不为单独的数组分配内存的情况下完成最后一步 编辑:我不是问如何在O(N)时间内进行排序,而是问如何使用O(1)空间在O(N)时间内重新排列后排序。很抱歉没有说清楚。有一个,尽管运行时间比O(N)(N log N)高一点。如果有O(N)个临时空间,我可以这样做——复制到新阵列并复制回来Algorithm 是否可以在O(N)中重新排列阵列?,algorithm,sorting,Algorithm,Sorting,如果我有一个大小为N的对象数组,并且我有一个范围为1…N的唯一数字数组,是否有任何算法可以按照数字列表指定的顺序重新排列对象数组,并且在O(N)时间内执行此操作 上下文:我正在对大小相当大的对象执行快速排序算法,因此在索引上进行交换比在对象本身上进行交换更快,并且只在最后一次传递中移动对象。我只是想知道我是否可以在不为单独的数组分配内存的情况下完成最后一步 编辑:我不是问如何在O(N)时间内进行排序,而是问如何使用O(1)空间在O(N)时间内重新排列后排序。很抱歉没有说清楚。有一个,尽管运行时间
编辑:我知道有一种算法将继续执行。其思想是在整数数组1..N上执行交换,同时在大型对象数组上镜像交换。我现在找不到算法。你的意思是,你有一个对象数组O[1..N],然后你有一个数组p[1..N],其中包含数字1..N的排列,最后你想得到一个对象数组O1,使得所有k=1..N的对象都是O1[k]=O[p[k]] 例如,如果您的对象是字母A、B、C…、Y、Z,并且您的数组p是[26,25,24,…,2,1],那么您希望的输出是Z、Y…、C、B、A吗 如果是的话,我相信你可以在线性时间内使用O(1)个额外的内存。反转数组元素是这种情况的特例。一般来说,我认为你需要考虑把排列p分解成循环,然后用它来移动原始数组o[[]/p>的元素。 如果这就是你想要的,我可以详细说明 编辑:其他人在我睡觉的时候已经提出了很好的解决方案,所以不需要在这里重复^_^
编辑:我的O(1)额外空间确实不完全正确。我只考虑了“数据”元素,但事实上,每个置换元素还需要存储一位,所以如果我们是精确的,我们需要O(logn)额外的位。但大多数情况下使用符号位(正如J.F.Sebastian所建议的那样)是可以的,因此在实践中,我们可能不需要比现有的更多的东西。如果您不介意为额外的索引哈希分配内存,您可以保留原始位置到当前位置的映射,以获得接近O(n)的时间复杂度。下面是Ruby中的一个示例,因为它是可读的,并且是伪代码。(这可能会更短,或者更习惯于使用Ruby-ish,但为了清晰起见,我已经把它写了出来。) 这显然需要一些额外的内存用于散列,但因为它只是用于索引,而不是“相当大”的对象,希望这是可以接受的。由于散列查找是O(1),即使由于一个项被多次向前交换,并且您必须多次重写
cur_location
,算法的复杂度也会略有增加,但作为一个整体,算法应该合理地接近O(n)
如果需要,可以提前构建从原始位置到当前位置的完整散列,或者保留从当前位置到原始位置的反向散列,并稍微修改算法,使其精确到O(n)。它会更复杂一些,占用更多的空间,所以这是我写的版本,但是修改应该不难
编辑:事实上,我相当确定时间复杂度仅为O(n),因为每个序数最多可以有一个跃点关联,因此最大查找次数限制为n。我认为应该这样做:
static <T> void arrange(T[] data, int[] p) {
boolean[] done = new boolean[p.length];
for (int i = 0; i < p.length; i++) {
if (!done[i]) {
T t = data[i];
for (int j = i;;) {
done[j] = true;
if (p[j] != i) {
data[j] = data[p[j]];
j = p[j];
} else {
data[j] = t;
break;
}
}
}
}
}
静态无效排列(T[]数据,int[]p){
布尔值[]完成=新布尔值[p.length];
for(int i=0;i
注意:这是Java。如果在没有垃圾收集的语言中执行此操作,请确保删除done
如果您关心空间,则可以为done
使用位集。我假设您可以为每个元素提供额外的位,因为您似乎愿意使用置换数组,它的大小是该数组的几倍
该算法复制tn+k次的实例,其中k是置换中的循环数。您可以通过跳过p[i]=i的i来将其减少到最佳拷贝数。方法是遵循排列的“排列周期”,而不是从左到右索引数组。但由于必须从某个地方开始,每次需要新的排列循环时,对未排列元素的搜索是从左到右: // Pseudo-code N : integer, N > 0 // N is the number of elements swaps : integer [0..N] data[N] : array of object permute[N] : array of integer [-1..N] denoting permutation (used element is -1) next_scan_start : integer;
next_scan_start = 0;
while (swaps < N ) { // Search for the next index that is not-yet-permtued. for (idx_cycle_search = next_scan_start; idx_cycle_search < N; ++ idx_cycle_search) if (permute[idx_cycle_search] >= 0) break;
next_scan_start = idx_cycle_search + 1;
// This is a provable invariant. In short, number of non-negative // elements in permute[] equals (N - swaps) assert( idx_cycle_search < N );
// Completely permute one permutation cycle, 'following the // permutation cycle's trail' This is O(N) while (permute[idx_cycle_search] >= 0) { swap( data[idx_cycle_search], data[permute[idx_cycle_search] ) swaps ++; old_idx = idx_cycle_search; idx_cycle_search = permute[idx_cycle_search]; permute[old_idx] = -1; // Also '= -idx_cycle_search -1' could be used rather than '-1' // and would allow reversal of these changes to permute[] array } } //伪码 N:整数,N>0//N是元素数 交换:整数[0..N] 数据[N]:对象的数组 置换[N]:表示置换的整数[-1..N]数组(使用的元素是-1) 下一次扫描开始:整数
下一次扫描开始=0
while(互换
下一次扫描开始=idx循环搜索+1;
//这是一个可证明的不变量。简言之,非负数 //置换[]中的元素等于(N-交换) 断言(idx_循环_搜索
next_scan_start = 0;
while (swaps < N ) { // Search for the next index that is not-yet-permtued. for (idx_cycle_search = next_scan_start; idx_cycle_search < N; ++ idx_cycle_search) if (permute[idx_cycle_search] >= 0) break;
next_scan_start = idx_cycle_search + 1;
// This is a provable invariant. In short, number of non-negative // elements in permute[] equals (N - swaps) assert( idx_cycle_search < N );
// Completely permute one permutation cycle, 'following the // permutation cycle's trail' This is O(N) while (permute[idx_cycle_search] >= 0) { swap( data[idx_cycle_search], data[permute[idx_cycle_search] ) swaps ++; old_idx = idx_cycle_search; idx_cycle_search = permute[idx_cycle_search]; permute[old_idx] = -1; // Also '= -idx_cycle_search -1' could be used rather than '-1' // and would allow reversal of these changes to permute[] array } }
#!/usr/bin/env python
def rearrange(objects, permutation):
"""Rearrange `objects` inplace according to `permutation`.
``result = [objects[p] for p in permutation]``
"""
seen = [False] * len(permutation)
for i, already_seen in enumerate(seen):
if not already_seen: # start permutation cycle
first_obj, j = objects[i], i
while True:
seen[j] = True
p = permutation[j]
if p == i: # end permutation cycle
objects[j] = first_obj # [old] p -> j
break
objects[j], j = objects[p], p # p -> j
def test():
import itertools
N = 9
for perm in itertools.permutations(range(N)):
L = range(N)
LL = L[:]
rearrange(L, perm)
assert L == [LL[i] for i in perm] == list(perm), (L, list(perm), LL)
# test whether assertions are enabled
try:
assert 0
except AssertionError:
pass
else:
raise RuntimeError("assertions must be enabled for the test")
if __name__ == "__main__":
test()
void make_swaps(vector<int> order, vector<pair<int,int>> &swaps)
{
// order[0] is the index in the old list of the new list's first value.
// Invert the mapping: inverse[0] is the index in the new list of the
// old list's first value.
vector<int> inverse(order.size());
for(int i = 0; i < order.size(); ++i)
inverse[order[i]] = i;
swaps.resize(0);
for(int idx1 = 0; idx1 < order.size(); ++idx1)
{
// Swap list[idx] with list[order[idx]], and record this swap.
int idx2 = order[idx1];
if(idx1 == idx2)
continue;
swaps.push_back(make_pair(idx1, idx2));
// list[idx1] is now in the correct place, but whoever wanted the value we moved out
// of idx2 now needs to look in its new position.
int idx1_dep = inverse[idx1];
order[idx1_dep] = idx2;
inverse[idx2] = idx1_dep;
}
}
template<typename T>
void run_swaps(T data, const vector<pair<int,int>> &swaps)
{
for(const auto &s: swaps)
{
int src = s.first;
int dst = s.second;
swap(data[src], data[dst]);
}
}
void test()
{
vector<int> order = { 2, 3, 1, 4, 0 };
vector<pair<int,int>> swaps;
make_swaps(order, swaps);
vector<string> data = { "a", "b", "c", "d", "e" };
run_swaps(data, swaps);
}