逆数对问题:归并
Java
解题思路
有两种方法: 一个是使用暴力解法,直接搜索就行,但是这样会导致判题超时,用的时间比较多,是时间复杂度为O(n的平方)的算法。方法名:w1。 另外一个是使用归并排序来解决此问题,众所周知,归并排序是把整个数组分成若干个有序数组,再合并有序数组从而完成整个数组的排序的,而在归并排序合并有序数组的时候,我们很容易统计出两个有序数组之间的逆数对有多少个。例如:数组A:{2,3,4},数组B:{1,3,5},合并时比较第一个,A1>B1,所以B1放到新数组中,又因为数组A是有序地从小到大排序的,所以得知A2和A3也比B1大,因此得到逆数对3个,也就是数组A中未处理的元素个数,同学们可以依此继续推算后面的,所以我们每次合并B数组中的元素的时候累加A数组中未合并的元素个数,所有数组合并完之后得到的累加值就是这两个有序数组之中的逆数对了。同理这两个有序数组合并的出来的新的有序数组和另外的合并好的有序数组也可以得到我们想要的逆数对的个数。当所有合并动作完成后,就得到了整个数组的逆数对的个数了,同时也排好序了。方法名:w2。
看起来第二个方法似乎很复杂? 但我们稍加分析一下即可得知第二个算法的时间复杂度为O(nlogn),明显优于第一个算法。 在我的电脑上性能对比如下:
第二个对第一个简直就是降维打击,省下来的这点儿时间用来多看看数据结构不香嘛,哈哈哈
代码
题解:
class Solution {
private int w2(int[] nums) {
int s = 0;
int[] temp = new int[nums.length];
for (int size = 1; size < nums.length; size += size) {
int l = 0;
for (int i = l; i + size < nums.length; i = l) {
l = i + size + size;
if (l > nums.length) {
l = nums.length;
}
s += merge(nums, i, i + size, l, temp);
}
}
return s;
}
private int merge(int[] nums, int r, int m, int l, int[] temp) {
int s = 0;
for (int i = r; i < l; i++) {
temp[i] = nums[i];
}
int IR = r, IL = m;
for (int i = r; i < l; i++) {
if (IR == m) {
nums[i] = temp[IL];
IL++;
} else if (IL == l) {
nums[i] = temp[IR];
IR++;
} else if (temp[IR] <= temp[IL]) {
nums[i] = temp[IR];
IR++;
} else {
s += (m - IR);
nums[i] = temp[IL];
IL++;
}
}
return s;
}
private int w1(int[] nums) {
int result = 0;
for (int i = 0; i < nums.length; i++) {
for (int j = i; j < nums.length; j++) {
if (nums[i] > nums[j]) {
result++;
}
}
}
return result;
}
public int reversePairs(int[] nums) {
return w2(nums);
}
}
测试用例:
public static void main(String[] args) {
Solution solution = new Solution();
int[] o = new int[50000];
int[] o1 = new int[50000];
Random random = new Random();
for (int i = 0; i < 50000; i++) {
o[i] = random.nextInt(50000);
o1[i] = o[i];
}
System.out.println("-> opCount = "+o.length);
System.out.println("------merge------");
long startTime = System.nanoTime();
System.out.println(solution.w2(o));
long endTime = System.nanoTime();
System.out.println("time = " + (endTime - startTime) / 1000000000.0 + " s");
System.out.println("------normal------");
startTime = System.nanoTime();
System.out.println(solution.w1(o1));
endTime = System.nanoTime();
System.out.println("time = " + (endTime - startTime) / 1000000000.0 + " s");
}
|