1 // haffman.cpp : 定義控制檯應用程序的入口點。 2 // 3 4 #include "stdafx.h" 5 #include<iostream> 6 using namespace std; 7 8 typedef struct hfnode 9 { 10 char ch; //存儲字符 11 int weight; //存儲權值 12 int parent; //雙親結點位置 13 int lchild; //左右孩子結點位置 14 int rchild; 15 } hfnode,*hfmtree; 16 17 typedef struct node 18 { 19 char code[10]; 20 } node,*hfcode; 21 22 #define Nsymbols 10 23 24 hfnode *Inithfm(hfnode *tree,int n) //千萬不能用關鍵字命名。把這些節點做爲帶權值的二叉樹的根節點,左右子樹爲空 25 { 26 //tree = new hfnode[n]; //多餘了,已經在main函數中聲明瞭。 27 for(int i = 0;i < n; i++) 28 { 29 tree[i].ch = '0'; 30 tree[i].weight = 0; 31 tree[i].lchild = 0; 32 tree[i].rchild = 0; 33 tree[i].parent = 0; 34 } 35 cout << "請輸入想要編碼的字符序列,按照先字符後次數的順序輸入" << endl; 36 for(int i = 0;i < 4; i++) 37 { 38 cin >> tree[i].ch >>tree[i].weight; 39 } 40 cout <<endl; 41 42 for(int i = 0;i < 4;i++) 43 cout << tree[i].ch << " "<<tree[i].weight<<" "; 44 cout << endl; 45 return tree; 46 } 47 48 void select(hfnode *tree,int n,int *p1,int *p2) //選擇兩棵根結點權值最小的樹做爲左右子樹構造一棵新的二叉樹,且至新的二叉樹的根結點的權值爲其左右子樹上根結點的權值之和。 49 { 50 //找一個做爲參考,返回的是結點 51 int x,y; 52 for(int i = 0;i <= n;i++) 53 { 54 if(tree[i].parent==0) 55 { 56 x = i; 57 break;//找到一個參考點便可 58 } 59 } 60 61 62 for(int i = 0;i <= n;i++) //找出最小的結點 63 { 64 if((tree[i].parent==0)&&(tree[i].weight!=0)) 65 { 66 if(tree[i].weight < tree[x].weight) 67 x = i; 68 } 69 } 70 71 for(int j = 0;j <= n;j++) 72 { 73 if((tree[j].parent==0)&&(j!=x)) 74 { 75 y = j; 76 break;//找到一個參考點便可 77 } 78 } 79 for(int j = 0;j <= n;j++) //找出次小的結點 80 { 81 if((tree[j].parent==0)&&(tree[j].weight!=0)&&j!=x) 82 { 83 if(tree[j].weight < tree[y].weight) 84 y = j; 85 } 86 } 87 *p1 = x; 88 *p2 = y; 89 } 90 //對哈弗曼樹進行編碼。 91 92 void HfCode(hfnode *tree,int n,node *hfmcode,int m) 93 { 94 int c,f; 95 int start = 0; 96 97 for(int i=0;i<10;i++) 98 { 99 for(c=i,f=tree[c].parent;f!=0;c=f,f=tree[c].parent) //追溯法,從葉子節點出發,一路往上追溯。 100 { 101 if(tree[f].lchild==c) 102 hfmcode[i].code[start++] = '0'; 103 else 104 hfmcode[i].code[start++] = '1'; 105 } 106 start = 0; 107 } 108 } 109 110 void Print(node *hfmcode,int m) 111 { 112 for(int i = 0;i < m;i++) 113 { 114 for(int j = 0;j < m;j++) 115 cout <<hfmcode[i].code[j]; 116 cout << endl; 117 } 118 cout << endl; 119 } 120 121 122 123 int main() 124 { 125 int p1=0,p2=0; //爲了接收最小和次小的兩個節點 126 hfnode tree[Nsymbols]; //當咱們不是用指針,這種狀況下,已經所有賦值了。不用再一次賦值了。 127 Inithfm(tree,Nsymbols); //初始化結構體數組。 128 129 //創建哈弗曼樹 130 131 for(int m = 4;m < 2*4-1;m++) //二叉樹性質 132 { 133 select(tree,m-1,&p1,&p2); 134 tree[p1].parent = m; 135 tree[p2].parent = m; 136 tree[m].lchild = p1; 137 tree[m].rchild = p2; 138 tree[m].weight = tree[p1].weight + tree[p2].weight; 139 tree[m].parent =0; //其實這句也能夠不用,由於初始化時已經初始化爲0了。 140 } 141 142 node hfmcode[4]; 143 // 初始化,否則打印的時候會出現未知錯。 144 for(int i = 0;i < 4;i++) 145 { 146 for(int j = 0;j < 4;j++) 147 hfmcode[i].code[j]='\0'; 148 } 149 150 HfCode(tree,Nsymbols,hfmcode,4); //編碼函數 151 Print(hfmcode,4); //打印函數 152 return 0; 153 }
實驗名稱:哈弗曼編碼node
實驗目的:瞭解前綴編碼的概念,理解數據壓縮的基本方法;ios
掌握Huffman編碼的設計思想並能熟練應用。編程
實驗要求:對有字符集{A,B,C,D},各字符在電文中出現的次數集爲{1,3,4,7};數組
要求編程實現Huffman樹的構造,並在此基礎上編程實現Huffman編碼。函數
實驗步驟及內容:工具
一、首先創建一個定義哈弗曼樹的結構體Node,及結構體指針LinkList,該結構體包含一個存儲字符的ch,存儲權值的weight,存儲雙親結點的parent,存儲左右孩子的lchild與rchild。代碼以下:編碼
typedef struct hfnodespa
{設計
char ch; //存儲字符指針
int weight; //存儲權值
int parent; //雙親結點位置
int lchild; //左右孩子結點位置
int rchild;
} hfnode,*hfmtree;
二、定義一個存儲編碼的結構體,主要是爲了方便打印,代碼以下:
typedef struct node
{
char code[10];
} node,*hfcode;
三、初始化函數,由於默認值是一個不肯定的值,必須進行初始化才行。
hfnode *Inithfm(hfnode *tree,int n) //千萬不能用關鍵字命名。把這些節點做爲帶權值的二叉樹的根節點,左右子樹爲空
{
//tree = new hfnode[n]; //多餘了,已經在main函數中聲明瞭。
for(int i = 0;i < n; i++)
{
tree[i].ch = '0';
tree[i].weight = 0;
tree[i].lchild = 0;
tree[i].rchild = 0;
tree[i].parent = 0;
}
cout << "請輸入想要編碼的字符序列,按照先字符後次數的順序輸入" << endl;
for(int i = 0;i < 4; i++)
{
cin >> tree[i].ch >>tree[i].weight;
}
cout <<endl;
for(int i = 0;i < 4;i++)
cout << tree[i].ch << " "<<tree[i].weight<<" ";
cout << endl;
return tree;
}
四、選擇兩棵根結點權值最小的樹做爲左右子樹。
void select(hfnode *tree,int n,int *p1,int *p2) //選擇兩棵根結點權值最小的樹做爲左右子樹構造一棵新的二叉樹,且至新的二叉樹的根結點的權值爲其左右子樹上根結點的權值之和。
{
//找一個做爲參考,返回的是結點
int x,y;
for(int i = 0;i <= n;i++)
{
if(tree[i].parent==0)
{
x = i;
break;//找到一個參考點便可
}
}
for(int i = 0;i <= n;i++) //找出最小的結點
{
if((tree[i].parent==0)&&(tree[i].weight!=0))
{
if(tree[i].weight < tree[x].weight)
x = i;
}
}
for(int j = 0;j <= n;j++)
{
if((tree[j].parent==0)&&(j!=x))
{
y = j;
break;//找到一個參考點便可
}
}
for(int j = 0;j <= n;j++) //找出次小的結點
{
if((tree[j].parent==0)&&(tree[j].weight!=0)&&j!=x)
{
if(tree[j].weight < tree[y].weight)
y = j;
}
}
*p1 = x;
*p2 = y;
}
五、構造哈弗曼樹
//創建哈弗曼樹
for(int m = 4;m < 2*4-1;m++) //二叉樹性質
{
select(tree,m-1,&p1,&p2);
tree[p1].parent = m;
tree[p2].parent = m;
tree[m].lchild = p1;
tree[m].rchild = p2;
tree[m].weight = tree[p1].weight + tree[p2].weight;
tree[m].parent =0; //其實這句也能夠不用,由於初始化時已經初始化爲了。
}
六、對哈弗曼樹進行編碼。編碼函數以下:
//對哈弗曼樹進行編碼。
void HfCode(hfnode *tree,int n,node *hfmcode,int m)
{
int c,f;
int start = 0;
for(int i=0;i<10;i++)
{
for(c=i,f=tree[c].parent;f!=0;c=f,f=tree[c].parent) //追溯法,從葉子節點出發,一路往上追溯。
{
if(tree[f].lchild==c)
hfmcode[i].code[start++] = '0';
else
hfmcode[i].code[start++] = '1';
}
start = 0;
}
}
七、打印編碼
void Print(node *hfmcode,int m)
{
for(int i = 0;i < m;i++)
{
for(int j = 0;j < m;j++)
cout <<hfmcode[i].code[j];
cout << endl;
}
cout << endl;
}
另配上一副本身用畫圖工具畫的理論分析圖:
結果與C++代碼結果一致。
總結:
這次實驗,讓我理解告終構體數組的使用以及初始化,內存管理等方面的熟悉,感受收穫挺大的。