逆序对 - 树状数组求解 - 高级数据结构

树状数组是一种非常高级的数据结构,用于求解动态的区间和问题。静态的区间和可以用前缀和用O(n)的时间预处理,再以O(1)的时间单次求解。而树状数组,维护的就是动态的前缀和。也就是说,树状数组可以实现下面两个操作:
1、修改一个数,将一个位置上的数加上一个值
2、求一段数的和
这两个操作的时间复杂度都是O(log2n),而且代码写起来非常简洁,用到了一些二进制的相应思想,比如lowbit(低位技术,求一个数二进制表示中最后一个1以及后面的所有0)。可以说,树状数组几乎是最好的维护区间和的工具。
下面就来步入正题吧。用树状数组如何求解逆序对这一经典问题?我们知道,逆序对的定义是对于序列a,其中所有的不重复的i和j,满足a[i]>a[j]并且i
【预备】
首先将序列离散化,将这些数按照原来的大小次序离散成从1到n的整数。比如说[1,5345,23465,24,5666]就将被离散化成[1,3,4,2]。这是一件很简单的事情,只需要一个快速排序或基数排序即可。时间复杂度大概是O(nlog2n)或O(n)。
【树状数组 - 应用】
然后维护一个树状数组,从后往前遍历,比如说现在遍历到第i个元素,这个元素的相应离散化后的值为x,那么我们只需要在[i,n]的区间里找有多少个在区间[1,x-1]的离散化后的相应数字。相应的,因为我们将序列离散化了,所以最多的值不会超过n。所以我们用一种计数排序的思想,在第x个位置加一即可。然后我们如果要查找已经出现的[1,x-1]的数字的总数,那我们就可以直接求[1,x-1]区间的前缀和即可,一个操作是O(log2n),并将返回的结果加入到逆序对的个数总和之中。接着进行完这个操作之后,我们就可以将第x个位置的数加上1。最后,直接将逆序对的个数总和输出即可,这就是答案。
【总结】
其实用树状数组求逆序对的这个过程还是非常简单的,精髓其实就是那个计数排序!首先按大小离散化(离散化还有一个目的,那就是将整个树状数组的大小占用大大减少,不然的话如果输入是10^18的数,不离散化就绝对做不了了),接着用相应的计数排序思想求前缀和就可以了。

你可能感兴趣的:(数据结构)