归并排序以及逆序对问题

归并排序以及逆序对问题

void merge_sort(int q[], int l, int r)
{
    if(l >= r) return;
    

    int mid = l+r>>1;
    merge_sort(q,l,mid);
    merge_sort(q,mid+1,r);
    
    int tmp[r-l+1]={0}, k = 0, i = l, j = mid+1;
    while(i<=mid && j<=r){
        if(q[i]<=q[j]) tmp[k++] = q[i++];
        else tmp[k++] = q[j++];
    }
    
    while(i<=mid) tmp[k++] = q[i++];
    while(j<=r) tmp[k++] = q[j++];
    
    for(int i = l, j = 0; i<=r; i++,j++) q[i] = tmp[j];
}

归并排序与快速排序的分治合差别:

归并是从下往上进行出递归的时候进行数据的处理,要进行合并。

快速排序是从上往下的时候进行分治,不用进行合并。

习题:

788.逆序对的数量

给定一个长度为n的整数数列,请你计算数列中的逆序对的数量。

逆序对的定义如下:对于数列的第 i 个和第 j 个元素,如果满足 i < j 且 a[i] > a[j],则其为一个逆序对;否则不是。

输入格式

第一行包含整数n,表示数列的长度。

第二行包含 n 个整数,表示整个数列。

输出格式

输出一个整数,表示逆序对的个数。

int res;
void merge_sort(int q[], int l, int r){
    if(l >= r) return;
    

    int mid = l + r >> 1;
    merge_sort(q, l ,mid);
    merge_sort(q, mid+1, r);
    
    int tmp[r-l+1] = {0}, k = 0 ,i = l, j = mid+1; 
    while(i <= mid&& j<=r){
        if(q[i] <= q[j]) tmp[k++] = q[i++];
        else{
            tmp[k++] = q[j++];
            //重点
            res+=mid-i+1;
        }
    }

    while(i <= mid) tmp[k++] = q[i++];
      
    
    while(j <= r) tmp[k++] = q[j++];
    
    for(int i = l, j = 0; i <= r; i++,j++) q[i] = tmp[j];

}

为什么只在归并排序的基础上增加了:

res+=mid-i+1;

就能解出这道题?

假设在最后一次排列中的数据为

2 3 4 || 1 5 6

mid = 4 ; i =2; j = 5;

首先,两部分数据在上一层递归中已经得到了排序,所以两部分数据均为有序状态。因此,上一层递归的逆序对已经算出。我们只需要将res加上本层递归所算出来的逆序对个数即可。

可以很明显的看出,要先放置1temp中,放了1之后,从i~mid都会比1大,因为程序中经过了此次对比

if(q[i] > q[j]) tmp[k++] = q[j++];

在左右两边均为有序的状态下,左边会有i - mid+1个数比此时的q[j]大,所以要用 res+=i-mid+1.

posted @ 2020-12-17 11:43  lsxkugou  阅读(69)  评论(0编辑  收藏  举报