YunYan

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

归并排序是众多排序方法中比较稳定的一种排序。时间复杂度是o(nlogn),是一种较优的排序方法。

归并排序是一种典型的分治法,其基本思想就是将大区间划分为小区间,先对小区间进行排序,然后在通过两个有序的小区间归并为一个有序的大区间。首先对待排序数组以二叉树的形式进行分割;

比如说对数组[5,4,3,2,1,0],分割后就变成了这样的形式。叶子节点只含有一个点,一定是有序的。然后从叶子节点开始依次向上进行合并,处理顺序就是二叉树的后序遍历顺序一样。

 

 

 

 合并的过程就是每两个有序的区间合并成一个大区间。

看一下代码,代码还是比较好理解的。

code:

void merge(int a[],int l,int r,int mid){
    for(int i=l;i<=r;i++) tmp[i]=a[i];// 暂时保存a当中的数 
    int i=l,j=mid+1;
    for(int k=l;k<=r;k++){
        if(i>mid)a[k]=tmp[j++];//左边区间的数处理完毕,只剩下右边的数。 
        else if(j>r)a[k]=tmp[i++];//右边区间的数处理完毕,只剩下左边的数。 
        else if(tmp[i]>tmp[j]) {//比较左右区间的数字,取小的。 
            a[k]=tmp[j++];
        }
        else a[k]=tmp[i++];
    }
} 
void merge_sort(int a[],int l,int r){
    if(l>=r) return ; 
    int mid=(l+r)/2;
    //
    merge_sort(a,l,mid);
    merge_sort(a,mid+1,r); 
    //
    merge(a,l,r,mid);
}

通过归并排序求数组的逆序数。

其思路就是在合并的过程中,

比如说此时的区间为

[a1,a2,a3,a4,b1,b2,b3,b4] 

左半部分区间为[a1,a2,a3,a4],一定是有序的,假设是从小到大的,右半部分的区间为[b1,b2,b3,b4]也一定是有序的。

假设左半部分比较到a2,下标为i,右区间比较到了b2,下标为j,如果说b2<a2,也那么b2一定排在a2的前边,又因为i<j,所以说一定会产生逆序对(a2,b2),又因为a3>a2>b2,并且a3的下标也一定小于b2的下标,所以说(a3,b2)也是逆序对。因此我们可以找到b2的逆序对数为(mid-i+1),mid为左边区间的右边界。

并且这个逆序数可以看做b2的左边比b2大的数的个数。若求b2左边比b2小的数的个数呢?当然这就不是逆序数了,这时我们只需要把从小到大改为从大到小排就可以了,想想看,从大到小排,两个子区间肯定也是从大到小的顺序,首先应该满足在b2的左边,如果b2大于左区间的一个数的吗,那么他肯定大于左区间该数右边的数。也就和求逆序数的方法一样了。

如果让求b2右边比它大的数呢?我们需要把原数组逆序一下,然后求从小到大求逆序数,求b2右边比它小的数,把原数组逆序然后从大到小求逆序数的个数。

 

posted on 2020-07-14 11:30  Target--fly  阅读(221)  评论(0编辑  收藏  举报