翻转对
难度:
标签:
题目描述
给定一个数组 nums
,如果 i < j
且 nums[i] > 2*nums[j]
我们就将 (i, j)
称作一个重要翻转对。
你需要返回给定数组中的重要翻转对的数量。
示例 1:
输入: [1,3,2,3,1] 输出: 2
示例 2:
输入: [2,4,3,5,1] 输出: 3
注意:
- 给定数组的长度不会超过
50000
。 - 输入数组中的所有数字都在32位整数的表示范围内。
代码结果
运行时间: 479 ms, 内存: 22.7 MB
/*
题目思路:
我们仍然需要找到数组中的所有重要翻转对(i, j),满足 i < j 且 nums[i] > 2 * nums[j]。
虽然Java Stream API不太适合进行复杂的索引操作和修改,但我们可以使用Stream API来简化某些部分。
此解决方案主要展示如何结合使用Stream API和传统循环。
*/
import java.util.stream.*;
public class SolutionStream {
public int reversePairs(int[] nums) {
if (nums == null || nums.length == 0) return 0;
return mergeSort(nums, 0, nums.length - 1);
}
private int mergeSort(int[] nums, int left, int right) {
if (left >= right) return 0;
int mid = left + (right - left) / 2;
int count = mergeSort(nums, left, mid) + mergeSort(nums, mid + 1, right);
count += (int) IntStream.range(left, mid + 1).boxed().mapToLong(i -> {
return IntStream.range(mid + 1, right + 1).filter(j -> nums[i] > 2L * nums[j]).count();
}).sum();
merge(nums, left, mid, right);
return count;
}
private void merge(int[] nums, int left, int mid, int right) {
int[] temp = new int[right - left + 1];
int i = left, j = mid + 1, k = 0;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) temp[k++] = nums[i++];
else temp[k++] = nums[j++];
}
while (i <= mid) temp[k++] = nums[i++];
while (j <= right) temp[k++] = nums[j++];
System.arraycopy(temp, 0, nums, left, temp.length);
}
}
解释
方法:
本题解采用了分治策略结合归并排序的方法来解决问题。首先,通过递归地将数组分解为更小的子数组,直到每个子数组只包含一个元素。然后,在归并排序的过程中,将这些子数组合并回去,并在合并的同时计算两个子数组之间可能的重要翻转对的数量。具体来说,在合并两个已排序的子数组时,对于左边数组的每个元素,查找右边数组中满足条件(左边元素大于右边元素的两倍)的元素的数量,并累加到总计数中。最后返回这个计数值。
时间复杂度:
O(n log n)
空间复杂度:
O(n)
代码细节讲解
🦆
这个算法为什么选择使用归并排序来处理这个问题?
▷🦆
在归并过程中,为什么要先对左数组的元素进行比较,而不是右数组的元素?
▷🦆
归并排序中的`merge_sort`函数递归地将数组分解,这种方式在空间复杂度上有什么影响?
▷相关问题
计算右侧小于当前元素的个数
给你一个整数数组 nums
,按要求返回一个新数组 counts
。数组 counts
有该性质: counts[i]
的值是 nums[i]
右侧小于 nums[i]
的元素的数量。
示例 1:
输入:nums = [5,2,6,1]
输出:[2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1)
2 的右侧仅有 1 个更小的元素 (1)
6 的右侧有 1 个更小的元素 (1)
1 的右侧有 0 个更小的元素
示例 2:
输入:nums = [-1] 输出:[0]
示例 3:
输入:nums = [-1,-1] 输出:[0,0]
提示:
1 <= nums.length <= 105
-104 <= nums[i] <= 104
区间和的个数
给你一个整数数组 nums
以及两个整数 lower
和 upper
。求数组中,值位于范围 [lower, upper]
(包含 lower
和 upper
)之内的 区间和的个数 。
区间和 S(i, j)
表示在 nums
中,位置从 i
到 j
的元素之和,包含 i
和 j
(i
≤ j
)。
示例 1:
输入:nums = [-2,5,-1], lower = -2, upper = 2 输出:3 解释:存在三个区间:[0,0]、[2,2] 和 [0,2] ,对应的区间和分别是:-2 、-1 、2 。
示例 2:
输入:nums = [0], lower = 0, upper = 0 输出:1
提示:
1 <= nums.length <= 105
-231 <= nums[i] <= 231 - 1
-105 <= lower <= upper <= 105
- 题目数据保证答案是一个 32 位 的整数