在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数。
示例 1:
输入: [7,5,6,4]
输出: 5
这个题基础做法是遍历,然后对于每个数,往后找是不是有数小于他,这样是两层循环,O(n^2)。
但是可以用时间换空间。一个数组可以分为左右两部分,而其中逆序对的数量等于左边子数组中逆序对数量,加右边子数组逆序对的数量,再加上合并时新产生的逆序对。
在统计完子数组中的逆序对数量后,我们需要把子数组从小到大排序(为了能够统计出归并时新产生的逆序对),这个过程其实与归并排序很相似,就可以直接采用归并排序的框架。
合并时,两个指针分别指向左右子数组的末尾,目标指针指向备用空间的末尾。
如果左指针指向的值大于右指针指向的值,那么就会产生逆序对。因为已经对子数组排序,所以右边子数组所有剩下的数都会小于左指针指向的数,所以产生的逆序对数量就等于此时右边子数组还没有合并进去的长度。而如果左指针指向的值不大于右指针指向的值,那么就没有逆序对。
1 class Solution { 2 public: 3 int recursive_pairs(int *origin, int *copy, int start, int end) 4 { 5 if (end == start) 6 { 7 copy[end] = origin[start]; 8 return 0; 9 } 10 11 int len = (end - start) / 2; 12 int left_pairs = recursive_pairs(copy, origin, start, start + len); 13 int right_pairs = recursive_pairs(copy, origin, start + len + 1, end); 14 int pairs = left_pairs + right_pairs; 15 int copy_index = end; 16 int left_index = start + len, right_index = end; 17 while (left_index >= start && right_index >= start + len + 1) 18 { 19 if (origin[left_index] > origin[right_index]) 20 { 21 copy[copy_index] = origin[left_index]; 22 pairs += right_index - start - len ; 23 left_index--; 24 copy_index--; 25 } 26 else 27 { 28 copy[copy_index] = origin[right_index]; 29 right_index--; 30 copy_index--; 31 } 32 } 33 while (left_index >= start) 34 { 35 copy[copy_index--] = origin[left_index--]; 36 } 37 while (right_index >= start + len + 1) 38 { 39 copy[copy_index--] = origin[right_index--]; 40 } 41 return pairs; 42 } 43 44 int reversePairs(vector<int> &nums) 45 { 46 int n = nums.size(); 47 if (n <= 1) 48 return 0; 49 int *copy = new int[n], *origin = new int[n]; 50 for (int i = 0; i < n; i++){ 51 origin[i] = nums.at(i); 52 copy[i]=nums.at(i); 53 } 54 int ans=recursive_pairs(origin, copy, 0, n-1); 55 delete[] copy; 56 delete[] origin; 57 return ans; 58 } 59 };
这里一开始犯了几个错误:
copy数组初始也需要赋值。如果不赋值,可能会在递归到底部的时候,导致origin的数组被copy覆盖。
此外,目标数组的指针也要一开始指向末尾,因为我们是从大往小,从后往前合并。
一开始错误地将目标指针置于开头,导致访问了数组越界问题很严重。此外,这样导致最后在delete申请的origin和copy的时候会报错,这个具体原因还没有搞懂。