归并排序 ( 递归 与 非递归)
归并排序又称为合并排序(合久必分,分久必和) 分而治之的策略
归并排序不是一下子将整个数组排序,而是不断的分离直到分为一个数,一个数本身就是有序的,最后不断的合
分而治之 大规模问题化为小规模问题,小规模问题的解合并即是大规模问题的解
合并排序之递归
Code 递归
#include <cstdio>
//归并排序采用递归方式
//先分 后比 再和
//自顶向下(递归)
//-------------------------------------------------------------
void MergeSort(int s[], int left, int mid, int right)
{
int a[8] = {0};//定义一个临时数组,来存放交换后的数组值
int i = left, j = mid;
int k = 0;
while (i < mid && j <= right)//
{//进入MergeSort函数中的左右两个part依次比较,谁小谁放在a数组的前面
if (s[i] < s[j])
a[k++] = s[i++];
else
a[k++] = s[j++];
}
while (i < mid)//a数组右part已经放完啦, 剩余的左part部分的数值都放在a数组后面
a[k++] = s[i++];
while (j <= right)//a数组左part已经放完啦, 剩余的右part部分的数值都放在a数组后面
a[k++] = s[j++];
k = 0;
for (int t = left; t <= right; t++)//最后将临时的a数组里的数值再赋值给s数组
s[t] = a[k++];
}
void Merge(int s[], int left, int right)
{
if (left < right)
{
int middle = (left + right) / 2;//找出中心位置
Merge(s, left, middle);//进入左边part
Merge(s, middle + 1, right);//进入右边part
MergeSort(s, left, middle + 1, right);//核心代码
//MergeSort执行的过程中是在递归回溯的过程中的执行的,看上面那张图片即可明白
}
//else return ;
// 可有可无
}
//---------------------------------------------------------------------
int main()
{
int arry[8] = {8, 4, 5, 7, 1, 3, 6, 2};
for (int i = 0; i < 8; i++)
printf("%d ", arry[i]);
printf("\n");
Merge(arry, 0, 7);//将arry数组传入到Merge中
for (int i = 0; i < 8; i++)
printf("%d ", arry[i]);
return 0;
}
合并排序之迭代
递归属于自顶向下, 那么迭代相当于自底向上
迭代是从左往右 一 一归并(元素本身有序,即元素位置基本不变动), 两 两归并,再四 四归并,再八 八归并 …一直往上加(不足2^k个数直接归并即可!如本例中的数值46) 最终使整个数组有序
但许多同学都说关于迭代的逻辑思路我都知道的,关键是如何实现呢?
的确, 思路很简单,许多同学卡在不知道如何用编程实现,说明编程能力还不够,需要多做题来加强,一起努力吧!
关于迭代其实有两种实现,大同小异,看你喜欢那种呀!主要是Merge函数中如何将临时数组b放到原数组中方法的区别
Code 迭代1(So easy)
#include <iostream>
#include <cstdio>
using namespace std;
void Merge(int a[], int n)
{
int *b = new int[n];
for (int i = 1; i < n; i = i * 2)//步长
{
int left_min, left_max, right_min, right_max;
for (left_min = 0; left_min < n - i; left_min = right_max)
// n- i 不满足步长的,无需继续循环
{
int Start = left_min;//因为left_min会变的,故应该用Start来保存初始值
left_max = right_min = left_min + i;
right_max = right_min + i;
int index = 0;//临时数组b的下标索引
if (right_max > n)//如果右边part不足步长的长度,直接和左边满足的比较
right_max = n;//限定范围的
int End = right_max;//与Start同理
while (left_min < left_max && right_min < right_max)
{
if (a[left_min] < a[right_min])
{
b[index++] = a[left_min++];
}
else
b[index++] = a[right_min++];
}
while (left_min < left_max)
{
b[index++] = a[left_min++];
}
while (right_min < right_max)
{
b[index++] = a[right_min++];
}
//三个while的循环代码和递归的一样
index = 0;
for (int j = Start; j < End; j++)
{//不要使用left_min,right_max,因为该两个变量实行了自加加
a[j] = b[index++];//临时数组赋值给原数组a
}
}
}
delete[] b;
}
int main()
{
int arry[9] = {63, 95, 84, 46, 18, 24, 27, 31, 46};
for (int i = 0; i < 9; i++)
printf("%d\t", arry[i]);
printf("\n");
Merge(arry, 9);
for (int i = 0; i < 9; i++)
printf("%d\t", arry[i]);
return 0;
}
归并排序中两个for循环是如何进行归并排序的,流程如下:
Code 迭代2(有点绕)
主要区别在与Merge函数中的第一个 while (left_min < left_max && right_min < right_max)下面的两个while的写法,其他 地方一某一样!!!
开动你的脑袋,为什么可以这样写呢?
#include <iostream>
#include <cstdio>
using namespace std;
void Merge(int a[], int n)
{
int *b = new int[n];
for (int i = 1; i < n; i = i * 2)//步长
{
int left_min, left_max, right_min, right_max;
for (left_min = 0; left_min < n - i; left_min = right_max)
{
left_max = right_min = left_min + i;
right_max = right_min + i;
int index = 0;
if (right_max > n)//如果右边part不足步长的长度,直接和左边满足的比较
right_max = n;//限定范围的
while (left_min < left_max && right_min < right_max)
{
if (a[left_min] < a[right_min])
{
b[index++] = a[left_min++];
}
else
b[index++] = a[right_min++];
}
//----------------------------------------------------------------------
// 分两种情况讨论,一是left_min还没到,二是right_min没到
// 1, left_min没到直接使用a数组赋值(因为待会b仍旧是要赋值给a的)
// 2, right_min没到直接待在结尾即可,无需改动
while (left_min < left_max)//此时说明right_min等于right_max
{
a[--right_min] = a[--left_max];
}
// 为什么不可以使用right_max ?
// 答:因为使用--right_max使下一轮中的left_min向已经做比阿尼移动好的元素偏移
while (index > 0)
{
a[--right_min] = b[--index];//这里必须使用right_min
}
//-----------------------------------------------------------------------------
}
}
delete[] b;
}
int main()
{
int arry[9] = {63, 95, 84, 46, 18, 24, 27, 31, 46};
for (int i = 0; i < 9; i++)
printf("%d\t", arry[i]);
printf("\n");
Merge(arry, 9);
for (int i = 0; i < 9; i++)
printf("%d\t", arry[i]);
return 0;
}
结语
归并排序无论是递归还是迭代,总是离不来分治策略
大规模问题转化为小规模问题,小规模问题的再进行合 即是规模问题
建议将以上三种方法的归并排序自己手动来实现一下!!!