归并排序 (MergeSort)
原理:假设初试序列含有n个记录,则可以看成是n个有序的子序列,每个子序列的长度为1,然后两两归并,得到n/2个长度为2(或为1 奇数个情况)的有序子序列;两两在归并,……,如此重复,直至得到一个长度为n的有序序列为止,这种排序方法称为2两路并归排序。
递归写法:
void MergeSortMethod1(int* head,int*arr, int left,int right)
{
if (left >= right)
return;
int middle = (left + right) / 2;
MergeSortMethod1(head, arr, left, middle);
MergeSortMethod1(head, arr, middle+1, right);
int x = left, y = middle + 1;
int i = 0;
while (x <= middle && y <= right)
{
if (head[x] < head[y])
{
arr[i] = head[x];
x++;
}
else
{
arr[i] = head[y];
y++;
}
i++;
}
while (x <= middle)
{
arr[i] = head[x];
x++;
i++;
}
while (y <= right)
{
arr[i] = head[y];
y++;
i++;
}
memmove(head+left, arr, (right - left + 1) * sizeof(head[0]));
}
也很好理解吧 递归思想嘛 任务分解
下面是非递归写法:
递归改非递归有两种方式,之前提到过了 要么采用栈(队列)辅助 要么采用循环
这里显然采用循环比较合适
非递归和递归思路是完全反着的,从归并的定义入手 开始是一一归并 然后是二二归并 ……
所以思路就有了 ,直接先一个一个归并 在两个两个 在四个四个 ……
//非递归写法
// gap是 每次归并单个组的元素个数 1-2-4-8-16…
void MergeSortMethod2(int* head, int* arr, int left, int right)
{
int gap = 1;
while (gap < (right - left + 1))
{
int j = 0;
for (j = left; j <= right; j = j + 2 * gap)
{
int size = 0; //记录写入arr的个数
int left1 = j, right1 = j + gap - 1;
int left2 = j + gap, right2 = j + 2 * gap - 1;
int x = left1, y = left2;
int i = 0;
while (x<=right&&y<=right&&x <= right1 && y <= right2)
{
if (head[x] < head[y])
{
arr[i] = head[x];
size++;
x++;
}
else
{
arr[i] = head[y];
size++;
y++;
}
i++;
}
while (x <= right&& x <= right1)
{
arr[i] = head[x];
size++;
x++;
i++;
}
while (y <= right && y <= right2)
{
arr[i] = head[y];
size++;
y++;
i++;
}
memmove(head + left1, arr, size * sizeof(head[0]));
}
gap = gap * 2;
}
free(arr);
}
主要解释一下这里, 就是怎么确定待比较的两个自序列 左右区间
这里的坐标问题就解决了 对于后面子序列比原序列长的话
其实不用去修订最后两个自序列的左右坐标 这样反而麻烦了 直接在后面归并的时候 加一个条件 他们都得小于 right就行 就避免了 越界的问题 下面两个while就是把那个还没有排完的序列 在写进去
为什么有两个 ? 因为不知道哪个排完了 直接两个在验证一遍就行
下面在介绍一下 增强版本的 非递归
上面的非递归你发现没 memmove做了很多无用功 每次归并完就要拷到原序列 再继续在原序列上面 拆下来 归并到临时数组 在拷回去 是不是很鸡肋
所以直接采用 反复归并
思路就是 原序列归并到临时数组 在临时数组归并到 原序列(这次归并 gap*2 才是有用的归并)
这样一下 少了一倍时间 就不用每次拷回去再归并了
原码:
//封装函数
void Merge(int* destination, int* source, int gap,int left,int right)
{
int i = 0;
for (int j = left; j <= right; j = j + 2 * gap)
{
int left1 = j, right1 = j + gap - 1;
int left2 = j + gap, right2 = j + 2 * gap - 1;
int x = left1, y = left2;
while (x <= right && y <= right && x <= right1 && y <= right2)
{
if (source[x] < source[y])
{
destination[i] = source[x];
x++;
}
else
{
destination[i] = source[y];
y++;
}
i++;
}
while (x <= right && x <= right1)
{
destination[i] = source[x];
x++;
i++;
}
while (y <= right && y <= right2)
{
destination[i] = source[y];
y++;
i++;
}
}
}
//非递归写法2 增强版
void MergeSortMethod3(int* head, int* arr, int left, int right)
{
int gap = 1;
while (gap < (right - left + 1))
{
Merge(arr, head, gap, left, right);
gap = gap * 2;
Merge(head, arr, gap, left, right);
gap = gap * 2;
}
free(arr);
}
这里就不多解释了 看到这里 说明你已经对这个理解很深了