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)?
答案 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)中可以找到的范围内的最小值。
例如:如果我们有这个笛卡尔树: 并且我们想将插入值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