机试指南 cha3 哈夫曼
自己写了一版代码+数据结构书上的标准代码
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
/*
1. 哈夫曼编码-顺序存储方式
2. 优先队列存储——堆存储
*/
void huffmanTree(int huffman[][5],int n)
{
for (int i=0;i<n;i++)
{
huffman[i][0] = i;
cin >> huffman[i][1];
huffman[i][2] = -1;
huffman[i][3] = -1;
huffman[i][4] = -1;
}
int num1 = 0,num2 = 0;
float min1 = 9999,min2 = 9999;
for (int i=0;i<n-1;i++)
{
num1 = 0;
num2 = 0;
min1 = 9999;
min2 = 9999;
for (int j=0;j<n+i;j++)
{
// 找到一组数中未被排序(没有父节点)的的最小值和次小值,最小值优先判断,判断完最小值后判断次小值
if (huffman[j][2] == -1 && huffman[j][1] < min1 )
{
if (min1 < min2)
{
min2 = min1;
num2 = num1;
}
min1 = huffman[j][1];
num1 = j;
}
else if (huffman[j][2] == -1 && huffman[j][1] < min2)
{
min2 = huffman[j][1];
num2 = j;
}
}
huffman[n+i][0] = n+i;
huffman[n+i][1] = huffman[num1][1]+huffman[num2][1];
huffman[n+i][2] = -1;
huffman[n+i][3] = num1;
huffman[n+i][4] = num2;
huffman[num1][2] = n+i;
huffman[num2][2] = n+i;
}
}
void printTree(int huffman[][5],int n)
{
for (int i=0;i<2*n-1;i++)
{
for (int j=0;j<5;j++)
cout << huffman[i][j] << ' ';
cout << endl;
}
}
void huffmanCode(int huffman[][5],int n)
{
// 自顶向下法:找到根到所有孩子的路径,每条路径均对应一个孩子
// 自底向上法:遍历每个叶子结点,往上寻找直到找到根为止
char code[20];
int q,parent;
int j = 0;
for (int i=0;i<n;i++)
{
q = i;
parent = huffman[q][2];
j = 0;
while (parent!=-1)
{
if (huffman[parent][3] == q)
{
// q是parent的左孩子
code[j] = '0';
}
if (huffman[parent][4] == q)
{
code[j] = '1';
}
j++;
q = parent;
parent = huffman[q][2];
}
code[j] = '\0';
cout << huffman[i][1] << " : " << code << endl;
}
}
char code[10];
void printPath(int huffman[][5],int n,int root,int level)
{
// 打印从根节点到所有叶子结点的路径
level ++;
if (huffman[root][3] != -1 && huffman[root][4]!=-1)
{
// 如果root不为根节点时
code[level] = '0';
printPath(huffman,n,huffman[root][3],level);
code[level] = '1';
printPath(huffman,n,huffman[root][4],level);
}
else {
code[level] = '\0';
for (int i=1;i<=level;i++)
cout << code[i];
cout << ' ' << huffman[root][1] <<endl;
}
}
// 课本上的代码方法,结构体数组封装一个结点而不是二维数组
typedef struct HuffmanNode
{
int weight;
int parent,lchild,rchild;
}HuffmanNode,**HuffmanTree;
typedef char ** HuffmanCode ; // 动态分配数组存储哈夫曼编码表
void select_2(HuffmanTree t,int n,int &s1,int &s2)
{
int min1 = 9999,min2 = 9999;
int num1=1,num2 = 1;
for (int i=1;i<=n;i++)
{
if (t[i]->weight < min1)
{
if ( min2 > min1 )
{ // 在替换min1之前判断min1舍弃的值是不是比min2小,如果是的话min2就占着大便宜!
min2 = min1;
num2 = num1;
}
num1 = i;
min1 = t[i]->weight;
}else if (t[i]->weight)
{
min2 = t[i]->weight;
num2 = i;
}
}
s1 = num1;
s2 = num2;
}
void HuffmanCoding(HuffmanTree &t,HuffmanCode &c,int *w,int n)
{
if (n<=1)
return;
int m = 2*n-1;
t = (HuffmanTree)malloc(sizeof(HuffmanNode)*(m+1)); // m+1是因为0号单元未用
for (int i=1;i<=n;i++)
{
t[i]->weight = w[i];
t[i]->parent = 0;
t[i]->lchild = 0;
t[i]->rchild = 0;
}
// 下面的循环可省略,因为遍历过程中一定会重新写一遍
for (int i=n+1;i<=m;i++)
{
t[i]->weight = 0 ;
t[i]->parent = 0 ;
t[i]->lchild = 0 ;
t[i]->rchild = 0 ;
}
int s1,s2;
// 建立哈夫曼树的循环
for (int i = n+1;i<=m;i++)
{
//找到1-i-1中的最小值和次小值
select_2(t,i-1,s1,s2);
t[s1]->parent = i;
t[s2]->parent = i;
t[i]->weight = t[s1]->weight+t[s2]->weight;
t[i]->lchild = s1;
t[i]->rchild = s2;
}
// 计算哈弗曼编码值,从叶子到根逆向求
c = (char **)malloc(sizeof(char*)*(n+1));//n个字符的头指针向量,从1开始
char *cd = (char *)malloc(n*sizeof(char));// 哈弗曼树的高度不会超过n
cd[n-1] = '\0';
for (int i=1;i<=n;i++)
{
int start = n-1;//倒着存储编码
for (int c = i,f = t[i]->parent;f!=0;c=f,f = t[f]->parent)
if (t[f]->lchild == c)
cd[--start] = '0';
else
cd[--start] = '1';
c[i] = (char *)malloc(sizeof(char)*(n-start));
strcpy(c[i],&cd[start]); // 复制编码
/*
char *p = (char *)malloc(sizeof(char)*10);
p = "helloworld";
cout << p << endl;
char *q = (char *)malloc(sizeof(char)*5);
strcpy(q,&p[5]);//两个参数都是地址,从起始地址到\0终止符之间的内容复制过去
cout << q;
*/
}
// // 无栈非递归遍历哈夫曼树,求哈夫曼编码
c = (HuffmanCode)malloc((n+1)*sizeof(char *));
int p = m;
int cdlen = 0;
for (int i=1;i<=m;i++)
t[i]->weight = 0; // 遍历哈夫曼树时用作结点状态标志
while (p){
if (t[p]->weight == 0 )
{
t[p]->weight = 1;
if (t[p]->lchild!=0)
{ // 左右孩子均不为0的状况
p = t[p]->lchild;
cd[cdlen++] = "0";
}
else if (t[p]->rchild == 0 )
{
// 左右孩子均为0,P结点为叶节点,登记字符编码
c[p] = (char *)malloc((cdlen+1)*sizeof(char));
cd[cdlen] = '\0';
strcpy(hc[p],cd);
}
}
else if (t[p]->weight == 1){
t[p]->weight = 2;
if (t[p]->rchild != 0)
{
p = t[p]->rchild;
cd[cdlen++] = '1';
}else
{
t[p]->weight = 0;
p = t[p]->parent;
--cdlen; //退回父节点,编码长度减一
}
}
}
}
int main()
{
int n;
cout << "n:" <<endl;
cin >> n;
// int huffman[2*n-1][5];
// huffmanTree(huffman,n);
// printTree(huffman,n);
// huffmanCode(huffman,n);
// int level = 0;
// printPath(huffman,n,2*n-2,level);
HuffmanTree t;
HuffmanCode c;
int *w;
for (int i=0;i<n;i++)
cin >> w[i];
HuffmanCoding(t,c,w,n);
return 0;
}
机试指南的题,优先队列哈夫曼树
优先队列的用法:
(1)priority_queue 默认设置大顶堆
(2)priority_queue
struct node
{
friend bool operator< (node n1, node n2)
{
return n1.priority < n2.priority;
}
int priority;
int value;
};
- 比较运算符外置
运算符重载:
用于对对象的运算符操作
box operator+(const box a,const box b)
{
box c;
c.weight = a.weight+b.weight;
return c;
}
友元函数:
1)C++中引入友元函数,是为在该类中提供一个对外(除了他自己意外)访问的窗口;
2)这个友元函数他不属于该类的成员函数,他是定义在类外的普通函数,只是在类中声明该函数可以直接访问类中的private或者protected成员。
3)友元函数可以访问类中的私有成员和其他数据,但是访问不可直接使用数据成员,需要通过对对象进行引用。
// 输出哈夫曼树树顶元素
int main()
{
int n;
priority_queue<int,vector<int>,greater<int> > q;
while (cin >> n)
{
while (!q.empty())
q.pop(); // 清空小顶堆元素
for (int i=1;i<=n;i++)
{
int d;
cin >> d;
q.push(d);
}
int ans = 0;
while (q.size() > 1 )
{
int a = q.top();
q.pop();
int b = q.top();
q.pop();
q.push(a+b);
}
ans = q.top();
cout << ans << endl;
}
return 0;
}
搬水果 改版上面的代码,只需要把ans加起来
#include <iostream>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <queue>
#include <vector>
using namespace std;
/*
搬水果
*/
int main()
{
int n;
priority_queue<int,vector<int>,greater<int> > q;
while (cin >> n)
{
while (!q.empty())
q.pop(); // 清空小顶堆元素
for (int i=1;i<=n;i++)
{
int d;
cin >> d;
q.push(d);
}
int ans = 0;
int sum = 0;
while (q.size() > 1 )
{
int a = q.top();
q.pop();
int b = q.top();
q.pop();
ans = a+b;
q.push(ans);
sum += ans;
}
cout << sum << endl;
}
return 0;
}