當前位置:首頁 » 服務存儲 » 哈夫曼樹詳細存儲結構
擴展閱讀
webinf下怎麼引入js 2023-08-31 21:54:13
堡壘機怎麼打開web 2023-08-31 21:54:11

哈夫曼樹詳細存儲結構

發布時間: 2023-08-28 18:36:51

『壹』 哈夫曼樹HT存儲結構的初態和末態怎麼寫

定義哈夫曼樹與編碼的存儲結構與數據類型
typedef struct
{
char data;//節點值
int weight; //權重
int parent; //雙親節點
int lchild; //左孩子節點
int rchild; //右孩子節點
} HTNode;

typedef struct
{
char cd[N]; //存放哈夫曼碼
int start;
} HCode;
void CreateHT(HTNode ht[],int n) //創建哈夫曼樹
void CreateHCode(HTNode ht[],HCode hcd[],int n) //根據哈夫曼樹計算每個元素的哈夫曼編碼
void DispHCode(HTNode ht[],HCode hcd[],int n) //輸出每個元素的哈夫曼編碼

『貳』 哈夫曼樹

這是我們的作業題,自己寫 的……(可能輸入的格式跟你要的不一致,自己改一下)

如果有什麼不懂的就問我,我可以把其中所有相關的文件發給你 ^^

註:1、 初始化創建哈夫曼樹有三種選擇,其中選擇編譯課本測試數據時和編譯源文件是,調用的輸入文件分別是:test.txt和input.txt;字母的哈夫曼編碼都保存在文件:hmfTree.txt;
2、 用戶自定義模式下,需要編碼的文件內容保存在ToBeTran.txt中;課本測試數據和源文件代碼分別保存在course.txt和sorse.txt中,在(1)中選擇不同的選項,則在編碼時調用相應的文件進行編碼,編碼結果保存在文件CodeFile.txt中。
3、 文件解碼時,調用文件CodeFile.txt進行解碼,得到的結果保存在文件TextFile.txt中。
4、 列印代碼文件:調用CodeFile.txt,結果顯示在終端並保存在文件CodePrin.txt中。
5、 列印哈夫曼樹:用凹入表形式把哈夫曼樹顯示在終端,同時將它保存在文件TreePrint..txt中。

#include <stdio.h>
#include<malloc.h>
#include <string.h>
#include<fstream>
#include<iostream>
using namespace std;

