归并排序(c++实现,同时解决逆序对个数问题)

131 阅读2分钟

归并排序

归并排序是一种高效的、通用的基于比较的排序算法。大多数实现都能产生稳定排序.同快排一样,采用分治的思想,平均时间复杂度为O(nlogn),然而归并排序需要一个辅助数组来完成归并操作,所以空间复杂度为O(n)。

在递归实现归并排序时,与快排不同的是,归并排序先递归,再执行归并,而快排是先进行快排,再递归,这是两者的又一区别。

步骤:

1)将数组 q[n] 按中位数划分为两个子区间,递归左右两个子区间

2)定义辅助指针 k, 左区间指针 i , 右区间指针 j , 取 q[i] 和 q[j] 较小者放入辅助数组 temp[n] ,辅助指针 k 以及指向较小数指针一起向后移动

3)i 和 j 两个指针其中一个指向区间末尾时,需要对另一区间进行 “扫尾”工作

4)将 temp 数组移回 q 数组

以上便是归并排序的简易步骤(详见后面代码)

值得一提的是,归并排序可同时解决逆序对个数问题。

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

在归并操作中,左右区间有 i < j , 假设 q[i] <= q[j], 进行归并操作,那么 q[i] > q[j] 刚好满足逆序对定义,此时 i 指针后的数均与 q[j] 成为逆序对,只需把个数累加最后返回即可。

最后,代码如下

#include <iostream>

using namespace std;

const int N = 1e5 + 10;
typedef long long LL;

int q[N], temp[N];
int n;

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

int main() {
    cin >> n;
    for (int i = 0; i < n; i ++) cin >> q[i];
    
    LL res = merge_sort(q, 0, n - 1);
    cout << res << endl;
    return 0;
}