二叉树的基本操作及哈夫曼编码/译码系统的实现
生活随笔
收集整理的這篇文章主要介紹了
二叉树的基本操作及哈夫曼编码/译码系统的实现
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
二叉樹的基本操作及哈夫曼編碼/譯碼系統的實現
實驗目的和要求
- 掌握二叉樹的二叉鏈表存儲表示及遍歷操作實現方法。
- 實現二叉樹遍歷運算的應用:求二叉樹中葉結點個數、結點總數、二叉樹的高度,交換二叉樹的左右子樹。
- 掌握二叉樹的應用——哈夫曼編碼的實現。
二叉樹的基本操作
#include <stdio.h> #include <stdlib.h>//二叉樹結點結構體typedef char ElemType; typedef struct btnode {ElemType element;struct btnode *lChild;struct btnode *rChild; }BTNode;//創建新節點 BTNode* NewNode(ElemType x, BTNode *ln, BTNode *rn);//先序構建二叉樹 BTNode* PreCreateBT(BTNode *t);//先序遍歷 void PreOrder(BTNode*t); //中序遍歷 void MidOrder(BTNode*t); //后序遍歷 void BehOrder(BTNode*t);//清空二叉樹 void Clear(BTNode * t);//創建新節點BTNode* NewNode(ElemType x, BTNode *ln, BTNode *rn) {BTNode *p = (BTNode*)malloc(sizeof(BTNode));p->element = x;p->lChild = ln;p->rChild = rn; }//先序構建二叉樹 BTNode* PreCreateBT(BTNode *t) {char ch;ch = getchar();if(ch =='#')t = NULL;else{t = (BTNode *)malloc(sizeof(BTNode));t->element = ch;t->lChild = PreCreateBT(t->lChild);t->rChild = PreCreateBT(t->rChild);}return t; }//先序遍歷 void PreOrder(BTNode*t) {if(!t)return;printf("%c ", t->element);PreOrder(t->lChild);PreOrder(t->rChild); }//中序遍歷 void MidOrder(BTNode*t) {if(!t)return;MidOrder(t->lChild);printf("%c ", t->element);MidOrder(t->rChild); }//后序遍歷 void BehOrder(BTNode*t) {if(!t)return;BehOrder(t->lChild);BehOrder(t->rChild); printf("%c ", t->element); }//清空二叉樹 void Clear(BTNode * t) {if(!t)return;Clear(t->lChild);Clear(t->rChild);free(t); }int main() {BTNode *p;p = PreCreateBT(p);printf("\n先序遍歷為:");PreOrder(p);printf("\n中序遍歷為:");MidOrder(p);printf("\n后序遍歷為:");BehOrder(p);printf("\n");Clear(p);system("pause");return 0; }采用先序輸入,如果子樹為空樹就用#代替。
小小例子請笑納:
二叉樹的基本操作進階
#include <stdio.h> #include <stdlib.h>//二叉樹結點結構體typedef char ElemType; typedef struct btnode {ElemType element;struct btnode *lChild;struct btnode *rChild; }BTNode;//創建新節點 BTNode* NewNode(ElemType x, BTNode *ln, BTNode *rn);//先序構建二叉樹 BTNode* PreCreateBT(BTNode *t);//先序遍歷 void PreOrder(BTNode*t); //中序遍歷 void MidOrder(BTNode*t); //后序遍歷 void BehOrder(BTNode*t);//清空二叉樹 void Clear(BTNode * t); //求二叉樹的結點個數 int Size(BTNode *t); //求二叉樹的高度 int Height(BTNode *t); //交換二叉樹所有左右子樹 void Swap(BTNode *t);//創建新節點BTNode* NewNode(ElemType x, BTNode *ln, BTNode *rn) {BTNode *p = (BTNode*)malloc(sizeof(BTNode));p->element = x;p->lChild = ln;p->rChild = rn; }//先序構建二叉樹 BTNode* PreCreateBT(BTNode *t) {char ch;ch = getchar();if(ch =='#')t = NULL;else{t = (BTNode *)malloc(sizeof(BTNode));t->element = ch;t->lChild = PreCreateBT(t->lChild);t->rChild = PreCreateBT(t->rChild);}return t; }//先序遍歷 void PreOrder(BTNode*t) {if(!t)return;printf("%c ", t->element);PreOrder(t->lChild);PreOrder(t->rChild); }//中序遍歷 void MidOrder(BTNode*t) {if(!t)return;MidOrder(t->lChild);printf("%c ", t->element);MidOrder(t->rChild); }//后序遍歷 void BehOrder(BTNode*t) {if(!t)return;BehOrder(t->lChild);BehOrder(t->rChild); printf("%c ", t->element); }//清空二叉樹 void Clear(BTNode * t) {if(!t)return;Clear(t->lChild);Clear(t->rChild);free(t); } //求二叉樹的結點個數 int Size(BTNode *t) {if(!t)return 0;return Size(t->lChild) + Size(t->rChild) + 1; } //求葉結點的個數 int LeafSize(BTNode *t) {if(!t)return 0;else if(!(t->lChild) && !(t->rChild))return 1;return LeafSize(t->lChild) + LeafSize(t->rChild);}//求二叉樹的高度 int Height(BTNode *t) {if(!t)return 0;return Height(t->lChild) >= Height(t->rChild) ? Height(t->lChild) +1 : Height(t->rChild) + 1; }//交換二叉樹所有左右子樹 void Swap(BTNode *t) {BTNode *temp;if(!t)return;temp = t->lChild;t->lChild = t->rChild;t->rChild = temp;Swap(t->lChild);Swap(t->rChild); } int main() {BTNode *p;p = PreCreateBT(p);printf("\n先序遍歷為:");PreOrder(p);printf("\n中序遍歷為:");MidOrder(p);printf("\n后序遍歷為:");BehOrder(p);printf("\n二叉樹的結點個數為: %d", Size(p));printf("\n葉結點的個數: %d", LeafSize(p));printf("\n二叉樹的高度為: %d", Height(p));Swap(p);printf("\n交換后先序遍歷為:");PreOrder(p);printf("\n交換后中序遍歷為:");MidOrder(p);printf("\n交換后后序遍歷為:");BehOrder(p);printf("\n");Clear(p);system("pause");return 0; }- 小小例子,附帶二叉樹圖
哈夫曼編碼/譯碼系統實現
點擊查看詳解
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <limits.h>// 統計字符頻度的臨時結點 typedef struct {unsigned char uch; // 以8bits為單元的無符號字符unsigned long weight; // 每類(以二進制編碼區分)字符出現頻度 } TmpNode;// 哈夫曼樹結點 typedef struct {unsigned char uch; // 以8bits為單元的無符號字符unsigned long weight; // 每類(以二進制編碼區分)字符出現頻度char *code; // 字符對應的哈夫曼編碼(動態分配存儲空間)int parent, lchild, rchild; // 定義雙親和左右孩子 } HufNode, *HufTree;// 選擇最小和次小的兩個結點,建立哈夫曼樹調用 void select(HufNode *huf_tree, unsigned int n, int *s1, int *s2) {// 找最小unsigned int i;unsigned long min = ULONG_MAX;for(i = 0; i < n; ++i) if(huf_tree[i].parent == 0 && huf_tree[i].weight < min){min = huf_tree[i].weight;*s1 = i;}huf_tree[*s1].parent=1; // 標記此結點已被選中// 找次小min=ULONG_MAX;for(i = 0; i < n; ++i) if(huf_tree[i].parent == 0 && huf_tree[i].weight < min){min = huf_tree[i].weight;*s2 = i;} } // 建立哈夫曼樹 void CreateTree(HufNode *huf_tree, unsigned int char_kinds, unsigned int node_num) {unsigned int i;int s1, s2;for(i = char_kinds; i < node_num; ++i) { select(huf_tree, i, &s1, &s2); // 選擇最小的兩個結點huf_tree[s1].parent = huf_tree[s2].parent = i; huf_tree[i].lchild = s1; huf_tree[i].rchild = s2; huf_tree[i].weight = huf_tree[s1].weight + huf_tree[s2].weight; } }// 生成哈夫曼編碼 void HufCode(HufNode *huf_tree, unsigned char_kinds) {unsigned int i;int cur, next, index;char *code_tmp = (char *)malloc(256*sizeof(char)); // 暫存編碼,最多256個葉子,編碼長度不超多255code_tmp[256-1] = '\0'; for(i = 0; i < char_kinds; ++i) {index = 256-1; // 編碼臨時空間索引初始化// 從葉子向根反向遍歷求編碼for(cur = i, next = huf_tree[i].parent; next != 0; cur = next, next = huf_tree[next].parent) if(huf_tree[next].lchild == cur) code_tmp[--index] = '0'; // 左‘0’else code_tmp[--index] = '1'; // 右‘1’huf_tree[i].code = (char *)malloc((256-index)*sizeof(char)); // 為第i個字符編碼動態分配存儲空間 strcpy(huf_tree[i].code, &code_tmp[index]); // 正向保存編碼到樹結點相應域中} free(code_tmp); // 釋放編碼臨時空間 }// 壓縮函數 int compress(char *ifname, char *ofname) {unsigned int i, j;unsigned int char_kinds; // 字符種類unsigned char char_temp; // 暫存8bits字符unsigned long file_len = 0;FILE *infile, *outfile;TmpNode node_temp;unsigned int node_num;HufTree huf_tree;char code_buf[256] = "\0"; // 待存編碼緩沖區unsigned int code_len;/*** 動態分配256個結點,暫存字符頻度,** 統計并拷貝到樹結點后立即釋放*/TmpNode *tmp_nodes =(TmpNode *)malloc(256*sizeof(TmpNode)); // 初始化暫存結點for(i = 0; i < 256; ++i){tmp_nodes[i].weight = 0;tmp_nodes[i].uch = (unsigned char)i; // 數組的256個下標與256種字符對應}// 遍歷文件,獲取字符頻度infile = fopen(ifname, "rb");// 判斷輸入文件是否存在if (infile == NULL)return -1;fread((char *)&char_temp, sizeof(unsigned char), 1, infile); // 讀入一個字符while(!feof(infile)){++tmp_nodes[char_temp].weight; // 統計下標對應字符的權重,利用數組的隨機訪問快速統計字符頻度++file_len;fread((char *)&char_temp, sizeof(unsigned char), 1, infile); // 讀入一個字符}fclose(infile);// 排序,將頻度為零的放最后,剔除for(i = 0; i < 256-1; ++i) for(j = i+1; j < 256; ++j)if(tmp_nodes[i].weight < tmp_nodes[j].weight){node_temp = tmp_nodes[i];tmp_nodes[i] = tmp_nodes[j];tmp_nodes[j] = node_temp;}// 統計實際的字符種類(出現次數不為0)for(i = 0; i < 256; ++i)if(tmp_nodes[i].weight == 0) break;char_kinds = i;if (char_kinds == 1){outfile = fopen(ofname, "wb"); // 打開壓縮后將生成的文件fwrite((char *)&char_kinds, sizeof(unsigned int), 1, outfile); // 寫入字符種類fwrite((char *)&tmp_nodes[0].uch, sizeof(unsigned char), 1, outfile); // 寫入唯一的字符fwrite((char *)&tmp_nodes[0].weight, sizeof(unsigned long), 1, outfile); // 寫入字符頻度,也就是文件長度free(tmp_nodes);fclose(outfile);}else{node_num = 2 * char_kinds - 1; // 根據字符種類數,計算建立哈夫曼樹所需結點數 huf_tree = (HufNode *)malloc(node_num*sizeof(HufNode)); // 動態建立哈夫曼樹所需結點 // 初始化前char_kinds個結點for(i = 0; i < char_kinds; ++i) { // 將暫存結點的字符和頻度拷貝到樹結點huf_tree[i].uch = tmp_nodes[i].uch; huf_tree[i].weight = tmp_nodes[i].weight;huf_tree[i].parent = 0; } free(tmp_nodes); // 釋放字符頻度統計的暫存區// 初始化后node_num-char_kins個結點for(; i < node_num; ++i) huf_tree[i].parent = 0; CreateTree(huf_tree, char_kinds, node_num); // 創建哈夫曼樹HufCode(huf_tree, char_kinds); // 生成哈夫曼編碼// 寫入字符和相應權重,供解壓時重建哈夫曼樹outfile = fopen(ofname, "wb"); // 打開壓縮后將生成的文件fwrite((char *)&char_kinds, sizeof(unsigned int), 1, outfile); // 寫入字符種類for(i = 0; i < char_kinds; ++i){fwrite((char *)&huf_tree[i].uch, sizeof(unsigned char), 1, outfile); // 寫入字符(已排序,讀出后順序不變)fwrite((char *)&huf_tree[i].weight, sizeof(unsigned long), 1, outfile); // 寫入字符對應權重}// 緊接著字符和權重信息后面寫入文件長度和字符編碼fwrite((char *)&file_len, sizeof(unsigned long), 1, outfile); // 寫入文件長度infile = fopen(ifname, "rb"); // 以二進制形式打開待壓縮的文件fread((char *)&char_temp, sizeof(unsigned char), 1, infile); // 每次讀取8bitswhile(!feof(infile)){// 匹配字符對應編碼for(i = 0; i < char_kinds; ++i)if(char_temp == huf_tree[i].uch)strcat(code_buf, huf_tree[i].code);// 以8位(一個字節長度)為處理單元while(strlen(code_buf) >= 8){char_temp = '\0'; // 清空字符暫存空間,改為暫存字符對應編碼for(i = 0; i < 8; ++i){char_temp <<= 1; // 左移一位,為下一個bit騰出位置if(code_buf[i] == '1')char_temp |= 1; // 當編碼為"1",通過或操作符將其添加到字節的最低位}fwrite((char *)&char_temp, sizeof(unsigned char), 1, outfile); // 將字節對應編碼存入文件strcpy(code_buf, code_buf+8); // 編碼緩存去除已處理的前八位}fread((char *)&char_temp, sizeof(unsigned char), 1, infile); // 每次讀取8bits}// 處理最后不足8bits編碼code_len = strlen(code_buf);if(code_len > 0){char_temp = '\0'; for(i = 0; i < code_len; ++i){char_temp <<= 1; if(code_buf[i] == '1')char_temp |= 1;}char_temp <<= 8-code_len; // 將編碼字段從尾部移到字節的高位fwrite((char *)&char_temp, sizeof(unsigned char), 1, outfile); // 存入最后一個字節}// 關閉文件fclose(infile);fclose(outfile);// 釋放內存for(i = 0; i < char_kinds; ++i)free(huf_tree[i].code);free(huf_tree);} }//compress// 解壓函數 int extract(char *ifname, char *ofname) {unsigned int i;unsigned long file_len;unsigned long writen_len = 0; // 控制文件寫入長度FILE *infile, *outfile;unsigned int char_kinds; // 存儲字符種類unsigned int node_num;HufTree huf_tree;unsigned char code_temp; // 暫存8bits編碼unsigned int root; // 保存根節點索引,供匹配編碼使用infile = fopen(ifname, "rb"); // 以二進制方式打開壓縮文件// 判斷輸入文件是否存在if (infile == NULL)return -1;// 讀取壓縮文件前端的字符及對應編碼,用于重建哈夫曼樹fread((char *)&char_kinds, sizeof(unsigned int), 1, infile); // 讀取字符種類數if (char_kinds == 1){fread((char *)&code_temp, sizeof(unsigned char), 1, infile); // 讀取唯一的字符fread((char *)&file_len, sizeof(unsigned long), 1, infile); // 讀取文件長度outfile = fopen(ofname, "wb"); // 打開壓縮后將生成的文件while (file_len--)fwrite((char *)&code_temp, sizeof(unsigned char), 1, outfile); fclose(infile);fclose(outfile);}else{node_num = 2 * char_kinds - 1; // 根據字符種類數,計算建立哈夫曼樹所需結點數 huf_tree = (HufNode *)malloc(node_num*sizeof(HufNode)); // 動態分配哈夫曼樹結點空間// 讀取字符及對應權重,存入哈夫曼樹節點for(i = 0; i < char_kinds; ++i) {fread((char *)&huf_tree[i].uch, sizeof(unsigned char), 1, infile); // 讀入字符fread((char *)&huf_tree[i].weight, sizeof(unsigned long), 1, infile); // 讀入字符對應權重huf_tree[i].parent = 0;}// 初始化后node_num-char_kins個結點的parentfor(; i < node_num; ++i) huf_tree[i].parent = 0;CreateTree(huf_tree, char_kinds, node_num); // 重建哈夫曼樹(與壓縮時的一致)// 讀完字符和權重信息,緊接著讀取文件長度和編碼,進行解碼fread((char *)&file_len, sizeof(unsigned long), 1, infile); // 讀入文件長度outfile = fopen(ofname, "wb"); // 打開壓縮后將生成的文件root = node_num-1;while(1){fread((char *)&code_temp, sizeof(unsigned char), 1, infile); // 讀取一個字符長度的編碼// 處理讀取的一個字符長度的編碼(通常為8位)for(i = 0; i < 8; ++i){// 由根向下直至葉節點正向匹配編碼對應字符if(code_temp & 128)root = huf_tree[root].rchild;elseroot = huf_tree[root].lchild;if(root < char_kinds){fwrite((char *)&huf_tree[root].uch, sizeof(unsigned char), 1, outfile);++writen_len;if (writen_len == file_len) break; // 控制文件長度,跳出內層循環root = node_num-1; // 復位為根索引,匹配下一個字符}code_temp <<= 1; // 將編碼緩存的下一位移到最高位,供匹配}if (writen_len == file_len) break; // 控制文件長度,跳出外層循環}// 關閉文件fclose(infile);fclose(outfile);// 釋放內存free(huf_tree);} }//extract()int main() {while(1){int opt, flag = 0; // 每次進入循環都要初始化flag為0char ifname[256], ofname[256]; // 保存輸入輸出文件名// 輸入所選擇操作類型的數字代號:1:壓縮,2:解壓,3:退出printf("Please input the number of operations:\n 1: compress\n 2: extract\n 3: quit\n");scanf("%d", &opt);if (opt == 3)break;else{printf("Please input the infile name: ");fflush(stdin); // 清空標準輸入流,防止干擾gets函數讀取文件名gets(ifname);printf("Please input the outfile name: ");fflush(stdin);gets(ofname);}switch(opt){case 1: printf("Compressing……\n");flag = compress(ifname, ofname); // 壓縮,返回值用于判斷是否文件名不存在break; case 2: printf("Extracting……\n");flag = extract(ifname, ofname); // 解壓,返回值用于判斷是否文件名不存在break; }if (flag == -1)printf("Sorry, infile \"%s\" doesn't exist!\n", ifname); // 如果標志為‘-1’則輸入文件不存在elseprintf("Operation is done!\n"); // 操作完成}return 0; }總結
以上是生活随笔為你收集整理的二叉树的基本操作及哈夫曼编码/译码系统的实现的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python实现画图哆啦A梦
- 下一篇: 力扣——整数反转(Java)