这是数据结构和算法课程的家庭作业问题。我不希望有人替我做作业。只是希望有人能告诉我我是否正在适当地解决这个问题。
public static void sort(int[] a) {
sort(a, 0, a.length - 1);
}
private static void sort(int[] a, int lo, int hi) {
if (hi <= lo) return;
int[] aux = new int[a.length];
int mid = (hi + lo) / 2;
sort(a, lo, mid);
sort(a, mid + 1, hi);
merge(a, lo, mid, hi, aux);
}
private static void merge(int[] a, int lo, int mid,
int hi, int[] aux) {
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++];
else if (j > hi)
a[k] = aux[i++];
else if(aux[j] < aux[i])
a[k] = aux[j++];
else
a[k] = aux[i++];
}
}
此实现与典型实现(在我们的类中已给出)之间的唯一区别是,在每次递归调用sort时都重新定义了aux数组,而在典型情况下,仅在public sort方法中定义了aux数组案件。典型情况是运行时间为O(nlog(n)),空间复杂度为O(n)。
我的任务是确定所示的mergesort修改实现的运行时间和空间复杂度。据我所知,运行时间没有改变,因此仍然是O(nlog(n)),空间复杂度也是O(nlog(n))。我得出这个结论的逻辑是,sort方法每次被调用时都会分配一个大小为n的数组,它被称为log(n)次。
到目前为止,我在围绕时空和时间的复杂性问题上很难过。我是在正确地考虑这一点吗?还是我要离开?
任何指针都非常感谢。
答案 0 :(得分:0)
是的,空间复杂度为O(n*logn)
,但需要澄清。
我得出这个结论的逻辑是排序方法 每次调用时分配一个大小为n的数组,并且该数组被调用 总共log(n)次。
实际上,在合并排序期间,sort
总共被调用n
次,但是在此过程中,最大递归深度为logn
。让我们绘制一棵递归调用树:
sort
/ \
sort sort
/\ /\
sort sort sort sort
...
在每个级别上,每个sort
函数都执行其父级的一半工作。因此,在级别0(根节点)上,sort
进行了n
次迭代,在级别1上,两种排序各自具有n/2
的运行时间(总计为n
),级别2的四种类别各有n/4
个等。组合起来,每个级别进行n
个迭代,并且由于树的深度为log n
,因此您得到了O(n*logn)
的时间复杂度。
但是,在您的情况下,aux
分配n
个元素,而不管当前深度如何,并且有n
种调用,因此乍一看,可以得出结论:复杂度为O(n*n) = O(n^2)
。 这不是,因为一旦完成每个递归调用,就会释放分配的空间,并且最多有logn
个递归调用。
请注意,aux
不必要地每次都会分配n
个元素的数组。如果您在aux
函数中声明merge
并设置适当的长度,则可以改进算法,如下所示:
private static void merge(int[] a, int lo, int mid, int hi) {
int[] aux = new int[hi - lo + 1];
int i = lo, j = mid + 1;
for (int k = lo; k <= hi; k++)
aux[k - lo] = a[k];
for (int k = lo; k <= hi; k++) {
if (i > mid)
a[k] = aux[j++ - lo];
else if (j > hi)
a[k] = aux[i++ - lo];
else if(aux[j - lo] < aux[i - lo])
a[k] = aux[j++ - lo];
else
a[k] = aux[i++ - lo];
}
}
HTH。