算法导论习题---求n个元素任何排列中逆序对的数量

来源:互联网 发布:淘宝有哪些靠谱的韩代 编辑:程序博客网 时间:2024/05/12 11:01

问题描述:

设A[1…n]是一个包含n个不同数的数组。如果在i < j的情况下,有A[i] > A[j],则(i,j)就称为A中的一个逆序对(inversion),(逆序对的元素是下标,而不是数组里的值)。给出一个算法,它能用Θ(nlgn)的最坏情况运行时间,确定n个元素的任何排列中逆序对的数目。

问题求解:

方法一:
循环从数组中取出一个元素k,然后从k之后的元素中找到比k小的元素个数,最后统计所有的个数即为排列中逆序对的数目。从数组中取元素的次数为n,每次取出一个元素后,需要遍历(n-i)次(i为当前元素的位置),其时间复杂度为
这里写图片描述
算法实现:

int CountInversions(const vector<int>& a){    int cnt = 0;    for (size_t i = 0; i < a.size(); i++)    {        for (size_t j = i + 1; j < a.size(); j++)        {            if (a[i] > a[j]) cnt++;        }    }    return cnt;}

方法二:插入排序

利用插入排序的方式来解决。

对数组用插入排序来做升序排列时,如果有元素需要移动,则每次移动时都相当于是找到了一个逆序对,因此插入队列中移动元素的次数,也就是排列中逆序对的数量。

但插入排序的时间复杂度为Θ(n^2),不符合题目要求。

方法三:修改归并排序(时间复杂度O(lgn))

归并排序的基本思想就是 Divide & Conquer: 将数组划分为左右两部分,归并排序左部分,归并排序右部分,然后 Merge 左右两个数组为一个新的数组,从而完成排序。

按照这个基本思想,我们也可以运用到计算逆序对中来。假设将数组划分左右两部分:
(1)左边部分的逆序对有 inv1 个
(2)右边部分的逆序对有 inv2 个
(3)剩余的逆序对必然是一个数出现在左边部分,一个数出现在右边部分,并且满足出现左边部分的数 a[i] > a[j]。左右两部分排序与否,不会影响这种情况下的逆序对数,因为左右两部分的排序只是消除了两部分内部的逆序对,而对于 a[i] 来自左边, a[j] 来自右边构成的逆序,各自排序后还是逆序。
这里写图片描述

接下来就需要在 Merge 的过程中计算 a[i], a[j] 分别来自左右两部分的逆序对数。

如下图所示,如果所有 a[i] 都小于 b[j] 的第一个数,显然是没有逆序的。只有当 a[i] > b[j] 是才会发生逆序,由于我们事先对 a[] 和 b[] 已经排好序,而所以如果发生 a[i] > b[j], 那么所有 a[ii] (ii > i) 也都满足 a[ii] > b[j], 也就是说和 b[j] 构成逆序的数有 {a[i], a[i+1]…a[end]},所以逆序数增加 end- i + 1个, 所以我们每次碰到 a[i] > b[j] 的情况, 逆序对数增加 (end - i + 1) 个即可。
这里写图片描述

算法的框架图:

这里写图片描述

伪代码:

这里写图片描述

这里写图片描述
这里写图片描述

The initial call is COUNT-INVERSIONS(A,1,n)

参考:http://www.cnblogs.com/python27/p/4381953.html

0 0
原创粉丝点击