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!\n");
 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!\n");
 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!\n");
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 ."\n";
307 }

运行结果:

 

运行环境:

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

 

posted @ 2017-02-26 23:26  GoodByeZ  阅读(415)  评论(0编辑  收藏  举报