LeetCode 493. 翻转对
题目描述
✅ 493. 翻转对
思路分析
解法一:归并排序计数(推荐)
核心思路:
- 在归并排序的分治过程中,左右两段已经有序。
- 对每个左侧元素
nums[i],用指针j在右侧找到第一个不满足nums[i] > 2 * nums[j]的位置。j左侧的元素均与i构成翻转对,累加数量后再进行归并。复杂度分析:
- 时间复杂度:O(n log n),其中 n 表示数组长度。
- 空间复杂度:O(n),归并辅助数组。
class Solution {
public int reversePairs(int[] nums) {
if (nums == null || nums.length == 0) {
return 0;
}
return (int) mergeSort(nums, 0, nums.length - 1);
}
private long mergeSort(int[] nums, int left, int right) {
if (left >= right) {
return 0;
}
int mid = left + (right - left) / 2;
long count = 0;
count += mergeSort(nums, left, mid);
count += mergeSort(nums, mid + 1, right);
int j = mid + 1;
for (int i = left; i <= mid; i++) {
while (j <= right && (long) nums[i] > 2L * nums[j]) {
j++;
}
count += j - (mid + 1);
}
merge(nums, left, mid, right);
return count;
}
private void merge(int[] nums, int left, int mid, int right) {
int[] tmp = new int[right - left + 1];
int i = left;
int j = mid + 1;
int k = 0;
while (i <= mid && j <= right) {
if (nums[i] <= nums[j]) {
tmp[k++] = nums[i++];
} else {
tmp[k++] = nums[j++];
}
}
while (i <= mid) {
tmp[k++] = nums[i++];
}
while (j <= right) {
tmp[k++] = nums[j++];
}
System.arraycopy(tmp, 0, nums, left, tmp.length);
}
}
func reversePairs(nums []int) int {
if len(nums) == 0 {
return 0
}
return int(mergeSort(nums, 0, len(nums)-1))
}
func mergeSort(nums []int, left int, right int) int64 {
if left >= right {
return 0
}
mid := left + (right-left)/2
count := int64(0)
count += mergeSort(nums, left, mid)
count += mergeSort(nums, mid+1, right)
j := mid + 1
for i := left; i <= mid; i++ {
for j <= right && int64(nums[i]) > 2*int64(nums[j]) {
j++
}
count += int64(j - (mid + 1))
}
merge(nums, left, mid, right)
return count
}
func merge(nums []int, left int, mid int, right int) {
tmp := make([]int, right-left+1)
i, j, k := left, mid+1, 0
for i <= mid && j <= right {
if nums[i] <= nums[j] {
tmp[k] = nums[i]
i++
} else {
tmp[k] = nums[j]
j++
}
k++
}
for i <= mid {
tmp[k] = nums[i]
i++
k++
}
for j <= right {
tmp[k] = nums[j]
j++
k++
}
for p := 0; p < len(tmp); p++ {
nums[left+p] = tmp[p]
}
}
解法二:树状数组 + 离散化
核心思路:
- 翻转对条件为
nums[i] > 2 * nums[j]且i < j。- 从左到右遍历,把已遍历的数加入树状数组。
- 对当前数
x,查询已出现元素中<= 2x的数量,用已出现总数减去该值即为贡献。- 为避免溢出与稀疏值,先对
nums[i]与2 * nums[i]做离散化。复杂度分析:
- 时间复杂度:O(n log n),其中 n 表示数组长度。
- 空间复杂度:O(n),用于离散化数组与树状数组。
class Solution {
public int reversePairs(int[] nums) {
List<Long> values = new ArrayList<>();
for (int num : nums) {
values.add((long) num);
values.add(2L * num);
}
Collections.sort(values);
List<Long> uniq = new ArrayList<>();
for (long v : values) {
if (uniq.isEmpty() || uniq.get(uniq.size() - 1) != v) {
uniq.add(v);
}
}
Fenwick tree = new Fenwick(uniq.size());
long ans = 0;
long seen = 0;
for (int num : nums) {
long target = 2L * num;
int idx = lowerBound(uniq, target) + 1;
long leCount = tree.query(idx);
ans += seen - leCount;
int pos = lowerBound(uniq, (long) num) + 1;
tree.add(pos, 1);
seen++;
}
return (int) ans;
}
private int lowerBound(List<Long> list, long target) {
int l = 0;
int r = list.size();
while (l < r) {
int m = l + (r - l) / 2;
if (list.get(m) >= target) {
r = m;
} else {
l = m + 1;
}
}
return l;
}
static class Fenwick {
private final long[] tree;
Fenwick(int n) {
tree = new long[n + 1];
}
void add(int idx, long delta) {
for (int i = idx; i < tree.length; i += i & -i) {
tree[i] += delta;
}
}
long query(int idx) {
long sum = 0;
for (int i = idx; i > 0; i -= i & -i) {
sum += tree[i];
}
return sum;
}
}
}
import "sort"
func reversePairs(nums []int) int {
values := make([]int64, 0, len(nums)*2)
for _, num := range nums {
values = append(values, int64(num))
values = append(values, int64(num)*2)
}
sort.Slice(values, func(i, j int) bool {
return values[i] < values[j]
})
uniq := make([]int64, 0, len(values))
for _, v := range values {
if len(uniq) == 0 || uniq[len(uniq)-1] != v {
uniq = append(uniq, v)
}
}
bit := newFenwick(len(uniq))
ans := int64(0)
seen := int64(0)
for _, num := range nums {
target := int64(num) * 2
idx := lowerBound(uniq, target) + 1
leCount := bit.query(idx)
ans += seen - leCount
pos := lowerBound(uniq, int64(num)) + 1
bit.add(pos, 1)
seen++
}
return int(ans)
}
func lowerBound(arr []int64, target int64) int {
l, r := 0, len(arr)
for l < r {
m := l + (r-l)/2
if arr[m] >= target {
r = m
} else {
l = m + 1
}
}
return l
}
type fenwick struct {
tree []int64
}
func newFenwick(n int) *fenwick {
return &fenwick{tree: make([]int64, n+1)}
}
func (f *fenwick) add(idx int, delta int64) {
for idx < len(f.tree) {
f.tree[idx] += delta
idx += idx & -idx
}
}
func (f *fenwick) query(idx int) int64 {
sum := int64(0)
for idx > 0 {
sum += f.tree[idx]
idx -= idx & -idx
}
return sum
}
相似题目
| 题目 | 难度 | 考察点 |
|---|---|---|
| 315. 计算右侧小于当前元素的个数 | 困难 | 归并排序/树状数组 |
| 327. 区间和的个数 | 困难 | 归并排序 |
| 1649. 通过指令创建有序数组 | 困难 | 树状数组 |
| 775. 全局倒置与局部倒置 | 中等 | 逆序对 |
| 3159. 查询数组中元素的出现位置 | 中等 | 二分/离散化 |
本博客所有文章除特别声明外,均采用
CC BY-NC-SA 4.0
许可协议,转载请注明出处!