赞
踩
总结一下,想让一个区间有序,划分成左右区间,分别让两个区间有序,然后归并
结束条件:当这个要归并的区间只有一个值时结束
void _MergeSort(int* a, int left, int right, int* tmp) { //当区间只有一个值时停止,不存在区间不存在的情况 if (left == right) return; int mid = left + (right - left) / 2; //让左右两个区间都有序 _MergeSort(a, left, mid,tmp); _MergeSort(a, mid + 1, right,tmp); //归并 int begin1 = left, end1 = mid; int begin2 = mid + 1, end2 = right; int begin = left; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] <= a[begin2]) { tmp[begin++] = a[begin1++]; } else { tmp[begin++] = a[begin2++]; } } while (begin1 <= end1) { tmp[begin++] = a[begin1++]; } while (begin2 <= end2) { tmp[begin++] = a[begin2++]; } //拷贝回原数组 memcpy(a+left, tmp+left, sizeof(int)*(right - left + 1)); } //归并排序 void MergeSort(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); if (tmp == NULL) { perror("malloc fail"); return; } _MergeSort(a, 0, n - 1, tmp); }
快排的非递归可以用栈模拟递归的过程,每次弹出区间的两个边界,从这个区间固定一个值的位置,再压入这个值的左右两个区间,这样的过程实际上模拟的是递归的前序遍历,先将本层的事做完,然后去遍历左右区间
不能的,因为归并排序是后序遍历的过程,先让左右区间各自有序,再进行归并操作。用栈模拟的过程中,试想一下,要让左右区间有序,所以先压右区间,再压左区间,然后要弹出栈顶元素,也就是左区间,再将这个区间的右区间、左区间压入栈中,以此类推,在过程中是没有机会进行归并操作的。
//归并非递归方法 void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); if (tmp == NULL) { perror("malloc fail"); return; } int gap = 1; while (gap < n) { for (int i = 0; i < n; i += gap) { int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; int begin = i; while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] <= a[begin2]) { tmp[begin++] = a[begin1++]; } else { tmp[begin++] = a[begin2++]; } } while (begin1 <= end1) { tmp[begin++] = a[begin1++]; } while (begin2 <= end2) { tmp[begin++] = a[begin2++]; } } memcpy(a, tmp, sizeof(int) * n); gap *= 2; } }
上面的情况刚好对数据的个数为2的次方时有效,如果是9个数,10个数呢,
9个数时:在第一轮归并时就会越界
10个数时:第二轮归并时越界
分析:
解决办法:
if(end1 > n || begin2 > n)
{
break;
}
if(end2 > n)
{
end2 = n-1;
}
修改后的整体代码:
//归并非递归方法:每次归并后及时拷贝回原数组(解决越界) void MergeSortNonR(int* a, int n) { int* tmp = (int*)malloc(sizeof(int) * n); if (tmp == NULL) { perror("malloc fail"); return; } int gap = 1; while (gap < n) { for (int i = 0; i < n; i += gap) { int begin1 = i, end1 = i + gap - 1; int begin2 = i + gap, end2 = i + 2 * gap - 1; int begin = i; //判断越界问题 if (end1 > n || begin2 > n) { break; } if (end2 > n) { end2 = n - 1; } //归并 while (begin1 <= end1 && begin2 <= end2) { if (a[begin1] <= a[begin2]) { tmp[begin++] = a[begin1++]; } else { tmp[begin++] = a[begin2++]; } } while (begin1 <= end1) { tmp[begin++] = a[begin1++]; } while (begin2 <= end2) { tmp[begin++] = a[begin2++]; } //每次归并后及时拷贝回原数组 memcpy(a+begin, tmp+begin, sizeof(int) * (end2-begin1+1)); } gap *= 2; } }
if(end1 > n)
{
end1 = n - 1;
begin2 = n; //将begin2和end2修改为不存在的区间[n,n-1]
end2 = n - 1;
}
else if(begin2 > n)
{
begin2 = n;
end2 = n - 1;
}
else if(end2 > n)
{
end2 = n - 1;
}
由于每次都是将区间平分成两个区间,让左区间有序,让右区间有序,再进行归并,类似递归的过程类似二叉树的结构,因此,
if(right - left + 1 < 10)
{
InsertSort(a,left,right);
return;
}
递归时,最后几层的递归调用次数占了整体的80%、90%,最后一层就占50%,所以要排的数的个数小于10个时,也就是最后三层,利用插入排序可以减少大量的递归次数,不过这种优化不会发生质的改变。
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。