归并排序往往以递归的形式实现,如果面试官要求你实现一个迭代版本,又该如何实现呢?
首先给出常见的递归版本:
<span style="font-family:Times New Roman;font-size:14px;">static void merge(int a[], int left, int mid, int right, int temp[])
{
int i = left, j = mid+1, k = 0;
while(i <= mid && j <= right)
{
if(a[i] <= a[j])
temp[k++] = a[i++];
else
temp[k++] = a[j++];
}
while(i <= mid)
temp[k++] = a[i++];
while(j <= right)
temp[k++] = a[j++];
for(int idx = 0; idx < k; idx++)
a[idx+left] = temp[idx];
}
static void mergeSort2(int ar[], int left, int right, int temp[])
{
if(left < right)
{
int mid = (right - left)/2 + left;
mergeSort2(ar, left, mid, temp);
mergeSort2(ar, mid+1, right, temp);
merge(ar, left, mid, right, temp);
}
}
void mergeSortRecur(int a[], int len)
{
int *temp = (int *)malloc(sizeof(int)*len);
mergeSort2(a, 0, len-1, temp);
}</span>
其原理不再详细阐述,下面给出迭代版本(其中merge 函数和递归版本的一样),参考自点击打开链接,附带部分注释:
<span style="font-family:Times New Roman;font-size:14px;">static void mergePass(int a[], int temp[], int s, int n)
{
int i = 0;
while(i < n - 2 * s)
{
merge(a, i , i+s-1, i + 2 * s -1, temp);
//Merge two adjacent segments of size s
i = i + 2 * s;
}
// fewer than 2s elements remain
//possible 1,(s, 2s) left
if(i + s < n)
merge(a, i, i + s-1, n-1, temp);
else //possible 2, (0, 2)left
for(int j = i; j <= n-1; j++)
temp[j] = a[j];
}
void mergeSortItera(int a[], int len)
{
int *temp = (int *)malloc(sizeof(int)*len);
int start = 1;
while(start < len)
{
mergePass(a, temp, start, len);
start += start;
/*mergePass(temp, a, start, len);
start += start;*/
}
}</span>
可以看出,迭代版的归并排序依然需要
O(n)
的额外空间,其首先设置步长为1,然后逐步加倍,完成归排序过程。边界条件是,在某个步长处,可能存在不能等分的情况,此时需要视情况而定。设步长为s,则不外乎如下两种情况:1、剩下的元素个数为
(s, 2*s)
,2、剩下的元素个数为
(0, s)
。前者可以再次归并,后者按照元素原来的顺序拷贝回去就可以了。