Huffman树

Huffman编码树

ASCII码与莫尔斯电报码

在计算机中ASCII标准编码将每个字符表示为一个包含七个二进制位的序列,采用七个二进为能够区分27=128种情况,即128个可能不同的字符。一般而言,如果我们要区分n个字符,那么我们就要为每个字符使用log2n个二进制位。这种是定长编码,每一个字符都是用相同个数的二进制位表示的。然而莫尔斯电报码并不是这么做得,莫尔斯电报码对于出现很频繁的字符采用较短的编码,如字母E只用一个点来表示。使用变长的编码能够节省很多得存储空间。

变长编码需要解决的一个问题

虽然变长编码能够节省空间,但是有这样一个问题。在定长的编码中,比如编码长度是3,那么读三个01序列就可以去解码了。但是在变长编码中如何确定一个字符结束了呢?看看莫尔斯电报码怎么做得,莫尔斯电报码是在每个字符结束后用一个特殊的分隔符(间歇)。这对于计算机编码来讲显然不是一个好的方法。计算机编码采用这样的一种方式:设计一种编码的方式,使得每个字符的完整编码都不是另一个字符编码开始的一段,这样的编码叫做前缀码。

Huffman编码树

一个Huffman编码可以表示为一棵二叉树,其中树叶是被编码的符号。树上每个非叶节点代表一个集合,其中包含了这一个结点之下的所有树叶上得符号。因为只有叶节点表示了一个字符,所以不会出现一个字符的编码是另一个字符前缀的情况。除此之外每个字符被赋予了一个权重,权重值越大表示出现的频率越高非叶节点的权重是这个节点之下所有树叶的权重和。

编码过程

给定一个Huffman树,怎么编码呢?首先从树根开始走。看要编码的字符在左子树还是右子树,就是看要编码的字符在左子结点的符号集合中还是右子结点的。在哪边就往哪边走,每次往左走就编个0,往右走就编个1,这样一直遵循这个规律走一定会走到这个字符的叶节点。产生的01序列就是这个字符的编码。

生成Huffman编码树

生成Huffman树的算法实际十分简单,其想法就是设法安排这棵树,使得那些带有最低频度的符号出现在离树根最远的地方。这一构造过程从叶节点的集合开始,这种节点中包含各个符号和它们的频度,这就是开始构造编码的初始数据。现在要找出两个具有最低权重的叶,并归并它们,产生出一个以这两个结点为左右分支的结点。新结点的权重就是那两个结点的权重之和。现在我们从原来集合里删除前面两个叶结点,并用这一新结点代替它们。继续这一过程直到只剩下一个结点。这个结点就是树根。

解码过程

解码过程就更简单了。从树根开始,0往左走,1往右走。到了叶结点,这个叶结点的符号就是解出来的字符。然后继续从根开始解码。直至整个01序列都被解码成字符。

Scheme实现Huffman树

