一、归并算法
2路归并。
采用分治的思想:将数组区间不断二分,直至每个区间只有一个元素。单个元素自然是有序的。然后将有序区间两两合并,最后得到一个有序数组。
用递归来实现分治。
void merge(int *arr, int L1, int R1, int L2, int R2)
{
// 申请辅助空间用于临时保存合并后的序列
int *temp = malloc(sizeof(int)*(R2-L1+1)), index = 0;
// 有序合并左右子区间:如果左右子区间不等长,最后有一方会有剩余元素,直接加入temp。
int i = L1, j = L2;
while(i <= R1 && j <= R2)
{
if(arr[i] <= arr[j]) temp[index++] = arr[i++];
else temp[index++] = arr[j++];
}
while(i <= R1) temp[index++] = arr[i++];
while(j <= R2) temp[index++] = arr[j++];
// 将合并后的有序区间放回arr
for(int k=0; k<R2-L1+1; k++)
{
arr[L1+k] = temp[k];
}
// 释放申请的辅助空间,避免内存泄露
free(temp);
}// end of function merge
void mergeSort(int *arr, int left, int right)
{
//递归终止条件:子区间长度等于1,即left == right,只剩一个元素
if(left < right)
{
// 二分法划分左、右子区间:[left, mid],[mid+1, right]
// 分别对左右子区间排序
int mid = left + (right-left)/2;
mergeSort(arr, left, mid);
mergeSort(arr, mid+1, right);
// 合并两个有序左右子区间
merge(arr, left, mid, mid+1, right);
}
}// end of function mergeSort
时间复杂度
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn):递归树高
(
l
o
g
2
n
)
+
1
(log_2n)+1
(log2n)+1,每一层中所有元素都参与过比较和移位,代价为
c
n
cn
cn,总代价为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn)。
空间复杂度
O
(
n
)
O(n)
O(n):辅助空间
O
(
n
)
O(n)
O(n),栈深
O
(
l
o
g
n
)
O(logn)
O(logn)。
二、测试代码
void printArray(int *arr, int length)
{
for(int i=0; i<length; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
}
void test()
{
int arr[] = {66,12,33,57,64,27,18};
printArray(arr, sizeof(arr)/sizeof(arr[0]));
mergeSort(arr, 0, sizeof(arr)/sizeof(arr[0])-1);
printArray(arr, sizeof(arr)/sizeof(arr[0]));
}
运行结果:
66 12 33 57 64 27 18
12 18 27 33 57 64 66