快速排序和归并排序都需要用递归的形式展开,那么有没有什么方法不需要递归就能实现归并和快速排序,有的!
我们可以借助栈来模拟递归。
递归的主要思想就是大事化小,小事化了。我们借助栈的 目的是将需要排序的“头” 和 “尾”找到,进而排序,然后找到合适的keyi并放入合适的位置,再将keyi两边的“头” 和 “尾”入栈,找到合适的keyi......如此重复下去.......
keyi需要入栈吗?keyi不需要入栈,因为keyi已经在合适的位置了



前后指针法,结合上一篇博客来看,
- int PatrSort3(int* a, int begin, int end)
- {
-
- int prev = begin, cur = begin+1;
-
- int keyi = begin;
-
- int midi = GetMidIndex(a , begin ,end);
- Swap(&a[keyi],&a[midi]);
-
- while (cur <= end)
- {
- //cur位置小于keyi发生交换
- if (a[cur] < a[keyi] && ++prev != cur)
- {
- //++prev;
- Swap(&a[prev], &a[cur]);
- }
-
- cur++;
- }
-
- Swap(&a[keyi],&a[prev]);
-
- keyi = prev;
- return keyi;
-
- }
- void QuickSortNonR(int* a, int begin, int end)
- {
- ST st;
- STInit(&st);
- STPush(&st,end);
- STPush(&st, begin);
-
- while (!STEmpty(&st))
- {
- int left = STTop(&st);
- STPop(&st);
-
- int right = STTop(&st);
- STPop(&st);
-
- int keyi = PatrSort3(a,left,right);
-
- if (keyi + 1 < right)
- {
- STPush(&st,right);
- STPush(&st,keyi+1);
- }
-
- if (left < keyi - 1)
- {
- STPush(&st, keyi - 1);
- STPush(&st, left);
- }
-
- }
-
- STDestroy(&st);
- }
归并排序的基本思想在上一篇已经讲过,快速排序是先对整个数组中选出适中的的keyi,放到合适的位置,然后切分子块,层层递归,进行排序,快速排序类似于前序遍历。归并排序是先找到最小的子块,然后层层排序,层层归并,归并排序类似于后序遍历。
归并排序还存在越界的问题:每个子区间应该怎么样确定,能给出明确的边界吗?
边界是怎么确定的:

gap每次增加是上一次的2倍,当gap = 4 时, 数组有10个元素,我们控制循环是按照一下的方式来控制的。
当 i = 0 的时候 begin1 end1 ,begin2 end2 。[0 ,3] [4 ,7] 。
进入下一轮循环: i = 8 , begin1 end1 ,begin2 end2 。[8,11] [12 ,15] 。
![]()
此时问题来了,数组只有十个元素,下标为 10-15 已经越界访问了 ,这是我们要处理越界访问问题。



- void MergeSortNonR(int* a, int n)
- {
-
- int* tmp = (int*)malloc(sizeof(int) * n);
- if (tmp == NULL)
- {
- perror("malloc fail");
- }
-
- int gap = 1;
- while (gap < n)
- {
- printf("gap:%d->", gap);
- for (int i = 0; i < n; i += 2*gap)
- {
-
-
- //[i,i+gap-1] [i+gap ,i + 2*gap-1] 控制边界
- int begin1 = i;
- int end1 = i + gap - 1;
- int begin2 = i + gap;
- int end2 = i + 2 * gap - 1;
-
-
- //越界,修正边界
- if (end1 >= n)
- {
- end1 = n - 1;
-
- // [begin2, end2]修正为不存在区间
- begin2 = n;
- end2 = n - 1;
- }
- else if (begin2 >=n)
- {
- // [begin2, end2]修正为不存在区间
- begin2 = n;
- end2 = n - 1;
- }
- else if (end2 >= n)
- {
- end2 = n - 1;
- }
-
- printf("[%d,%d] [%d, %d]--", begin1, end1, begin2, end2);
-
- int m = end2 - begin1+1;
- int j = begin1;
-
- while (begin1 <= end1 && begin2 <= end2)
- {
- if (a[begin1] < a[begin2])
- {
- tmp[j++] = a[begin1++];
- }
- else
- {
- tmp[j++] = a[begin2++];
- }
- }
-
- while (begin1 <= end1)
- {
- tmp[j++] = a[begin1++];
- }
-
- while (begin2 <= end2)
- {
- tmp[j++] = a[begin2++];
- }
-
- memcpy(a+i, tmp+i, sizeof(int) * m);
-
-
- }
- // memcpy(a, tmp, sizeof(int) * m);
-
- printf("\n");
-
- gap *= 2;
- }
-
- free(tmp);
-
- }
- void TestMergeSort()
- {
- int a[] = { 9, 1, 2, 5, 7, 4, 8, 6, 3, 5 };
- //MergeSort(a, sizeof(a) / sizeof(int) );
- MergeSortNonR(a, sizeof(a) / sizeof(int));
- PrintArray(a, sizeof(a) / sizeof(int));
- }
归并排序的特性总结:
1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序问题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定
