【資料結構X.11】程式碼實現 哈夫曼樹的建立,建立,構造,實現哈夫曼編碼
主題
程式碼實現哈夫曼樹的建立,建立,構造,實現哈夫曼編碼,實現思路和要點:
抓住哈夫曼樹的性質,每輪選出2個權值最小的點進行構造樹。
抓住哈夫曼編碼的性質,從根出發,向左標0,向右標1。
各種語言的實現可以看下面這個:
哈夫曼樹的各種語言java,python,c++,c,c#的實現
哈夫曼樹的構造思路:
網上說得都挺複雜的,看了半天還是看不懂,所以自己寫了個:
- 每輪選出2個權值最小的點,權值加和後,放到空的新位置,標註左孩子和右孩子節點。例如這裡,第一輪中,
d和e
點最小,權值的和為5,於是把空位置6更新為權值為5,左孩子d右孩子e。 - 一直這樣挑選,直到所有的點都被挑選過
used=1
,只剩下一個點沒有被used
。 - 利用這個得到的表去構造哈夫曼樹。這個過程其實可以放到一個
哈夫曼node集陣列
中,用for迴圈,從0到nodeNum
遍歷,一邊遍歷,一遍把左右節點連線上,最後返回Node[NodeNum-1]
,也就是這個沒有被used過
的節點。顯然,它是根節點。這個其實還挺巧妙的,從這個題得到的啟發:【資料結構X.8】程式碼實現和演算法解析 已知一顆樹的層次序列及每個節點的度, 編寫演算法構造這個樹的孩子兄弟連結串列 - 最後進行編碼。由於
樹的非遞迴後序遍歷具有能儲存其全部根節點的性質
,這是因為進行後序遍歷時,最後才會訪問祖先節點,所以,當訪問x節點時,其所有的祖先節點都存在棧
裡。利用這個性質,用棧儲存從祖先節點,到X節點的路徑,然後訪問棧裡的祖先節點,如果是左子樹,標0,如果是右子樹,就標1。
注意:
同一組節點,可以組成多個不同形狀的哈夫曼樹,他們的共同點是WPL值相同
構造1
哈夫曼樹可以有不同的構造,例如下面的:
他們的WPL值都相同
W
P
L
值
=
路
徑
長
度
∗
權
重
WPL值=路徑長度*權重
WPL值=路徑長度∗權重都是
23
∗
2
+
11
∗
3
+
5
∗
4
+
3
∗
4
+
2
∗
29
+
3
∗
14
+
4
∗
7
+
8
∗
4
=
271
23*2+11*3+5*4+3*4+2*29+3*14+4*7+8*4=271
23∗2+11∗3+5∗4+3∗4+2∗29+3∗14+4∗7+8∗4=271
僅僅是左右換了下順序
構造2
這裡雖然形狀改變了:
但是
路
徑
長
度
∗
權
重
路徑長度*權重
路徑長度∗權重沒有改變,依然滿足字首碼的性質
,也是哈夫曼樹。
5
∗
5
+
29
∗
2
+
7
∗
4
+
8
∗
3
+
14
∗
3
+
23
∗
2
+
3
∗
5
+
11
∗
3
=
271
5*5+29*2+7*4+8*3+14*3+23*2+3*5+11*3=271
5∗5+29∗2+7∗4+8∗3+14∗3+23∗2+3∗5+11∗3=271
程式碼實現:
函式呼叫:
#include "HalfManTree.h"
int main()
{
int num = 8;
int weight[num] = {5, 29, 7, 8, 14, 23, 3, 11};
//int weight[num] = {10, 5, 4, 2, 3, 6};
char data[num] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
HalfManItem item[MAX_SIZE];
for (int i = 0; i < MAX_SIZE; i++)
{
item[i].data = 0;
item[i].weight = 0;
item[i].lchild = item[i].rchild = 0;
item[i].used = false;
}
HalfManItem *ht = CreateHalfTable(item, data, weight, num);
//PrintHalfManTable(ht, num);
HalfManNode *tr = CreateHalfManTree(ht, num);
//VisitHalfManTree(tr);
char x = 'a';
std::cout << "\na: ";
GetHalfManCode(tr, x);
std::cout << "\nb: ";
GetHalfManCode(tr, 'b');
std::cout << "\nc: ";
GetHalfManCode(tr, 'c');
std::cout << "\nd: ";
GetHalfManCode(tr, 'd');
std::cout << "\ne: ";
GetHalfManCode(tr, 'e');
std::cout << "\nf: ";
GetHalfManCode(tr, 'f');
std::cout << "\ng: ";
GetHalfManCode(tr, 'g');
std::cout << "\nh: ";
GetHalfManCode(tr, 'h');
//GetHalfManCode(tr,'d');
}
函式定義:
#include <iostream>
#include <stack>
#include <queue>
#include <malloc.h>
#include <string.h>
#define MAX_SIZE 200
#define MAX_INT 10000
//哈夫曼樹的構造
typedef struct HalfManNode
{
int freq; //出現頻率
char ch; //儲存的字元
HalfManNode *lchild, *rchild;
} HalfManNode;
typedef struct HalfManItem
{
char data; //儲存字元
bool used; //是否已經加入樹了
int weight; //權重
int lchild, rchild; //左孩子、右孩子序號
};
bool Select2Min(HalfManItem *ht, int &nodeNum);
HalfManItem *CreateHalfTable(HalfManItem *item, char data[], int weight[], int &nodeNum);
/***
* 建立哈夫曼樹
* 輸入 : 資料和權重,總節點數目
*/
HalfManItem *CreateHalfTable(HalfManItem *item, char data[], int weight[], int &nodeNum)
{
for (int i = 0; i < nodeNum; i++)
{
item[i].data = data[i];
item[i].weight = weight[i];
item[i].lchild = item[i].rchild = -1;
item[i].used = false;
}
int parentNum = nodeNum - 1;
//由於有N個節點,每次插入都選擇兩個根節點權值最小的樹作為新節點左右子樹,並且建立一個新節點
//根據哈夫曼樹的性質,會新建N-1個新節點,一共需要選N-1次
for (int i = nodeNum; i < nodeNum + parentNum; i++)
{
if (Select2Min(item, nodeNum))
{
std::cout << "成功新建節點,左孩子:" << item[nodeNum - 1].lchild << " 右孩子: "
<< item[nodeNum - 1].rchild << " 權重:" << item[nodeNum - 1].weight << std::endl;
}
else
{
return item;
}
}
return item;
}
/**
* 選擇哈夫曼樹的兩個最小的節點
* 輸入:
* HalfManTree 樹的根節點
* n:節點數量
* *s1\*s2:
*/
bool Select2Min(HalfManItem *ht, int &nodeNum)
{
int min1 = MAX_INT, min2 = MAX_INT;
int minId1 = 0, minId2 = 0;
HalfManItem *p = ht;
int i = 0;
for (; p < ht + nodeNum; p++)
{
if (!p->used && p->weight < min1)
{
min2 = min1;
minId2 = minId1;
min1 = p->weight;
minId1 = i;
} //小於min1,min1更新了,min2也要更新
else if (!p->used && p->weight < min2)
{
min2 = p->weight;
minId2 = i;
}
//比min1大,但是比min2小的情況
i++;
}
if (min2 == MAX_INT)
{
return false;
}
//std::cout << min1<<"|"<<min2<<std::endl;
(ht + nodeNum)->weight = min1 + min2;
(ht + nodeNum)->data = ' ';
(ht + nodeNum)->lchild = minId1;
(ht + nodeNum)->rchild = minId2;
(ht + nodeNum)->used = false;
(ht + minId1)->used = true;
(ht + minId2)->used = true;
nodeNum += 1;
return true;
}
/**
* 列印二叉樹全部節點
*/
void PrintHalfManTable(HalfManItem table[], int nodeNum)
{
//std::cout << *table<<" ---- ";
for (int i = 0; i < nodeNum; i++)
{
std::cout << table[i].data << ": 左孩子:" << table[i].lchild << " 右孩子: "
<< table[i].rchild << " 權重:" << table[i].weight << std::endl;
}
return;
}
/***
* 建立哈夫曼樹,用一個陣列儲存一堆哈夫曼節點,最後返回根節點
*/
HalfManNode *CreateHalfManTree(HalfManItem *ht, int nodeNum)
{
HalfManNode *halfTree[nodeNum];
for (int i = 0; i < nodeNum; i++)
{
halfTree[i] = (HalfManNode *)malloc(sizeof(HalfManNode));
std::cout << "訪問節點" << ht[i].data << " 權重:" << ht[i].weight << " lchild:"
<< ht[i].lchild << " rchild:" << ht[i].rchild << std::endl;
halfTree[i]->ch = ht[i].data;
halfTree[i]->freq = ht[i].weight;
if (ht[i].lchild != -1)
{
halfTree[i]->lchild = halfTree[ht[i].lchild];
//std::cout << "當前節點:" << halfTree[i]->ch << " "<< "左孩子:" << halfTree[i]->lchild->ch;
}
else
{
halfTree[i]->lchild = NULL;
}
if (ht[i].rchild != -1)
{
halfTree[i]->rchild = halfTree[ht[i].rchild];
//std::cout << "右孩子:" << halfTree[i]->rchild->ch << std::endl;
}
else
{
halfTree[i]->rchild = NULL;
}
//std::cout << "訪問節點" << halfTree[i]->ch << " 權重:" << halfTree[i]->freq << " " << std::endl;
}
return halfTree[nodeNum - 1];
}
/***
* 訪問一下哈夫曼樹
*/
void VisitHalfManTree(HalfManNode *root)
{
if (root)
{
std::cout << "訪問節點" << root->ch << " 權重: " << root->freq << " " << std::endl;
VisitHalfManTree(root->lchild);
VisitHalfManTree(root->rchild);
}
}
/***
* 已有哈夫曼樹,獲得哈夫曼編碼
*
*/
void GetHalfManCode(HalfManNode *tr, char ch)
{
std::stack<HalfManNode *> s;
HalfManNode *p = tr, *r = NULL;
while (!s.empty() || p)
{
if (p)
{
s.push(p);
p = p->lchild;
}
else
{
p = s.top();
if (p->rchild && p->rchild != r)
{ //p有右孩子,且沒有被訪問過
p = p->rchild;
s.push(p);
p = p->lchild;
}
else
{
//std::cout << p->freq << " ";
//std::cout << p->ch<<" "<<ch << " "<<std::endl;
if (ch == p->ch)
{
std::stack<HalfManNode *> s1;
while (!s.empty())
{
s1.push(s.top());
s.pop();
}
while (!s1.empty())
{
p = s1.top();
s1.pop();
if (!s1.empty())
{
if (s1.top() == p->lchild)
{
std::cout << "0";
}
else if (s1.top() == p->rchild)
{
std::cout << "1";
}
else
{
std::cout << "error";
}
}
else
{
return;
}
}
}
s.pop();
r = p;
p = NULL;
}
}
}
}
參考:
其實沒怎麼參考,感覺網上的都寫的好複雜
哈夫曼樹的c實現
哈夫曼樹的各種語言java,python,c++,c,c#的實現
OBST(最優二叉搜尋樹)
題目:
已知某系統在通訊聯絡中只可能出現8種字元,其概率分別為0.05,0.29,0.07,0.08,0.14,0.23,0.03,0.11 試著設計赫夫曼編碼。
假設權w=(5,29,7,8,14,23,3,11),n=8,則m=15,按照上述演算法構造一顆哈夫曼樹
相關文章
- Java 樹結構實際應用 二(哈夫曼樹和哈夫曼編碼)Java
- 資料結構-哈夫曼樹(python實現)資料結構Python
- 高階資料結構---赫(哈)夫曼樹及java程式碼實現資料結構Java
- 哈夫曼樹及其編碼
- 資料結構與演算法——赫夫曼樹(哈夫曼樹)資料結構演算法
- 哈夫曼編碼
- 哈夫曼編碼 —— Lisp 與 Python 實現LispPython
- 【資料結構】哈夫曼樹的建立與基礎應用資料結構
- 重學資料結構之哈夫曼樹資料結構
- 哈夫曼樹
- 資料結構與演算法:哈夫曼樹資料結構演算法
- 最優二叉樹(哈夫曼樹)Java實現二叉樹Java
- 哈夫曼樹學習筆記筆記
- 6.6 哈夫曼樹及其應用
- 資料結構之哈弗曼樹資料結構
- Task A2 哈夫曼樹的應用
- C#資料結構-赫夫曼樹C#資料結構
- 從哈夫曼編碼中我們學到了什麼?
- 一本正經的聊資料結構(6):最優二叉樹 —— 哈夫曼樹資料結構二叉樹
- 哈夫曼樹及其應用(檔案壓縮)
- 哈夫曼實現檔案壓縮解壓縮(c語言)C語言
- 一本正經的聊資料結構(7):哈弗曼編碼資料結構
- 小任的第一篇部落格-哈夫曼樹
- 有趣的赫夫曼樹
- 曼哈頓距離與切比雪夫距離
- 曼哈頓距離與切比雪夫距離的互化
- 資料結構實驗一:順序表的建立與操作實現、順序表實現約瑟夫環問題資料結構
- 資料結構 - 樹,三探之程式碼實現資料結構
- 資料結構 - 圖之程式碼實現資料結構
- 樹狀的資料結構的建立資料結構
- 【資料結構】串(String、StringBuilder、StringBuffer)的JAVA程式碼實現資料結構UIJava
- Java關於資料結構的實現:樹Java資料結構
- 關於資料壓縮、信源編碼、赫夫曼碼的一些研究,以及由此引出對決策樹模型的資訊理論本質的思考模型
- 資料結構 - 雜湊表,三探之程式碼實現資料結構
- 基本資料結構實現--單連結串列【含測試程式碼】資料結構
- 基本資料結構實現--雙連結串列【含測試程式碼】資料結構
- 具體實現程式碼@資料結構探險——順序表資料結構
- 資料結構-二叉搜尋樹的實現資料結構