我看到我在网上看到的mergesort的许多实现,例如https://www.geeksforgeeks.org/merge-sort/传递参数l,m和r来知道子数组的开始和结束位置。我想知道,如果我们改为制作子数组的副本并将其传递进去,运行时和空间的复杂性是否会保持不变。示例建议代码如下:
class Solution {
//mergeSort implementation
public int[] sortArray(int[] nums) {
if (nums.length == 1) {
return nums;
}
int arrayLength = nums.length;
// make copies instead of passing indices
int[] left = Arrays.copyOfRange(nums, 0, arrayLength/2);
int[] right = Arrays.copyOfRange(nums, arrayLength/2, arrayLength);
left = sortArray(left);
right = sortArray(right);
return merge(left, right);
}
public int[] merge(int[] left,int[] right) {
int[] merged = new int[left.length + right.length];
int mergedCounter = 0;
int i = 0; //leftCounter
int j = 0; //rightCounter
while (mergedCounter != left.length + right.length) {
if (i == left.length) {
merged[mergedCounter] = right[j];
mergedCounter++;
j++;
} else if (j == right.length) {
merged[mergedCounter] = left[i];
mergedCounter++;
i++;
} else {
if (left[i] <= right[j]) {
merged[mergedCounter] = left[i];
mergedCounter++;
i++;
} else {
merged[mergedCounter] = right[j];
mergedCounter++;
j++;
}
}
}
return merged;
}
}
我相信运行复杂度不会增加,因为制作副本所花费的运行时间与通过新的int [n]初始化n长度的新数组相同。问题的这一部分还包括O(n)合并子函数,因此无关紧要。
我也相信空间复杂度保持不变。在递归调用中创建两个临时子数组时,将在合并步骤中创建与在线解决方案中指定的空间相同的空间,使用的指针分别指向l,m和r。
我的思维过程是否有效,或者我缺少什么?
答案 0 :(得分:2)
任何其他复制操作都会增加运行时间。复制子数组会增加一个常数的空间需求,但是由于“复杂度”忽略了常数和/或低阶项,因此空间“复杂度”将保持不变。
大多数库使用自底向上合并排序的某些变体,其中索引(或指针)是动态更新的,而不是通过递归传递的,并且通常基于编译器优化而保存在寄存器中。自上而下的合并排序通常用作学习练习。
通过基于自底向上合并排序的合并过程或基于自顶向下合并排序的递归级别,可以通过更改合并方向来最小化复制操作。
对于C / C ++,传递数组和索引的另一种方法是传递指针或迭代器,从而将每次调用所需的参数数量减少1个参数。