;构建形如(leaf E 1)的叶节点
(define (make-leaf symbol weight)
  (list 'leaf symbol weight))

;判断一个分支是不是叶结点,看它第一个元素是不是leaf
(define (leaf? object)
  (eq? (car object) 'leaf))

;拿到一个叶结点所代表的符号
(define (symbol-leaf x) (cadr x))

;拿到一个叶节点所代表的字符
(define (weight-leaf x) (caddr x))

;生成一个编码树 args(左分支 右分支 字符集合 新的权重值)
(define (make-code-tree left right)
  (list left
        right
        (append (symbols left) (symbols right))
        (+ (weight left) (weight right))))

;拿到编码树的左分支
(define (left-branch tree)
  (car tree))

;拿到编码树的右分支
(define (right-branch tree)
  (cadr tree))

;如果是树叶获取符号,如果是编码树获取符号集
(define (symbols tree)
  (if (leaf? tree)
      (list (symbol-leaf tree))
      (caddr tree)))

;如果是树叶拿到权重,如果是树那个树的权重(这个结点下面所有叶结点的权重和)
(define (weight tree)
  (if (leaf? tree)
      (weight-leaf tree)
      (cadddr tree)))
                                        ;
;编码过程
(define (decode bits tree)
  (define (decode-1 bits current-branch)
    (if (null? bits)
        '()
        (let ((next-branch
               (choose-branch (car bits) current-branch)))
          (if (leaf? next-branch)
              (cons (symbol-leaf next-branch)
                    (decode-1 (cdr bits) tree))
              (decode-1 (cdr bits) next-branch)))))
  (decode-1 bits tree))

;选择分支 0左 1右
(define (choose-branch bit branch)
  (cond ((= bit 0) (left-branch branch))
        ((= bit 1) (right-branch branch))
        (else (error "bad bit -- CHOOSE-BRANCH" bit))))

;将一个结点添加结点集合中,按照从小到大排列,简单的插入排序。
(define (adjoin-set x set)
  (cond ((null? set) (list x))
        ((< (weight x) (weight (car set))) (cons x set))
        (else (cons (car set)
                    (adjoin-set x (cdr set))))))

;把给定的字母权重序对集生成叶节点集
;即((E 1) (F 2)) -> ((leaf E 1) (leaf F 2))
(define (make-leaf-set pairs)
  (if (null? pairs)
      '()
      (let ((pair (car pairs)))
        (adjoin-set (make-leaf (car pair)
                               (cadr pair))
                    (make-leaf-set (cdr pairs))))))

;编码过程
(define (encode message tree)
  (if (null? message)
      '()
      (append (encode-symbol (car message) tree)
              (encode (cdr message) tree))))

;将单个字符编码
(define (encode-symbol symbol tree)
  (define (encode-symbol-1 current-branch bits)
    (if (leaf? current-branch)
        bits
        (let ((l-branch (left-branch current-branch))
              (r-branch (right-branch current-branch)))
          (cond ((contains (symbols l-branch) symbol)
                 (encode-symbol-1 l-branch
                                  (append bits (list 0))))
                ((contains (symbols r-branch) symbol)
                 (encode-symbol-1 r-branch
                                  (append bits (list 1))))
                (else (error "Bad Char --ENCODE-SYMBOL" symbol))))))
  (if (leaf? tree)
      (if (eq? symbol (symbol-leaf tree))
          (encode-symbol-1 tree (list 0))
          (error "Bad Char --ENCODE-SYMBOL" symbol))
      (encode-symbol-1 tree '())))

;确定字符集中是否包含指定字符
(define (contains set symbol)
  (cond ((null? set) #f)
        ((eq? (car set) symbol) #t)
        (else (contains (cdr set) symbol))))

;生成Huffman树
(define (generate-huffman-tree pairs)
  (successive-merge (make-leaf-set pairs)))

;合并分支的过程
;只剩一个结点就是生成的树了,拿出来
;大于等于两个结点进行归并
(define (successive-merge branches)
  (cond ((null? branches) '())
        ((= 1 (length branches))
         (car branches))
        (else (successive-merge (adjoin-set (make-code-tree (car branches)
                                                            (cadr branches))
                                            (cddr branches))))))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • 本文将通过 Huffman 编码树的构造问题,介绍优先队列结构的具体应用。 二进制编码 通讯系统可以帮助人们将一段...
    峰峰小阅读 1,034评论 0 0
  • 1. 定义 huffman编码是一种可变长编码方式,依据字符在需要编码文件中出现的概率提供对字符的唯一编码,并且保...
    安安zoe阅读 417评论 0 0
  • 最近学到Huffman编码,于是想要实现出来看看,查阅了一些资料,就开始动手了。首先整理一下思路,Huffman编...
    John_Tsemin阅读 1,352评论 0 0
  • B树的定义 一棵m阶的B树满足下列条件: 树中每个结点至多有m个孩子。 除根结点和叶子结点外,其它每个结点至少有m...
    文档随手记阅读 13,005评论 0 25
  • 你路过的风景也想让我看见 你那小小的彩虹,我欣然接受
    BULABULA小八阅读 134评论 0 1