版权声明:莉莉莉 https://blog.csdn.net/qq_41700151/article/details/83276753
归并排序又叫合并排序,是一种利用分治法的一种稳定的排序
1.简介
将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为2-路归并。
2.描述
归并排序具体算法描述如下(递归版本):
1、Divide: 把长度为n的输入序列分成两个长度为n/2的子序列。
2、Conquer: 对这两个子序列分别采用归并排序。
3、Combine: 将两个排序好的子序列合并成一个最终的排序序列。
归并排序的效率是比较高的,设数列长为N,将数列分开成小数列一共要logN步,每步都是一个合并有序数列的过程,时间复杂度可以记为O(N),故一共为O(NlogN)。因为归并排序每次都是在相邻的数据中进行操作,所以归并排序在O(NlogN)的几种排序方法(快速排序,归并排序,希尔排序,堆排序)也是效率比较高的。
核心代码
例题:有一个序列,通过交换相邻数直到升序,求最小次数
#include<iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <map>
#include <cstdlib>
#include <algorithm>
#define ll long long
#define INF 0x3f3f3f
const int MAX=1e6;
using namespace std;
ll a[500000+10];
ll temp[500000+10];
ll sum;
void Merge(int l,int r,int m)
{
int i=l;
int j=m+1;
int k=l;
while(i<=m&&j<=r)
{
if(a[i]>a[j])
{
sum+=m-i+1;
temp[k++]=a[j++];
}
else
temp[k++]=a[i++];
}
while(i<=m)
temp[k++]=a[i++]; //temp作排序后的数组
while(j<=r)
temp[k++]=a[j++];
for(i=l;i<=r;i++)
a[i]=temp[i];
}
void mergesort(int l,int r)
{
if(l<r)
{
int mid=(l+r)/2;
mergesort(l,mid); //分成只有一个数的n个小区间
mergesort(mid+1,r);
Merge(l,r,mid); //归并小区间
}
}
int main()
{
ll n;
ios::sync_with_stdio(false);
while(scanf("%lld",&n)!=EOF)
{
if(n==0)
break;
for(ll i=1; i<=n; i++)
scanf("%lld",&a[i]);
sum=0;
mergesort(1,n);
printf("%lld\n",sum);
}
return 0;
}
例题1、
题目输入一个数组,数组元素的大小在0->999.999.999的范围内,元素个数为0-500000范围。题目要求通过相邻的元素的交换,使得输入的数组变为有序,要求输出交换的次数
#include<iostream>
#include<stdlib.h>
using namespace std;
void printArray(int arry[],int len)
{
for(int i=0; i<len; i++)
cout<<arry[i]<<" ";
cout<<endl;
}
int MergeArray(int arry[],int start,int mid,int end,int temp[])//数组的归并操作
{
//int leftLen=mid-start+1;//arry[start...mid]左半段长度
//int rightLlen=end-mid;//arry[mid+1...end]右半段长度
int i=mid;
int j=end;
int k=0;//临时数组末尾坐标
int count=0;
//设定两个指针ij分别指向两段有序数组的头元素,将小的那一个放入到临时数组中去。
while(i>=start&&j>mid)
{
if(arry[i]>arry[j])
{
temp[k++]=arry[i--];//从临时数组的最后一个位置开始排序
count+=j-mid;//因为arry[mid+1...j...end]是有序的,如果arry[i]>arry[j],那么也大于arry[j]之前的元素,从a[mid+1...j]一共有j-(mid+1)+1=j-mid
}
else
{
temp[k++]=arry[j--];
}
}
cout<<"调用MergeArray时的count:"<<count<<endl;
while(i>=start)//表示前半段数组中还有元素未放入临时数组
{
temp[k++]=arry[i--];
}
while(j>mid)
{
temp[k++]=arry[j--];
}
//将临时数组中的元素写回到原数组当中去。
for(i=0; i<k; i++)
arry[end-i]=temp[i];
printArray(arry,8);//输出进过一次归并以后的数组,用于理解整体过程
return count;
}
int InversePairsCore(int arry[],int start,int end,int temp[])
{
int inversions = 0;
if(start<end)
{
int mid=(start+end)/2;
inversions+=InversePairsCore(arry,start,mid,temp);//找左半段的逆序对数目
inversions+=InversePairsCore(arry,mid+1,end,temp);//找右半段的逆序对数目
inversions+=MergeArray(arry,start,mid,end,temp);//在找完左右半段逆序对以后两段数组有序,然后找两段之间的逆序对。最小的逆序段只有一个元素。
}
return inversions;
}
int InversePairs(int arry[],int len)
{
int *temp=new int[len];
int count=InversePairsCore(arry,0,len-1,temp);
delete[] temp;
return count;
}
void main()
{
//int arry[]={7,5,6,4};
int arry[]= {1,3,7,8,2,4,6,5};
int len=sizeof(arry)/sizeof(int);
//printArray(arry,len);
int count=InversePairs(arry,len);
//printArray(arry,len);
//cout<<count<<endl;
system("pause");
}