我们有一个n节点二进制堆,它包含n
个不同的项(根目录下的最小项)。对于k<=n
,找一个O(klogk)
时间算法从堆中选择kth
个最小元素。
O(klogn)
显而易见,但无法找出O(klogk)
一个。也许我们可以使用第二堆,不确定。
答案 0 :(得分:25)
嗯,你的直觉是正确的,我们需要额外的数据结构来实现O(klogk),因为如果我们只是在原始堆上执行操作,那么术语logn将保留在最终的复杂性中。
从目标复杂度O(klogk)中猜测,我想创建并维护一个大小为k的堆来帮助我实现目标。您可能已经意识到,以自上而下的方式构建一个大小为k的堆需要O(klogk),这真的让我想起了我们的目标。
以下是尝试获得O(klogk)的尝试(不一定优雅或高效):
我们创建一个新的min heap,将其root初始化为原始堆的根。
我们通过删除当前根并在当前堆中插入当前根的两个子节点来更新新的最小堆。我们重复这个过程k次。
生成的堆将由k个节点组成,其根目录是原始堆中的第k个最小元素。
注意:新堆中的节点应该在原始堆中存储其相应节点的索引,而不是节点值本身。在步骤2的每次迭代中,我们实际上在新堆中添加了一个节点的网络(一个删除,两个插入),其中k次迭代将导致我们的新堆大小为k。在第i次迭代期间,要删除的节点是原始堆中的第i个最小元素。
时间复杂度:在每次迭代中,从一个元素中删除一个元素需要O(3logk)时间,并将两个元素插入到新堆中。在k次迭代之后,它是O(3klogk)= O(klogk)。
希望这个解决方案能够激发你的灵感。
答案 1 :(得分:22)
假设我们使用的是minheap,那么根节点总是小于其子节点。
Create a sorted list toVisit, which contains the nodes which we will traverse next. This is initially just the root node.
Create an array smallestNodes. Initially this is empty.
While length of smallestNodes < k:
Remove the smallest Node from toVisit
add that node to smallestNodes
add that node's children to toVisit
完成后,第k个最小节点位于smallestNodes [k-1]。
根据toVisit的实现,您可以在log(k)时间插入并在恒定时间内删除(因为您只删除了最顶层的节点)。这使得O(k * log(k))总计。