逆序对问题

ACM 专栏收录该内容
151 篇文章 1 订阅

逆序对问题

逆序对问题。给一列数 a1,a2,,an ,求它的逆序对数,即有多少个有序对 (i,j) ,使得 i<j ai>aj n 可以高达106

由于 n 的数量级到了106,所以采用 O(n2) 及以上的时间复杂度肯定会超时,所以必须选取 O(nlog2n) 及以下时间复杂度的算法。

逆序对的求解思路和归并排序很像,尝试写出 分治三部曲
划分问题:将原序列分解成尽可能长度相等的两个子序列
递归过程:统计左子序列和右子序列的逆序对
合并问题:统计左右子序列合并后的逆序对

可以看出和归并排序的算法思想差不多,那么,它们之间的关系是什么呢?

每当左右子序列合并的时候,可以发现如果右子序列的元素 i 放入临时容器中,那么左子序列中当前元素j到末尾的元素全部都是大于 i <script id="MathJax-Element-2642" type="math/tex">i</script>的,由此就可以得到ans+= center - lef + 1这样的一个表达式。

参考程序

#include <iostream>

using namespace std;


// 求解逆序对
// 分治法
// 划分问题:把序列分成元素个数尽量相等的两半
// 递归求解:求解两边的逆序对
// 合并问题:合并成一个序列时的逆序对

int ans = 0;
void merges(int *a, int lef, int righ) {
    // 递归边界
    if(lef == righ) {
        return ;
    }

    // 取中值
    int center = lef + (righ - lef) / 2;

    // 递归左半
    merges(a, lef, center);
    // 递归右半
    merges(a, center + 1, righ);

    // 合并左半和右半
    // 该大小为左半右半大小之和
    int totalSize = righ - lef + 1;
    int tmp[totalSize];
    int n = lef;
    int m = center + 1;
    int i = 0;
    // 左半右半其一不为空
    while(n <= center || m <= righ) {
        // 右半为空 或者 左半不为空且左半值小于右半值
        if(m > righ || (n <= center && a[n] <= a[m])) {
            // 从左半数组复制到临时空间
            tmp[i++] = a[n++];
        } else {
            // 从右半数组复制到临时空间
            tmp[i++] = a[m++];
            ans += center - n + 1;
        }
    }

    n = lef;
    // 将临时数组的值放入原数组
    for(int i = 0; i < totalSize; i++) {
        a[n++] = tmp[i];
    }
}


void mergeSort(int *a, int n) {
    merges(a, 0, n - 1);
}

int main() {
    int a[] = {-1, 3, 3, 5, 5, 41, 5435, -11, 3423, 4432, -4421, 34432};
    int n = 12;
    ans = 0;
    mergeSort(a, n);
    cout << "逆序对为:" << ans << endl;
    return 0;
}

输出结果

逆序对为:19

Process returned 0 (0x0)   execution time : 0.131 s
Press any key to continue.
  • 1
    点赞
  • 2
    评论
  • 6
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值