【问题标题】:Finding the medians of multiple subarrays in an unsorted array在未排序的数组中查找多个子数组的中位数
【发布时间】:2018-01-09 22:33:36
【问题描述】:

假设给定一个未排序的整数数组 S 和 T 中的范围列表,返回每个范围的中位数列表。

例如,S = [3,6,1,5,0,0,1,-2],T = [[1,3],[0,5],[4,4]]。返回 [5, 2, 0]。

有没有比在每个范围上运行 Median of Medians 更好的方法?我们可以以某种方式预先计算/缓存结果吗?

【问题讨论】:

  • 范围是什么意思?当您使用范围 [1, 3] 进行查询时,您的意思是找到 S[1]、S[2]、S[3] 的中位数还是 S 的第一个、第二个、第三个最小的中位数?
  • @PetarPetrovic 对于[1,3],表示[6,1,5]的中位数。
  • 那么[6,1,5]的中位数1是多少?
  • @m69 我的错误,已更新。

标签: algorithm data-structures


【解决方案1】:

让我给你介绍一个有趣的数据结构,叫做 Wavelet Tree:

您通过查看整数的位串表示并递归地二等分它们来构建它:

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

如果你重复这个直到最低有效位,你会得到一个像这样的树结构(注意索引只是为了说明,你应该只存储位向量):

你可以很容易地看到,这个数据结构的构建需要 O(n log N) 时间,其中 n 是整数的个数,N 是它们的最大值。

小波树具有很好的特性,即它们同时表示原始序列以及排序后的对应物:

  • 如果您读取最高位向量,您将获得输入序列的 MSB。要重建条目的下一位,您可以交替查看根的左孩子(如果 MSB 为 0)或右孩子(如果 MSB 为 1)中的位向量。对于后面的位,可以递归继续。

  • 如果从左到右读取叶节点,则得到排序后的序列。

要有效地使用小波树,您需要对位向量进行两个基本操作:

  • rank1(k) 告诉你在位向量中第 k 个位置之前有多少个 1,rank0 对 0 也是如此
  • select1(k) 告诉你位向量中第 k 个 1 的索引,select0 对 0s 做同样的事情

请注意,有些位向量表示只需要 o(n)(小 o)位的额外存储空间即可在 O(1) 中实现这些操作

您可以按如下方式使用它们:

  • 如果您查看上述序列中的前 7 个,则它的索引为 3。如果您现在想知道它在右子节点中的索引,只需调用 rank1(3) 在根位向量上得到 2,正好是右孩子中前 7 个的索引

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

现在如何用这个实现范围中值查询?你需要做的最重要的认识是找到一个大小范围k中值等同于选择 k/第 2 个 元素。

该算法的基本思想类似于快速选择:将元素范围一分为二并仅递归到包含您要查找的元素的范围内。

假设我们要找到从第二个 2(包括)开始到 1(不包括)结束的范围的中位数。 这些是 7 个元素,因此中位数在该范围内具有等级 4(第四小元素)。 现在使用 rank0/1 在这个范围的开始和结束的根位向量中调用,我们在根的孩子中找到相应的范围:

如您所见,左侧范围(仅包含较小的元素)只有 3 个元素,因此排名为 4 的元素必须包含在根的右孩子中。我们现在可以在该右孩子中递归搜索排名为4 - 3 = 1 的元素。通过递归地递减小波树直到到达叶子,因此您可以在小波树的每个级别仅使用两个秩操作(à O(1) 时间)来识别中值,因此整个范围中值查询需要 O(log N) N 是输入序列中的最大数的时间。

如果您想查看这些小波树的实际实现,请查看实现上述位向量和不同 WT 变体的Succinct Data Structures Library (SDSL)

【讨论】:

  • 我想你应该提到这个数据结构需要大约 o(|s|log(N)) 来构建。所以没有优势w.r.t。在标记子集后对序列进行排序。它可能会更快,因为在这种情况下,中值查询需要 O(1)
  • 我忘了说 |s|是序列的长度
  • 公平点,我将添加施工时间。你能详细说明你的 O(1) 解决方案吗?在这种情况下,小波树的主要优点是它们允许范围中值查询,而无需先验知识将查询哪些范围。你的解决方案也可以吗?
  • 我的错:O(1) 是无法实现的。或者 - 更准确地说 - 重建每个有序子数组是 O(N),然后从中提取是 O(1)。包含 1.sort(整个序列) 2.restore 具有索引的子数组 3.extract 中位数(站在 x/2 处)所以整个过程将是 O(|s|log|s|) + [O(| s|) +O(1)]·|子| = O(|s|log|s|) + O(|s||subs|) 其中 |subs|是子序列的数量。在你的情况下 1.build 小波树 2.extract O(|s|logN) + [O(1)]·|subs| = O(|s|logN) + O(|subs|)
猜你喜欢
  • 1970-01-01
  • 2021-11-20
  • 2019-12-12
  • 2016-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多