使用归并排序计算逆序对个数

  • 给出一个数组 arr[8] = {8,7,4,5,4,10,2,1},比如8,7就是逆序,使用归并排序找出逆序对的个数,代码如下:
#include <iostream>

int nixu = 0;//定义一个全局变量来存储逆序对的个数
using namespace std;


template <typename T>
void __merge(T arr[], int l, int mid, int r) {

    T aux[r-l+1];

    for (int i = l; i <= r; ++i) {
        aux[i-l] = arr[i];
    }

    int i = l, j = mid + 1;
    for (int k = l; k <= r; ++k) {
        if(i > mid) {
            arr[k] = aux[j-l];
            j++;
        }
        else if (j > r) {
            arr[k] = aux[i-l];

            i++;
        }
        else if (aux[i-l] <= aux[j-l]) {
            arr[k] = aux[i-l];
            i++;
        }
        else {

            nixu += mid - i + 1; 
            //关键在于这个地方,每次aux[i-l] > aux[j-l],表明aux[j-l] < aux[i-l...mid-l],所以这个地方增加mid - i + 1
            arr[k] = aux[j-l];
            j++;
        }
    }
}


template <typename T>
void mergeSortBU(T arr[], int n) {

    //从size=1开始使用自底向上的归并排序
    for (int sz = 1; sz <= n; sz += sz) {
        for (int i = 0; i < n - sz; i += sz + sz) {
                //[i,i+sz-1]和[i+sz, i+sz+sz-1]归并排序
                __merge(arr, i, i+sz-1, min(i+sz+sz-1, n-1) );
        }
    }
}



int main() {

    int arr[8] = {8,7,4,5,4,10,2,1};
    mergeSortBU(arr, 8);
    cout << "nixu: " << nixu;
    return 0;
}
output:

nixu: 21
Process finished with exit code 0
  • 一个完整的自底向上的归并排序只需要修改两个地方就实现了上述的计算数组中逆序对个数的算法。
<think>好的,我现在需要帮助用户了解如何用归并排序计算数组中的逆序对个数。首先,我得回忆一下归并排序的基本原理,因为用户可能已经知道,但可能需要复习一下。归并排序是分治法的典型应用,通过将数组分成子数组,排序后再合并。在这个过程中,合并阶段可以统计逆序对数量。 接下来,我要明确逆序对的定义:如果前面一个数字大于后面的数字,那么这两个数字就组成一个逆序对。比如数组[3,1,2]中,逆序对是(3,1)和(3,2),所以总数是2。 接下来,我需要思考如何在归并排序的合并过程中统计逆序对归并排序在合并两个已排序的子数组时,会比较左右两边的元素。当左边的元素大于右边的元素时,这里可能存在多个逆序对。例如,如果左边子数组是[4,5],右边是[1,3],当合并时,4比1大,那么4和后面的所有右边元素都构成逆序对,这里就是2个逆序对(4和1,4和3)。 这时候,我需要考虑如何具体实现这一点。在合并过程中,每次从左右两个子数组中取出较小的元素放入临时数组。当右半部分的元素被取出时,说明当前左半部分剩余的所有元素都比这个右半部分的元素大,因此可以累加逆序对数量。比如,当合并到右半部分的元素1时,左半部分剩下的元素是4和5,所以此时逆序对数量增加2。 然后,我需要考虑分治法的步骤。递归地将数组分成左右两部分,分别计算左右内部的逆序对,然后在合并时计算跨越左右的逆序对。最后将这三部分的结果相加。这样就能在O(n log n)的时间复杂度内解决问题。 接下来,我需要考虑具体的代码实现。归并排序通常需要一个辅助数组来合并,而在统计逆序对时,需要在合并过程中添加计数逻辑。比如,当右边的元素被选入辅助数组时,计数器加上左半部分剩余元素的个数。 还要注意边界条件和递归终止条件,比如当子数组长度为1时,逆序对数量为0。另外,需要考虑数组索引的处理,避免越界错误。 然后,我需要验证这个方法的正确性。例如,对于一个简单的测试用例,如[3,1,2],归并排序在合并过程中应该正确统计出2个逆序对。或者更复杂的例子,比如[7,5,6,4],逆序对总数应该是5。 最后,需要总结这个过程,确保用户能够理解分治法的应用,以及如何在归并排序的合并阶段统计逆序对数量。同时,可能还要比较归并排序与其他方法(如暴力法)的时间复杂度,说明归并排序的优势。</think>### 使用归并排序计算逆序对个数的原理与实现 #### 核心思想 归并排序的分治特性天然适合统计逆序对。在合并两个有序子数组时,通过比较左右两部分的元素,可以**直接计算跨越左右子数组的逆序对数量**,再递归统计子数组内部的逆序对[^1][^3]。 #### 关键步骤 1. **分治递归** 将数组分成左右两部分,分别递归计算左右子数组内部的逆序对数量: $$ 总逆序对数 = 左子数组逆序对数 + 右子数组逆序对数 + 合并时发现的逆序对数 $$ 2. **合并计数** 合并两个有序子数组时,当发现右子数组元素小于左子数组元素时,说明当前左子数组中所有未处理的元素都与该右子数组元素构成逆序对。例如: ``` 左子数组:[5,7], 右子数组:[4,6] 当右子数组元素4被取出时,左子数组剩余元素5、7都 > 4,此时累加2个逆序对 ``` #### 代码实现 ```python def merge_sort_count(arr): if len(arr) <= 1: return arr, 0 mid = len(arr) // 2 left, cnt_left = merge_sort_count(arr[:mid]) right, cnt_right = merge_sort_count(arr[mid:]) merged, cnt_cross = merge(left, right) return merged, cnt_left + cnt_right + cnt_cross def merge(left, right): merged = [] i = j = cnt = 0 while i < len(left) and j < len(right): if left[i] <= right[j]: merged.append(left[i]) i += 1 else: merged.append(right[j]) cnt += len(left) - i # 核心计数点 j += 1 merged += left[i:] merged += right[j:] return merged, cnt ``` #### 算法特性 - 时间复杂度:$O(n\log n)$,与归并排序时间复杂度一致[^2] - 空间复杂度:$O(n)$,来自合并时的临时数组[^3] - 优势:相比暴力解法的$O(n^2)$时间复杂度,效率显著提升
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值