1. 归并排序的引入
有这样一个题:将两个有序数组合并成一个有序数组。
我们的思路是:定义三个变量分别指向三个数组,cur1指向第一个有序数组array1,cur2指向第二个有序数组array2,index指向合并的有序数组array。开始比较两个有序数组的元素:
①当array1[cur1]<array2[cur2]时,将array1[cur1]移动到array数组中并将cur1++;
②当array1[cur1]>=array2[cur2]时,将array2[cur2]移动到array数组中并将cur2++;
③当某一个有序数组先结束,则直接将其另一个有序数组中剩余的元素均移动到array数组中。
此时便完成了两个有序数组合并为一个有序数组的操作。
那么问题来了?当现在不止两个有序数组而是多个有序数组,如何合并为一个有序数组呢?这就引入了归并排序。
2. 归并排序的基本思想
首先由于原本的数组是无序数组,利用之前两个有序数组合并为一个有序数组的思想,合并数组前的所有数组应该是有序的。故先将无序数组划分为有序数组,而划分为一个个有序数组的方法就是将数组二分再二分再二分直到数组为1个元素时,此时每个数组一定为有序数组。其次对划分的有序数组进行归并方法还是两个有序数组的归并思想,即对两两相邻的两对有序数组依次进行归并,直到所有数组归并完成。
3. 递归实现归并排序
思路:利用递归的方法去递归划分无序数组,直到划分的数组只有一个元素时,对其相邻两个数组进行归并。
//思路:1.对无序数组进行递归划分为只有一个元素的有序数组
// 2.依次对划分的有序数组两两归并
void MergeSort(int array[] ,int64_t size)
{
//当size<=1时,表示有序或非法元素个数,直接return
if(size<=1)
{
return;
}
//为了避免搬运的麻烦,重新申请空间存放归并数组元素
int* tmp=(int*)malloc(sizeof(int)*size);
_MergeSort(array,0,size,tmp);
free(tmp);
}
//用于对无序数组递归划分为只有一个元素的数组后再对数组进行归并
void _MergeSort(int array[],int64_t beg,int64_t end,int* tmp)
{
//递归出口为当数组被划分为一个元素时,故end-beg<1时
if(end-beg<=1)
{
return;
}
int64_t mid=beg+(end-beg)/2; //为了防止beg+end时溢出
//递归去划分无序数组
_MergeSort(array,beg,mid,tmp);
_MergeSort(array,mid,end,tmp);
//进行归并处理
_MergeArray(array,beg,mid,end,tmp);
}
void _MergeArray(int array[],int64_t beg,int64_t mid,int64_t end,int* tmp)
{
//该操作功能:两个有序数组合并为一个有序数组
int64_t cur1=beg;
int64_t cur2=mid;
int64_t index=0;
while(cur1<mid&&cur2<end)
{
if(array[cur1]<array[cur2])
{
tmp[index]=array[cur1];
cur1++;
index++;
}
else
{
tmp[index]=array[cur2];
cur2++;
index++;
}
}
//当cur1指向的数组先结束比较,cur2指向的数组还有元素时
//将cur2指向的数组中的剩余元素都拷贝到tmp中
while(cur1<mid)
{
tmp[index++]=array[cur1++];
}
//当cur2指向的数组先结束比较,cur1指向的数组还有元素时
//将cur1指向的数组中的剩余元素都拷贝到tmp中
while(cur2<end)
{
tmp[index++]=array[cur2++];
}
//最后将有序元素拷贝到数组中
memcpy(array+beg,tmp,sizeof(int)*(end-beg));
}
4. 非递归实现归并排序
思路与递归实现归并排序一致,先将数组划分为一个个数组,再进行有序数组的归并。不同的是,由于递归实现归并排序时可以对数组进行递归划分和归并,而非递归实现划分和归并操作,需要通过for循环实现。
void MergeSortByLoop(int array[],int64_t size)
{
//size<=1时直接return
if(size<=1)
return;
//为了方面数组元素的归并,动态申请一块内存
int* tmp=(int*)malloc(sizeof(int)*size);
//先对无序数组划分为多个数组,再进行数组之间的归并
int64_t gap=1;
for(;gap<size;gap*=2)
{
//第一趟归并为数组元素个数为1的两个相邻数组归并
//第二趟归并为数组元素个数为2的两个相邻数组归并
//第三趟归并为数组元素个数为4的两个相邻数组归并
//...
size_t i=0;
//每次循环是在处理两个相邻区间的归并
for(;i<size;i+=2*gap) //i=0 2 4 6 ...
{
int64_t beg=i;
int64_t mid=i+gap;
if(mid>size){
mid=size;
}
int64_t end=i+2*gap;
if(end>size){
end=size;
}
_MergeArray(array,beg,mid,end,tmp);
}
}
}