数据结构--哈夫曼树-创建,带权路径长度

青旅半醒 2023-07-03 14:30 93阅读 0赞

数据结构–哈夫曼树-创建,带权路径长度

Project:哈夫曼树 构造 编码 译码 计算wpl
Date: 2020/02/04
Author: WX_timi

void CreateHT()创建哈夫曼树
void Code() 哈夫曼树编码
void Encode() 哈夫曼树解码
void WPL() 计算带权路径长度

  1. #include<cstdio>
  2. #include<cstdlib>
  3. #include<cstring>
  4. #include<cmath>
  5. #include<string>
  6. #include<set>
  7. #include<list>
  8. #include<vector>
  9. #include<map>
  10. #include<iterator>
  11. #include<algorithm>
  12. #include<iostream>
  13. #define MAX 1000 //哈夫曼树最大结点个数
  14. #define MAXW 1000 //权值最大
  15. using namespace std;
  16. //哈夫曼树结点结构
  17. typedef struct HNode
  18. {
  19. char data; //数据,非叶节点为NULL
  20. double weight;//权重
  21. int parent;//双亲,-1表示没有双亲,即根节点
  22. int lchild;//左孩子,数组下标,-1表示无左孩子,即叶节点
  23. int rchild;//右孩子
  24. } Hnode;
  25. //编码结构
  26. typedef struct HCNode
  27. {
  28. char data; //数据
  29. string code;//该字符编码
  30. };
  31. //两个最小结点下标
  32. typedef struct minnodes
  33. {
  34. int m1;//两者更小权值结点下标
  35. int m2;
  36. bool flag;//若找到则为true,否则为false,false说明仅有一个结点
  37. };
  38. //辅助标志数组 标记该结点为根的树是否已加入哈夫曼树
  39. bool flag[MAX] = {
  40. false };
  41. Hnode HT[MAX];//哈夫曼树
  42. HCNode HC[MAX];//哈夫曼编码数组
  43. //选择两棵最小权值的树 参数max,当前有权值结点下标+1
  44. minnodes Select(int max)
  45. {
  46. double min = MAXW;
  47. minnodes mins;
  48. mins.m2 = -1;
  49. //查找第一个最小权值的结点下标
  50. for (int i = 0; i < max; i++)
  51. {
  52. if (!flag[i] && HT[i].weight < min)//未加入哈夫曼树,权值更小
  53. {
  54. min = HT[i].weight;//更新最小权值
  55. mins.m1 = i;
  56. }
  57. }
  58. flag[mins.m1] = true;//将结点加入哈夫曼树
  59. min = MAXW;
  60. //查找第二个最小权值结点下标,可能不存在
  61. for (int i = 0; i < max; i++)
  62. {
  63. if (!flag[i] && HT[i].weight < min)//未加入哈夫曼树,权值更小
  64. {
  65. min = HT[i].weight;//更新最小权值
  66. mins.m2 = i;
  67. }
  68. }
  69. flag[mins.m2] = true;//将结点加入哈夫曼树
  70. if (-1 == mins.m2)//仅剩余一个结点未加入哈夫曼树
  71. {
  72. mins.flag = false;//未找到两棵最小权值树
  73. }
  74. else
  75. {
  76. mins.flag = true;
  77. }
  78. return mins;
  79. }
  80. //打印哈夫曼树
  81. void PrintHT(int max)
  82. {
  83. cout <<"下标\t"<< "数据\t" << "权重\t" << "双亲\t" << "左孩子\t" << "右孩子" << endl;
  84. for (int i = 0; i<max; i++)
  85. {
  86. cout <<i<< "\t"<<HT[i].data<<"\t"<< HT[i].weight << "\t" << HT[i].parent << "\t" << HT[i].lchild << "\t" << HT[i].rchild << endl;
  87. }
  88. }
  89. //打印编码
  90. void PrintHC(int n)
  91. {
  92. for (int i = 0; i < n; i++)
  93. {
  94. cout << HC[i].data << ":" << HC[i].code << endl;;
  95. }
  96. }
  97. //创建哈夫曼树
  98. void CreateHT()
  99. {
  100. int n;//字符个数,即哈夫曼树叶节点个数
  101. minnodes mins;
  102. cout << "请输入字符个数:" << endl;
  103. cin >> n;
  104. cout << "请输入字符及权值:" << endl;
  105. for (int i=0; i<n; i++)
  106. {
  107. cin >> HT[i].data >> HT[i].weight;
  108. HT[i].lchild = -1;
  109. HT[i].rchild = -1;
  110. }
  111. /*
  112. HT[0].data = 'a';HT[0].weight = 45;HT[0].lchild = -1;HT[0].rchild = -1;
  113. HT[1].data = 'b';HT[1].weight = 13;HT[1].lchild = -1;HT[1].rchild = -1;
  114. HT[2].data = 'c';HT[2].weight = 12;HT[2].lchild = -1;HT[2].rchild = -1;
  115. HT[3].data = 'd';HT[3].weight = 16;HT[3].lchild = -1;HT[3].rchild = -1;
  116. HT[4].data = 'e';HT[4].weight = 9; HT[4].lchild = -1;HT[4].rchild = -1;
  117. HT[5].data = 'f';HT[5].weight = 5; HT[5].lchild = -1;HT[5].rchild = -1;*/
  118. int i = n;
  119. for (;; i++)
  120. {
  121. mins = Select(i);//找到两棵根权值最小的树
  122. if (mins.flag == false)//仅剩余一棵树时跳出
  123. {
  124. HT[mins.m1].parent = -1;
  125. break;
  126. }
  127. HT[i].weight = HT[mins.m1].weight + HT[mins.m2].weight;//新加入哈夫曼树结点为两个结点权值之和
  128. HT[i].data = ' ';
  129. HT[mins.m1].parent = i; //两个权值最小结点双亲为新加入结点
  130. HT[mins.m2].parent = i;
  131. HT[i].lchild = mins.m1;//左小又大
  132. HT[i].rchild = mins.m2;
  133. }
  134. PrintHT(i);//打印哈夫曼树
  135. }
  136. //哈夫曼树编码
  137. void Code()
  138. {
  139. int i = 0;
  140. for (;; i++) //给所有叶子结点编码
  141. {
  142. int j = i;
  143. string str="";
  144. HC[i].data = HT[i].data;//复制数据
  145. while (-1!=HT[j].parent)//从叶节点找到根
  146. {
  147. if (HT[HT[j].parent].lchild == j)//左0右1
  148. {
  149. str += '0';
  150. }
  151. else
  152. {
  153. str += '1';
  154. }
  155. j = HT[j].parent;
  156. }
  157. reverse(str.begin(),str.end());//逆序
  158. HC[i].code = str; //保存至编码
  159. if (HT[i].lchild == -1 && HT[i].rchild == -1)continue;//非叶子不编码
  160. else break;
  161. }
  162. PrintHC(i);
  163. }
  164. //哈夫曼树解码 从根开始,左0右1,直至叶节点
  165. void Encode()
  166. {
  167. string s;
  168. int root=0;//记录根节点的下标
  169. cout << "请输入01字符串:" << endl;
  170. cin >> s;
  171. while (HT[root].parent != -1) root++;
  172. int j = root;
  173. for (int i=0; i<s.length(); i++) //遍历输入的01串
  174. {
  175. if ('0' == s[i])
  176. {
  177. j = HT[j].lchild;
  178. }
  179. else
  180. {
  181. j = HT[j].rchild;
  182. }
  183. if (HT[j].lchild == -1 && HT[j].rchild == -1)//到达叶节点
  184. {
  185. cout << HT[j].data;
  186. j = root;//返回根节点继续
  187. }
  188. }
  189. cout << endl;
  190. }
  191. //计算WPL
  192. void WPL()
  193. {
  194. double WPL=0;
  195. for (int i = 0;; i++)
  196. {
  197. if (HT[i].lchild != -1 || HT[i].rchild != -1)break;
  198. WPL += HT[i].weight*HC[i].code.length();//权值×路径长度(编码长度)
  199. }
  200. cout << "WPL:" << WPL << endl;
  201. }
  202. //菜单
  203. void menu()
  204. {
  205. cout << "************1.创建哈夫曼树 2.编码************" << endl;
  206. cout << "************3.解码 4.计算wpl*********" << endl;
  207. cout << "************5.退出" << endl;
  208. }
  209. //主函数
  210. int main()
  211. {
  212. int choice = 0;
  213. while (1)
  214. {
  215. menu();
  216. printf("请输入菜单序号:\n");
  217. scanf("%d", &choice);
  218. if (5 == choice) break;
  219. switch (choice)
  220. {
  221. case 1:
  222. CreateHT();
  223. break;
  224. case 2:
  225. Code();
  226. break;
  227. case 3:
  228. Encode();
  229. break;
  230. case 4:
  231. WPL();
  232. break;
  233. default:
  234. printf("输入错误!!!\n");
  235. break;
  236. }
  237. }
  238. return 0;
  239. }

发表评论

表情:
评论列表 (有 0 条评论,93人围观)

还没有评论,来说两句吧...

相关阅读

    相关 路径长度

    最近刷题刷到了这一题,此题是北邮往年复试题,看了一些网上的讲解,大多数是方法比较复杂,有些巧妙的方法又往往却缺少解释,为了方便大家理解,给小伙伴们梳理梳理 题目描述: