如何删除堆树的最小元素?
此元素位于树的根部。如果我删除它,我将留下两个独立的子树。
data Heap a = Empty
| Node a (Heap a) (Heap a)
该功能的类型是:
removeMin :: Heap a -> (a, Heap a)
它应该返回树并删除最小值。
我应该创建一个辅助函数来构建一个新树,还是有更快的方法来做到这一点?
答案 0 :(得分:4)
你写的类型提出了一些问题:
问:removeMin Empty
的输出是什么?
答:您无法从零开始生成a
,因此结果应包含在Maybe
中。
问:如果我将(+)
,(-)
和(*)
放入Heap (Int -> Int -> Int)
,哪一个应由removeMin
归还?
答:并非所有数据类型都有排序(特别是函数缺少一个),因此要求数据类型具有Ord
实例是有意义的。
因此更新的类型变为:
removeMin :: Ord a => Heap a -> Maybe (a, Heap a)
现在考虑个案:
Empty
没有min元素:
removeMin Empty = Nothing
如果一个分支为空,则剩余的堆是另一个分支
removeMin (Node a Empty r) = Just (a, r)
removeMin (Node a l Empty) = Just (a, l)
说服自己,这适用于Node a Empty Empty
。
如果两个分支都不为空,则新的最小min元素必须是其中一个分支的根。
结果Heap
中的分支只是较大元素的分支,而较小元素的分支,最小元素的分支。
幸运的是,我们已经有了一个帮助,可以从Heap
中删除最小值!
removeMin (Node a l@(Node la _ _) r@(Node ra _ _)) = Just (a, Node mina maxN minN')
where (minN, maxN) = if la <= ra then (l,r) else (r,l)
Just (mina, minN') = removeMin minN
现在,虽然这会生成有效的堆,但它不一定是最好的算法,因为它不能保证生成平衡堆。一个不平衡的堆并不比链表更好,为您提供O(n)
插入和删除时间,平衡堆可以为您提供O(log n)
。
答案 1 :(得分:1)
您应该构建一个合适的函数来构建新树,但不要担心 - 它不会表现不佳。 GHC可以优化这样的用例,这个操作可以和你想要的一样快(包括大型,甚至无限(递归)数据结构)。
我知道你可以自己创造这样的辅助功能吗?它很简单 - 无论如何,如果遇到麻烦我可以稍后再写。
答案 2 :(得分:1)
以这种方式思考:删除顶部节点后,您将留下两个堆。所以你需要实现(递归)合并两个堆,比如
merge :: (Ord a) => Heap a -> Heap a -> Heap a
您还可以为Heap
instance (Ord a) => Monoid (Heap a) where
mempty = Empty
mappend = -- the merging function