从堆树中删除根元素

时间:2015-02-08 02:17:33

标签: haskell

如何删除堆树的最小元素?

此元素位于树的根部。如果我删除它,我将留下两个独立的子树。

data Heap a = Empty
            | Node a (Heap a) (Heap a)

该功能的类型是:

removeMin :: Heap a -> (a, Heap a)

它应该返回树并删除最小值。

我应该创建一个辅助函数来构建一个新树,还是有更快的方法来做到这一点?

3 个答案:

答案 0 :(得分:4)

你写的类型提出了一些问题:

  • 问:removeMin Empty的输出是什么?

    答:您无法从零开始生成a,因此结果应包含在Maybe中。

  • 问:如果我将(+)(-)(*)放入Heap (Int -> Int -> Int),哪一个应由removeMin归还?

    答:并非所有数据类型都有排序(特别是函数缺少一个),因此要求数据类型具有Ord实例是有意义的。

因此更新的类型变为:

removeMin :: Ord a => Heap a -> Maybe (a, Heap a)

现在考虑个案:

  1. Empty没有min元素:

    removeMin Empty = Nothing
    
  2. 如果一个分支为空,则剩余的堆是另一个分支

    removeMin (Node a Empty r) = Just (a, r)
    removeMin (Node a l Empty) = Just (a, l)
    

    说服自己,这适用于Node a Empty Empty

  3. 如果两个分支都不为空,则新的最小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
    
  4. 现在,虽然这会生成有效的堆,但它不一定是最好的算法,因为它不能保证生成平衡堆。一个不平衡的堆并不比链表更好,为您提供O(n)插入和删除时间,平衡堆可以为您提供O(log n)

答案 1 :(得分:1)

您应该构建一个合适的函数来构建新树,但不要担心 - 它不会表现不佳。 GHC可以优化这样的用例,这个操作可以和你想要的一样快(包括大型,甚至无限(递归)数据结构)。

我知道你可以自己创造这样的辅助功能吗?它很简单 - 无论如何,如果遇到麻烦我可以稍后再写。

答案 2 :(得分:1)

以这种方式思考:删除顶部节点后,您将留下两个堆。所以你需要实现(递归)合并两个堆,比如

merge :: (Ord a) => Heap a -> Heap a -> Heap a

您还可以为Heap

实现monoid实例
instance (Ord a) => Monoid (Heap a) where
    mempty = Empty
    mappend = -- the merging function