题目描述:在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。例如,在数组{7,5,6,4}中,一共存在5个逆序对,分别是(7,6)、(7,5)、(7,4)、(6,4)、(5,4)。
分析:可以用类似归并排序的思想,将数组二分,直到数组中只有一个元素时,此时数组逆序数组个数为0,然后开始合并数组,分别统计两个合并数组中逆序对的个数,这样自底向上地完成数组的排序及逆序对的统计,实际上是和归并排序是相同的方法。
具体地对于计算统计两个子数组的逆序对的个数,我们用两个指针分别指向两个子数组的末尾,并每次比较两个指针指向的数字,如果第一个子数组中的数字大于第二个子数组中的数字,则构成逆序对,并且逆序对的数目等于第二个子数组中剩余数字的个数。如果第一个数组中的数字小于或等于第二个数组中的数字,则不构成逆序对。每次比较,我们都把较大的数字从后往前复制到一个辅助数组,确保辅助数组中的数字是递增排列。
int InversePairs(int* data,int length){
if(data == nullptr || length < 0){
return 0;
}
int* copy = new int[length];
for(int i = 0;i < length;++i){ //用一个辅助数组存放排序后的数组元素
copy[i] = data[i]; //****归并排序需要将辅助数组元素merge回原数组完成排序*****//
}
int count = InversePairsCore(data,copy,0,length - 1);
//如果要保存排序后的数组可将data和copy参数交换位置:即InversePairCore(copy,data,0,length - 1);
delete[] copy;
return count;
}
int InversePairsCore(int* data,int* copy,int start,int end){
if(start == end){ //数组中只有一个元素,返回0
// copy[start] = data[start];
return 0;
}
int length = (end - start) / 2;
int left = InversePairsCore(copy,data,start,start + length);
//copy数组中存放已排序的子数组,接下来会对copy数组作合并和排序操作,
//操作的结果放在data数组中,作为下一次合并排序的copy数组(即两个数组,是互相备份的关系),
//***此操作也修改了原输入数组中的元素值***
int right = InversePairsCore(copy,data,start + length + 1,end);
//i初始化为前半段最后一个元素的一下标
int i = start + length;
//j初始化为后半段最后一个元素的一下标
int j = end;
int indexCopy = end; //辅助数组的下标元素从数组结尾开始
int count = 0;
while(i >= start && j >= start + length + 1){
if(data[i] > data[j]){
copy[indexCopy--] = data[i--];
count += j - start - length;
}
else{
copy[indexCopy--] = data[j--];
}
}
for(;i >= start;--i){
copy[indexCopy--] = data[i];
}
for(;j >= start + length + 1;--j){
copy[indexCopy--] = data[j];
}
return left + right + count;
}