Huffman树及其编码(STL array实现)

这篇随笔主要是Huffman编码,构建哈夫曼树有各种各样的实现方法,如优先队列,数组构成的树等,但本质都是堆。

这里我用数组来存储数据,以堆的思想来构建一个哈弗曼树,并存入vector中,进而实现哈夫曼编码

    

步骤:  1生成哈夫曼树  (取最小权值树和次小权值树生成新树,排列后重新取树,不断重复)

        2编码   (遵循左零右一的原则)

             3解码(是编码的逆向,本文还未实现,日后有机会补充)

 

data.txt  测试数据:

5
1 2 3 4 5
abcde

 

 

 

结果:

 

 

下面贴代码:

 

  1 #include <iostream>
  2 #include <fstream>
  3 #include <algorithm>
  4 #include <vector>
  5 #include <array>
  6 
  7 using namespace std;
  8 
  9 #define ARR_SIZE 100   //缓冲区大小
 10 
 11 typedef struct Tree
 12 {
 13     int freq;
 14     char key = '\0';
 15     Tree *left, *right;
 16     Tree()
 17     {
 18         freq = 0;
 19         key = '\0';
 20         left = NULL;
 21         right = NULL;
 22     }
 23 } Tree, *pTree;
 24 union key_or_point
 25 {
 26     char key;
 27     pTree point;
 28 };
 29 enum infor_type
 30 {
 31     key_s,
 32     point_s
 33 };
 34 class infor
 35 {
 36 public:
 37     int freq;//权值
 38     key_or_point kp;//记录键值或者 新生成的树的地址
 39     infor_type type;//  联合体key_or_point的类型由infor_type标志
 40     infor()
 41     {
 42         freq = 0;
 43         kp.key = NULL;
 44         type = key_s;
 45     }
 46 };
 47 
 48 array<infor, ARR_SIZE> arr;//用来读取要处理的数据
 49 vector<pTree> trees;  //所有生成的树都放在vector里面
 50 
 51 int num;   //要处理的数据个数
 52 
 53 bool cmp(infor a, infor b)
 54 {
 55     return a.freq > b.freq;
 56 }
 57 
 58 void Huffman()
 59 {
 60     //找出最小权值和次小权值
 61     sort(&arr[0], &arr[num], cmp);
 62     int cal = num - 1;
 63     while (cal > 0)
 64     {
 65 
 66         pTree pta = new Tree();
 67         vector<pTree>::iterator it;
 68 
 69         pTree ptl = new Tree();
 70         ptl->freq = arr[cal].freq;
 71         // pt all 的左子树
 72         if (arr[cal].type == point_s)
 73         {
 74             pta->left = arr[cal].kp.point;//如果存放的是地址,那么该树已入vector
 75             //无需重复操作
 76         }
 77         else
 78         {
 79             ptl->key = arr[cal].kp.key;
 80             trees.push_back(ptl);
 81             it = trees.end() - 1;
 82             pta->left = *it;
 83         }
 84 
 85 
 86         pTree ptr = new Tree();
 87         ptr->freq = arr[cal - 1].freq;
 88         // pt all 的右子树
 89         if (arr[cal - 1].type == point_s)
 90         {
 91             pta->right = arr[cal - 1].kp.point; //如果存放的是地址,那么该树已入vector
 92             //无需重复操作
 93         }
 94         else
 95         {
 96             ptr->key = arr[cal - 1].kp.key;
 97             trees.push_back(ptr);
 98             it = trees.end() - 1;
 99             pta->right = *it;
100         }
101 
102         pta->freq = arr[cal].freq + arr[cal - 1].freq;
103         trees.push_back(pta);//pt all 本树
104 
105         it = trees.end() - 1;
106         arr[cal - 1].kp.point = *it;
107         arr[cal - 1].type = point_s;//保存新生成树的地址
108 
109         arr[cal - 1].freq = arr[cal - 1].freq + arr[cal ].freq;
110         //最小权值的树和次权值的树组成新树后,放回原数组
111         //新树的key_or_point此时类型变为point_s指针指向vector存放的位置
112 
113         //第一次循环会有三棵树入vector,重新排列后,新树无需重复入vector
114         cal--;
115         sort(&arr[0], &arr[cal + 1], cmp);
116 
117     }
118 
119 }
120 
121 void traversTree(pTree pt, string st = "")
122 {
123     //中序遍历二叉树
124     //遵循左0右1的原则 
125     if (pt->left == NULL && pt->right == NULL)
126     {
127         cout.flags(ios::left);
128         cout.width(10);
129         cout << st.c_str() << "  ";
130         cout << pt->key << endl;
131         return;
132     }
133     if (pt->left != NULL)
134     {
135         st += '0';
136         traversTree(pt->left, st);
137         st.pop_back();//从左边出来后要回退一个字符,避免进入右边时多出一个字符
138     }
139 
140     if (pt->right != NULL)
141     {
142         st += '1';
143         traversTree(pt->right, st);
144     }
145     return ;
146 }
147 
148 void printCode()
149 {
150     vector<pTree>::iterator it;
151     it = trees.end() - 1;
152     pTree pt = *it; //取出最顶端的树
153     cout << "print HuffmanCode:" << endl;
154     traversTree(pt);
155 }
156 int main()
157 {
158     ifstream filein("data.txt");
159     cin.rdbuf(filein.rdbuf());//重定向输入
160     cin >> num;//要处理的数据个数
161     for (int i = 0; i < num; i++)
162     {
163         cin >> arr[i].freq;
164     }
165     for (int i = 0; i < num; i++)
166     {
167         cin >> arr[i].kp.key;
168     }
169     Huffman();
170     printCode();
171     return 0;
172 }

分析:

这是以上测试数据生成的树的情况。

只有叶子节点表示有效的符号,所以遍历树时返回条件是叶子节点(如果是叶子节点则返回)

总结:

1 编程时用的一些小技巧总结:

  1.1  输出调试信息:可以采用如下方式

      #ifdef DEBUG  

        cout调试信息....

      #endif 

  1.2 联合体union需要取得类型时,可以加一个enum来记录和标志uninon的类型

2  编程方法反思:

  可以看到源码中用到了两次sort,这是省事的做法了。

  目前想到的改进的方法是用二分插入(数据已经排序)

  

  对比起来,我觉得优先队列的方式更易懂且效率更高,但此文也算是一次小探索,值得记录下来

3 感想:

  本人入园第一次随笔,如有不足或错误,还望指出。

以上

posted @ 2019-02-03 10:00  virgil_devil  阅读(843)  评论(0编辑  收藏  举报