typedef struct {
unsigned int weight;
char ch1;
unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree;

typedef char **HuffmanCode;

typedef struct {
char ch;
char code[7];
}codenode,*code;

void select(HuffmanTree HT,int n,int & s1,int &s2){ //從哈夫曼樹中選擇出最小的兩個節點
for(int i=1;i<=n;i++)
if(!HT[i].parent){
s1=i; break;
}
for(i++;i<=n;i++)
if(!HT[i].parent){
s2=i; break;
}
if(HT[s1].weight-HT[s2].weight){
int temp; temp=s1; s1=s2; s2=temp;
}
for(i=1;i<=n;i++) //對數組進行遍歷,尋找最小的兩個節點
if(!HT[i].parent){
if(HT[i].weight<HT[s1].weight){
s2=s1; s1=i;
}
else if(HT[i].weight<HT[s2].weight&&i!=s1)
s2=i;
}
}

void prin(){ //終端輸出選擇菜單
cout<<"----------------------------------------------------\n\n"
<<" ∣ I---創建哈夫曼樹 ∣\n"
<<" ∣ ∣\n"
<<" ∣ E---文件編碼 ∣\n"
<<" ∣ ∣\n"
<<" ∣ D---文件解碼 ∣\n"
<<" ∣ ∣\n"
<<" ∣ P---列印代碼文件 ∣\n"
<<" ∣ ∣\n"
<<" ∣ T---印哈夫曼樹 ∣\n"
<<" ∣ ∣\n"
<<" ∣ O---哈夫曼樹的存儲結構 ∣\n"
<<" ∣ ∣\n"
<<" ∣ Q---退出 ∣\n"
<<"\n-----------------------------------------------------\n\n";
printf("選擇菜單功能選項:");
}

void output (HuffmanTree th,int n){ //輸出哈夫曼樹的存儲結構
int i=0;
cout<<"序號"<<" "<<"字元"<<" "<<"雙親"<<" "<<"左孩子"<<" "<<"右孩子"<<" "<<"權值"<<endl;
for(;i<2*n-1;i++){
th++;
cout<<i<<" "<<th->ch1<<" "<<th->parent<<" "<<th->lchild<<" "<<th->rchild<<" "<<th->weight <<endl;
}
}

void initial(HuffmanTree &HT,HuffmanCode &HC,int w[],int &n,char ch[],int &k){ //創建哈夫曼樹
cout<<"----------------------------------------------------\n\n"
<<" ∣ 1---自定義 ∣\n"
<<" ∣ ∣\n"
<<" ∣ 2---編碼課本測試數據 ∣\n"
<<" ∣ ∣\n"
<<" ∣ 3---編碼源程序 ∣\n"
<<"\n-----------------------------------------------------\n\n";
printf("選擇菜單功能選項:");
scanf("%d",&k);
if(k==1){
printf("輸入需要編碼的字元總數: ");
scanf("%d",&n);
printf("\n輸入需要編碼字元的權值:\n");
for(int d=0;d<n;d++) {
scanf("%d",&w[d]);
}
printf("\n輸入需要編碼的字元串: ");
scanf("%s",ch);
}
else if(k==2){
ifstream fin2 ("test.txt");
fin2>>n;
for(int d=0;d<n;d++)
fin2>>w[d];
fin2>>ch;
fin2.close();
}
else if(k==3){
ifstream fin1 ("input.txt");
fin1>>n;
for(int d=0;d<n;d++)
fin1>>w[d];
fin1>>ch;
fin1.close();
}
if(n<=1)
return;
int s1,s2,i,num=2*n-1;
HuffmanTree p;
HT=(HuffmanTree)malloc((num+1)*sizeof(HTNode));
for(p=HT+1,i=1;i<=n;i++,p++){
p->weight=w[i-1]; p->lchild=0; p->parent=0; p->rchild=0; p->ch1 =ch[i-1];
}
for(;i<=num;p++,i++){
p->weight=0; p->lchild=0; p->parent=0; p->rchild=0; p->ch1 ='$';
}
for(i=n+1;i<=num;i++){
select(HT,i-1,s1,s2);
HT[s1].parent=i; HT[s2].parent=i; HT[i].lchild=s1;
HT[i].rchild=s2; HT[i].weight=HT[s1].weight+HT[s2].weight;
}
HC=(HuffmanCode)malloc((n+1)*sizeof(char *));
char * temp=(char *)malloc(n*sizeof(char));
temp[n-1]='\0';
for(i=1;i<=n;i++){
int start=n-1;
for(int f=HT[i].parent,h=i;f;h=f,f=HT[f].parent)
if(HT[f].lchild==h)
temp[--start]='0';
else
temp[--start]='1';
HC[i]=(char *)malloc((n-start)*sizeof(char));
strcpy(HC[i],&temp[start]);
}
ofstream fout ("hfmTree.txt");
fout<<ch<<endl;
for(int j=1;j<=n;j++)
fout<<HC[j]<<endl;
fout.close();
free(temp);
}

void encoding(int n,int select){ //編碼:對文件TobeTran.txt進行解碼
char a[100],b[100][20];
ifstream fin ("hfmTree.txt");
fin>>a;
for(int j=0;j<n;j++) fin>>b[j];
fin.close();
ifstream fin1 ("course.txt");
ifstream fin2 ("sorse.txt");
ifstream fin3 ("ToBeTran.txt");
char s[1000];
if(select==3)
fin2>>s;
else if(select==2)
fin1>>s;
else fin3>>s;
ofstream fout ("CodeFile.txt");
while(s[0]!='\0'){
for(int i=0;s[i]!='\n'&&s[i]!='\0'&&i<30;i++ ){
for(int g=0;a[g]!=s[i];g++) ;
fout<<b[g];
}
fout<<'\n';
if(select==3)
fin2>>s;
else if(select==2)
fin1>>s;
else fin3>>s;
}
fin3.close();
fin2.close();
fin1.close();
fout.close();
}

void decoding(HuffmanTree ht,int n){ //解碼:對CodeFile.txt文件進行解碼
ifstream fin ("CodeFile.txt");
ofstream fout ("TextFile.txt");
char s[500];
fin>>s;
HuffmanTree head=ht+2*n-1;
int i=0;
while(s[0]!='\0'){
while(s[i]!='\0'){
if(s[i]=='1') head=ht+head->rchild;
else if(s[i]=='0') head=ht+head->lchild;
if((head->lchild)==0&&(head->rchild) ==0) {
fout<<(head->ch1);
head=ht+2*n-1;
}
i++;
}
fout<<' ' ;
i=0;
fin>>s;
}
fin.close();
fout.close();
}

void Print(){ //列印代碼文件,顯示在終端,每行50個代碼
ifstream fin ("CodeFile.txt");
char s[2000];
int j=0;
int i=1;
fin>>s;
ofstream fout ("CodePrin.txt");
while(s[0]!='\0'){
for(;s[j]!='\0';j++){
printf("%c",s[j]);
fout<<s[j];
if(i%50==0){
fout<<endl;
printf("\n");
}
i++;
}
j=0;
fin>>s;
}
fin.close();
printf("\n");
fout.close();
}

void printTree( HuffmanTree node,HuffmanTree node1, int level ) { //列印哈夫曼樹形(在參數的傳遞上,是文科給自己提出的意見才很好的解決了之後的操作難題^^)
if( node == NULL ) return;
if( node1->rchild!=0) {
printTree( node,node+node1->rchild, level + 1 );
}
fstream fout ;
fout.open ("TreePrint.txt",ios::in | ios::out|ios::ate);//這個挺有用的:在文件末尾加入內容
for( int i = 0; i < level; i++ ) {
fout<<"|……";
printf( "……");
}
fout<<node1->weight<<endl;
printf( "%d\n", node1->weight );
if( node1->lchild!=0 ) {
printTree( node,node+node1->lchild, level + 1 );
}
fout.close();
}

void main(){
int select;
int n;
char ch[100];
int w[100];
HuffmanTree HT=NULL;
HuffmanCode hc=NULL;
prin();
char c='I';
scanf("%c",&c);
while(c!='Q'){
switch(c){
case 'I':
initial(HT,hc,w,n,ch,select);
prin();
break;
case 'E':
encoding(n,select);
prin();
break;
case 'D':
decoding(HT,n);
prin();
break;
case 'P':
Print();
prin();
break;
case 'T':
printTree(HT,HT+2*n-1,1);
prin();
break;
case 'O':
output(HT,n);
prin();
break;
}
scanf("%c",&c);
}

}

註:
input.txt文件中保存一下數據:
88
56
26
89
45
62
78
61
13
20
29
89
46
51
25
86
123
20
10
9
57
6
1
57
62
2
37
61
15
19
89
91
2
8
19
49
67
18
19
64
35
67
61
61
84
20
30
50
84
19
28
84
67
31
67
29
20
10
56
56
12
23
56
23
45
85
16
29
94
68
35
97
58
21
29
3
94
58
16
21
64
29
84
64
59
19
48
37
186
!,./;':[]\1234567890-=+)(*&%$#<>"|{}

『叄』 赫夫曼樹

註:第一題 huffman 樹以及 huffman編碼
我將第二題與第三題與用鄰接矩陣存儲圖相關的操作放在了一起完成
第三題則是利用鄰接表

1.第一題
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#define LEN 8
#define MAXLEAF 6 // 最大葉子結點數目
#define MAXNODE (MAXLEAF*2)-1

typedef float ElemType;
typedef struct /* this structure stores the information of code */
{
int start; /* 存放編碼的起始位置右至左(高位至低位)*/
int bit[LEN]; /* 存放 huffman編碼 */
}HCode;

typedef HCode HuffCode[MAXLEAF];
typedef struct /* huffman tree結點的結構 */
{
int parent;
int LChild;
int RChild;
ElemType weight;
}HNode;

typedef HNode Huffman[MAXLEAF*2-1];

void createHuffmanTree(Huffman h,int leaves,ElemType *weight)
{
int i,j;
for(i=0;i<leaves*2-1;i++) /* 初始化huffman tree */
{
(h+i)->parent=-1;
(h+i)->LChild=-1;
(h+i)->RChild=-1;
(h+i)->weight=0;
}
for(i=0;i<leaves;i++) /* 給葉子賦權重 */
{
(h+i)->weight=*(weight+i);
}
/* 上一個循環葉子已經帶權,下面這個循環用來生成新根
* 新根數量為n-1
*/
for(i=0;i<leaves-1;i++)
{
ElemType m1, m2;
int m1_pos, m2_pos;
m1=m2=65536; /* m1存放最小的權值m2存放次小的權值 */
m1_pos=m2_pos=0; /* m1存放最小的權值對應下標m2存放次小的權值對應下標*/

for(j=0;j<leaves+i;j++)
{

if((h+j)->weight<m1&&(h+j)->parent==-1)
{
m2=m1;
m1=(h+j)->weight;
m2_pos=m1_pos;
m1_pos=j;
}

else if((h+j)->weight<m2&&(h+j)->parent==-1)
{
m2=(h+j)->weight;
m2_pos=j;
}
}

(h+leaves+i)->parent=-1; // 生成新根,無雙親-1
(h+leaves+i)->LChild=m1_pos; // 新根左孩子在數組中的下標
(h+leaves+i)->RChild=m2_pos; // 新根右孩子在數組中的下標
(h+m1_pos)->parent=leaves+i; // 原根的父親位置
(h+m2_pos)->parent=leaves+i; // 原根的父親位置

(h+leaves+i)->weight=m2+m1;
}
}

void huffmancode(Huffman h,HuffCode code,int leaves)
{
int i,j,p,c;
HCode hf;
/*從葉子結點開始向上回溯 從而計算出huffman code */
for(i=0;i<leaves;i++)
{
c=i;
p=h[i].parent;
hf.start=LEN-1;
while(p!=-1)
{
if(h[p].LChild==c)
{
hf.bit[hf.start]=0;
}
else
{
hf.bit[hf.start]=1;
}
--hf.start;
c=p;
p=h[c].parent;
}
for(j=hf.start+1;j<LEN;j++)
{
code[i].bit[j]=hf.bit[j];
}
code[i].start=hf.start+1;
}
}

void printhuffmantree(Huffman h,int leaves)
{
int i;
for(i=0;i<leaves*2-1;i++)
{
printf("weight=%-3.2f",h[i].weight);
printf("parent=%-3d",h[i].parent);
printf("LChild=%-3d",h[i].LChild);
printf("RChild=%-3d\n",h[i].RChild);
}
}

void printhuffcode(HuffCode hcode,char characters[])
{
int i,j;
for(i=0;i<strlen(characters);i++)
{
printf("%c的huffman編碼:",characters[i]);

for(j=hcode[i].start;j<LEN;j++)
{
printf("%d",hcode[i].bit[j]);
}
printf("\n");
}
}

int main(void)
{
Huffman h;
HuffCode hcode;
char characters[]={"abcdef"}; /* 待編碼的字元 */
ElemType weights[]={0.3,0.7,0.4,0.5,0.9,0.1}; /* 字元出現的頻率 */
createHuffmanTree(h,strlen(characters),weights);
printhuffmantree(h,strlen(characters));
huffmancode(h,hcode,sizeof(characters));
printhuffcode(hcode,characters);
system("pause");
return 0;
}

2.第二題 代碼如下,以及使用說明

例如有如下的圖

a->b
/ \
|
c

首先輸入頂點與弧的數目
3 2
提示輸入頂點
abc
輸入弧(弧頭弧尾)
ab
ca
那些插入以及刪除的操作已經放入主函數
顧回車後可以看到進行相關操作後圖的變化

#include<stdio.h>
#include<stdlib.h>

#define MAXVERT 10 // 需要在圖中進行插入操作所以設定一個最大值
#define INFINITE 32767
#define ERROR -1
#define FALSE 0
#define OK 1
typedef int ElemType;

enum maritype{DG,UDG,DN,UDN};

typedef struct
{
char vertex[MAXVERT];
ElemType arc[MAXVERT][MAXVERT];
int ArcNum;
int VertexNum;
}adjacentMatrix;

int locate(adjacentMatrix *G,char v)
{
int i, k=ERROR;
for(i=0;i<G->VertexNum;i++)
{
if(G->vertex[i]==v)
{
k=i;
break;
}
}
return(k);
}

void init(adjacentMatrix *G)
{
int i,j;
for(i=0;i<G->VertexNum;i++)
{
for(j=0;j<G->VertexNum;j++)
{
G->arc[i][j]=0;
}
}
}

void createDN(adjacentMatrix *G)
{
int i,j,k;
char v1,v2,blank;
printf("請輸入頂點與弧的數目 \n");
scanf("%d%d",&G->VertexNum,&G->ArcNum);
init(G);
printf("請輸入頂點(用字母表示):\n");
getchar();
for(i=0;i<G->VertexNum;i++)
{
scanf("%c",&G->vertex[i]);
}
getchar();
for(i=0;i<G->ArcNum;i++)
{
printf("請輸入弧%d的弧頭與弧尾",i+1);
scanf("%c%c",&v1,&v2);
getchar();
j=locate(G,v1);
k=locate(G,v2);
G->arc[j][k]=1;
}
}

int InsertVex(adjacentMatrix *G,char v) /* insert vertex */
{
int i;
if(G->VertexNum>MAXVERT-1)
{
return(FALSE);
}

G->vertex[G->VertexNum++]=v;
for(i=0;i<G->VertexNum;i++)
{
G->arc[i][G->VertexNum-1]=G->arc[G->VertexNum-1][i]=0;
}
return(OK);
}

int InsertAar(adjacentMatrix *G,char v,char w) //插入邊
{
int i,j;
i=locate(G,v);
j=locate(G,w);
if(i==-1||j==-1)
{
return(FALSE);
}
G->arc[i][j]=1;
return(OK);
}

int DeleteVex(adjacentMatrix *G,char v) //(刪除頂點)
{
int i, k;
k=locate(G,v);
if(k==-1)
{
printf("The vertex has not found\n");
return(FALSE);
}
for(i=k;i<G->VertexNum;i++)
{
G->vertex[i]=G->vertex[i+1];
}
--G->VertexNum;
return(OK);
}

int DeleteArc(adjacentMatrix *G,char v,char w)
{
int i,j;
i=locate(G,v);
j=locate(G,w);
if(i==-1||j==-1)
{
return(ERROR);
}
G->arc[i][j]=0;
return(OK);
}

void degree(adjacentMatrix *G)
{
int i, j, odsum, idsum, zero=0;
for(i=0;i<G->VertexNum;i++)
{
odsum=0;
idsum=0;
for(j=0;j<G->VertexNum;j++)
{
odsum+=G->arc[i][j];
idsum+=G->arc[j][i];

}
if(!odsum)
{
++zero;
}
printf("頂點 %c 的出度與入度是",G->vertex[i]);
printf("%3d%3d\n",odsum,idsum);
}
printf("度為0的頂點 %d\n",zero);
}

void print(adjacentMatrix *G)
{
int i,j;
for(i=0;i<G->VertexNum;i++)
{
printf("%8c",G->vertex[i]);
}
printf("\n");
for(i=0;i<G->VertexNum;i++)
{
for(j=0;j<G->VertexNum;j++)
{
if(!j)
{
printf("%c",G->vertex[i]);
}
printf("%8d",G->arc[i][j]);
}
printf("\n");
}
}

int main(void)
{
int k;
char v, w;
adjacentMatrix G;
createDN(&G);
print(&G); // 鄰接矩陣列印
degree(&G); // 求所有頂點出度入度 及度為0的點
InsertVex(&G,'f'); // 插入頂點f
InsertAar(&G,'f','c'); // 插入邊 fc
degree(&G); // 觀察插入邊頂點後度的變化
print(&G); // 鄰接矩陣列印
DeleteArc(&G,'f','c'); // 刪除邊 fc
print(&G); // 鄰接矩陣列印 觀察變化
DeleteVex(&G,'a'); // 刪除頂點a
print(&G); // 鄰接矩陣列印 觀察刪除頂點a後變化
system("pause");
return(0);
}

3.使用同上 示例圖也如上所畫 使用方式也基本一直
按你的要求只輸出 頂點的出度入度以及度為0的頂點

#include<stdio.h>
#include<stdlib.h>
#define MAX_VERTEX_NUM 10
#define ERROR -1
#define FALSE 0
#define OK 1

typedef char VertexType;
typedef struct ArcNode // 邊表的結構
{
int adjvex; // 與頂點相鄰接的頂點在表頭結點表(圖中)的位置
struct ArcNode *nextarc;
}ArcNode;

typedef struct VertexNode // 表頭結點表的結構
{
VertexType data;
ArcNode *firstarc;
}VertexNode;

typedef struct // 存放圖信息的結構
{
int vexnum, arcnum; // 頂點與弧的數目
VertexNode vertex[MAX_VERTEX_NUM];
}Adjlist;

int locate(Adjlist *G,char v)
{
int i, k=ERROR;
for(i=0;i<G->vexnum;i++)
{
if(G->vertex[i].data==v)
{
k=i;
break;
}
}
return(k);
}

void createDG(Adjlist *G)
{
int i, j, k;
char v1, v2;
ArcNode *s;
printf("輸入頂點與弧的數目 \n");
scanf("%d%d",&G->vexnum,&G->arcnum);
getchar();
printf("請輸入頂點(用字母表示):");
for(i=0;i<G->vexnum;i++) // 生成表頭結點表
{
scanf("%c",&G->vertex[i].data);
G->vertex[i].firstarc=NULL;
}
getchar();
for(i=0;i<G->arcnum;i++)
{
printf("請輸入第%d條邊的信息 弧尾與弧頭:",i+1);
scanf("%c%c",&v1,&v2);
getchar();
j=locate(G,v1);
k=locate(G,v2);
s=(ArcNode *)malloc(sizeof(ArcNode));
s->adjvex=k;
s->nextarc=G->vertex[j].firstarc;
G->vertex[j].firstarc=s;
}
}

void od(Adjlist *G)
{
int odsum, i;
ArcNode *p;
for(i=0;i<G->vexnum;i++) // 生成表頭結點表
{
odsum=0;
p=G->vertex[i].firstarc;
while(p)
{
++odsum;
p=p->nextarc;
}
printf("\n%c的出度是:%d\n ",G->vertex[i].data,odsum);
}
}

void ind(Adjlist *G)
{
int insum, i, j, k;
ArcNode *p;
for(i=0;i<G->vexnum;i++) // 生成表頭結點表
{
insum=0;

for(j=0;j<G->vexnum;j++)
{
if(i==j)
{
continue;
}
p=G->vertex[j].firstarc;
while(p)
{
if(p->adjvex==i)
{
++insum;
}
p=p->nextarc;
}
}
printf("\n%c的入度是:%d\n ",G->vertex[i].data,insum);
}
}

int main(void)
{
Adjlist G;
int i;
createDG(&G);
od(&G);
ind(&G);
system("pause");
return(0);
}

『肆』 簡述哈夫曼樹的性質。

哈 夫 曼 樹

2.9 二叉樹的應用

2.9.1 哈夫曼樹及應用

哈夫曼樹又稱最優樹(二叉樹),是一類帶權路徑最短的樹。構造這種樹的演算法最早是由哈夫曼(Huffman)1952年提出,這種樹在信息檢索中很有用。

結點之間的路徑長度:從一個結點到另一個結點之間的分支數目。

樹的路徑長度:從樹的根到樹中每一個結點的路徑長度之和。

結點的帶權路徑長度:從該結點到樹根之間的路徑長度與結點上權的乘積。

樹的帶權路徑長度:樹中所有葉子結點的帶權路徑長度之和,記作:

WPL為最小的二叉樹就稱作最優二叉樹或哈夫曼樹。

完全二叉樹不一定是最優二叉樹。

哈夫曼樹的構造:

(1)根據給定的n個權值{w1,w2,...,wn}構造n棵二叉樹的集合F={T1,T2,...,Tn},其中Ti中只有一個權值為wi的根結點,左右子樹為空;
(2)在F中選取兩棵根結點的權值為最小的數作為左、右子樹以構造一棵新的二叉樹,且置新的二叉樹的根結點的權值為左、右子樹上根結點的權值之和。
(3)將新的二叉樹加入到F中,刪除原兩棵根結點權值最小的樹;
(4)重復(2)和(3)直到F中只含一棵樹為止,這棵樹就是哈夫曼樹。

例1:

例2:

結點的存儲結構:

構造哈夫曼樹的演算法說明:

#define n /* 葉子總數 */
#define m 2*n-1 /* 結點總數 */
證:由性質3,葉子結點數 n0=n2+1,故哈夫曼樹結點總數為 n0+n2=n0+(n0-1)=2*n0-1

例3 在解某些判定問題時,利用哈夫曼樹獲得最佳判定演算法。

(a)

WPL=0.05*1+0.15*2+0.4*3+0.3*4+0.1*4=3.15

(b)(c)

WPL=0.4*1+0.3*2+0.15*3+0.05*4+0.1*4=2.05 WPL=0.05*3+0.15*3+0.4*2+0.3*2+0.1*2=2.2

哈夫曼編碼

從哈夫曼樹根結點開始,對左子樹分配代碼「0」,右子樹分配代碼「1」,一直到達葉子結點為止,然後將從樹根沿每條路徑到達葉子結點的代碼排列起來,便得到了哈夫曼編碼。

例,對電文 EMCAD 編碼。若等長編碼,則

EMCAD => 000001010011100 共15位

設各字母的使用頻度為 {E,M,C,A,D}={1,2,3,3,4}。用頻度為權值生成哈夫曼樹,並在葉子上標注對應的字母,樹枝分配代碼「0」或「1」:

各字母的編碼即為哈夫曼編碼: EMCAD => 000001011011 共12位

2.9.2 二叉排序樹

二叉排序樹是一種特殊結構的二叉樹,它作為一種表的組織手段,通常被稱為樹表。可以作為一種排序和檢索的手段。

定義 二叉排序樹或是空樹,或是具有下述性質的二叉樹:其左子樹上所有結點的數據值均小於根結點的數據值;右子樹上所有結點的數據值均大於或等於根結點的數據值。左子樹和右子樹又各是一棵二叉排序樹。

對二叉排序樹,若按中序遍歷就可以得到由小到大的有序序列。如上圖,中序遍歷得:

{2,3,4,8,9,9,10,13,15,18}

二叉排序樹的生成

對任意一組數據元素序列{R1,R2,...,Rn},要生成一棵二叉排序樹的過程為:

(1)令R1為二叉樹的根;
(2)若R2<R1,令R2為R1左子樹的根結點,否則R2為R1右子樹的根結點;
(3)對R3,...,Rn結點的插入方法同上。

例,數據元素序列{10,18,3,8,12,2,7,3},其生成二叉排序樹的過程如下:

二叉排序樹中結點的刪除

要求刪除一個結點後的二叉樹仍是一棵二叉排序樹。演算法思想,分以下幾種情況考慮:

(1)被刪除的結點是葉子結點,則只需修改其雙親結點的指針既可;
(2)被刪除結點p只有左子樹pL或右子樹pR,此時只要使左子樹pL或右子樹pR成為p雙親結點q的左子樹或右子樹即可。
(3)若被刪除結點p的左、右子樹均非空,有兩種做法:

*

令pL直接鏈接到q的左(或右)孩子鏈域上,pR鏈接到p結點中序前趨結點s上(s是pL最右下的結點);
*

以p結點的直接中序前趨或後繼替代p所指結點,然後再從原二叉排序樹中刪去該直接前趨或後繼。