基本思想
- 用递归的形式对序列进行拆分,并对拆分后的小块序列排序
- 将小块序列不断合并成最终排好序的长序列
操作演示
原素组的形式
对数组进行第一次拆分
对数组进行第二次拆分
对数组进行第三次拆分
到这一步为止,原数组已被拆分为数个长度为1的小数组。
这一步的可以通过对原数组进行递归拆分来实现
void MergSort(int a[], int length){
if(length > 1){
int* list1 = a;
int list1_size = length / 2;
int* list2 = a + list1_size;
int list2_size = length - list1_size;
MergSort(list1, list1_size);
MergSort(list2, list2_size);
//用于对拆分后的子数组进行排序
Merge(list1, list1_size, list2, list2_size);
}
}
特别注意的是:
- 虽然在图形中,我们讲数组分割为了很多个小数组,但是在实际的内存操作中,我们并没有为这些拆分后的数组分配额外的内存
- 我们实际上是通过两个数组指针,list1、list2来对原数组进行虚拟的拆分操作
操作如图所示:
根据代码中的递归调用顺序,在进行了第一次拆分之后,会先对左半部分进行拆分。
操作后的到的结果如下:
红色线条表示第一次递归拆分
黑色线条表示第二次递归拆分
蓝色线条表示第三次递归拆分
至此,左半部分拆分完毕,此时,程序会调用的语句为
Merge(list1, list1_size, list2, list2_size);
此时,函数中的参数为:
参数 | Value |
---|---|
list1 | 5 |
list1_size | 1 |
list2 | 2 |
list2_size | 1 |
Merge函数
核心思想
1.构建一个临时数组temp[],将list1和list2里面的数据进行比较,按顺序放在temp数组里
2.使用temp数组,对list1进行覆盖
代码组成
void Merge(int* list1, int list1_size, int* list2, int list2_size){
/**temp这个临时数组的大小是和原始数组的大小相同的*/
int temple[8] ={};
/**list1_point: 用来指向list1中现在正在进行比较的数的序列号*/
/**list2_point: 用来指向list2中现在正在进行比较的数的序列号*/
/**temple_point: 用来指向temple中这在进行处理的数的序列号*/
int list1_point = 0;
int list2_point =0;
int temple_point = 0;
/**如果list1中的数和list2中的数都没有遍历完,就不断对list1和list2中的数据进行比较*/
while(list1_point < list1_size && list2_point <list2_size){
/**list1和list2中,谁里面的数更小,就把谁的数放到temp里面去,并且它和temp的序列号都会前进一格*/
if(list1[list1_point] > list2[list2_point]){
temple[temple_point] = list2[list2_point];
temple_point++;
list2_point++;
}
else{
temple[temple_point] = list1[list1_point];
temple_point++;
list1_point++;
}
}
//右边列表空了,但是左边列表还有数,要把他放到现在的临时数组中去
while(list1_point < list1_size){
temple[temple_point] = list1[list1_point];
temple_point++;
list1_point++;
}
//左边列表空了,但是右边列表还有数,要把他放到现在的临时数组中去
while(list2_point < list2_size){
temple[temple_point] = list2[list2_point];
temple_point++;
list2_point++;
}
//第一波排序完了,把临时数组里的数字,按顺序放到list1里面去,因为list1开头还是原来的数组,只是用指针将原来的数组分割成了两个部分,实际上原数组并没有改变
for(int m = 0; m < list1_size + list2_size; m++){
list1[m] = temple[m];
}
}
操作演示
这是现在原始列表的原始形式,为了方便讲解,现将数组的一部分单独拿出来
这是Merge函数,进行操作时的数据形式。
比较操作完成之后,所得到的结果数据形式结果为:
最后使用temple来覆盖原数组
当执行完上述的这一次操作后,原始数组也会发生改变。
之后的操作也和上述的操作同理,就不再详细演示了。
完整代码
#include <iostream>
#include <cstdlib>
void Merge(int* list1, int list1_size, int* list2, int list2_size){
/**temp这个临时数组的大小是和原始数组的大小相同的*/
int temple[8] ={};
/**list1_point: 用来指向list1中现在正在进行比较的数的序列号*/
/**list2_point: 用来指向list2中现在正在进行比较的数的序列号*/
/**temple_point: 用来指向temple中这在进行处理的数的序列号*/
int list1_point = 0;
int list2_point =0;
int temple_point = 0;
/**如果list1中的数和list2中的数都没有遍历完,就不断对list1和list2中的数据进行比较*/
while(list1_point < list1_size && list2_point <list2_size){
/**list1和list2中,谁里面的数更小,就把谁的数放到temp里面去,并且它和temp的序列号都会前进一格*/
if(list1[list1_point] > list2[list2_point]){
temple[temple_point] = list2[list2_point];
temple_point++;
list2_point++;
}
else{
temple[temple_point] = list1[list1_point];
temple_point++;
list1_point++;
}
}
//右边列表空了,但是左边列表还有数,要把他放到现在的临时数组中去
while(list1_point < list1_size){
temple[temple_point] = list1[list1_point];
temple_point++;
list1_point++;
}
//左边列表空了,但是右边列表还有数,要把他放到现在的临时数组中去
while(list2_point < list2_size){
temple[temple_point] = list2[list2_point];
temple_point++;
list2_point++;
}
//第一波排序完了,把临时数组里的数字,按顺序放到list1里面去,因为list1开头还是原来的数组,只是用指针将原来的数组分割成了两个部分,实际上原数组并没有改变
for(int m = 0; m < list1_size + list2_size; m++){
list1[m] = temple[m];
}
}
void MergSort(int a[], int length){
if(length > 1){
int* list1 = a;
int list1_size = length / 2;
int* list2 = a + list1_size;
int list2_size = length - list1_size;
MergSort(list1, list1_size);
MergSort(list2, list2_size);
//用于对拆分后的子数组进行排序
Merge(list1, list1_size, list2, list2_size);
}
}
int main(){
int a[8] = {5, 2, 9, 1, 4, 7, 8, 3};
int length = sizeof(a) / sizeof(a[0]);
//传进来的时候length=4
MergSort(a, length);
for(int m=0; m < length; m++){
std::cout << a[m] << " ";
}
system("pause");
}
运行结果为: