这道题的题目是这样的:
快速归并。 实现一个merge()
方法,按降序将a[]
的后半部分复制到aux[]
,然后将其归并回a[]
中。这样就可以去掉内循环中检测某半边是否用尽的代码。
起初,看不懂这个题目是什么意思,后来询问AI,才明白是什么意思:
习题2.2.10 "快速归并" 来自《算法》第四版,主要关注于优化归并排序中合并两个已排序数组的过程。通常,在实现归并排序时,我们会将一个数组分为两半,分别对每一半进行排序,然后将这两个有序的半部合并成一个有序的整体。在合并过程中,需要检查两边是否已经处理完毕,以决定是否直接将另一部分剩余的元素添加到结果数组中。
该题目要求你改进这个合并过程,通过一种特定的方式减少或消除这种检查操作。具体来说,它建议你调整复制数据到辅助数组(auxiliary array)的方式:将原数组的后半部分按照降序方式复制到辅助数组中,而前半部分保持升序(或者反过来)。这样做的目的是使得在执行合并步骤时,能够从两端向中间同时进行比较和合并操作。由于两边的数据一个是升序另一个是降序,所以在进行归并时就自然地避免了某一半边被完全处理完的情况,从而不需要额外的检查来判断是否某一半边已经处理完毕。
简而言之,这道题目意在让你思考如何通过改变辅助数组中数据的排列方式,来简化归并排序中的合并步骤,并提高其效率。这样做可以使得合并过程更加高效,避免不必要的边界检查。
所以我们的merge()方法如下:
public static void merge(Comparable[] a, int lo, int mid, int hi) { Comparable[] aux = new Comparable[a.length]; // 将a[]复制到aux[]中 for (int k = lo; k <= mid; k++) { aux[k] = a[k]; } // 将后半部分倒序 int x = hi; for (int k = mid + 1; k <= hi; k++) { aux[k] = a[x]; x--; } // i表示开头索引,j表示结尾 // 从两侧向中间归并,当i == j时,表示还剩一个元素 int i = lo, j = hi; for (int k = lo; k <= hi; k++) { if (i == j) { a[k] = aux[i]; } else if (less(aux[j], aux[i])) { a[k] = aux[j--]; } else { a[k] = aux[i++]; } } }