逆序对问题:
给定一个数组,如:[8, 8, 3, 0, 6, 8, 9, 3],前大后小的数据即为一个逆序对,例如[8,3]、[3,0],求出这一数组中的所有逆序对。
思路:暴力法:设置双重循环,数量标志,比较两两之间的大小,符合逆序对标准则数量标志+1,复杂度O(n^2),本例中不考虑这个方法。
合并排序:合并排序是将一个数组均分,递归调用对子数组进行排序,然后将子数组合并,主要使用分治思想。在本题中,示例数组可分为:
[8,8,3,0]
[6,8,9,3]
分别排序之后为
A: [0,3,8,8]
B: [3,6,9,8]
设置i=0,j=0分别标志A、B数组,在合并的过程中,比较A[i]、B[j],若A[i]>B[j] 则将B[i]放入原数组,i++,同理将数据依次放入。
解决逆序对的最关键的就是合并这一步,假设A[i]>B[i]了,那么A[i]之后的所有数据,都是大于B[i]的,此时设置一个标识,用于记录数据,返回这个数据,递归,最后就可以得到所有逆序对了,这一方法的时间复杂度为O(nlogn),在数组数据非常多的时候可以明显感受到它的效率。
以下是它的实现代码,其实就是在合并排序的基础上,记录一下逆序对个数:
public static void DecTest(int a[]) {
// System.out.println(Arrays.toString(a));
System.out.println("逆序对个数:"+mergeSort(a));
}
private static int mergeSort(int a[]) {
int count = 0;
int n = a.length;
if (n <= 1) {
return 0;
}
int b[] = new int[n / 2];
System.arraycopy(a, 0, b, 0, n / 2);
int c[];
if (n % 2 == 0) {
c = new int[n / 2];
System.arraycopy(a, n / 2, c, 0, n / 2);
} else {
c = new int[n / 2 + 1];
System.arraycopy(a, n / 2, c, 0, n / 2 + 1);
}
count += mergeSort(b);
count += mergeSort(c);
count += merge(b, c, a);
return count;
}
private static int merge(int b[], int c[], int a[]) {
int count = 0; //标志
int i = 0, j = 0, k = 0;
int p = b.length, q = c.length;
while (i < p && j < q) {
if (b[i] <= c[j]) {
a[k] = b[i];
i++;
} else {
a[k] = c[j];
j++;
count += p - i; //记录逆序对个数
}
k++;
}
if (i == p) {
for (; j < q; j++, k++) {
a[k] = c[j];
}
} else if (j == q) {
for (; i < p; i++, k++) {
a[k] = b[i];
}
}
return count;
}
public static void main(String args[]) {
int a[];
int n = (int) (Math.random() * 1000000 + 1);
a = new int[n];
for (int i = 0; i < n; i++) {
a[i] = (int) (Math.random() * 10);
}
long t = System.currentTimeMillis();
DecTest(a);
System.out.print("耗时:");
System.out.println(System.currentTimeMillis() - t);
}
本例中测试了百万个数据,以下是运行结果:
百万个数据119mm运行完毕,速度是十分可观的。