數據結構與算法實驗報告 姓名:孫瑞霜
一、實驗目的
1、復習Huffman樹及其創建等基本操作;
2、掌握最小堆的定義及其建立、插入刪除等操作的實現。
3、掌握Huffman編碼的方法。
二、實驗要求:
1. 認真閱讀和掌握教材上和本實驗相關的內容和算法。
2. 上機將相關算法實現。
3. 實現上面實驗目的要求的功能,並能進行簡單的驗證。
三、實驗內容
1、 必做內容:Huffman樹的創建
按照課本上最小堆(代碼4.26,改成最小堆)及建立Huffman樹的算法(代碼4.27),編程實現Huffman樹的建立。要求能夠進行簡單的輸入輸出驗證,輸出建好后的Huffman樹的形態,比如輸出其先序和中序遍歷序列。
typedef struct TreeNode *HuffmanTree;
struct TreeNode{
int Weight;
HuffmanTree Left;
HuffmanTree Right;
};
2、 選做內容:實現Huffman編碼,並進行簡單輸入輸出驗證,比如輸入一些字符及其權值,輸出每種字符的Huffman編碼。可以查閱資料,結合程序進行分析,其存儲結構及其操作上和建立Huffman樹有何異同。
三、算法描述
首先我們應明確什么是哈夫曼樹,假設有n個權值,構造有n個葉子結點的二叉樹,每個葉子結點的權值是n個權值之一,其中帶權路徑長度最小的一棵就是哈夫曼樹。在哈夫曼樹的構造過程中,選取兩棵根結點權值最小的樹作為左、右子樹,每次將權值最小的兩棵樹進行合並,構造成一棵新的二叉樹,置新二叉樹根結點的權值等於左、右子樹根結點的權值之和。從集合中刪除作為左、右子樹的兩棵二叉樹,並將新構造的二叉樹加入到集合中;重復操作,直到集合中只含一棵二叉樹為止,這棵二叉樹便是要建立的哈夫曼樹。
四、詳細設計
五、程序代碼
#include<stdio.h>
#include<stdlib.h>
#define MinData -1
typedef struct TreeNode *HuffmanTree;//哈夫曼樹類型
struct TreeNode{
int Weight; //結點權值
HuffmanTree Left;//指向左子樹
HuffmanTree Right;//指向右子樹
}HuffmanNode;//定義新的結構變量
typedef struct HeapStruct *MinHeap;
struct HeapStruct{
HuffmanTree *Data; //存儲堆元素的數組 存儲時從下標1開始
int Size; //堆的當前元素的個數
int Capacity; //堆的最大容量
};
HuffmanTree NewHuffmanNode();
//構造新的哈夫曼樹
MinHeap CreateMinHeap(int MaxSize);
//創建容量為MaxSize的最小堆
bool Insert(MinHeap H,HuffmanTree item);
//將元素item插入到最小堆H中
HuffmanTree DeleteMin(MinHeap H);
//從最小堆H中取出權值為最小的元素,並刪除一個結點
MinHeap BuildMinHeap(MinHeap H);
//將H->data[]按權值調整為最小堆
HuffmanTree Huffman(MinHeap H);
//最小堆構造哈夫曼樹
void PreOrderTraversal(HuffmanTree BST);
//先序遍歷哈夫曼樹
int main()
{
int i,N;
MinHeap h;
HuffmanTree T,BT = NULL;
printf("輸入葉子結點的個數:\n");
scanf("%d",&N);
h = CreateMinHeap(2*N); //創建最小堆
printf("輸入%d個葉子結點對應的權值:\n",N);
for(i=1; i<=N; i++){/*最小堆元素賦值*/
T = NewHuffmanNode();
scanf("%d",&(T->Weight));
h->Data[++(h->Size)] = T;
}
BT = Huffman(h); //構造哈夫曼樹
printf("先序遍歷此哈夫曼樹:\n");
PreOrderTraversal(BT); //先序遍歷哈夫曼樹
return 0;
}
/*哈夫曼樹構造算法*/
HuffmanTree Huffman(MinHeap H)
{/*假設H->Size個權值已經存在H->data[]->Weight里*/
int i,n;
HuffmanTree T;
BuildMinHeap( H ); //將H->data[]按權值調整為最小堆
/*此處必須將H->Size的值交給num,因為后面做DeleteMin()和 Insert()函數會改變H->Size的值*/
n = H->Size;
for(i=1; i<n; i++){ //做 H->Size-1次合並
T = NewHuffmanNode(); //建立一個新的根結點
T->Left = DeleteMin(H); //從最小堆中刪除一個節點,作為新T的左子結點
T->Right = DeleteMin(H); //從最小堆中刪除一個節點,作為新T的右子結點
T->Weight = T->Left->Weight+T->Right->Weight; //計算新權值
Insert(H,T); //將新T插入到最小堆
}
T = DeleteMin(H);
return T;
}
//先序遍歷哈夫曼樹
void PreOrderTraversal(HuffmanTree BST)
{
if( BST ){
printf("%d ",BST->Weight); //先訪問根節點
PreOrderTraversal(BST->Left); //再訪問左子樹
PreOrderTraversal(BST->Right); //最后訪問右子樹
}
}
HuffmanTree NewHuffmanNode()
{
HuffmanTree BST = (HuffmanTree)malloc(sizeof(HuffmanNode));
BST->Weight = 0;
BST->Left = BST->Right = NULL;
return BST;
}
MinHeap CreateMinHeap(int MaxSize)
{ /*創建容量為MaxSize的最小堆*/
MinHeap H = (MinHeap)malloc(sizeof(struct HeapStruct));
H->Data = (HuffmanTree *)malloc((MaxSize+1) * sizeof(HuffmanTree));
H->Size = 0;
H->Capacity = MaxSize;
HuffmanTree T = NewHuffmanNode();
T->Weight = MinData; /*定義哨兵-為小於堆中所有可能元素權值的值,便於以后更快操作*/
H->Data[0] = T;
return H;
}
//插入新增節點
bool IsFull(MinHeap H)
{
return (H->Size == H->Capacity);
}
bool IsEmpty(MinHeap H)
{
return (H->Size == 0);
}
bool Insert(MinHeap H,HuffmanTree item)
{ //將元素item插入到最小堆H中
int i;
if( IsFull(H) ){
printf("最小堆已滿\n");
return false;
}
i = ++H->Size; //i指向插入后堆中的最后一個元素的位置
for(; H->Data[i/2]->Weight > item->Weight; i/=2) //無哨兵,則增加判決條件 i>1
H->Data[i] = H->Data[i/2]; //向下過濾結點
H->Data[i] = item; //將item插入
return true;
}
HuffmanTree DeleteMin(MinHeap H)
{/*從最小堆H中取出權值為最小的元素,並刪除一個結點*/
int parent,child;
HuffmanTree MinItem,temp = NULL;
if( IsEmpty(H) ){
printf("最小堆為空\n");
return NULL;
}
MinItem = H->Data[1]; //取出根結點-最小的元素-記錄下來
//用最小堆中的最后一個元素從根結點開始向上過濾下層結點
temp = H->Data[H->Size--]; //最小堆中最后一個元素,暫時將其視為放在了根結點
for(parent=1; parent*2<=H->Size; parent=child){
child = parent*2;
if((child != H->Size) && (H->Data[child]->Weight > H->Data[child+1]->Weight)){
/*有右兒子,並且左兒子權值大於右兒子*/
child++; //child指向左右兒子中較小者
}
if(temp->Weight > H->Data[child]->Weight){
H->Data[parent] = H->Data[child]; //向上過濾結點-temp存放位置下移到child位置
}else{
break; //找到了合適的位置
}
}
H->Data[parent] = temp; //temp存放到此處
return MinItem;
}
MinHeap BuildMinHeap(MinHeap H)
{
int i,parent,child;
HuffmanTree temp;
for(i=H->Size/2;i>0;i--){ //從最后一個父結點開始,直到根結點
temp = H->Data[i];
for(parent=i; parent*2<=H->Size; parent=child){
/*向下過濾*/
child = parent*2;
if((child != H->Size) && (H->Data[child]->Weight > H->Data[child+1]->Weight)){/*有右兒子,並且左兒子權值大於右兒子*/
child++; //child指向左右兒子中較小者
}
if(temp->Weight > H->Data[child]->Weight){
H->Data[parent] = H->Data[child]; //向上過濾結點-temp存放位置下移到child位置
}else{
break; //找到了合適的位置
}
}/*結束內部for循環對以H->data[i]為根的子樹的調整*/
H->Data[parent] = temp; //temp存放到此處
}
return H;
}
六、測試和結果
測試用例:課本154頁哈夫曼樹的生成過程
七、用戶手冊
打開devC++,新建一個源程序,拷貝5部分的代碼進去,點擊運行,在出現的界面中按照提示輸入數據,一步步按下回車鍵即可運行該程序,最后測試完畢,關閉界面。