C/C++:
#include<stdio.h>
#include<stdlib.h>
//----------------------定义结点数据---------------
#define N_LEAVE 26 //N个权值,则有N个叶结点,树共2N-1个结点
#define N_NODE (26*2-1)
typedef struct _Node
{
char character;
float weight;
int lchild;
int rchild;
int parent;
}Node,*pNode;
typedef struct _Code
{
int HufCode[N_LEAVE]; //叶节点最长编码位数应该为树的最长路径
int Start; //编码起始位(相对编码数组)
char Char; //编码的字符值
}Code,*pCode;
//--------------------------------------------------
//----------------------构造哈夫曼树----------------
void Huffman(Node Ht[],float Wt[])
{
int i,j,x1,x2;
float min1,min2;
//初始化结点数组Ht
for(i=0;i<N_NODE;i++)
{
Ht[i].parent = -1;
Ht[i].lchild = -1;
Ht[i].rchild = -1;
if(i<N_LEAVE)
{
Ht[i].weight = Wt[i];
Ht[i].character = i+65; //A-Z的ASCii码
}
else
{
Ht[i].weight = 0;
Ht[i].character = '?'; //生成的中间结点字符值标记为'?'
}
}
//控制n-1次结点的结合(若有n个叶结点)
for(i=1;i<=N_LEAVE-1;i++)
{
min1 = min2 = 100; //min1、min2记录当前最小、次小权值
x1 = x2 = 0; //x1、x2记录当前最小次小权值结点的位置(数组标号)
for(j=0;j<N_LEAVE-1+i;j++) //在[0-j]范围内找最小次小权值结点
{
if(Ht[j].parent == -1 && Ht[j].weight<min1 ) //parent元素的判断是为了排除已结合过的结点,结合过的结点parent有正值
{
min2 = min1; //当前结点权值小于最小值,所以当前结点变成最小权值结点,原最小结点变成原来的次小结点
x2 = x1;
min1 = Ht[j].weight;
x1 = j;
}
else
{
if( Ht[j].parent == -1 && Ht[j].weight<min2 ) //当前结点权值大于最小值,小于次小值,则取代次小结点
{
min2 = Ht[j].weight;
x2 = j;
}
}
}
//将找到的最小、次小权值结点结合成树,为其父结点赋值,可见该哈夫曼树的根节点应该是Ht数组最后一个结点Ht[N_NODE-1]
Ht[x1].parent = N_LEAVE-1+i;
Ht[x2].parent = N_LEAVE-1+i;
Ht[N_LEAVE-1+i].weight = Ht[x1].weight + Ht[x2].weight;
Ht[N_LEAVE-1+i].lchild = x1;
Ht[N_LEAVE-1+i].rchild = x2;
}
}
void Code_Ht(Node Ht[],Code Hc[])
{
int i,d,p,j;
Code x;
//依次每个叶结点(在哈夫曼结点数组的最前面的空间中)寻找双亲直到root,记录路径,路径就是哈夫曼编码
for(i=0;i<N_LEAVE;i++)
{
x.Char = Ht[i].character;
x.Start = N_LEAVE-1; //默认编码起点为编码数组最后一位
d = i;
p = Ht[i].parent;
while( 1 )
{
if(Ht[p].lchild == d)
x.HufCode[x.Start] = 0; //默认编码为左0右1
else if(Ht[p].rchild == d)
x.HufCode[x.Start] = 1;
else
printf("ERROR!");
d = p;
p = Ht[d].parent;
if(p == -1) break; //Ht[i]为root结点退出循环,说明已经回溯到了根结点
x.Start--;
}
for(j=x.Start;j<=N_LEAVE-1;j++)
{
Hc[i].HufCode[j] = x.HufCode[j];
}
Hc[i].Start = x.Start;
Hc[i].Char = x.Char;
}
}
//输出每个字符的的哈夫曼编码
void PrintCode(Code Hc[])
{
int i,j;
for(i=0;i<N_LEAVE;i++)
{
for(j=Hc[i].Start;j<N_LEAVE;j++)
{
printf("%d",Hc[i].HufCode[j]);
}
printf("%5c\n",Hc[i].Char);
}
}
//查询字符的编码
void FindCode(Code Hc[])
{
int i,j;
char x;
printf("\n请输入一个大写字母:");
scanf("%c",&x);
getchar();
for(i=0;i<N_LEAVE;i++)
{
if( x == Hc[i].Char )
{
printf("字符%c的哈夫曼编码是:",x);
for(j=Hc[i].Start;j<N_LEAVE;j++)
{
printf("%d",Hc[i].HufCode[j]);
}
putchar('\n');
getchar();
return ;
}
}
}
//--------------------------------------------------
//---------------------主函数-----------------------
int main()
{
Node HufTree[N_NODE]; //存放所有结点数据
Code HCode[N_LEAVE];
float Wt[N_LEAVE] = {0.0856,0.0139,0.0297,0.0378,0.1304,0.0289,0.0199,0.0528,0.0627,
0.0013,0.0042,0.0339,0.0249,0.0707,0.0797,0.0199,0.0012,0.0677,
0.0607,0.1045,0.0249,0.0092,0.0149,0.0017,0.0199,0.0008}; //存放叶结点权值
Huffman(HufTree,Wt);
Code_Ht(HufTree,HCode);
PrintCode(HCode);
FindCode(HCode);
return 0;
}
Java:
import java.util.Arrays;
import java.util.Scanner;
// 节点类型
class Code {
char ch; //结点信息
int w; //权值->频率
int parent, lChild, rChild; //父节点及左右子节点
}
// 树定义
class HuffmanTree {
int m; // 外部结点(叶子结点,也就是要编码的字母及空格)的个数
int root; // 哈夫曼树根在数组中的下标
Code[] codes; // 存放(2*m-1)个结点的数组
}
// 哈夫曼编码的类型定义
class CodeType {
int[] bits; /*保存二进制编码*/
int start; /*每个字符对应的二进制编码在bits中的起始位置*/
@Override
public String toString() {
return "CodeType{" +
"bits=" + Arrays.toString(bits) +
", start=" + start +
'}';
}
}
/**
* 本程序仅提供对空格和26个字母的编码及译码,默认字母使用频率以给出
* 也可自行输入
*/
public class HuffmanCode {
public static void main(String[] args) {
Scanner s = new Scanner(System.in);
/*程序实现是27个字符的编码译码相关操作*/
int[] w = new int[]{186, 64, 13, 22, 32, 103, 21, 15, 47, 57, 1, 5, 32, 20, 57, 63, 15, 1, 48, 51, 80, 23, 8, 18, 1, 16, 1};
int m = w.length;
/*
System.out.println("请输入空格的使用频率:");
w[0] = s.nextInt();
for(int i = 1; i < 27; i++){
System.out.println("请输入" + (char) ('A' + i - 1) + "的使用频率:");
w[i] = s.nextInt();
}
*/
CodeType[] hcd = new CodeType[2*m-1];
for (int i = 0; i < 2*m-1; i++) {
hcd[i] = new CodeType();
}
// 创建树
HuffmanTree hct = createHuffmanTree(m, w);
//show_huffmantree(hct);
HuffmanCode(hct, hcd, m);
showChar_Encode(w, hcd, m);
while(true){
System.out.println("\n==========菜单=============");
System.out.println(" 0、退出系统");
System.out.println(" 1、编码");
System.out.println(" 2、译码");
System.out.println("============================");
System.out.println("请输入要执行的操作编号:");
int choose = s.nextInt();
switch (choose){
case 0:
return;
case 1:
//调用编码系统
System.out.println("请输入要编码的文本(英文字母+空格):");
//获取编码文本
s.nextLine(); // 读取缓冲区回车
String str = s.nextLine();
System.out.println("打印字符串" + str.toUpperCase() + "的Huffman编码:");
print_Encode(hcd, str.toUpperCase().toCharArray(), m);
break;
case 2:
//调用译码系统
s.nextLine(); // 读取缓冲区回车
System.out.println("请输入要译码的二进制huffman码:");
str = s.nextLine();
System.out.println("打印huffman码对应的字符串:");
print_Decode(hct, str.toCharArray(), m);
break;
default:
System.out.println("输入有误,请重新输入!");
}
}
}
//构造具有m个叶子结点的哈夫曼树
static HuffmanTree createHuffmanTree(int m, int[] w) {
HuffmanTree ht = new HuffmanTree();
ht.codes = new Code[2 * m - 1];
int i, j, x1, x2, m1, m2;
for (i = 0; i < 2 * m - 1; i++) {
/*初始化所有结点为-1*/
ht.codes[i] = new Code();
ht.codes[i].parent = -1;
ht.codes[i].lChild = -1;
ht.codes[i].rChild = -1;
/*初始化结点内容信息,前27(m)个节点为空格加字母,后边的全部为NULL*/
if (i < m) {
ht.codes[i].w = w[i];//频率(权值)
if (i == 0)
ht.codes[i].ch = ' ';//内容,第一个为空格
else
ht.codes[i].ch = (char) ('A' + (i - 1));
} else // 后边的全部为非叶子节点,权值初始化为-1
{
ht.codes[i].w = -1;//频率
}
}
// 冒泡排序,每次找出权值最小的结点
for (i = 0; i < m - 1; i++) // i < m - 1
{
m1 = Integer.MAX_VALUE;
m2 = Integer.MAX_VALUE;
x1 = -1;
x2 = -1;
for (j = 0; j < m + i; j++) {
if (ht.codes[j].w < m1 && ht.codes[j].parent == -1) // 左节点,ht.codes[j].parent == -1表示该节点未被选中
{
m2 = m1;
x2 = x1;
m1 = ht.codes[j].w;
x1 = j;
} else { //右节点
if (ht.codes[j].w < m2 && ht.codes[j].parent == -1) {
m2 = ht.codes[j].w;
x2 = j;
}
}
}
ht.codes[x1].parent = m + i;
ht.codes[x2].parent = m + i;
ht.codes[m + i].w = m1 + m2;
ht.codes[m + i].lChild = x1;
ht.codes[m + i].rChild = x2;
}
ht.root = 2 * m - 2;
return ht;
}
//求m个叶子结点的哈夫曼编码。
static void HuffmanCode(HuffmanTree ht, CodeType[] hcd, int m) {
int i, c, f;
for (i = 0; i < m; i++) //走每个叶子结点
{
CodeType cd = new CodeType(); //中间变量,形成每个字符对应的哈夫曼编码结点
cd.start = m; //初始化让每个编码的起始位置都是最大值,倒叙存入编码
cd.bits = new int[m];
c = i; //之后保存的是该节点下标
f = ht.codes[c].parent; //从叶子倒着遍历到树根结点,形成哈夫曼编码
while (f != -1) //不是根就还没有走到头。
{
cd.start--; //往前移一个位置
if (ht.codes[f].lChild == c) // 如果父节点的左孩子等于该节点下标,就是0
cd.bits[cd.start] = 0;
else
cd.bits[cd.start] = 1;
c = f;
f = ht.codes[f].parent; //继续走双亲的双亲结点
} //end of while
hcd[i] = cd; //遍历到根即为该叶子结点的哈夫曼编码
}//end of for
}
//打印函数:输出所有字符及字符的频度、字符的huffman编码
static void showChar_Encode(int[] w, CodeType[] hcd, int m) {
int i, j;
System.out.println("序号\t字符\t频度\t哈夫曼编码\t序号\t字符\t频度\t哈夫曼编码");
for (i = 0; i < m; i++) {
if (i == 0)//第一个是空格,特殊处理
System.out.printf("%d\t%c\t%d\t\t", i + 1, ' ', w[i]);
else
System.out.printf("%d\t%c\t%d\t", i + 1, 'A' + i - 1, w[i]);
for (j = hcd[i].start; j < m; j++)//正序输出
System.out.print(hcd[i].bits[j]);
System.out.print("\t");
// 格式化
if (m - hcd[i].start < 10) //如果编码长度小于10追加\t
System.out.print("\t");
if (i % 2 != 0) //一行输出两个
System.out.print("\n");
}
}
//编码函数:给定字符串打印出二进制编码。
static void print_Encode(CodeType[] hcd, char[] s, int m) {
int i = 0, id, j;
while (i < s.length) {
if (s[i] == ' ')
id = 0;
else{
id = s[i] - 'A' + 1;//A是第一个,空格是第0个,获取hcd数组下标
}
for (j = hcd[id].start; j < m; j++){
System.out.print(hcd[id].bits[j]);
}
i++;
}//end of while
}
//译码函数:给定二进制编码打印出对应的字符串。
static void print_Decode(HuffmanTree ht, char[] hfcs, int m) {
int i = 0, id, j;
id = 2 * m - 2; //树根的位置(下标)
if (ht.codes[id].lChild == -1 && ht.codes[id].rChild == -1)//如果该树只有一个节点
System.out.print(ht.codes[id].ch);
else {
while (i < hfcs.length) {
if (hfcs[i] == '0' || hfcs[i] == '1') {
if (hfcs[i] == '0') //左孩子
id = ht.codes[id].lChild;
else
id = ht.codes[id].rChild; // 右孩子
if (ht.codes[id].lChild == -1 && ht.codes[id].rChild == -1)//找到了该叶子节点,如果没有进入if,则表示当前编码没有对应的字符,i++后和下一个编码联合匹配
{
System.out.print(ht.codes[id].ch);
id = 2 * m - 2; // 将下标恢复为根
} //打印叶子结点后,又从树根开始
i++;
} else {
System.out.println("非法二进制串,无法译码!");
break;
}
}
}
}
}
qq:1351006594