【问题标题】:How to sort an array in-place given an array of target indices?如何在给定目标索引数组的情况下对数组进行就地排序?
【发布时间】:2016-06-09 11:36:25
【问题描述】:

如果给定目标索引数组ind,您将如何就地对给定数组arr进行排序?

例如:

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

【问题讨论】:

  • 为什么againagain会问同样的问题?
  • 你厨房里没有别的菜吗?
  • @trincot 如果您仔细阅读,您会发现您链接的其他问题中的要求略有不同。我在问题的末尾添加了一个注释以使其清楚。

标签: javascript arrays algorithm sorting


【解决方案1】:

算法失败,因为它对列表的索引只有一个循环。

在你的算法中发生的事情是这样的:

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 交换,否则您将不会再次访问它,这在本示例中不会发生。

您的算法遗漏了一个贯穿索引子循环的内部循环。尝试在rearrange 中将if 替换为while

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 循环确保您检查每个可能的循环。

【讨论】:

  • 对我来说似乎是正确的。您使用索引数组将元素/循环标记为正在处理,因此可以跳过这些,将不可变输入的复杂度从 O(n^2) 降低到最优 O(n)。
【解决方案2】:

我建议交换直到达到相同的索引,然后取下一个外部索引。

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"];

【讨论】:

    【解决方案3】:

    你会如何在 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];
      }  
    }
    

    【讨论】:

    • 对不起,我忘了说允许的额外空间是 O(1)。
    • @MishaMoroshko 抱歉,没明白 extra space 的意思
    【解决方案4】:

    我建议您将结果返回到一个新数组中

       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))
    

    【讨论】:

    • 虽然这段代码 sn-p 可以解决问题,但including an explanation 确实有助于提高帖子的质量。请记住,您正在为将来的读者回答问题,而这些人可能不知道您的代码建议的原因。也请尽量不要用解释性的 cmets 挤满你的代码,这会降低代码和解释的可读性!
    【解决方案5】:

    关于类似问题的讨论很多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】:

      如果您对使用复杂度较低的方法感到满意,那么有一种方法:

      • 创建一个长度等于arr 长度且包含空对象的新数组
      • spliceind[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);
      };

      【讨论】:

        【解决方案7】:

        “循环”循环比使用交换要快一些。 C 示例:

            // 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;
                }
            }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2022-11-15
          • 2020-09-11
          • 2011-07-06
          • 1970-01-01
          • 1970-01-01
          • 2013-04-13
          • 2012-03-24
          相关资源
          最近更新 更多