一、前言
您好,我是夏日弥,叫我“夏”或者“小弥”都可以。
今天先来讲 归并排序 ——一种时间复杂度为 N●logN 的排序算法;
让我们看看今天我们将要进入的主题吧!
二、归并排序
2.1、概念&思想:
归并排序是一种稳定的,时间复杂度为N●logN的排序算法,它由两个部分组成:
递归划分(divide):将数组划分成左右两个部分,再将左右两个部分分别再划分成两个部分,以此类推,直至数组里的元素被划分成数个只有一个元素的数组;
图解如下:(图丑莫见怪,小夏不会画画哈)
我们把数组划分成这个样子后,递归合并回去;
合并数组:数组元素较小的放在前面,数组元素较大的放在后面;
图解如下:
我们将两个小数组合并成一个局部有序的数组:
以1248和3567为例:
我们将 i 和 j 指向的那个较小数字拿出来放到 tmp[0] 里面:
也就是拿出 1 放到 tmp[0] 里,随后我们移动指针 i 前进,tmp 的指针 t 前进;
再比较2 和 3,把2 放进 tmp[1] 里,移动指针 i 和 t 前进;
这次是3 和 4比较,把3放进去,移动指针 j 和 t前进;
依次类推,直到 i 或 j 走到终点,再将剩余的数字全部复刻进tmp;
1248 就是 12 和 48 以此种规则递归而来;3567依次类推;
2.2、代码实现:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N],tmp[N];
unsigned int result;
void merge_sort(int a[],int l,int r){
if( l >= r)return ;//递归的排序的算法必须考虑到的,当数组只剩一个元素时就要返回单个元素了;
int mid = (r + l )/2;//定义中点;
merge_sort(a,l,mid);//递归前半部分;
merge_sort(a,mid+1,r);//递归后半部分;
/* 以下为缝合两个有序的数组的部分 */
int i = l;//定义第一数组的前指针;
int j = mid+1;//定义第二数组的前指针;
int t = 0;//定义暂存数组的前指针;
while(i <= mid && j <= r ) //扫描第一数组和第二数组;
{
if(a[i] <= a[j])
{
tmp[t++] = a[i++];//把较小的数字放入tmp数组中,再分别移动t针和i针;
}else{
tmp[t++] = a[j++];
}
}
/* 以下为补齐部分,将第一或者第二数组的剩余部分补齐进入tmp数组;*/
while(i <= mid ){
tmp[t++] = a[i++];
}
while(j <= r ){
tmp[t++] = a[j++];
}
/* 以下为复制补全部分*/
for(int i = l ,j = 0; i <= r ;i++,j++ ){
a[i] = tmp[j];
}/*为什么要从 i = l 开始?
为什么是i针扫到r结束 ?
*/
}
int main(){
cin>>n;
for(int i = 0;i<n;i++)cin>>a[i];
merge_sort(a,0,n-1);
for(int i = 0 ;i<n;i++)cout<<a[i]<<" ";
}
经典的递归排序算法模板;
merge_sort (int a[],int l,int r)
//第一个参数为需要排序的数组,第二个参数为排序起点,第三个参数为排序终点
三、例题:
3.1、归并排序:
使用上面的模板就可以AC了,更多是锻炼您对模板的记忆;
3.2、活动 - AcWing
唔....我们将利用递归思想分析这个问题:
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100010;
int n;
int a[N],tmp[N];
unsigned int result = 0;
void merge_sort(int a[],int l,int r){
if( l >= r)return ;//递归的排序的算法必须考虑到的,当数组只剩一个元素时就要返回单个元素了;
int mid = (r + l )/2;//定义中点;
merge_sort(a,l,mid);//递归前半部分;
merge_sort(a,mid+1,r);//递归后半部分;
/* 以下为缝合两个有序的数组的部分 */
int i = l;//定义第一数组的前指针;
int j = mid+1;//定义第二数组的前指针;
int t = 0;//定义暂存数组的前指针;
while(i <= mid && j <= r ) //扫描第一数组和第二数组;
{
if(a[i] <= a[j])
{
tmp[t++] = a[i++];//把较小的数字放入tmp数组中,再分别移动t针和i针;
}else{
tmp[t++] = a[j++];
result += (mid - i + 1);
}
}
/* 以下为补齐部分,将第一或者第二数组的剩余部分补齐进入tmp数组;*/
while(i <= mid ){
tmp[t++] = a[i++];
}
while(j <= r ){
tmp[t++] = a[j++];
result += (mid-i+1);
}
/* 以下为复制补全部分*/
for(int i = l ,j = 0; i <= r ;i++,j++ ){
a[i] = tmp[j];
}/*为什么要从 i = l 开始?
为什么是i针扫到r结束 ?
*/
}
int main(){
cin>>n;
for(int i = 0;i<n;i++)cin>>a[i];
merge_sort(a,0,n-1);
cout<<result;
}
四、尾声
感谢您的阅读和理解。
小夏累了,下周以及下下周预计会比较忙,有不足之处还望您指出。
特别鸣谢 lym 对本文的指导。