arr 在 给定目标索引数组的情况下,如何对给定数组 进行 排序ind?
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)额外空间中解决此问题?
您能否提供证明您的算法有效的证据?
注意: 这个问题看起来与此类似,但是这里ind允许变异。
该算法失败,因为它在列表的索引上只有一个循环。
您的算法中发生的事情是这样的:
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,在此示例中不会发生这种情况。
1
0
您的算法错过的是一个内部循环,该内部循环贯穿索引的子循环。尝试更换if用while在rearrange:
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循环确保你检查每一个可能的周期。
for