在Haskell中使用min-heap构建Huffman树

时间:2018-05-21 23:38:10

标签: haskell heap huffman-code

我遇到了很大的问题。我不知道如何制作霍夫曼树,因为它是自下而上建立的(从辟邪到根)。

我是Haskell和函数式编程的新手。我看到有其他类似于我的帖子,但他们没有帮助我。

这是我的代码

    import Data.Map

type Value = Int
type Key = [Char]
type NodeValue = (Key,Value)

data Heap_ a = Empty
        | Node a (Heap_ a) (Heap_ a)
        deriving(Show, Eq)

type Heap a = Heap_ NodeValue

frequencyOfCharacters :: [Char] -> Map Key Value
frequencyOfCharacters [] = Data.Map.empty
frequencyOfCharacters (character:text) = insertWith (+) [character] 1 (frequencyOfCharacters text)

makeLeaf :: NodeValue -> Heap a
makeLeaf a = Node a Empty Empty

mergeHeaps :: Heap a -> Heap a -> Heap a
mergeHeaps Empty rightHeap = rightHeap
mergeHeaps leftHeap Empty = leftHeap
mergeHeaps leftHeap@(Node a lefta righta) rightHeap@(Node b leftb rightb)
    | snd a < snd b = Node a (mergeHeaps lefta rightHeap) righta
    | otherwise = Node b leftb (mergeHeaps leftHeap rightb)

addToHeap :: Heap a->NodeValue->Heap a
addToHeap Empty a =  makeLeaf a
addToHeap h a = mergeHeaps h (makeLeaf a)


takeHeadFromHeap :: Heap a -> (NodeValue,Heap a)
takeHeadFromHeap Empty = (("",-1), Empty)
takeHeadFromHeap (Node a leftBranch rightBranch) = (a, mergeHeaps leftBranch rightBranch)

makeHeap :: Map Key Value -> Heap a
makeHeap map_ = makeHeap_ $ toList map_

makeHeap_ :: [(Key,Value)] -> Heap a
makeHeap_ [] = Empty
makeHeap_ (x:xs) = addToHeap (makeHeap_ xs) x


huffmanEntry :: [Char]-> Heap a
huffmanEntry text = makeHeap $ frequencyOfCharacters text

我正在考虑霍夫曼树的这个数据结构

data HuffmanTree h = Leaf [Char]
                   | NodeHuff [Char] (HuffmanTree h) (HuffmanTree h)
                   deriving(Show, Eq)

但我不知道如何从最小堆制作霍夫曼树。

在ghci min heap中的这行代码之后,由输入字符串

组成
 *Main> huffmanEntry "Aasdqweqweasd"

1 个答案:

答案 0 :(得分:0)

你需要制作一个带有最小堆的霍夫曼树,你说“我不知道如何从最小堆制作霍夫曼树”。在开始编码之前,让我们弄清楚你需要做什么,特别是在你可能不熟悉的语言中。

我想我们应该在网上查一下制作霍夫曼树的方法。关于霍夫曼编码的维基百科页面怎么样? (https://en.wikipedia.org/wiki/Huffman_coding

  

最简单的构造算法使用优先级队列   具有最低概率的节点被赋予最高优先级:

     
      
  1. 为每个符号创建一个叶节点,并将其添加到优先级队列。
  2.   
  3. 虽然队列中有多个节点:      
        
    • 从中删除最高优先级(最低概率)的两个节点   队列
    •   
    • 使用这两个节点创建一个新的内部节点   儿童,概率等于两个节点的总和   概率。
    •   
    • 将新节点添加到队列中。
    •   
  4.   
  5. 其余节点是根节点,树已完成。
  6.   

您已经有代码来查找给定字符串中每个符号的频率 - 这是您的frequencyOfCharacters函数。

现在你需要的只是一个优先级队列!您绝对可以找到使用最小堆实现优先级队列的方法。

我希望这可以帮助你将逻辑拼凑在一起。

如果您想逐步处理问题,为什么不首先尝试使用优先级队列(http://hackage.haskell.org/package/PSQueue)的工作实现来制作Huffman树?

完成后,您可以尝试使用自己的小队列模块替换这个现成模块,使用最小堆(http://hackage.haskell.org/package/heap)的工作实现。

最后,你可以自己编写一个准系统min heap模块(你已经有很多代码)并用它替换外部堆模块。

更新:关于如何构建树的一些更具体的建议。这需要一点设置,所以请耐心等待。假设您有一个Tree.hs模块,允许您使用二叉树:

module Tree where

-- Binary Tree
data Tree k v =
    Empty
  | Node (k, v) (Tree k v) (Tree k v)
    deriving ( Show )

-- takes a (key, value) pair and returns a binary tree
-- containing one node with that pair
singleton :: (k, v) -> Tree k v
singleton = undefined

-- takes three things: a (key, value) pair, a binary tree t1
-- and another binary tree t2
-- then it constructs the tree
--    (key, val)
--   /         \
-- t1           t2
joinWith :: (k, v) -> Tree k v -> Tree k v -> Tree k v
joinWith = undefined

-- returns the value associated with the (key, value) pair
-- stored in the root node of the binary tree
value :: Tree k v -> v
value = undefined

并且您还有一个Queue.hs模块,可以让您使用优先级队列(我假设您有一个可用的最小堆模块)

module Queue where

import Heap

-- a priority queue
type Queue k v = Heap k v

-- returns an empty queue
empty :: (Ord v)  => Queue k v
empty = undefined

-- adds a (key, value) pair to the queue and returns a
-- new copy of the queue containing the inserted pair
enqueue :: (Ord v) => (k, v) -> Queue k v -> Queue k v
enqueue = undefined

-- removes the lowest-value (key, value) pair from the queue
-- and returns a tuple consisting of the removed pair
-- and a copy of the queue with the pair removed
dequeue :: (Ord v) => Queue k v -> ((k, v), Queue k v)
dequeue = undefined

-- returns the number of elements in the queue
size :: (Ord v) => Queue k v -> Int
size = undefined

然后,您可以尝试使用您可以使用的工具制作Huffman.hs模块。

module Huffman where

import Queue
import Tree

type HuffmanTree = Tree Char Int

-- takes a list of (character, frequency) pairs and turns them into
-- a Huffman Tree
makeHuffmanTree :: [(Char, Int)] -> HuffmanTree
makeHuffmanTree pairs = let
  nodeList = map (\pair -> (singleton pair, snd pair)) pairs
  nodeQueue = foldr enqueue empty nodeList
    in
  reduceNodes nodeQueue

-- takes pairs of nodes from the queue and combines them
-- till only one node containing the full Huffman Tree is
-- present in the queue
-- then this last node is dequeued and returned
reduceNodes :: Queue HuffmanTree Int -> HuffmanTree
reduceNodes q
  | size q == 0 = error "no nodes!" 
  | size q == 1 = fst (fst (dequeue q))
  | otherwise   = let
      ((tree1, freq1), q') = dequeue q
      ((tree2, freq2), q'') = dequeue q'
      freqSum = freq1 + freq2
      newTree = joinWith ('.', freqSum) tree1 tree2
        in
      reduceNodes (enqueue (newTree, freqSum) q'')

由于类型检查,我成功编译了这些模块的堆栈项目。如果您认为自己拥有所需的霍夫曼树构建代码,那么您可以使用它们实际应该执行的操作填写undefined函数,并且您很好!