leetcode
leetcode 2601 ~ 2650
交易逆序对的总数

交易逆序对的总数

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 1776 ms, 内存: 20 MB


/*
 * 思路:
 * 我们可以用Java Streams的组合方式来计算逆序对。
 * 首先,我们用IntStream.range来生成所有可能的(i, j)对。
 * 然后过滤出满足条件的对并计数。
 */
import java.util.stream.IntStream;

public class StockInversionPairsStream {
    public long countInversionPairs(int[] record) {
        return IntStream.range(0, record.length)
                .mapToLong(i -> IntStream.range(i + 1, record.length)
                        .filter(j -> record[i] > record[j])
                        .count())
                .sum();
    }
    public static void main(String[] args) {
        StockInversionPairsStream sips = new StockInversionPairsStream();
        int[] record = {9, 7, 5, 4, 6};
        System.out.println(sips.countInversionPairs(record)); // 输出:8
    }
}

解释

方法:

该题解使用了分治算法结合归并排序来寻找逆序对。首先,将数组分成左右两部分,递归地在每部分中找逆序对。在合并两部分的过程中,通过比较左右两部分的元素,统计跨越中点的逆序对的数量。如果左边元素大于右边元素,表明存在逆序对,并且逆序对的数量等于左边数组中从当前元素到中点的元素个数。

时间复杂度:

O(n log n)

空间复杂度:

O(n)

代码细节讲解

🦆
在归并排序中,为什么合并时如果右边的元素小于左边的元素,就可以确定存在多个逆序对?具体是如何计算这些逆序对的数量的?
在归并排序中,左右两部分各自已经是排序好的。当合并时,如果某个右侧元素小于左侧的元素,说明这个右侧元素小于左侧当前元素及所有该元素右侧的元素(因为左侧已排序)。因此,每当右侧的元素小于左侧的元素时,逆序对的数量就是左侧数组中从该元素到中点的元素个数,即`mid - i + 1`。这是因为左侧数组的这部分元素都大于右侧的当前元素。
🦆
归并排序中递归的基本条件是什么?在题解中提到的`if lo >= hi`是如何确保递归能正确结束的?
归并排序中递归的基本终止条件是子数组长度为1或0,即没有更多的分割可以进行。在题解中,`if lo >= hi`这个条件检查是为了确定子数组是否可以继续分割。如果`lo`大于等于`hi`,意味着子数组长度小于或等于1,因此不需要进一步分割或合并,确保了递归过程能够在正确的时刻结束。
🦆
题解中提到使用了一个辅助数组`aux`,这个数组的具体作用是什么?在算法的哪一部分被使用,并为何需要使用辅助数组?
在归并排序中,辅助数组`aux`用于暂存主数组的元素,以便在合并过程中可以正确地比较和重新排列元素。在`merge`函数中,首先将主数组`a`的元素复制到`aux`中,然后使用`aux`来比较元素并更新主数组`a`。使用辅助数组是为了避免在原数组上直接操作导致数据覆盖,确保在合并时原始元素顺序被保持,从而正确地合并两个有序子数组。
🦆
在题解的`merge`函数中,对于边界处理(如`if i > mid`和`elif j > hi`),它们是如何确保所有元素都被正确地合并回主数组的?
在`merge`函数中,`if i > mid`检查是否所有左侧部分的元素都已经被合并到主数组中,如果是,则剩下的元素都是右侧部分的,直接将这些元素复制回主数组。类似地,`elif j > hi`检查是否所有右侧部分的元素都已经被合并,如果是,则剩下的元素都是左侧部分的,也直接复制回主数组。这两个条件保证了不管合并过程中元素如何比较,所有元素最终都被正确地合并回主数组中,无论是从左侧还是右侧。

相关问题