给定反转的子阵列数

时间:2015-12-29 17:58:56

标签: algorithm

我最近在编程竞赛中未能解决以下问题 -

给定包含 n 值的数组ar[],您需要找到包含大于或等于 k 反转的子数组的数量。

数组中的反转次数定义为索引(i,j)的对数,以便

1 <= i < j <= n and ar[i] > ar[j].

预计O(nlogn)最严重的案件复杂性。

1 个答案:

答案 0 :(得分:2)

您可以在O(N log N)时间内执行此操作:

如果ar [i] ... ar [j]中至少存在K个反转,则将(i,j)定义为 minimal 子数组,并且在任何较短的子数组中至少有K个反转从i开始,即,如果你移除一个元素作为最小子数组的末尾,那么它将具有少于K的反转。

如果(i,j)是最小子阵列,则确切地存在N + 1-j个子阵列,其至少K个倒置从位置i开始,因为向末尾添加元素从不减少反转次数。此外,如果有任何从i开始的&gt; = K反转的子阵列,则确实存在从开始的一个最小子阵列。

所以我们所要做的就是在每个位置找到最小子阵列(如果存在),并将相应的子阵列总数加起来,如下所示(伪代码):

let total=0;
let i,j=1,1;
while(i<=N)
{
    while (j<i || inversions in ar[i]...ar[j] < K)
    {
        ++j;
        if (j>N)
        {
            return total; //out of subarrays
        }
    }
    // (i,j) is now a minimal subarray
    total+=N+1-j

    ++i; //next start position

    //now, IF (i,j) has enough inversions, then it is STILL
    //minimal, because otherwise the previous subarray would not have
    //been minimal either
}

为了满足我们的总复杂度目标,我们需要inversions in ar[i]...ar[j]检查每次迭代最多花费O(log N)。

我们可以通过将子阵列元素保存在平衡搜索树中来实现这一点,每个节点中的计数会记住该节点的子树的总大小。当J从1增加到N时,我们将向该树添加最多N个元素,总成本为O(N log N)。当我从1增加到N时,我们将从这棵树中删除最多N个元素,总成本为O(N log N)。

此外,当我们向树中添加元素时,子数组中的反转次数会增加大于我们添加的元素的数量,因为新元素位于子数组的末尾。当我们从子阵列的开头移除一个元素时,反转的数量会减少少于我们添加的元素的数量。这两个数字都需要O(log N)来确定,因此我们可以在O(N log N)时间内跟踪子阵列中的反转总量。