设A[1..n]是一个包含N个非负整数的数组。如果在i〈 j的情况下,有A〉A[j],则(i,j)就称为A中的一个逆序对。
例如,数组(3,1,4,5,2)的“逆序对”有<3,1>,<3,2><4,2><5,2>,共4个。
使用归并排序可以用O(nlogn)的时间解决统计逆序对个数的问题
定义:对于一个给定的数列,如果有i<j,且Ai>Aj,则称(i,j)为一逆序对.
要解决的问题是,给出一个数列,求出这个数列包含多少个逆序对
今天在《算法导论》上看到了这题,提示用归并算法,没想明白。在google搜了一下,有一些实现但是没讲清楚,后来自己动手做了一下算是弄明白了。
普通实现 O(n^2):
template<class Iterator>
int countInversePair(Iterator first, Iterator last)
{
int count = 0;
Iterator it;
while(first != last)
{
it = first;
while(it != last)
{
if(*it < *first)
count++;
it++;
}
first++;
}
cout << count << endl;
return count;
}
归并实现 O(nlogn):
int gCount = 0;
template<class Iterator>
int merge(Iterator begin, Iterator mid, Iterator end)
{
Iterator iL = begin;
Iterator iR = mid;
int count = distance(begin, end);
vector<int> v(count);
vector<int>::iterator it = v.begin();
while(iL != mid && iR != end)
{
if(*iL <= * iR)
{
*it++ = *iL++;
}
else
{
gCount += distance(iL, mid);
*it++ = *iR++;
}
}
if(iL == mid) copy(iR, end, it);
if(iR == end) copy(iL, mid, it);
copy(v.begin(), v.end(), begin);
return 0;
}
template<class Iterator>
int mergeSort(Iterator begin, Iterator end)
{
int count, step;
count = distance(begin, end);
if(count <= 1)
{
return 0;
}
step = count / 2;
mergeSort(begin, begin + step);
mergeSort(begin + step, end);
merge(begin, begin + step, end);
return 0;
}
重点在 gCount += distance(iL, mid)
逆序对数实质就是插入排序过程中要移动元素的次数
归并的时候 前后两个序列都是有序的,可以把这个过程想象成插入排序,将第二个序列的内容插入到第一个有序的序列中
那么插入第二个序列中的元素时,要移动的元素的位数即第一个序列中还未插入到新序列中的元素的个数
即: distance(iL, mid)