Insertion Sort is a simple sorting technique which was covered in previous challenges. Sometimes, arrays may be too large for us to wait around for insertion sort to finish. Is there some other way we can calculate the number of times Insertion Sort shifts each elements when sorting an array?
If ki is the number of elements over which ith element of the array has to shift then total number of shift will be k1 + k2 + ... + kN.
Input:
The first line contains the number of test cases T. T test cases follow. The first line for each case contains N, the number of elements to be sorted. The next line contains N integers a[1],a[2]...,a[N].
Output:
Output T lines, containing the required answer for each test case.
Constraints:
1 <= T <= 5
1 <= N <= 100000
1 <= a[i] <= 1000000
好恶心的题,从早上做到现在才过,整个人昏昏沉沉的,还没从昨天20多个小时车程中恢复过来T_T
ok,来说说这道题,是要求insertion sort时数组中元素移动的次数。最naive的方法当然是直接进行一边insertion sort,输出移动次数了,这个复杂度是O(N),会超时。
其实,Insertion Sort中元素移动的次数和这个序列中逆序数的和是相等的。因为,在把元素a[i]插入数组的过程中,已经排好序的部分数组中比它大的元素都要后移。
所以我们的问题就简化为怎样快速的求出一个序列的逆序数。网上的方法很多:Binary Search Tree(当树为单支二叉树的时候算法复杂度退化到O(N),红黑树,归并排序等等。
这里采用归并排序的方法:在归并排序的归并这一步时候,由两个数组ar1和ar2,它们分别是原数组的前半部分和后半部分。每次取ar1和ar2数组头部最小的元素插入到排好序的数组中,当这个最小元素是从ar2中取出的时候,ar1中当前游标之后的元素都比这个最小元素大,而且在原数组中位于这个元素前面,所以此时一共输出m-point1对逆序数(m是ar1的长度,point1是当前ar1的游标)。这样我们就可以在归并排序的过程中统计出逆序数对了。
举个例子:
如上图所示,左边的图显示了整个归并过程中统计逆序数的过程。橙色的数字表示在每次归并中输出的逆序数对,最后把数出的所有逆序数对相加即可。
右边的图显示了具体合并(1,7)和(2,9)的过程,第二步要合并2的时候,发现在第一个数组中有个7比2大,那么此时(7,2)就是序列的一对逆序数。
最后一个细节是,当ar1和ar2的头部元素相等的时候,那么直接取ar1头部元素放入归并后的数组就可以了。
代码如下:
import java.util.*; public class Solution {
private static long answer = 0; private static int[] Merge(int[] ar1,int[] ar2){
int m = ar1.length;
int n = ar2.length; int point1 = 0;
int point2 = 0;
int index_result = 0;
int[] result = new int[m+n];
while(point1 < m && point2 < n){
if(ar1[point1] < ar2[point2]){
result[index_result] = ar1[point1];
point1++;
index_result++;
}
else if(ar1[point1] > ar2[point2]){
answer += m - point1;
result[index_result] = ar2[point2];
index_result++;
point2++;
}
else{
result[index_result] = ar1[point1];
index_result++;
point1++;
}
}
while(point1 < m){
result[index_result] = ar1[point1];
index_result++;
point1++;
}
while(point2 < n){
answer += m - point1;
result[index_result] = ar2[point2];
index_result++;
point2++;
}
return result;
}
private static int[] mergeSort(int[] ar){
int n = ar.length;
if(n <= 1)
return ar;
int mid = n/2;
int[] ar1 = new int[mid];
int[] ar2 = new int[n-mid];
System.arraycopy(ar, 0, ar1, 0, mid);
System.arraycopy(ar, mid, ar2, 0, n-mid);
int[] sorted_ar1 = mergeSort(ar1);
int[] sorted_ar2 = mergeSort(ar2);
int[] result = Merge(sorted_ar1, sorted_ar2);
return result;
}
public static void main(String[] args) { Scanner in = new Scanner(System.in);
int T = in.nextInt();
for(int k = 0;k < T;k++){
answer = 0;
int n = in.nextInt();
int[] ar = new int[n];
for(int i = 0;i < n;i++)
ar[i] = in.nextInt();
mergeSort(ar);
System.out.println(answer);
}
}
}