归并排序的基本原理是把两个有序数组合为一个有序数组。
例如我们现在将数组a{2, 4, 6, 8}与数组b{1, 3, 5, 7}合成一个有序数组c。 我们依次比较a,b中的元素,谁小将谁放入新数组c中,直到a数组与b数组中的一个被取完。再将剩下的一个数组中的元素依次放入新数组c。
Mergr(int a[], int b[], int m, int n) //m为a数组的长度 - 1,n为b数组长度 - 1。
{
int c[20];
int i = 0, j = 0, k = 0;
while (i <= m && j <= n)
{
if (a[i] <= b[j])
{
c[k++] = b[i++];
}
else
{
c[k++] = b[j++];
}
}
while (i <= m)
{
c[k++] = a[i++];
}
while (j <= n)
{
c[k++] = b[j++];
}
}
上述代码可以合并两个有序数组,而归并排序的思想就是将一个数组分成两个有序数组在合并为一个数组。 因为传递的参数是一个数组,我们需要对上面的Merge函数稍作改变。先建立两个新数组,再将目标数组分为两部分拷贝到新数组内,最后将两个新数组用上述方法合并到目标数组内
void Merge(int *arr, int left, int mid, int right)
{
int *nums1 = MyCopy(arr, left, mid);
int *nums2 = MyCopy(arr, mid + 1, right); //将数组的前后两部分拷贝到新数组内
//MyCopy函数是我自己写的函数,将目标数组的一部分拷贝到新空间内,并返回空间地址(代码发在下面)
int i = 0, j = 0;
int k = left;
while (i <= mid - left && j <= right - mid - 1)
{
/*mid - left是nums1的长度减一,right - mid - 1是nums2的长度减一*/
if (nums1[i] <= nums2[j])
{
arr[k++] = nums1[i++];
}
else
{
arr[k++] = nums2[j++];
}
}
while (i <= mid - left)
{
arr[k++] = nums1[i++];
}
while (j <= right - mid - 1)
{
arr[k++] = nums2[j++];
} //将两个新数组合并到目标数组内
free(nums1);
free(nums2); //释放空间
}
MyCopy函数
int* MyCopy(int *arr, int left, int right)
{
int *nums;
int i = left, j = 0;
nums = (int*)malloc(sizeof(int) * (right - left + 1));
for (; i <= right; i++)
{
nums[j] = arr[i];
j++;
}
return nums;
}
如果要用上述方法,我们要保证数组的前半部分和后半部分分别有序。 这里我们可以继续将前半部分在拆为两部分,然后继续拆,一直拆到一个部分只有一个元素,此时必然有序,然后合并。 对后半部分,我们做同样处理。 这里我们使用递归实现。
void Merge_Sort(int *arr, int left, int right)
{
if (left == right)
{
return; // 只有一个元素时返回
}
else
{
int mid = (left + right) / 2;
Merge_Sort(arr, left, mid); //对前半部分排序
Merge_Sort(arr, mid + 1, right); //对后半部分排序
Merge(arr, left, mid, right); //合并
}
}