赫夫曼编码(优先队列实现)

/*
Name: 赫夫曼编码(优先队列实现) 
Copyright:
Author: 巧若拙 
Date: 28/09/14 12:17
Description: 
采用优先队列把一个普通线性表改造成赫夫曼树,再进行赫夫曼编码,得到一个同时记录了明文和对应编码的密码本。
使用优先队列(最小堆)构造赫夫曼树是一种高效的方法,比每次都遍历整个线性表要快很多。
我在构造密码本时确保密码本数组递增排序,这样每次插入新结点时可以折半查找插入,效率较高。
有序的密码本在把明文编码成密文时也可以大大提高查找效率。 
*/


#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<math.h>
#include<time.h>


#define MAXSIZE 300
#define OK 1
#define ERROR 0
#define TRUE 1
#define FALSE 0 


typedef char ElemType;
typedef int Status; //函数类型,其值是函数结果状态代码,如OK等 


typedef struct {
ElemType data;
int weight;
int parent;
int lchild;
int rchild;
} HNodeType;    //结点结构体


typedef struct {
ElemType data; //明文信息 
char *code;    //密文信息 
} HCodeType;    //编码结构体


void BuildMinHeap(HNodeType Hf[], int n);//将线性表Hf改造成一个最小堆 
void MinHeapSiftDown(HNodeType Hf[], int n, int pos);//向下调整二叉堆的第pos个元素,使其满足最小堆的特征 
void HuffmanTree (HNodeType Hf[], int n);//将线性表改造成赫夫曼树
void DisplayCodebook(HCodeType Cb[], int n);//显示密码本中的明码和对应密码信息 
int InsertCodeLib(HCodeType Cb[], int n, ElemType x, char *s, int len);//将新结点插入到密码本的编码线性表中 
int HfCoding(HCodeType Cb[], HNodeType Hf[], int n);//根据赫夫曼树进行赫夫曼编码,生成密码本


int main(void)
{
HNodeType Hf[MAXSIZE*2];   //赫夫曼树 
HCodeType codebook[MAXSIZE]; //密码本 
int lenHf = 5; //赫夫曼树叶子结点个数 
int lenCb;    //密码本长度(等于lenHf) 
int i;

for (i=0; i<lenHf; i++) //随机产生数据和权值 
{
Hf[i].data = 'A' + i;
Hf[i].weight = rand() % 10 + 1;
printf("%c : %d\n", Hf[i].data, Hf[i].weight);
}


    HuffmanTree (Hf, lenHf); //将线性表改造成赫夫曼树

    lenCb = HfCoding(codebook, Hf, lenHf*2-1);//根据赫夫曼树进行赫夫曼编码,生成密码本
    puts("密码本:"); 
    DisplayCodebook(codebook, lenCb);//显示密码本中的明码和对应密码信息 
    
    return 0;
}


/*
函数功能:将线性表改造成赫夫曼树
初始条件:非递减线性表Hf已经存在 
操作结果:先将原线性表改造成最小堆(优先队列),然后按照如下顺序处理该最小堆:
删除权重最小的两个结点,并将他们加入到队列尾部,将该两个最小结点的权值相加,生成一个新结点,并将新结点加入到优先队列。
不断调整该最小堆,直到只剩下一个结点,即得到赫夫曼树。 
注意:每次产生新结点时,只需设置新结点的权重及孩子结点信息,双亲结点信息等赫夫曼树构造完毕后再统一计算。  
*/
void HuffmanTree (HNodeType Hf[], int n)
{
int i;
int len = 2*n - 1;
int front = n -1;
int rear = len - 1;

BuildMinHeap(Hf, n);

for (i=0; i<len; i++) //初始化孩子结点位置 
{
Hf[i].lchild = Hf[i].rchild = -1;
}

while (front > 0)
{
//删除权重最小的两个结点,并将他们加入到队列尾部 
Hf[rear] = Hf[0];
Hf[0] = Hf[front];
MinHeapSiftDown(Hf, front+1, 1);//向下调整二叉堆的第1个元素,使其满足最小堆的特征 
Hf[rear-1] = Hf[0];
//设置新结点的权重及孩子结点信息 
Hf[0].weight = Hf[rear].weight + Hf[rear-1].weight;
Hf[0].lchild = rear;
Hf[0].rchild = rear - 1;
MinHeapSiftDown(Hf, front, 1);//向下调整二叉堆的第1个元素,使其满足最小堆的特征 
rear -= 2; //由于有两个结点加入新队列,队尾指示前移2位 
front--;//由于有(2-1 = 1)结点出列,队头指示前移1位  
}

//计算父亲结点位置 
Hf[0].parent = -1;
for (i=0; i<len; i++)
{
Hf[Hf[i].lchild].parent = Hf[Hf[i].rchild].parent = i;
}
}


/*
函数功能:将新结点插入到密码本的编码线性表中 
初始条件:每个编码的明文和密文信息分别记录在x和s中,len是字符串s的长度 
操作结果:先折半查找插入位置,然后插入新结点,将x和s的信息复制给新结点,注意s中存储的结点编码的路径是从叶子到根,存储到密码本时需要逆序复制 。 
*/
int InsertCodeLib(HCodeType Cb[], int n, ElemType x, char *s, int len)//采用二分查找插入排序 
{
int i, mid;
int left = 0, right = n-1;

while (left <= right)//折半查找插入位置
{
mid = (left + right) / 2;

   if (x > Cb[mid].data)
{
left = mid + 1;
}
else
{
right = mid -1;
}
}
//新数据,执行插入操作 
for (i=n; i>left; i--)
{
Cb[i] = Cb[i-1];
}

Cb[left].code = (char*)malloc(sizeof(char)*(len+1));
if (!Cb[left].code) 
{
printf("Out of space!");
exit(0);
}

for (i=len-1; i>=0; i--) //把s的字符串逆序复制到code 
{
Cb[left].code[len-1-i] = s[i];
}
Cb[left].code[len] = '\0';
Cb[left].data = x;


return n+1;
}


