提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
题目
一道相当没有牌面的困难题。。。
第一时间想到动态规划,测了几个案例通过,心想:好嘛,爷困难题的一血要有了。
但是一提交,居然超时了。错误我都能理解,超时我确实无法理解,我第一次见到动态规划这个无往不利的都能超时,难道我的算法有问题?
估算了以下,大约(1 + 2 + 3 + … + (n - 1))= O(n^2),看了一下超时的数组,长度得有几千吧,难怪超时了。
二、归并排序法
看到题解中说要用归并排序,我迷茫了,为什么不用其他的排序,非要用归并呢?
归并的模板有点复杂,但是以前背过,大约分为三个函数来写,大概也知道要把计数放在哪里,开始。
归并排序的思路是:
递归地将子数组划分为左右两个部分,需要参数left,right,mid,辅助数组temp【存储递归过程中排序的结果】
- 主方法:原测试方法;
- “分”方法:递归算法,用于拆数组
- “合并”方法:将左右两边合并到一个数组中。【合并方法使用左右对照双指针,而本计数的核心也在这里,若存在一次左>右,将count += mid - left + 1【因为此时来了两边都已有序,右边的比左指针的右边都要小】】
int count = 0;
int temp[] = null;
int[] nums = null;
public int reversePairs(int[] nums) {
if (nums == null || nums.length < 2) return 0;
int len = nums.length;
this.nums = nums;
temp = new int[len];
mergeSort(0, len - 1);
return count;
}
public void mergeSort(int left, int right) {
if (left >= right) return;
int mid = (left + right) / 2;
mergeSort(left, mid);
mergeSort(mid + 1, right);
merge(left, mid, right);
}
public void merge(int left, int mid, int right) {
int lo = left;
int hi = mid + 1;
int index = left;
while(lo <= mid && hi <= right) {
if (nums[lo] > nums[hi]) {
count += mid - lo + 1; //比归并排序多的地方,也是解题的关键
temp[index++] = nums[hi++];
} else{
temp[index++] = nums[lo++];
}
}
while(lo <= mid) temp[index++] = nums[lo++];
while(hi <= right) temp[index++] = nums[hi++];
for (int i = left; i <= right; i++) {
nums[i] = temp[i];
}
}
public int reversePairs(int[] nums) {
if (nums == null || nums.length < 2) return 0;
int[] f = new int[nums.length];
f[0] = 0;
for (int i = 1; i < nums.length; i++) {
int count = 0;
for (int j = i - 1; j >= 0; j--) {
if (nums[j] > nums[i]) count++;
f[i] = f[i - 1] + count;
}
}
return f[nums.length - 1];
}
顺便贴一波之前的垃圾动态规划,我测了很大的数组也通过了,按理说应该没啥问题了。