在未排序的数组中找到多个子数组的中位数

5
假设您有一个未排序的整数数组S和一个范围列表T,返回每个范围的中位数列表。
例如,S = [3,6,1,5,0,0,1,-2],T = [[1,3],[0,5],[4,4]]。返回[5,2,0]。
除了在每个范围上运行Median of Medians之外,是否有更好的方法?我们可以预先计算/缓存结果吗?

阅读此链接:https://dev59.com/1oTba4cB1Zd3GeqP2CY9 - DPDP
2
什么是范围?当您使用范围[1,3]进行查询时,是指查找S [1],S [2],S [3]的中位数,还是S的前三个最小值的中位数? - Petar Petrovic
@PetarPetrovic 对于[1,3],它表示[6,1,5]的中位数。 - Pig
那么1如何成为[6,1,5]的中位数? - m69 ''snarky and unwelcoming''
@m69 我的错误,已更新。 - Pig
1个回答

5

让我向您介绍一个有趣的数据结构,称为Wavelet Tree

您可以通过查看整数的位串表示并递归地对其进行二分来构建它:

首先将整数分成具有最高有效位(MSB)0和具有MSB 1的两个子集。但是,您应该在位向量中以原始顺序存储MSBs。然后,对于这些整数的每个子集,您忽略MSB并递归地重复此构造以获取下一个最显著位。

如果您一直重复到最低有效位,您将得到以下树形结构(请注意,索引仅用于说明,您应该只存储位向量):

wavelet tree on 462725342471306

你可以轻松地看到,构建这个数据结构需要O(n log N)的时间,其中n是整数的数量,N是它们的最大值。
Wavelet树具有良好的特性,它们同时代表了原始序列和排序后的序列:
- 如果读取顶部的位向量,则可以获得输入序列的MSB。要重构条目的下一个位,可以在根节点的左子节点(如果MSB为0)或右子节点(如果MSB为1)中交替查找。对于以下位数,您可以继续递归。 - 如果从左到右读取叶节点,则可以得到排序后的序列。
要有效地使用Wavelet树,您需要对位向量执行两个基本操作:
  • rank1(k)告诉你在位向量中第k个位置之前有多少个1,rank0同样适用于0
  • select1(k)告诉你位向量中第k个1的索引,select0同样适用于0

请注意,有些位向量表示方法只需要额外存储o(n)(小o)比特即可在O(1)时间内实现这些操作

您可以按照以下方式利用它们:

  • 如果您正在查看上述序列中的第一个7,则其索引为3。现在,如果您想知道它在右子节点中的索引位置,只需在根比特向量上调用rank1(3),并获得2,这正是右子树中第一个7的索引。

  • 如果您在包含4544的子节点处,并想知道包含46754476的父节点中第二个4(索引为2)的位置,则可以在父节点的比特向量上调用 select0(2),并获取索引5。

现在,如何使用此实现 区间中位数查询?您需要进行的最重要的觉悟是,找到大小为k的范围的中位数等同于选择k/2个元素。

该算法的基本思想类似于快速选择(Quickselect):将元素范围一分为二,并仅递归到包含所寻找元素的范围。

假设我们想找到从第二个2(包括)开始到1(不包括)结束的范围的中位数。 这些是7个元素,因此中位数在该范围内排名为4(第四小的元素)。 现在,在根比特向量的开头和结尾使用rank0/1调用,我们可以在根节点的子节点中找到相应的范围:

WT: descent into children

正如您所看到的,左范围(仅包含较小的元素)仅有3个元素,因此排名为4的元素必须包含在根节点的右子节点中。现在我们可以在右子节点中递归搜索排名为4-3=1的元素。通过递归下降波束树直到达到叶子,您可以仅使用每个Wavelet树级别的两个排名操作(即O(1)时间)来识别中位数,因此整个范围中位数查询需要O(log N)时间,其中N是您输入序列中的最大数字。
如果您想查看这些Wavelet树的实际实现,请查看Succinct Data Structures Library (SDSL),该库实现了上述位向量和不同的WT变体。

我认为你应该提到这个数据结构需要大约o(|s|log(N))来构建。因此,在标记子集之后对序列进行排序没有任何优势。这可能会更快,因为在这种情况下,中位数查询只需要O(1)。 - Fabio Veronese
我忘了提到 |s| 是序列的长度。 - Fabio Veronese
好的,我会加上构建时间。您能详细说明一下您的O(1)解决方案吗?在这种情况下,小波树的主要优点是它们允许进行范围中位数查询,而不需要事先知道将查询哪些范围。您的解决方案是否也可以实现这一点? - Tobias Ribizel
我的错:O(1)是不可实现的。或者更准确地说,重构每个有序子数组是O(N),然后从中提取是O(1)。为了包含1.排序(整个序列)2.使用索引恢复子数组3.提取中位数(站在x/2处) 因此,整个过程将是O(|s|log|s|) + [O(|s|) + O(1)]·|subs| = O(|s|log|s|) + O(|s||subs|) 其中|subs|是子序列的数量。在您的情况下1.构建小波树2.提取 O(|s|logN) + [O(1)]·|subs| = O(|s|logN) + O(|subs|) - Fabio Veronese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接