相似解法题目多元组
- 归并排序:< BM20 数组中的逆序对,BM5 合并 k
个已排序的链表,BM12 单链表的排序 >
BM20 数组中的逆序对
描述
在数组中的两个数字,如果前面一个数字大于后面的数字,则这两个数字组成一个逆序对。输入一个数组,求出这个数组中的逆序对的总数P。并将
P 对 1000000007 取模的结果输出。 即输出 P mod 1000000007。
题目保证输入的数组中没有的相同的数字。
思路
归并排序
在归并排序过程中会将数组换分成最小为 1
个元素的子数组,然后依次比较子数组的每个元素的大小,依次取出较小的一个合并成大的子数组。
主要流程:取中间 -> 左右划分 -> 合并
我们可以用相同的方法划分,划分之后相邻一个元素的子数组可以根据大小统计逆序对。在不断往上合并的时候,因为已经排好序了,逆序对可以向上累计。
F1:归并排序
步骤
- 划分阶段:将待划分区间从中点划分成两部分,两部分进入递归继续划分,直到子数组长度为
1。
- 排序阶段:
- 使用归并排序递归地处理子序列,同时统计逆序对,因为在归并排序中,我们会依次比较相邻两组子数组各个元素的大小,并累计遇到的逆序情况。
- 对排好序的两组,右边大于左边时,它大于了左边的所有子序列,基于这个性质,我们可以不用每次加
1 来统计,减少运算次数。
- 合并阶段:将排好序的子序列合并,同时累加逆序对。
归并排序统计逆序对
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public static int mod = 1000000007; public int inversePairs(int[] array) { int n = array.length; int[] help = new int[n]; return mergeSort(0, n - 1, array, help); }
public int mergeSort(int left, int right, int[] data, int[] help) { if (left >= right) return 0; int mid = left + ((right - left) >> 1); int res = mergeSort(left, mid, data, help) + mergeSort(mid + 1, right, data, help); res %= mod; int i = left, j = mid + 1; for (int k = left; k <= right; k++) { help[k] = data[k]; } for (int k = left; k <= right; k++) { if (i == mid + 1) { data[k] = help[j]; j++; } else if (j == right + 1) { data[k] = help[i]; i++; } else if (help[i] <= help[j]) { data[k] = help[i]; i++; } else { data[k] = help[j]; j++; res += mid - i + 1; } } return res % mod; }
|
时间复杂度
\(O(nlog_2n)\)
归并排序利用分治思想,树形递归每次二分,总共能分为 \(log_2n\)
层,每层合并都需要遍历数组所有元素,即 \(O(n)\)。
空间复杂度
\(O(n)\)
辅助数组 help 长度为 n 及递归栈最大深度不会超过 n。