/*
函数功能:根据赫夫曼树进行赫夫曼编码,生成密码本 
初始条件:赫夫曼树Hf已经存在,n是赫夫曼树的长度 
操作结果:对赫夫曼树的叶子结点进行编码,并存储到密码本,返回密码本长度。
对密码本中的各个结点采用二分查找插入,构造非递减序列,以便于查找和编码。 
*/
int HfCoding(HCodeType Cb[], HNodeType Hf[], int n)
{
int i, j, k, f;
int top = 0; //为编码结构体构造一个栈,top指示栈顶 
char str[MAXSIZE]; //存储编码的临时字符串 


for (i=0; i<n; i++)
{
if (Hf[i].lchild == -1)//是叶子结点
{
j = i;
f = Hf[j].parent;
k = 0;

while (f != -1) //逆序存储该结点的编码,注意结点编码的路径是从叶子到根,存储到密码本时需要逆序复制 
{  
if (j == Hf[f].lchild)
{
str[k++] = '0';
}
else
{
str[k++] = '1';
}

j = f;
f = Hf[j].parent;
}
str[k] = '\0';

top = InsertCodeLib(Cb, top, Hf[i].data, str, k);//采用二分查找插入排序 

}

return top;
}


/*
函数功能:显示密码本中的明码和对应密码信息 
初始条件:密码本已经存在 
操作结果:显示密码本中的明码和对应密码信息。 
*/
void DisplayCodebook(HCodeType Cb[], int n)
{
int i, j;

for (i=0; i<n; i++)
{
printf("%c: ", Cb[i].data);
puts(Cb[i].code);
}
}


/*
函数功能:向下调整二叉堆的第pos个元素,使其满足最小堆的特征 
初始条件:最小堆Hf已经存在,只有第pos个元素不满足特征 
操作结果:向下调整二叉堆的第pos个元素,使其满足最小堆的特征。 
*/
void MinHeapSiftDown(HNodeType Hf[], int n, int pos)
{
HNodeType temp = Hf[pos-1];
int child = pos * 2; //指向左孩子 

while (child <= n)
{
if (child < n && Hf[child-1].weight > Hf[child].weight) //有右孩子,且右孩子更小些,定位其右孩子 
child += 1;

if (Hf[child-1].weight < temp.weight) //通过向上移动孩子结点值的方式,确保双亲大于孩子 
{
Hf[pos-1] = Hf[child-1]; 
pos = child;
child = pos * 2;
}
else
break;
}

Hf[pos-1] = temp; //将temp向下调整到适当位置 
}


/*
函数功能:将线性表Hf改造成一个最小堆 
初始条件:线性表Hf已经存在
操作结果:将线性表Hf改造成一个最小堆 
*/
void BuildMinHeap(HNodeType Hf[], int n)
{
int i;

for (i=n/2; i>0; i--)
{
MinHeapSiftDown(Hf, n, i);
}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是C++实现赫夫曼编码算法的代码,其中包括构造赫夫曼树和求解赫夫曼编码的函数: ```cpp #include <iostream> #include <queue> #include <vector> #include <string> #include <map> using namespace std; // 赫夫曼树结点 struct HuffmanNode { int weight; // 权值 char ch; // 字符 HuffmanNode *left, *right; // 左右子树 HuffmanNode(int w, char c = '\0') : weight(w), ch(c), left(nullptr), right(nullptr) {} }; // 比较器,用于优先队列 struct cmp { bool operator() (HuffmanNode* a, HuffmanNode* b) { return a->weight > b->weight; } }; // 构造赫夫曼树 HuffmanNode* buildHuffmanTree(int* weights, int n) { priority_queue<HuffmanNode*, vector<HuffmanNode*>, cmp> pq; for (int i = 0; i < n; i++) { pq.push(new HuffmanNode(weights[i], char('a' + i))); } while (pq.size() > 1) { HuffmanNode* left = pq.top(); pq.pop(); HuffmanNode* right = pq.top(); pq.pop(); HuffmanNode* parent = new HuffmanNode(left->weight + right->weight); parent->left = left; parent->right = right; pq.push(parent); } return pq.top(); } // 递归求解赫夫曼编码 void getHuffmanCode(HuffmanNode* root, string code, map<char, string>& res) { if (!root) return; if (!root->left && !root->right) { res[root->ch] = code; return; } getHuffmanCode(root->left, code + "0", res); getHuffmanCode(root->right, code + "1", res); } // 求解赫夫曼编码 map<char, string> HuffmanCoding(int* weights, int n) { HuffmanNode* root = buildHuffmanTree(weights, n); map<char, string> res; getHuffmanCode(root, "", res); return res; } int main() { int weights[] = {5, 2, 4, 7, 1, 3, 6}; int n = sizeof(weights) / sizeof(weights[0]); map<char, string> res = HuffmanCoding(weights, n); for (auto it = res.begin(); it != res.end(); it++) { cout << it->first << ": " << it->second << endl; } return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值