我正在尝试实现时间分析的Merge排序,当测试用于实现此功能的函数时,我得到了一些时髦的结果,并且无法弄清楚原因。我生成一个包含20个随机值的数组,然后调用mergeSort
然后打印“已排序”数组的结果。
没有错误消息,但结果不符合预期。输出将显示为前几个值排序,然后在它们之间的一些0,并最终以非常大的值结束,即使生成的数字应该在1和100之间。输出如下:
>sort-timings
1 3 8 11 0 14 17 24 0 0 29 96 20 2293400 3 2293400 2293400 26085452 1971496002 1971496002 >Exit code: 0 Time: 0.4162
我实施的代码是:
void merge(int A[], int leftStart, int leftEnd, int rightStart, int rightEnd, int W[]) {
//Merge A[leftStart]....[leftEnd] with A[rightStart]...[rightEnd]
//Into W, indexed by k, copy resulting W into A
int i = leftStart;
int j = rightStart;
int k = leftStart;
while( i <= leftEnd && j <= rightEnd) {
if(A[i] < A[j]) {
W[k++] = A[i++];
}
else if(A[i] > A[j]) {
W[k++] = A[j++];
}
else {
W[k++] = A[i++];
W[k++] = A[j++];
}
}
for(i = leftStart; i <= rightEnd; i++) {
A[i] = W[i];
}
}
void mergeSort(int A[], int low, int high, int W[]) {
//mergeSort Helper Function
if(low == high) {
return; //1 element is sorted
}
int mid = (low + high) / 2;
mergeSort(A, low, mid, W); //Sort first half
mergeSort(A, mid + 1, high, W); //Sort second half
merge(A, low, mid, mid + 1, high, W);
return;
}
void mergeSort(int A[], int W[], int n) {
mergeSort(A, 0, n - 1, W);
}
void generateRandomArray(int A[], int n) {
unsigned int seed = time(0);
srand(seed);
for(int i = 0; i < n; i++) {
A[i] = (rand() % 100) + 1; // 1 <= A[i] <=10000
}
}
int main() {
const int ARRAY_SIZE = 20;
int array[ARRAY_SIZE];
int tempArray[ARRAY_SIZE];
generateRandomArray(array, ARRAY_SIZE);
mergeSort(array, tempArray, ARRAY_SIZE);
for(int i = 0; i < ARRAY_SIZE; i++) {
cout << array[i] << " ";
}
}
答案 0 :(得分:1)
您要提前停止merge
循环。当i
超出范围或 j
超出范围时,它会暂停,这会使一些值未复制到W
,从而导致您的未初始化值输出
解决此问题的一种简单方法是在主循环完成后复制其余值。如果循环因i
超出范围而结束,则要复制j
的其余部分,类似地,如果循环结束,因为j
超出范围,则要复制其余部分i
。
您可以通过在主循环后添加循环来实现此目的,以确保i
和j
到达其范围的末尾:
while (i <= leftEnd) {
W[k++] = A[i++];
}
while (j <= rightEnd) {
W[k++] = A[j++];
}
在将for
复制到W
的最终A
循环之前放置。
另一个替代方法是更改循环,使条件为||
,这意味着它将继续,而任何一个数字都在范围内。然后,在使用之前,必须测试一个数字是否在范围内。有很多方法可以做到这一点,一种简单的方法是先测试它:
while (i <= leftEnd || j <= rightEnd) {
if (j > rightEnd) {
W[k++] = A[i++];
}
else if (i > leftEnd) {
W[k++] = A[j++];
}
else if (A[i] < A[j]) {
...
答案 1 :(得分:1)
使用标志(mtoa)根据递归级别跟踪合并方向的备用版本,以避免复制数据。它还只在TopDownMerge()中递增索引后检查索引超出范围;我不确定这是否会产生显着的性能差异。
void TopDownMergeSort(int a[], int b[], size_t n)
{
if(n < 2)
return;
TopDownSplitMerge(a, b, 0, n, true);
}
void TopDownSplitMerge(int a[], int b[], size_t ll, size_t ee, bool mtoa)
{
size_t rr;
if ((ee - ll) == 1){ // if size == 1
if(!mtoa) // copy to b if merging a to b
b[ll] = a[ll];
return;
}
rr = (ll + ee)>>1; // midpoint, start of right half
TopDownSplitMerge(a, b, ll, rr, !mtoa);
TopDownSplitMerge(a, b, rr, ee, !mtoa);
if(mtoa) // if merging to a, merge b to a
TopDownMerge(b, a, ll, rr, ee);
else // else merge a to b
TopDownMerge(a, b, ll, rr, ee);
}
void TopDownMerge(int a[], int b[], size_t ll, size_t rr, size_t ee)
{
size_t o = ll; // b[] index
size_t l = ll; // a[] left index
size_t r = rr; // a[] right index
while(1){ // merge data
if(a[l] <= a[r]){ // if a[l] <= a[r]
b[o++] = a[l++]; // copy a[l]
if(l < rr) // if not end of left run
continue; // continue (back to while)
while(r < ee){ // else copy rest of right run
b[o++] = a[r++];
}
break; // and return
} else { // else a[l] > a[r]
b[o++] = a[r++]; // copy a[r]
if(r < ee) // if not end of right run
continue; // continue (back to while)
while(l < rr){ // else copy rest of left run
b[o++] = a[l++];
}
break; // and return
}
}
}