计算机考研机试指南(六)——哈夫曼树

机试指南 cha3 哈夫曼

自己写了一版代码+数据结构书上的标准代码

  1 #include <iostream>
  2 #include <string.h>
  3 #include <stdio.h>
  4 #include <stdlib.h>
  5 
  6 
  7 using namespace std;
  8 /*
  9 1. 哈夫曼编码-顺序存储方式
 10 2. 优先队列存储——堆存储
 11 */
 12 
 13 void huffmanTree(int huffman[][5],int n)
 14 {
 15 
 16     for (int i=0;i<n;i++)
 17     {
 18         huffman[i][0] = i;
 19         cin >> huffman[i][1];
 20         huffman[i][2] = -1;
 21         huffman[i][3] = -1;
 22         huffman[i][4] = -1;
 23     }
 24     int num1 = 0,num2 = 0;
 25     float min1 = 9999,min2 = 9999;
 26 
 27     for (int i=0;i<n-1;i++)
 28     {
 29         num1 = 0;
 30         num2 = 0;
 31         min1 = 9999;
 32         min2 = 9999;
 33         for (int j=0;j<n+i;j++)
 34         {
 35             // 找到一组数中未被排序(没有父节点)的的最小值和次小值,最小值优先判断,判断完最小值后判断次小值
 36             if (huffman[j][2] == -1 && huffman[j][1] < min1 )
 37             {
 38                     if (min1 < min2)
 39                 {
 40                     min2 = min1;
 41                     num2 = num1;
 42                 }
 43                 min1 = huffman[j][1];
 44                 num1 = j;
 45             }
 46             else if (huffman[j][2] == -1 && huffman[j][1] < min2)
 47             {
 48                 min2 = huffman[j][1];
 49                 num2 = j;
 50             }
 51         }
 52         huffman[n+i][0] = n+i;
 53         huffman[n+i][1] = huffman[num1][1]+huffman[num2][1];
 54         huffman[n+i][2] = -1;
 55         huffman[n+i][3] = num1;
 56         huffman[n+i][4] = num2;
 57         huffman[num1][2] = n+i;
 58         huffman[num2][2] = n+i;
 59 
 60     }
 61 }
 62 void printTree(int huffman[][5],int n)
 63 {
 64     for (int i=0;i<2*n-1;i++)
 65     {
 66         for (int j=0;j<5;j++)
 67             cout << huffman[i][j] << ' ';
 68         cout << endl;
 69     }
 70 }
 71 void huffmanCode(int huffman[][5],int n)
 72 {
 73     // 自顶向下法:找到根到所有孩子的路径,每条路径均对应一个孩子
 74     // 自底向上法:遍历每个叶子结点,往上寻找直到找到根为止
 75     char code[20];
 76     int q,parent;
 77     int j = 0;
 78     for (int i=0;i<n;i++)
 79     {
 80         q = i;
 81         parent = huffman[q][2];
 82         j = 0;
 83         while (parent!=-1)
 84         {
 85             if (huffman[parent][3] == q)
 86             {
 87                 // q是parent的左孩子
 88                 code[j] = '0';
 89             }
 90             if (huffman[parent][4] == q)
 91             {
 92                 code[j] = '1';
 93             }
 94             j++;
 95             q = parent;
 96             parent = huffman[q][2];
 97         }
 98         code[j]  = '\0';
 99         cout << huffman[i][1] << " : " << code << endl;
100     }
101 }
102 char code[10];
103 void printPath(int huffman[][5],int n,int root,int level)
104 {
105     // 打印从根节点到所有叶子结点的路径
106     level ++;
107     if (huffman[root][3] != -1 && huffman[root][4]!=-1)
108     {
109         // 如果root不为根节点时
110         code[level] = '0';
111         printPath(huffman,n,huffman[root][3],level);
112         code[level] = '1';
113         printPath(huffman,n,huffman[root][4],level);
114     }
115     else {
116         code[level] = '\0';
117         for (int i=1;i<=level;i++)
118             cout << code[i];
119         cout << ' ' << huffman[root][1] <<endl;
120     }
121 
122 }
123 
124 // 课本上的代码方法,结构体数组封装一个结点而不是二维数组
125 typedef struct HuffmanNode
126 {
127     int weight;
128     int parent,lchild,rchild;
129 }HuffmanNode,**HuffmanTree;
130 
131 typedef char ** HuffmanCode ; // 动态分配数组存储哈夫曼编码表
132 
133 
134 void select_2(HuffmanTree t,int n,int &s1,int &s2)
135 {
136     int min1 = 9999,min2 = 9999;
137     int num1=1,num2 = 1;
138     for (int i=1;i<=n;i++)
139     {
140         if (t[i]->weight < min1)
141         {
142             if ( min2 > min1 )
143             { // 在替换min1之前判断min1舍弃的值是不是比min2小,如果是的话min2就占着大便宜!
144                 min2 = min1;
145                 num2 = num1;
146             }
147             num1 = i;
148             min1 = t[i]->weight;
149         }else if (t[i]->weight)
150         {
151             min2 = t[i]->weight;
152             num2 = i;
153         }
154     }
155     s1 = num1;
156     s2 = num2;
157 }
158 void HuffmanCoding(HuffmanTree &t,HuffmanCode &c,int *w,int n)
159 {
160    if (n<=1)
161     return;
162 
163    int m = 2*n-1;
164    t = (HuffmanTree)malloc(sizeof(HuffmanNode)*(m+1)); // m+1是因为0号单元未用
165    for (int i=1;i<=n;i++)
166    {
167        t[i]->weight = w[i];
168        t[i]->parent = 0;
169        t[i]->lchild = 0;
170        t[i]->rchild = 0;
171    }
172    // 下面的循环可省略,因为遍历过程中一定会重新写一遍
173    for (int i=n+1;i<=m;i++)
174    {
175        t[i]->weight = 0 ;
176        t[i]->parent = 0 ;
177        t[i]->lchild = 0 ;
178        t[i]->rchild = 0 ;
179    }
180     int s1,s2;
181    // 建立哈夫曼树的循环
182    for (int i = n+1;i<=m;i++)
183    {
184        //找到1-i-1中的最小值和次小值
185        select_2(t,i-1,s1,s2);
186        t[s1]->parent = i;
187        t[s2]->parent = i;
188        t[i]->weight = t[s1]->weight+t[s2]->weight;
189        t[i]->lchild = s1;
190        t[i]->rchild = s2;
191    }
192 
193    // 计算哈弗曼编码值,从叶子到根逆向求
194    c = (char **)malloc(sizeof(char*)*(n+1));//n个字符的头指针向量,从1开始
195    char *cd = (char *)malloc(n*sizeof(char));// 哈弗曼树的高度不会超过n
196    cd[n-1] = '\0';
197    for (int i=1;i<=n;i++)
198    {
199        int start = n-1;//倒着存储编码
200        for (int c = i,f = t[i]->parent;f!=0;c=f,f = t[f]->parent)
201         if (t[f]->lchild == c)
202         cd[--start] = '0';
203        else
204         cd[--start] = '1';
205 
206        c[i] = (char *)malloc(sizeof(char)*(n-start));
207        strcpy(c[i],&cd[start]); // 复制编码
208        /*
209         char *p = (char *)malloc(sizeof(char)*10);
210     p = "helloworld";
211     cout << p << endl;
212     char *q = (char *)malloc(sizeof(char)*5);
213     strcpy(q,&p[5]);//两个参数都是地址,从起始地址到\0终止符之间的内容复制过去
214     cout << q;
215     */
216    }
217 
218    // // 无栈非递归遍历哈夫曼树,求哈夫曼编码
219 
220     c = (HuffmanCode)malloc((n+1)*sizeof(char *));
221     int p = m;
222     int cdlen = 0;
223     for (int i=1;i<=m;i++)
224         t[i]->weight = 0; // 遍历哈夫曼树时用作结点状态标志
225     while (p){
226         if (t[p]->weight == 0 )
227         {
228             t[p]->weight = 1;
229             if (t[p]->lchild!=0)
230             { // 左右孩子均不为0的状况
231                 p = t[p]->lchild;
232                 cd[cdlen++] = "0";
233             }
234             else if (t[p]->rchild == 0 )
235             {
236                 // 左右孩子均为0,P结点为叶节点,登记字符编码
237                 c[p] = (char *)malloc((cdlen+1)*sizeof(char));
238                 cd[cdlen] = '\0';
239                 strcpy(hc[p],cd);
240             }
241         }
242         else if (t[p]->weight == 1){
243             t[p]->weight = 2;
244             if (t[p]->rchild != 0)
245             {
246                 p = t[p]->rchild;
247                 cd[cdlen++] = '1';
248             }else 
249             {
250                 t[p]->weight = 0;
251                 p = t[p]->parent;
252                 --cdlen; //退回父节点,编码长度减一
253             }
254         }
255     }
256 }
257 
258 
259 int main()
260 {
261     int n;
262     cout << "n:" <<endl;
263     cin >> n;
264 //    int huffman[2*n-1][5];
265 //    huffmanTree(huffman,n);
266 //    printTree(huffman,n);
267 //    huffmanCode(huffman,n);
268 //    int level = 0;
269 //    printPath(huffman,n,2*n-2,level);
270     HuffmanTree t;
271     HuffmanCode c;
272     int *w;
273     for (int i=0;i<n;i++)
274         cin >> w[i];
275     HuffmanCoding(t,c,w,n);
276     return 0;
277 }

 

