归并排序

归并

如果a,b数组是已经排序后的两个数组,最终我们需要的是合并到c数组。将a数组的所有数字单独看成n个子数组,将b数组所有数字都单独看成m个子数组。每次将a,b的两个子数组比较,最后可能某个数组没有到最后,再都添加到c数组。这样按大小添加到c数组中,保证了每个数字前后左右要么是同一数组,要么也经过了大小比较排序,肯定是排好序的数组

const int maxn=?;
int a[n],b[m],c[maxn];   //c数组必须开的足够大
void merge(){  
    int i,j,k;
    i=j=k=0;
    while(i<n && j<m){  //n、m分别是a和b数组的长度
        if (a[i]<b[j])  //实现从大到小排序就把<改为>
            c[k++]=a[i++];
        else c[k++]=b[j++];
    }
    while(i<n) c[k++]=a[i++];
    while(j<m) c[k++]=b[j++];
}

若想实现去重复,先将k的初始值改为1,再将while里的语句改为下面:

故输出时c数组从下标1开始

if(a[i]<b[j]) 
	c[k]=a[i++];
else 
	c[k]=b[j++];
if(c[k]!=c[k-1])
    k++;
归并排序

归并排序是建立在归并操作上的一种有效的排序算法,该算法是采用分治法的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。一般是将两个有序表合并成一个有序表。归并排序是一种稳定的排序方法,时间复杂度O(nlogn)
在这里插入图片描述
按照归并的思想,显然需要一个辅助数组t,在归并的时候,将上面归并的几个判断条件简化,即:

1.只要有一个序列非空就要继续合并

2.如果第二个为空,则复制左半部分a[p]。如果两个都非空,因为我们要将较小的排在前面,那么就要当a[p]<=a[q]时复制a[p]

3.否则复制a[q]

const int maxn=?;
int a[maxn],t[maxn];

void merge_sort(int x,int y){
    if(y-x>1){
     	int mid=x+(y-x)/2;
        int p=x,q=mid,i=x;
        merge_sort(x,mid);
        merge_sort(mid,y);
        while(p<mid || q<y){
            if(q>=y || (p<mid && a[p]<=a[q])) t[i++]=a[p++];
            else t[i++]=a[q++];
        }
        for(int i=x;i<y;i++) a[i]=t[i];     //从辅助数组复制到原数组
    }
}
应用

逆序对

对于一个数组a若 i > j && a[i] < a[j] 那么称a[i]与a[j]是一个逆序对,其实逆序对就是求每个数前面有多少个比它大的数

归并排序可以顺带完成逆序对数的计算:由于合并操作是从小到大进行的,当右边的a[q]复制到t[i]中时,左边还没来得及复制到t的数就是比a[q]大的数,因此在累加器中加上左边元素个数m-p即可

尽管我们在归并时归并多次,但是每次归并后都能保证同一区间前面的数比它小,后面就不需考虑

#include <iostream>
using namespace std;
const int maxn=5e5+10;
int a[maxn],t[maxn];
long long cnt=0;

void merge_sort(int x,int y){
    if(y-x>1){
     	int mid=x+(y-x)/2;
        int p=x,q=mid,i=x;
        merge_sort(x,mid);
        merge_sort(mid,y);
        while(p<mid || q<y){
            if(q>=y || (p<mid && a[p]<=a[q])) t[i++]=a[p++];
            else{
                t[i++]=a[q++];
                cnt+=mid-p;
            }
        }
        for(int i=x;i<y;i++) a[i]=t[i];
    }
}

int main()
{
    int n;
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    merge_sort(0,n);
    printf("%lld\n",cnt);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值