归并排序(Merge Sort)是一种分治算法

羊爸
羊爸
Published on 2025-03-10 / 26 Visits
2
0

归并排序(Merge Sort)是一种分治算法,其基本思想是将一个数组分成两半,分别对这两半进行排序,然后再将这两个有序的半数组合并成一个有序的完整数组。这个过程是递归进行的,直到数组中的每个元素都成为一个有序的子数组。以下是归并排序算法的步骤:

主要步骤

1. 分解(Divide)

- 如果数组的长度大于1,那么将数组分成两个子数组,每个子数组的长度大约是原数组长度的一半。

2. 解决(Conquer)

- 对两个子数组分别进行归并排序,这个过程是递归进行的。

3. 合并(Combine)

- 将两个已排序的子数组合并成一个有序数组。在合并过程中,逐个比较两个子数组的元素,将较小的元素放入新数组中,直到所有元素都被合并。

归并排序的关键在于合并步骤,它确保了最终数组的有序性。以下是合并步骤的详细描述:

- 初始化两个指针,分别指向两个子数组的起始位置。

- 比较两个指针所指向的元素,将较小的元素复制到新数组中,并将该指针向前移动一位。

- 重复上述步骤,直到一个子数组的所有元素都被复制到新数组中。

- 将另一个子数组中剩余的元素复制到新数组中,因为它们已经是有序的。

归并排序的时间复杂度为O(n log n),其中n是数组的长度。这是因为每次分解都将数组分成两半,需要log n次分解,每次合并操作需要O(n)的时间。

在归并排序的过程中,还可以计算数组中的逆序对数量。逆序对是指数组中元素对(i, j),满足i < j但a[i] > a[j]。在合并步骤中,当从右侧子数组中取元素时,左侧子数组中所有尚未处理的元素都比右侧子数组的当前元素大,因此它们与右侧子数组的当前元素形成了逆序对。通过在合并过程中累加这些逆序对的数量,可以得到整个数组的逆序对总数。

模板代码

求逆序对

#include<bits/stdc++.h>
using namespace std;

// 定义一个整型数组a,其大小为100001,用于存储输入的整数序列
int a[100001];
// 定义一个长整型变量sum,用于存储逆序对的数量
long long sum=0;

// 定义一个递归函数merge,用于合并两个有序序列,并计算逆序对的数量
void merge(int ll,int rr){
    // 定义一个临时数组t,用于存储合并后的序列
    int t[100001];
    // 如果序列的长度小于等于1,则不需要合并
    if(rr -ll <=1) return ;
    // 计算中间索引
    int mid = ll +(rr-ll)/2;
    // 递归地合并左半部分序列
    merge(ll,mid);
    // 递归地合并右半部分序列
    merge(mid,rr);
    // 初始化两个指针p和q,分别指向左半部分和右半部分序列的起始位置
    // 初始化一个指针s,用于指向临时数组t的当前位置
    int p=ll,q=mid,s=ll;
    // 合并两个有序序列,并计算逆序对的数量
    while(s < rr){
        // 如果左半部分序列已经全部合并,或者右半部分序列的当前元素小于左半部分序列的当前元素
        if(p>=mid ||(q < rr && a[p]>a[q])){
            // 将右半部分序列的当前元素复制到临时数组t中
            t[s++]=a[q++];
            // 更新逆序对的数量
            sum+=mid-p;
        }else{
            // 将左半部分序列的当前元素复制到临时数组t中
            t[s++]=a[p++];
        }
    }
    // 将临时数组t中的元素复制回原数组a中
    for(int i=ll;i<rr;i++) a[i]=t[i];
}

// 主函数
int main(){
    // 读取整数序列的长度
    int n;
    cin >> n;
    // 读取整数序列
    for(int i=0;i<n;i++){
        cin >> a[i];
    }
    // 调用merge函数,合并整数序列,并计算逆序对的数量
    merge(0,n);

    // 输出逆序对的数量
    cout << sum;
    
    // 返回0,表示程序正常结束
    return 0;
}


Comment