机试指南的题,优先队列哈夫曼树

优先队列的用法:
(1)priority_queue 默认设置大顶堆
(2)priority_queue

struct node  
{  
    friend bool operator< (node n1, node n2)  
    {  
        return n1.priority < n2.priority;  
    }  
    int priority;  
    int value;  
};

 

  1. 比较运算符外置

运算符重载:
用于对对象的运算符操作

box operator+(const box a,const box b) 
{ 
box c; 
c.weight = a.weight+b.weight; 
return c; 
}

 

友元函数:
1)C++中引入友元函数,是为在该类中提供一个对外(除了他自己意外)访问的窗口;
2)这个友元函数他不属于该类的成员函数,他是定义在类外的普通函数,只是在类中声明该函数可以直接访问类中的private或者protected成员。
3)友元函数可以访问类中的私有成员和其他数据,但是访问不可直接使用数据成员,需要通过对对象进行引用。

// 输出哈夫曼树树顶元素

 1 int main()
 2 {
 3     int n;
 4     priority_queue<int,vector<int>,greater<int> > q;
 5     while (cin >> n)
 6     {
 7         while (!q.empty())
 8             q.pop(); // 清空小顶堆元素
 9 
10         for (int i=1;i<=n;i++)
11         {
12             int d;
13             cin >> d;
14             q.push(d);
15         }
16         int ans = 0;
17         while (q.size() > 1 )
18         {
19             int a = q.top();
20             q.pop();
21             int b = q.top();
22             q.pop();
23             q.push(a+b);
24 
25         }
26         ans = q.top();
27         cout << ans << endl;
28     }
29 
30     return 0;
31 }

 

搬水果 改版上面的代码,只需要把ans加起来

 1 #include <iostream>
 2 #include <string.h>
 3 #include <stdio.h>
 4 #include <stdlib.h>
 5 #include <queue>
 6 #include <vector>
 7 
 8 using namespace std;
 9 /*
10 搬水果
11 */
12 
13 
14 int main()
15 {
16     int n;
17     priority_queue<int,vector<int>,greater<int> > q;
18     while (cin >> n)
19     {
20         while (!q.empty())
21             q.pop(); // 清空小顶堆元素
22 
23         for (int i=1;i<=n;i++)
24         {
25             int d;
26             cin >> d;
27             q.push(d);
28         }
29         int ans = 0;
30         int sum = 0;
31         while (q.size() > 1 )
32         {
33             int a = q.top();
34             q.pop();
35             int b = q.top();
36             q.pop();
37             ans = a+b;
38             q.push(ans);
39             sum += ans;
40 
41         }
42         cout << sum << endl;
43     }
44 
45 
46     return 0;
47 }

 

posted @ 2018-08-21 09:42  暴走的二萌  阅读(661)  评论(0编辑  收藏  举报