获取使用O(klogk)中的稀疏表排序的范围内的k个最小元素

时间:2018-12-25 19:59:47

标签: arrays algorithm data-structures

Sparse Table是一种数据结构,可让您回答O(1)查询时间和O(nlogn)准备时间和空间中的RMQ问题(可以改进为O(n)但常量更大)。

意味着对于给定的静态数组(元素不变),我们可以在恒定时间内找到任意两个索引之间的最小元素。

问题:

给出数组的稀疏表,我们要添加另一个查询以获取范围排序的k个最小元素(升序)。

Query input  : k, left index, right index
Query output : k smallest elements in range sorted

不使用稀疏表的解决方案:

我们可以使用Selection Algorithm来排序k个最小的元素,但是时间复杂度为O(L + klogk),其中L是范围的长度(k <= L <= n)。

但是我们如何使用稀疏表将查询的时间复杂度降低到O(klogk)而不是O(L + klogk)?

1 个答案:

答案 0 :(得分:0)

这个想法与如何从以O(klogk)排序的Cartesian Tree中获得k个最小的元素非常相似。

解决方案是使用min Binary Heap并插入树的根,然后k次,我们从堆中删除min元素并将其子元素插入树。

笛卡尔树问题的伪代码:

minValues(k, root):
  arr  = Array[0 . . . k - 1]
  heap = emptyMinHeap()

  heap.insert(root)
  for(i = 0 . . . k - 1):
      node <- heap.pop()
      arr[i] <- node.key

      if node has left son:  heap.insert(node.left)
      if node has right son: heap.insert(node.right)

  return arr

之所以起作用,是因为笛卡尔树中节点的子代不小于节点本身。因此,我们知道最小的节点位于根,因此这是K个最小的节点中的第一个。 现在,通过类似的论点,下一个最小的元素必须是该节点的两个子元素之一。 倒数第二个是根的另一个子节点,或者是我们刚取节点的两个子节点之一,依此类推……

那么我们如何使用它来解决问题?

我们可以说阵列中的任何特定范围为笛卡尔树,方法是说该范围中的最小值是根(我们可以使用稀疏表在O(1)中找到它),并且它的左孩子是根和最左索引之间的最小值(除非最小值位于最左索引中,在这种情况下它将为NULL),左子的右子元素是左子和根之间的最小值,并且我们可以像这样继续获取每个节点的子节点,而无需实际构建笛卡尔树。

因此,我们可以对笛卡尔树使用相同的算法,但不是插入笛卡尔树中的堆节点,而是插入节点子树的范围,而堆的键将是该范围的RMQ(在O(1)中可以找到的范围内的最小值。

例如:如果我们有这个笛卡尔树: Cartesian tree 并且我们想将插入值3的节点插入堆中,而不是插入范围:

0,2

如果要插入值为10的节点,请插入堆中:

5,9

如果要插入根,则将其插入堆:

0,10

依此类推...

由于找到节点的值(范围中的最小值)为O(1),因此该算法的复杂度将相同(O(klogk))。

伪代码:

//assuming our static array is called: St

K_MinValuesInRange(k, left, right, SparseTable):
  arr  = Array[0 . . . k - 1]

  /*key of node in the heap is determined
    by St[SparseTable.RMQ(node.left, node.right)]
    which takes O(1) to calculate*/
  heap = emptyMinHeap()

  heap.insert(left, right)
  for(i = 0 . . . k - 1):
      node <- heap.pop()
      min_pos <- SparseTable.RMQ(node.left, node.right)
      arr[i]  <- St[min_pos]

      /* if node has left son: insert left son*/
      if node.left < min_pos: heap.insert(node.left, min_pos - 1)

      /* if node has right son: insert right son*/
      if node.right > min_pos: heap.insert(min_pos + 1, node.right)

  return arr