给定n个权值作为n个叶子结点,构造一棵二叉树,若带权路径长度达到最小,称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
构造
假设有n个权值,则构造出的哈夫曼树有n个叶子结点。 n个权值分别设为 w1、w2、…、wn,则哈夫曼树的构造规则为:
(1) 将w1、w2、…,wn看成是有n 棵树的森林(每棵树仅有一个结点);
(2) 在森林中选出两个根结点的权值最小的树合并,作为一棵新树的左、右子树,且新树的根结点权值为其左、右子树根结点权值之和;
(3)从森林中删除选取的两棵树,并将新树加入森林;
(4)重复(2)、(3)步,直到森林中只剩一棵树为止,该树即为所求得的哈夫曼树
题目描述
哈夫曼树,第一行输入一个数n,表示叶结点的个数。需要用这些叶结点生成哈夫曼树,根据哈夫曼树的概念,这些结点有权值,即weight,题目需要输出所有结点的值与权值的乘积之和。
输入描述:
输入有多组数据。 每组第一行输入一个数n,接着输入n个叶节点(叶节点权值不超过100,2<=n<=1000)。
输出描述:
输出权值。
示例1
输入
5 1 2 2 5 9
输出
37
解题思路:做这题一开始想得是每次选择两个数据相加后马上进行一次快速排序。
这个方法对小数据可以,但是对PAT那种变态大数据就运行超时了。
1 #include<stdio.h> 2 #include<stdlib.h> 3 4 int cmp( const void *a, const void *b) 5 { 6 return *(int *)a - *(int *)b; //从小到大排序 7 } 8 int num[1002]; 9 int main() 10 { 11 int n; 12 int ans; 13 int i; 14 while( scanf("%d",&n)!=EOF) 15 { 16 for( i=0; i<n; i++) 17 { 18 scanf("%d",&num[i]); 19 } 20 ans = 0; 21 for( i=0; i<n-1; i++) 22 { 23 //切记这里是i<n-1,最后一个数字就是答案 24 qsort( &num[i],n-i,sizeof(num[0]),cmp); 25 ans += num[i]+ num[i+1]; 26 num[i+1] = num[i]+ num[i+1]; 27 } 28 printf("%d\n",ans); 29 } 30 31 return 0; 32 33 }
上面的做法容易超时,下面使用C++利用优先队列构建小顶堆来实现哈夫曼树速度更快,道理都是一样的,换了个实现方法。
1 #include<stdio.h> 2 #include<queue> 3 using namespace std; 4 priority_queue<int, vector<int>,greater<int> > Q; //建立一个小顶堆 5 //priority_queue<int> Q; //默认建立一个大顶堆 6 7 int main() 8 { 9 int n; 10 int ans,temp1,temp2; 11 int i; 12 13 while( scanf("%d",&n)!=EOF) 14 { 15 while( Q.empty()==false) Q.pop(); //清除堆中元素 16 17 for( i=1; i<=n; i++) 18 { 19 scanf("%d",&temp1); 20 Q.push(temp1); 21 } 22 ans = 0; 23 while( Q.size()>1) //当堆中元素个数大于1 24 { 25 temp1 =Q.top(); 26 Q.pop(); 27 temp2 =Q.top(); 28 Q.pop(); //取出堆中两个最小的元素 29 ans += temp1+temp2; 30 Q.push( temp1+temp2); //将该双亲结点的权值放回堆中 31 } 32 printf("%d\n",ans); 33 } 34 return 0; 35 }