数组中的逆序对计数
算法分析
所谓逆序对,是指数组中的两个元素 A[i]A[i]A[i] 和 A[j]A[j]A[j],其下标 i<ji < ji<j,但是考察元素的值,却有 A[i]>A[j]A[i] > A[j]A[i]>A[j]。
输入:一个包含 nnn 个元素的数组 A[0..n−1]A[0..n − 1]A[0..n−1];
输出:数组中的逆序对的数目。
从最简单的实例入手:如果数组 A 只有两个元素 A[0] 和 A[1],我们只需比较这两个元素,即可计算出逆序数。
接下来我们考虑如何求解规模更大的实例。对于一个包含 nnn 个元素的数组 A,我们可以很容易地依据下标将 A 分解成两个小的数组,即左一半 A[0..⌈n2⌉−1]A[0..⌈ \frac{n}{2} ⌉ − 1]A[0..⌈2n⌉−1](简记为 LLL)和右一半 A[⌈n2⌉..n−1]A[⌈ \frac{n}{2} ⌉..n − 1]A[⌈2n⌉..n−1](简记为 RRR)。在将大的实例分解成子实例之后,我们可以假定子实例已经求解,即使用递归调用分别求出两个元素都在 LLL 中的逆序对数目、 以及两个元素都在 RRR 中的逆序对数目;因此只剩下最后一个困难:如何将子实例的 解“组合”成原始给定实例的解。
逆序对计数算法中计算一个元素在右半边,一个元素在右半边的逆序对数目:如果 LLL 和 RRR 都已经排好序的话,只需执行 O(n)O(n)O(n) 次比较即可完成逆序对的计数。
时间复杂度
T(n)=2T(n2)+O(n)=O(nlogn)T(n) = 2T(\frac{n}{2}) + O(n) = O(n \log n) T(n)=2T(2n)+O(n)=O(nlogn)
选择问题:对数组的归约
如何从数组中找出第 k 小的数
输入:一个包含 nnn 个元素的数组 A[0..n−1]A[0..n − 1]A[0..n−1],以及一个整数 k,(0≤k≤n−1)k,(0 ≤ k ≤ n − 1)k,(0≤k≤n−1);
输出:数组 AAA 中第 kkk 小的元素。
采用依据元素值拆分数组的方案:
选择分组中位数的中位数作为中心元
算法分析
方法:将数组AAA分组,然后以组中位数作为AAA的近似,进而以组中位数的中位数作为中心元
可以看到,组中位数的中位数在该数前面和后面均有一定量的值大于和小于他们(可以计算,如分组数为mmm,每组有nnn个数,为了方便计算,假设m、nm、nm、n都是奇数,那么比该数小的数至少有⌈n2⌉×⌈m2⌉−1\lceil \frac{n}{2} \rceil \times \lceil \frac{m}{2} \rceil - 1⌈2n⌉×⌈2m⌉−1,比组中位数的中位数大的数也有⌈n2⌉×⌈m2⌉−1\lceil \frac{n}{2} \rceil \times \lceil \frac{m}{2} \rceil - 1⌈2n⌉×⌈2m⌉−1个)
时间复杂度分析
算法总共需要比较的次数不超过24n24n24n次
T()≤T(n5)+T(7n10)+O(n)=O(n)T() \leq T(\frac{n}{5}) + T(\frac{7n}{10}) + O(n) = O(n) T()≤T(5n)+T(107n)+O(n)=O(n)
依据随机样本的统计量确定中间元
算法分析
构造数组的近似,一个直观的想法是对数组进行随机采样,以随机采样来近似数组,进而利用样本的统计量来确定中心元,比如采用样本中位数作为中心元,还进行一个扩展,不是只使用样本的中位数,而是使用14分位数和\frac{1}{4}分位数和41分位数和34\frac{3}{4}43分位数,将点估计扩展成区间估计。
假设随机采样的数量为rrr,采样rrr的元素得到的样本为SSS,之后计算出SSS的分位数uuu和vvv,接着对每个元素都和uuu和vvv比较,依据比较结果分别放入集合L,M,HL,M,HL,M,H,最后将MMM排序,返回其中位数作为数组的中位数估计:
时间复杂度分析
对于一个包含nnn个元素的数组AAA,当设置r=n34,δ=n−14r = n^{\frac{3}{4}},\delta = n^{-\frac{1}{4}}r=n43,δ=n−41,算法的期望时间复杂度是O(n)O(n)O(n),此时位于u,vu,vu,v区间的SSS中的元素共有n−14×rn^{-\frac{1}{4}} \times rn−41×r个,所以可以期望位于u,vu,vu,v区间的AAA中的元素共有n−14×n=n34n^{-\frac{1}{4}} \times n = n^{\frac{3}{4}}n−41×n=n43个,所以∣∣M∣∣||M||∣∣M∣∣不会太大也不会太小
采用随机选择的一个元素作中心元
算法分析
我们有12\frac{1}{2}21的概率选到中间区域的元素,所以连续迭代两轮,可以以很高的概率使得子问题的规模呈指数级降低
时间复杂度分析
可以看到将每两次算法执行作为一期
T(n)=T(3n4)+2n=O(n)T(n) = T(\frac{3n}{4}) + 2n = O(n) T(n)=T(43n)+2n=O(n)