想要了解归并排序,首先需要稍微了解一下分治算法。现在用简单的话来讲,分治就是将一个大的问题分成许多个相同的子问题,这就是分治的思想。分别将这些子问题求出来,再合并这些子问题的解用来建立原问题的解。这种思想通常使用递归实现。
归并排序就是使用了这样的算法。这里书中讲了一些分治的操作,这里我用直白一点的话来讲。假设有一个一串还没有排序的一串数 1 4 4 2 7 9 从中间断开,他是两个有序数组。如果我们想要将两个有序数列还原成一个完整的有序数列,这里我们需要类似于双指针的思想。
假设我们需要从小到大进行排列。 左右两边指针所指向的元素分别指向1 ,2。这里我们假设从小到大进行排序,1比2小,所以我们将1 放入新的数组,指针指向下一个元素
这里再进行比较 , 2 是小于 4 的。 所以此时我们将2插入这个数组,指针指向下一个元素
现在再进行比较,4是小于7的 所以将4插入到这个数组,指针指向下一个元素
重复之前的步骤 ,这里我就不过多赘述了,现在是将4插入
发现黑色数组中的元素已经没了,因为两个数组均是有序的,所以我们只需要将红色指针所指的第二个数组依次插入到我们新创建的数组中
最后就会形成一个有序数列。可是光是这样是不够的,因为我们每次提供的数组并不是左半边和右半边分别有序的数列,因此这里我们就需要用到递归的思想了,将左右两边的数列再次进行分化,当分化到最后左右两边只剩下一个数字的时候,就可以将左右两边看作两个有序数列(这一部分有点像之前的插入排序 ,将第一个只有的一个元素视为一个有序数列)。这里我们可以参考算法导论上的一张图, 这里提供一个数列,我们使用递归将他的左右两边分化到左右两边只剩下一个数字,(这里假设为该数列为 5 2 4 7 1 3 2 6),书上画的是分治的合并 , 这里我画递归自顶向下的图片你方便大家理解为什么要递归
现在就开始我们的coding吧 ,我并没有直接使用书上的代码,换了一种写法。这里我们一步步来,先将左右两边合并为一个有序数组的代码写出来,再进行递归。
这里我们声明一个函数,将需要排序的数组,他的左端点 l(left)和右端点r (right) 传入。mid是数组的中点。
void merge(int q[], int l, int mid, int r)
这里我们再创建一个数组,将原数组的有序序列传入这个数组
int tmp[10000] = { 0 };
int i = l, j = mid + 1;
int k = 0;
i 表示第一个数组的起始位置,j表示第二个数组的起始位置
现在开始将数组先进行插入吧,这里需要一些c语言的知识,i++是先将q[i] 传入给tmp[i]之后再进行自加(++)
while (i <= mid && j <= r) {
if (q[i] < q[j]) {
tmp[k++] = q[i++];
}
else tmp[k++] = q[j++];
}
到这里两个数组肯定其中有一个数组排序排完了(话哦徐两个数组均排完了),此时我们要将另一个数组中的剩下的序列插入个tmp这个临时数组
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
现在我们需要将这个临时排好顺序的数组放回原来的数组
for (int i = l, j = 0; i <= r; i++, j++) {
q[i] = tmp[j];
}
这一块将两个有序数组合并的代码就完成了,现在我们需要通过递归来讲数组不断进行二分,分到最后一个仅剩一个数字,那么他的终止条件就应该为
if (l >= r) {
return;
}
准确的来说,就是当l和r都代表了数组的一个数时,递归就开始逐渐返回。 因为之前我们时从中间将数组一分为二进行排序,所以这里我们还需要知道数组的中间的坐标
int mid = l + r >> 1;
这里>>1代表数字的二进制向右移动一位,这里同样可以写为(l + r ) / 2,效果是一样的。这时我们开始递左右两边
MergeSort(q, l, mid);
MergeSort(q, mid + 1, r);
再进行左右两边的排序
merge(q, l, mid, r);
现在我将整个归并排序的代码放在下面,需要的同学可以做个参考,顺带着 做个测试。希望大家能在理解的基础上吧这段代码可以默写下来。
int main() {
int a[] = { 5,2,4,6,1,3 };
MergeSort(a, 0, 6 - 1);
for (int i = 0; i < 6; i++) {
cout << a[i] << " ";
}
return 0;
}
void merge(int q[], int l, int mid, int r) {
int tmp[10000] = { 0 };
int i = l, j = mid + 1;
int k = 0;
while (i <= mid && j <= r) {
if (q[i] < q[j]) {
tmp[k++] = q[i++];
}
else tmp[k++] = q[j++];
}
while (i <= mid) tmp[k++] = q[i++];
while (j <= r) tmp[k++] = q[j++];
for (int i = l, j = 0; i <= r; i++, j++) {
q[i] = tmp[j];
}
}
void MergeSort(int q[], int l, int r) {
if (l >= r) {
return;
}
int mid = l + r >> 1;
MergeSort(q, l, mid);
MergeSort(q, mid + 1, r);
merge(q, l, mid, r);
}