题目描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将P对1000000007取模的结果输出。 即输出P%1000000007
输入描述:
题目保证输入的数组中没有的相同的数字
数据范围:
对于%50的数据,size<=10^4
对于%75的数据,size<=10^5
对于%100的数据,size<=2*10^5
示例:
输入:
{1, 2, 3, 4, 5, 6, 7, 0}
返回值:
7
解题思路及代码
方法一:暴力列举
暴力列举所有的数对,然后判断是否逆序。
具体方法是:按住一个arr[i], 依次判断{i+1 ... n-1]是否满足条件。n为数组的大小。
代码如下:
class Solution {
private:const int kmod = 1000000007;
public:int InversePairs(vector<int> data) {int ret = 0;int n = data.size();for (int i = 0; i < n; ++i) {for (int j = i + 1; j < n; ++j) {if (data[i] > data[j]) {ret += 1;ret %= kmod;}}}return ret;}
};
对于10^5数据,O(N^2)算法显然超时。
时间复杂度:O(N^2)
空间复杂度:O(1)
方法二:归并排序思想
对数组一边进行归并排序,一边计算逆序对。
首先明确归并排序的过程:
- 递归划分整个区间为基本相等的左右两个区间
- 合并两个有序区间
归并排序的代码:
// 合并过程
void merge__(vector<int> &arr, int l, int mid, int r) {// 在这个地方创建额外空间,是一种不好的做法,更好的做法是:直接在最外层开辟一个足够大的数组,然后传引用到函数。vector<int> tmp(r - l + 1);int i = l, j = mid + 1, k = 0;while (i <= mid && j <= r) {if (arr[i] >= arr[j]) {tmp[k++] = arr[j++];}else {tmp[k++] = arr[i++];}}while (i <= mid) {tmp[k++] = arr[i++];}while (j <= r) {tmp[k++] = arr[j++];}for (k = 0, i = l; i <= r; ++i, ++k) {arr[i] = tmp[k];}
}// 递归划分过程
void merge_sort__(vector<int> &arr, int l, int r) {// 只有一个数字,则停止划分if (l >= r) {return;}int mid = l + ((r - l) >> 1);merge_sort__(arr, l, mid);merge_sort__(arr, mid + 1, r);// 合并两个有序区间merge__(arr, l, mid, r);
}
// 要排序的数组 arr
void merge_sort(vector<int>& arr) {merge_sort__(arr, 0, arr.size() - 1);
}
本题中如何利用归并排序的思想?
如果两个区间为[4, 3] 和[1, 2]
那么逆序数为(4,1),(4,2),(3,1),(3,2),同样的如果区间变为有序,比如[3,4] 和 [1,2]的结果是一样的,也就是说区间有序和无序结果是一样的。
但是如果区间有序会有什么好处吗?
当然,如果区间有序,比如[3,4] 和 [1,2]
如果3 > 1, 显然3后面的所有数都是大于1, 这里为 4 > 1, 明白其中的奥秘了吧。所以我们可以在合并的时候利用这个规则。
代码如下:
class Solution {
private:const int kmod = 1000000007;
public:int InversePairs(vector<int> data) {int ret = 0;// 在最外层开辟数组vector<int> tmp(data.size());merge_sort__(data, tmp, 0, data.size() - 1, ret);return ret;}void merge_sort__(vector<int> &arr, vector<int> &tmp, int l, int r, int &ret) {if (l >= r) {return;}int mid = l + ((r - l) >> 1);merge_sort__(arr, tmp, l, mid, ret);merge_sort__(arr, tmp, mid + 1, r, ret);merge__(arr, tmp, l, mid, r, ret);}void merge__(vector<int> &arr, vector<int> &tmp, int l, int mid, int r, int &ret) {int i = l, j = mid + 1, k = 0;while (i <= mid && j <= r) {if (arr[i] > arr[j]) {tmp[k++] = arr[j++];// 奥妙之处ret += (mid - i + 1);ret %= kmod;}else {tmp[k++] = arr[i++];}}while (i <= mid) {tmp[k++] = arr[i++];}while (j <= r) {tmp[k++] = arr[j++];}for (k = 0, i = l; i <= r; ++i, ++k) {arr[i] = tmp[k];}}};
Java 版:
private long cnt = 0;
private int[] tmp; // 在这里声明辅助数组,而不是在 merge() 递归函数中声明public int InversePairs(int[] nums) {tmp = new int[nums.length];mergeSort(nums, 0, nums.length - 1);return (int) (cnt % 1000000007);
}private void mergeSort(int[] nums, int l, int h) {if (h - l < 1)return;int m = l + (h - l) / 2;mergeSort(nums, l, m);mergeSort(nums, m + 1, h);merge(nums, l, m, h);
}private void merge(int[] nums, int l, int m, int h) {int i = l, j = m + 1, k = l;while (i <= m || j <= h) {if (i > m)tmp[k] = nums[j++];else if (j > h)tmp[k] = nums[i++];else if (nums[i] <= nums[j])tmp[k] = nums[i++];else {tmp[k] = nums[j++];this.cnt += m - i + 1; // nums[i] > nums[j],说明 nums[i...mid] 都大于 nums[j]}k++;}for (k = l; k <= h; k++)nums[k] = tmp[k];
}
时间复杂度:O(NlogN)
空间复杂度:O(N)
解析参考来自:
数组中的逆序对_牛客网www.nowcoder.com