给定一系列目标索引arr
,您如何对给定数组ind
就地进行排序?
例如:
var arr = ["A", "B", "C", "D", "E", "F"];
var ind = [ 4, 0, 5, 2, 1, 3 ];
rearrange(arr, ind);
console.log(arr); // => ["B", "E", "D", "F", "A", "C"]
arr = ["A", "B", "C", "D"];
ind = [ 2, 3, 1, 0 ];
rearrange(arr, ind);
console.log(arr); // => ["D", "C", "A", "B"]
我尝试了以下算法,但在上面的第二个例子中失败了。
function swap(arr, i, k) {
var temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
function rearrange(arr, ind) {
for (var i = 0, len = arr.length; i < len; i++) {
if (ind[i] !== i) {
swap(arr, i, ind[i]);
swap(ind, i, ind[i]);
}
}
}
你如何在O(n)时间和O(1)额外空间中解决这个问题?
您能提供算法有效的证据吗?
注意:此问题与this one类似,但此处允许变更ind
。
答案 0 :(得分:7)
算法失败,因为它只有一个循环覆盖列表的索引。
您的算法中会发生以下情况:
i=0 -> ["A", "B", "C", "D"] , [ 2, 3, 1, 0 ]
i=1 -> ["C", "B", "A", "D"] , [ 1, 3, 2, 0 ]
i=2 -> ["C", "D", "A", "B"] , [ 1, 0, 2, 3 ]
i=3 -> ["C", "D", "A", "B"] , [ 1, 0, 2, 3 ]
请注意,第一次交换时,1
处于0
位置,除非您将其与0
交换,否则您不会再次访问它,这在此示例中不会发生。< / p>
您的算法遗漏的是一个内部循环,它贯穿索引的子循环。尝试将if
替换为while
中的rearrange
:
function rearrange(arr, ind) {
for (var i = 0, len = arr.length; i < len; i++) {
while (ind[i] !== i) {
swap(arr, i, ind[i]);
swap(ind, i, ind[i]);
}
}
}
关于复杂性的注意事项:虽然这是一个双循环,但复杂性不会改变,因为在每次交换时,一个元素被正确放置,每个元素最多读取两次(一次循环,一旦通过for循环)。
关于证明的说明:我不会在这里完整地证明这个算法,但我可以给出引导。如果ind
是排列,那么所有元素都属于闭合的置换子循环。 while
循环确保您重复整个周期,for
循环可确保您检查每个可能的周期。
答案 1 :(得分:1)
我建议交换,直到达到相同的索引,然后取下一个外部索引。
function swap(arr, i, k) {
var temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
function rearrange(arr, ind) {
var i = arr.length;
while (i--) {
while (ind[i] !== i) {
swap(arr, i, ind[i]);
swap(ind, i, ind[i]);
}
}
}
var arr = ["A", "B", "C", "D", "E", "F"];
var ind = [ 4, 0, 5, 2, 1, 3 ];
rearrange(arr, ind);
console.log(arr); // => ["B", "E", "D", "F", "A", "C"]
arr = ["A", "B", "C", "D"];
ind = [ 2, 3, 1, 0 ];
rearrange(arr, ind);
console.log(arr); // => ["D", "C", "A", "B"];
&#13;
答案 2 :(得分:0)
你会在O(n)时间内解决这个问题吗?
只需使用临时数组并迭代数组两次,就可以将其减少到O(n)
function rearrange(arr, ind)
{
var temp = [], len = arr.length;
//first re-arrange in a new array
for(var counter = 0; counter < len; counter++)
{
temp[ind[counter]] = arr[counter];
}
//copy the values as per new indices from temp
for(var counter = 0; counter < len; counter++)
{
arr[counter] = temp[counter];
}
}
答案 3 :(得分:0)
如果您对复杂度较低的方法表示满意,那么就有一个:
arr
长度且空对象splice
新数组的索引为ind[i]
,删除1个对象,并将其替换为arr[i]
arr
)与新数组
window.onload = function () {
'use strict';
var arr = ["A", "B", "C", "D", "E", "F"];
var ind = [ 4, 0, 3, 2, 1, 5 ];
var temp = [];
for (var i = 0; i < arr.length; i++) {
temp[i] = '';
}
for (var v = 0; v < arr.length; v++) {
temp.splice(ind[v], 1, arr[v]);
}
arr = temp;
console.log(arr);
};
答案 4 :(得分:0)
我建议您将结果返回到新数组
function createArray(arr, ind) {
var result = [];
for (var i = 0, len = arr.length; i < len; i++) {
result.push(arr[ind[i]]);
}
return result;
}
这是在O(n)时间
var arr = ["A", "B", "C", "D", "E", "F"];
var ind = [4, 0, 5, 2, 1, 3];
console.log(createArray(arr,ind))
答案 5 :(得分:0)
关于类似问题In-place array reordering?
的讨论很多不同之处在于原始问题ind[i]
是元素arr[i]
的必需索引,而在类似问题中i
是元素arr[ind[i]]
的必需索引
回到原始算法,一个可行的替代方案可能是
function swap(arr, i, k) {
var temp = arr[i];
arr[i] = arr[k];
arr[k] = temp;
}
function rearrange(arr, ind) {
for (var i = 0, len = arr.length; i < len; i++) {
if (ind[i] !== i) {
swap(arr, i, ind[i]);
swap(ind, i, ind[i]);
if (ind[i] < i) {
i = ind[i]-1;
}
}
}
}
它仍然是O(1)
空间,但它会进行冗余检查,可能比O(n)
更复杂
答案 6 :(得分:0)
“旋转”周期而不是使用交换更快一些。例如:
// reorder A in place according to sorted indices in I
// tA is temp value for A
for(i = 0; i < n; i++){
if(i != I[i]){
tA = A[i];
k = i;
while(i != (j = I[k])){
A[k] = A[j];
I[k] = k;
k = j;
}
A[k] = tA;
I[k] = k;
}
}