数组中逆序数对
2015-08-30
次访问
剑指offer面试
题目
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组的逆序对的总数。
思路
最初见到题目想到的思路应该不会太难:对于遍历到的每一个数字,都以此数字作为起点,与后面的数字作为比较,如果大于后面的数字,则逆序数加一。
于是这种算法的时间复杂度为:O(N^2)。
有没有更小的时间复杂度呢?
当时想到了以空间换取时间的思路,但是没有想到具体的实现方法的思路。看了剑指offer上面的提示,才知道:原来用一种排序方法可以做到:归并排序。
该排序有O(NlogN)的时间复杂度,但是由于在其中用到辅助数组,故空间复杂度为O(N).
关于归并排序的详细思路请参见:https://github.com/leexuehan/leexuehan.github.com/issues/4。
此处只说怎么把统计逆序数加进去。
就举一个简单的例子:数组 [9,5,2,7].
因为归并是一个不断分割再排序的过程,所以大致过程如下:
9 | 5 | 2 | 7
5 9 | 2 7
2 5 7 9
接下来需要将 2 与 5 和 9 相比较。
因为往辅助数组拷贝的时候,首先拷贝到其中的数字是 2,然后因为5 是第一组里面最小的数字,如果 2 比 5 还小的话,说明比后面的都小,所以此时逆序数的值就要加上 第一组的长度,在代码中就是 mid - i -1.
剩下的依次类推。
代码
package com.ans;
public class Solution4 {
private static int retVal = 0;
public static int merge(int start, int end, int[] arr, int[] tmp) {
if (start >= end)
return -1;
int mid = (start + end) / 2;
merge(start, mid, arr, tmp);
merge(mid + 1, end, arr, tmp);
retVal += Sort(start, mid, end, arr, tmp);
return retVal;
}
public static int Sort(int start, int mid, int end, int[] arr, int[] ret) {
if (start >= end)
return -1;
int revCount = 0;
int i = start;
int j = mid + 1;
int k = start;// 注意 K 的起始位置,不能为0
while (i <= mid && j <= end) {
if (arr[i] < arr[j]) {
ret[k++] = arr[i++];
} else {
revCount += mid - i + 1;
ret[k++] = arr[j++];
}
}
if (i > mid) {
while (j <= end) {
ret[k++] = arr[j++];
}
}
if (j > end) {
while (i <= mid) {
ret[k++] = arr[i++];
}
}
for (int n = 0; n <= end; n++) {
arr[n] = ret[n];
}
return revCount;
}
public static void main(String[] args) {
//int[] arr = { 3, 5, 9, 2, 1, 4, 7 };
int[] arr = {9, 2, 1, 4, 7 };
int[] tmp = new int[arr.length];
int ReverseCount = merge(0, arr.length - 1, arr, tmp);
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " ");
}
System.out.println();
System.out.println("逆序数为:" + ReverseCount);
}
}
总结
归并排序还是不太熟,但是比以前好多了,现在能手写差不多,继续努力。