mergesort与逆序对个数

本文介绍了如何在归并排序过程中统计逆序对的数量。通过分析merge过程,当左边数组元素大于右边时,会形成逆序对,并且左、右两侧归并及整体归并的逆序对不会重复计算。总逆序对数等于各部分之和。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

mergesort代码如下:

def merge_lists(left, right):
    res = []
    lc = rc = 0
    while lc < len(left) and rc < len(right):
        if left[lc] <= right[rc]:
            res.append(left[lc])
            lc += 1
        else:
            res.append(right[rc])
            rc += 1
    res.extend(left[lc:])
    res.extend(right[rc:])
    return res

def merge_sort(li):
    if len(li) == 1:
        return li
    # split
    mid_index = len(li) // 2
    left = merge_sort(li[:mid_index])
    right = merge_sort(li[mid_index:])
    # merge
    return merge_lists(left, right)

merge的过程里可以统计逆序对的个数。


inversion_count = 0

def merge_lists(left, right):
    global inversion_count
    res = []
    lc = rc = 0
    while lc < len(left) and rc < len(right):
        if left[lc] <= right[rc]:
            res.append(left[lc])
            lc += 1
        else:
            res.append(right[rc])
            rc += 1
            # 统计逆序对个数
            inversion_count += len(left[lc:])
    res.extend(left[lc:])
    res.extend(right[rc:])
    return res

假设正在merge的数组里左右两边都已经排号序,那么这个数组的逆序对只可能是在数组左边取一个和右边取一个组成了。在两个数组merge的过程中,只要一出现左边数组大于右边的情况,就确定了一个逆序对,还可以确定的是在左边数组中该数的左边剩下的数都可以与右边那个数字组成逆序对。

总的逆序对的个数=左侧归并时求的逆序对个数+右侧归并时求得的逆序对个数 + 对整体进行归并时的逆序对个数。

可能会怀疑这三种情况会有重复,但是并没有。左侧归并找到的逆序对相当于从左侧数组中取2个数,而整体归并的时候是分别从左右数组中取1个数,不可能发生重复!

 

分治法求逆序对个数c的实现如下: 1. 将数组a分成两个子数组a1和a2,分别对a1和a2递归地求逆序对个数c1和c2,然后合并a1和a2并计算跨越a1和a2的逆序对个数c3。 2. 合并a1和a2时,设置两个指针i和j分别指向a1和a2的起始位置,然后将较小的元素放入新数组b中,并将指针向后移动,直到其中一个子数组遍历完毕。此时,将另一个子数组的剩余元素全部放入b中。 3. 计算跨越a1和a2的逆序对个数c3时,设置两个指针i和j分别指向a1和a2的末尾位置,并将它们向前移动,同时设置一个计数器count用于计算跨越a1和a2的逆序对个数。比较a1[i]和a2[j]的大小,如果a1[i]>a2[j],则说明a2[j]和a1[i+1]到a1[n-1]都构成了逆序对,因为a1[i+1]到a1[n-1]都比a2[j]大,此时将count加上i+1的值,并将a1[i]放入新数组b中,然后将指针i向前移动;否则,将a2[j]放入新数组b中,并将指针j向前移动。 4. 返回c1+c2+c3作为逆序对个数。 代码实现如下: ``` int mergeSort(vector<int>& a, int left, int right) { if (left >= right) return 0; int mid = (left + right) / 2; int c1 = mergeSort(a, left, mid); int c2 = mergeSort(a, mid + 1, right); vector<int> b(right - left + 1); int i = left, j = mid + 1, k = 0, c3 = 0; while (i <= mid && j <= right) { if (a[i] <= a[j]) b[k++] = a[i++]; else { b[k++] = a[j++]; c3 += mid - i + 1; } } while (i <= mid) b[k++] = a[i++]; while (j <= right) b[k++] = a[j++]; for (int i = left; i <= right; i++) a[i] = b[i - left]; return c1 + c2 + c3; } int countInversePairs(vector<int>& nums) { return mergeSort(nums, 0, nums.size() - 1); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值