zoukankan      html  css  js  c++  java
  • PHP 哈夫曼的实现

      1 <?php
      2 namespace Test;
      3 
      4 use Iterator;
      5 use ArrayAccess;
      6 use Exception;
      7 
      8 // 叶子结点或者连接结点的基类
      9 class HuffmanBase
     10 {
     11     protected $weight;       // 权重
     12     protected $parent;
     13 
     14     public function setParent($parent)
     15     {
     16         $this->parent = $parent;
     17     }
     18 
     19     public function getWeight()
     20     {
     21         return $this->weight;
     22     }
     23 
     24     public function getParent()
     25     {
     26         return $this->parent;
     27     }
     28 }
     29 
     30 // 叶子结点
     31 class HuffmanLeafNode extends HuffmanBase
     32 {
     33     protected $code;   // 需要编码的字母
     34 
     35     public function __construct($weight,$code,$parent = null)
     36     {
     37         if(!is_int($weight) || $weight <= 0 || is_null($code))
     38         {
     39             throw new Exception('Param is error!' .__FILE__.__LINE__);
     40         }
     41         $this->weight = abs($weight);
     42         $this->code = $code;
     43         $this->parent = $parent;
     44     }
     45 
     46     public function getCode()
     47     {
     48         return $this->code;
     49     }
     50 }
     51 
     52 // 连接结点
     53 class HuffmanJoinNode extends HuffmanBase
     54 {
     55     protected $lChild;
     56     protected $rChild;
     57 
     58     public function __construct($weight = 0,$lChild = null,$rChild = null)
     59     {
     60         $this->weight = $weight;
     61         $this->rChild = $rChild;
     62         $this->lChild = $lChild;
     63     }
     64 
     65     public function setWeight($leftWeight,$rightWeight)
     66     {
     67         if(!is_int($this->rChild) || !is_int($this->lChild))
     68         {
     69             throw new Exception("Please initialize the left child or the right child!
    ");
     70         }
     71         $this->weight = $leftWeight + $rightWeight;
     72     }
     73 
     74     public function setChild($child,$leftOrRight)
     75     {
     76         if('left' == $leftOrRight)
     77         {
     78             $this->lChild = $child;
     79         }
     80         elseif('right' == $leftOrRight)
     81         {
     82             $this->rChild = $child;
     83         }
     84         else
     85         {
     86             throw new Exception("Please input 'left' or 'right' to leftOrRight!
    ");
     87         }
     88     }
     89 
     90     public function getChild($leftOrRight)
     91     {
     92         if('left' == $leftOrRight)
     93         {
     94             return $this->lChild;
     95         }
     96         elseif('right' == $leftOrRight)
     97         {
     98             return $this->rChild;
     99         }
    100         else
    101         {
    102             throw new Exception("Please input 'left' or 'right' to leftOrRight!
    ");
    103         }
    104     }
    105 }
    106 
    107 // 哈夫曼树
    108 class HuffmanTree implements ArrayAccess,Iterator
    109 {
    110     protected $nodes = array();
    111 
    112     public function &getAllNodes()
    113     {
    114         return $this->nodes;
    115     }
    116 
    117     public function offsetExists($offset)
    118     {
    119         // TODO: Implement offsetExists() method.
    120         return isset($this->nodes[$offset]);
    121     }
    122 
    123     public function offsetGet($offset)
    124     {
    125         // TODO: Implement offsetGet() method.
    126         if(isset($this->nodes[$offset]))
    127         {
    128             return $this->nodes[$offset];
    129         }
    130         else
    131         {
    132             return null;
    133         }
    134     }
    135 
    136     public function offsetSet($offset,$value)
    137     {
    138         // TODO: Implement offsetSet() method.
    139         if(!($value instanceof HuffmanBase))
    140         {
    141             throw new Exception('Param is error!' .__FILE__.__LINE__);
    142         }
    143 
    144         if(is_null($offset))
    145         {
    146             $this->nodes[] = $value;
    147         }
    148         else
    149         {
    150             $this->nodes[$offset] = $value;
    151         }
    152     }
    153 
    154     public function offsetUnset($offset)
    155     {
    156         // TODO: Implement offsetUnset() method.
    157         unset($this->nodes[$offset]);
    158     }
    159 
    160     public function current()
    161     {
    162         // TODO: Implement current() method.
    163         return current($this->nodes);
    164     }
    165 
    166     public function key()
    167     {
    168         // TODO: Implement key() method.
    169         return key($this->nodes);
    170     }
    171 
    172     public function next()
    173     {
    174         // TODO: Implement next() method.
    175         next($this->nodes);
    176     }
    177 
    178     public function rewind()
    179     {
    180         // TODO: Implement rewind() method.
    181         reset($this->nodes);
    182     }
    183 
    184     public function valid()
    185     {
    186         // TODO: Implement valid() method.
    187         return $this->offsetExists(key($this->nodes));
    188     }
    189 
    190     public function length()
    191     {
    192         return count($this->nodes);
    193     }
    194 }
    195 
    196 // 从[$left,$right]区间选择parent=0并且weight最小的两个结点,其序号分别为$minNode1,$minNode2;
    197 function selectTwoMinWeightNode(HuffmanTree &$huffmanTree,$left,$right,&$minNode1,&$minNode2)
    198 {
    199     $left = abs($left);
    200     $right = abs($right);
    201 
    202     if(!is_int($left) || !is_int($right) || $left == $right)
    203     {
    204         throw new Exception('Param is error!' .__FILE__.__LINE__);
    205     }
    206 
    207     if($left > $right)
    208     {
    209         $tmp = $left;
    210         $left = $right;
    211         $right = $tmp;
    212     }
    213 
    214     $nodes = $huffmanTree->getAllNodes();
    215     if(!isset($nodes[$right]))
    216     {
    217         throw new Exception('Over the array index!'.__FILE__.__LINE__);
    218     }
    219 
    220     $tmp = array();
    221     for($i = $left;$i <= $right; ++$i)
    222     {
    223         $huffmanNode = $huffmanTree[$i];
    224         if(!is_null($huffmanNode->getParent()))
    225         {
    226             continue;
    227         }
    228         $tmp[$i] = $huffmanNode->getWeight();
    229     }
    230 
    231     if(count($tmp) <= 1)
    232     {
    233         throw new Exception('Not enough number!'.__FILE__.__LINE__);
    234     }
    235     asort($tmp,SORT_NUMERIC);
    236     $t = array_keys($tmp);
    237     $minNode1 = $t[0];
    238     $minNode2 = $t[1];
    239 }
    240 
    241 // (编码 => 权重)
    242 $nodes = array('A' => 3, 'B' => 4, 'C' => 7, 'D' => 10);
    243 
    244 $huffmanTree = new HuffmanTree();
    245 
    246 // 初始化哈夫曼树的叶子结点
    247 foreach($nodes as $code => $weight)
    248 {
    249     $huffmanNode = new HuffmanLeafNode($weight,$code);
    250     $huffmanTree[] = $huffmanNode;
    251 }
    252 
    253 $leafCount = $huffmanTree->length();        // 叶子结点的数量(大于1的值)
    254 $nodeCount = 2 * $leafCount -1 ;           // 哈夫曼树结点的数量
    255 
    256 // 初始化哈夫曼树的非叶子结点(如果编译器未优化,--$i应该是比$i++效率高点的)
    257 for($i = $nodeCount - $leafCount;$i >= 1; --$i)
    258 {
    259     $huffmanNode = new HuffmanJoinNode();
    260     $huffmanTree[] = $huffmanNode;
    261 }
    262 
    263 // 建立哈夫曼树
    264 for($i = $leafCount;$i < $nodeCount; ++$i)
    265 {
    266     selectTwoMinWeightNode($huffmanTree,0,$i-1,$minNode1,$minNode2);
    267     $huffmanNode1 = $huffmanTree[$minNode1];
    268     $huffmanNode2 = $huffmanTree[$minNode2];
    269     $huffmanNode1->setParent($i);
    270     $huffmanNode2->setParent($i);
    271     $huffmanTree[$i]->setChild($minNode1,'left');
    272     $huffmanTree[$i]->setChild($minNode2,'right');
    273     $huffmanTree[$i]->setWeight($huffmanNode1->getWeight(),$huffmanNode2->getWeight());
    274 }
    275 
    276 // 从叶子到根的遍历,得到字母的编码
    277 $huffmanCode = array();
    278 for($i = 0;$i < $leafCount; ++$i)
    279 {
    280     $leafNode = $huffmanTree[$i];
    281     $code = $leafNode->getCode();
    282     $reverseCode = array();
    283     for($c = $i,$pi = $leafNode->getParent();!is_null($pi);$pi = $huffmanTree[$pi]->getParent())
    284     {
    285         $huffmanNode = $huffmanTree[$pi];
    286         if($huffmanNode->getChild('left') === $c)
    287         {
    288             $reverseCode[] = 0;
    289         }
    290         elseif($huffmanNode->getChild('right') === $c)
    291         {
    292             $reverseCode[] = 1;
    293         }
    294         else
    295         {
    296             throw new Exception('Something error happened!' .__FILE__.__LINE__);
    297         }
    298         $c = $pi;
    299     }
    300     $huffmanCode[$code] = array_reverse($reverseCode);
    301 }
    302 
    303 foreach($huffmanCode as $key => $value)
    304 {
    305     $s = implode(',',$value);
    306     echo $key. " : " .$s ."
    ";
    307 }

    运行结果:

    运行环境:

    ArrayAccess  :(PHP 5 >= 5.0.0, PHP 7)

    学习记录,方便复习
  • 相关阅读:
    调用GOOGLE的TTS实现文字转语音(XE7+小米2)(XE10.1+小米5)
    Android实例-设置消息提醒(XE8+小米2)
    FastReport二维码打印存在的问题
    一个DELPHI操作USB摄像头类
    利用Delphi编程控制摄像头(图)
    Delphi实现拍照控件的程序代码
    win dos命令行设置ip和dns
    daemontools管理fast-fail的zookeeper
    zookeeper定时清理log
    生成所有全排列 非递归和递归实现
  • 原文地址:https://www.cnblogs.com/jingjingdidunhe/p/6459489.html
Copyright © 2011-2022 走看看