归并排序:
(复制粘贴百度百科没什么意思),简单来说,就是对数组进行分组,然后分组进行排序,排序完最后再整合起来排序!
我看了很多博客,都是写的8个数据呀什么的(2^4,分组方便),我就想着,要是10个数据呢,他们那种就没怎么讲清楚,下面我来讲一下我所理解的归并排序!
思路整理如下所示:
下面用代码来实现(代码注释超详细):
用来判断整合几次的最外层循环!
void MergeSort(int *arr, int len) //这块用了函数调用 当然你给上面那玩意写到这个里面完全可以
{
for(int i=1; i<len; i*=2)//首先来看假如数组有八位满足不满足条件 (2^3)<8不成立 也就是四四合并之后 就完成了合并 查看上面图 满足条件
//再来看假如有10个值 (2^4)<10不成立 也就是八八合并之后 需要四次合并 查看上图 满足条件
{
Merge(arr, len, i); //调用函数主体 直接写到这个循环中也完全ok
}
}
函数主体:
//8 6 3 2 7 1 9 5 4 0
//如上所示 单个数的时候 L1 = H1就是这个数字本身
//68 23 17 59 04
//这块拿前两个分好的数组为例 arr[L1] = 6 arr[H1] = 8 arr[L2] = 2 arr[H2] = 3
//2368 1579 04
//拿这组举例的话 arr[L1] = 2 arr[H1] = 8 arr[L2] = 1 arr[H2] = 9
//12356789 04
//arr[L1] = 1 arr[H1] = 9 arr[L2] = 0 arr[H2] = 4
void Merge(int* arr, int len, int gap)
{
//数组本身空间不够,这块早堆区申请一个和数组等长的动态内存
int* brr = (int*)malloc(sizeof(int) * len);
assert(brr != NULL);
//
int L1 = 0; //这块就到归并排序的精髓了 假设第一步,gap的值是1 一一合并L1 = 0 H1 = 0
int H1 = L1 + gap - 1; //接上行 合并之后L1 = 0,H1 = 1
int L2 = H1 + 1; //到这儿之后 按照上图来说68 23 arr[L1] = 6 arr[H1] = 8;
int H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1;//那么arr[L2] = 2 arr[H2] = 3 ; L2 = H1 + 1 = 2 ;
//H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1 = 3;这里写个三目运算符主要是用来判断L2+gap如果越界len的话,那么H2的下标值为len-1
int i = 0;// 堆区申请来的空间brr存放数据的下标
while (L2 < len) //这块为什么要L2 < len 呢 先想一下 如果用L1 H1的话 跑到后期 L2早就越界了导致左边的白跑
{ //然后使用L2的时候可以保证右边的数组最少取到一个值 使用H2的话会导致提前越界 导致有些数据不能成功排序
while (L1 < H1 && L2 < H2)
{
if (arr[L1] <= arr[L2])
{
brr[i++] = arr[L1++]; //这块后置++的原因是先用再加 难以理解的话可以写成下面这种
/*brr[i] = arr[L1];
i++, L1++;*/
}
else
{
brr[i++] = arr[L2++];
}
}
while (L1 <= H1)//因为上面的内层while中是 "&&" 可能其中一项满足 但是另外一项不满足 因此退出了while循环 因此写这么一句
{ //如果左边还有数据 挪下来
brr[i++] = arr[L1++];
}
while (L2 <= H2)//如果右边组还有数据,挪下来
{
brr[i++] = arr[L2++];
}
L1 = H2 + 1; //因为数据得遍历完分组排序 这是在新的分好的数组中的左值与右值 与上面一毛一样
H1 = L1 + gap - 1;
L2 = H1 + 1;
H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1;
}
//这块退出while就是右边的arr中数组L2越界了(while(L2<len)) 此时右边肯定没有数据了
//至于arr中左边的数组还有没有值 不确定再来个判断
while (L1 < len)
{
brr[i++] = arr[L1++];
}
//然后再将brr中数组导入到arr中即可
for (int i = 0; i < len; i++)
{
arr[i] = brr[i];
}
free(brr); //堆区空间的释放
}
将代码写一块就是:
//8 6 3 2 7 1 9 5 4 0
//如上所示 单个数的时候 L1 = H1就是这个数字本身
//68 23 17 59 04
//这块拿前两个分好的数组为例 arr[L1] = 6 arr[H1] = 8 arr[L2] = 2 arr[H2] = 3
//2368 1579 04
//拿这组举例的话 arr[L1] = 2 arr[H1] = 8 arr[L2] = 1 arr[H2] = 9
//12356789 04
//arr[L1] = 1 arr[H1] = 9 arr[L2] = 0 arr[H2] = 4
void Merge(int* arr, int len, int gap)
{
//数组本身空间不够,这块早堆区申请一个和数组等长的动态内存
int* brr = (int*)malloc(sizeof(int) * len);
assert(brr != NULL);
//
int L1 = 0; //这块就到归并排序的精髓了 假设第一步,gap的值是1 一一合并L1 = 0 H1 = 0
int H1 = L1 + gap - 1; //接上行 合并之后L1 = 0,H1 = 1
int L2 = H1 + 1; //到这儿之后 按照上图来说68 23 arr[L1] = 6 arr[H1] = 8;
int H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1;//那么arr[L2] = 2 arr[H2] = 3 ; L2 = H1 + 1 = 2 ;
//H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1 = 3;这里写个三目运算符主要是用来判断L2+gap如果越界len的话,那么H2的下标值为len-1
int i = 0;// 堆区申请来的空间brr存放数据的下标
while (L2 < len) //这块为什么要L2 < len 呢 先想一下 如果用L1 H1的话 跑到后期 L2早就越界了导致左边的白跑
{ //然后使用L2的时候可以保证右边的数组最少取到一个值 使用H2的话会导致提前越界 导致有些数据不能成功排序
while (L1 < H1 && L2 < H2)
{
if (arr[L1] <= arr[L2])
{
brr[i++] = arr[L1++]; //这块后置++的原因是先用再加 难以理解的话可以写成下面这种
/*brr[i] = arr[L1];
i++, L1++;*/
}
else
{
brr[i++] = arr[L2++];
}
}
while (L1 <= H1)//因为上面的内层while中是 "&&" 可能其中一项满足 但是另外一项不满足 因此退出了while循环 因此写这么一句
{ //如果左边还有数据 挪下来
brr[i++] = arr[L1++];
}
while (L2 <= H2)//如果右边组还有数据,挪下来
{
brr[i++] = arr[L2++];
}
L1 = H2 + 1; //因为数据得遍历完分组排序 这是在新的分好的数组中的左值与右值 与上面一毛一样
H1 = L1 + gap - 1;
L2 = H1 + 1;
H2 = L2 + gap - 1 < len ? L2 + gap - 1 : len - 1;
}
//这块退出while就是右边的arr中数组L2越界了(while(L2<len)) 此时右边肯定没有数据了
//至于arr中左边的数组还有没有值 不确定再来个判断
while (L1 < len)
{
brr[i++] = arr[L1++];
}
//然后再将brr中数组导入到arr中即可
for (int i = 0; i < len; i++)
{
arr[i] = brr[i];
}
free(brr); //堆区空间的释放
}
void MergeSort(int *arr, int len) //这块用了函数调用 当然你给上面那玩意写到这个里面完全可以
{
for(int i=1; i<len; i*=2)//首先来看假如数组有八位满足不满足条件 (2^3)<8不成立 也就是四四合并之后 就完成了合并 查看上面图 满足条件
//再来看假如有10个值 (2^4)<10不成立 也就是八八合并之后 需要四次合并 查看上图 满足条件
{
Merge(arr, len, i); //调用函数主体 直接写到这个循环中也完全ok
}
}