冒泡排序(信助教的鬼话)
上机心路历程
- 啊冒泡排序。。。那我就模拟,每次记录交换次数,最后输出结果就好了,冒泡排序,直接打板子。
- 几分钟后。。。我去怎么还是TLE,我明明用了flag判断当没有发生交换时就退出循环,即使O(n2)也应该擦擦边过掉吧。
- 百思不得其解。。。上机前似乎看到比赛简介中写:归并排序。对了!!!冒泡排序交换的过程,其实就是消除逆序对的过程呀,而且想想似乎是一对一的关系,有多少个逆序对,就需要交换多少次,所以这题便可以由冒泡排序的模拟问题,转化成归并排序求逆序对数的问题了,O(nlogn),perfect!
解题思路
如上所说,将冒泡排序的模拟问题,转化成归并排序求逆序对数的问题,而这个问题在之前多次做到,可以称之为“板子题”了,与归并排序唯一不同的地方是,多了一个语句用以记录逆序对数。
cnt += len1 - lPos;
即某步归并过程中消除的逆序对数等于当左数组指针(一个整型类型的下标)所指数大于右数组指针所指数时,左数组长度与左数组指针的差值。
#include <stdio.h>
#include <stdlib.h>
#define infinate 2147483647
int a[1000000];
long long cnt;
void Merge(int left,int mid,int right){
int len1 = mid - left + 1;
int len2 = right - mid;
int i;
int leftArray[len1 + 1],rightArray[len2 + 1];
for(i = 0;i < len1;i++){
leftArray[i] = a[left + i];
}
for(i = 0;i < len2;i++){
rightArray[i] = a[mid + i + 1];
}
leftArray[len1] = rightArray[len2] = infinate;
int lPos = 0,rPos = 0;
for(i = left;i <= right;i++){
if(leftArray[lPos] <= rightArray[rPos]){
a[i] = leftArray[lPos++];
}
else{
a[i] = rightArray[rPos++];
cnt += len1 - lPos;//最关键的步骤,记录逆序数,因为左右各自有序,这次归并消除的逆序数可以用这个公式表示,除了这一步,其他所有都与归并排序算法一致
}
}
}
void mergeSort(int left,int right){
if(left < right){
int mid = (left + right) / 2;
mergeSort(left,mid);
mergeSort(mid + 1,right);
Merge(left,mid,right);
}
}
int main()
{
long long n;
scanf("%lld", &n);
cnt = 0;
int i;
for(i = 0;i < n;i++){
scanf("%d", &a[i]);
}
mergeSort(0,n - 1);
printf("%lld",cnt);
return 0;
}