討論赫夫曼編碼問題,赫夫曼編碼的思想就是變長編碼。變長編碼就是讓字元表中出現概率高的字元的編碼長度盡可能小,而出現概率高的字元的編碼長度相對較長。然後還要遵循字首碼的要求,就是任意乙個編碼都不是其他編碼的字首碼,這樣方便解碼。
對於下表中的字元和相應的出現概率,有對應圖中的編碼樹:
可以比較容易的看出來,每個葉節點就代表乙個字元,從根節點到葉節點走過的路徑拼接起來,就代表這個字元的編碼,比如f是1100,e是1101,而f和e是深度最深的節點也是概率最小的兩個節點。這也就是我們要求的赫夫曼編碼形式。這種最優的編碼形式,總是一顆滿的二叉樹。
算導上有大量的篇幅來論證用貪心演算法,每次選擇概率最小的兩個節點來,可以完成赫夫曼編碼。這裡只說實現方法。
由於每次都要找出出現概率最小的那個節點,彈出來,並刪掉,所以我們可以使用最小優先佇列來做。注意一點是,編碼樹的葉子節點個數等於字元的個數,而內部節點個數則等於字元的個數減去一,所以求內部節點的迴圈只需要n-1次即可,n為字元數。
小根堆操作:
最小優先佇列操作:#include #include using namespace std;
#define max_index 11
struct node
};//陣列從1號元素開始算起
int left_child(int i)
int right_child(int i)
int parent(int child)
void swap(node* a, node* b)
void print_heap(node* a, int len)
cout << endl;}/*
* 將乙個左右子樹都是小根堆的堆轉化成小根堆
*/void min_heapify(node heap, int root, int n)
if (r <= n && heap[min].freq > heap[r].freq)
if (min != root) }/*
* 構建乙個小根堆
*/void build_min_heap(node heap, int n)
}
赫夫曼編碼形成編碼樹:/*
* 最小優先佇列要實現的操作:
* * ①insert(s,x)
* * ②minimum(s)
* * ③extract_min(s)
* * ④decrease_key(s,x,k)
* */
/* * 最小優先佇列
*/struct min_priority_queue
};/*
* 返回最小元素
*/node* heap_minimum(min_priority_queue* mpq)
return mpq->min_heap + 1;}/*
* 彈出並移除最小的元素
*/node* heap_extract_min(min_priority_queue* mpq)
//這裡必須要新建乙個節點返回去,如果直接返回原節點,則會導致後面insert的時候,左右孩子的指標指向的內容發生變化
//新建乙個節點
node* min = new node();
//複製最小節點的內容到新建節點,最後將新建的節點的指標返回
*min = *(mpq->min_heap + 1);
swap(mpq->min_heap + 1, mpq->min_heap + mpq->len);
//刪除彈出的節點,防止記憶體洩露
delete (mpq->min_heap + mpq->len);
//將最後乙個節點從堆中去掉
(mpq->len)--;
//重新維護小根堆的性質
min_heapify(mpq->min_heap, 1, mpq->len);
//返回min
return min;}/*
* 把優先佇列中原來為x的元素的值,換成k,並維護最小堆的性質
*/void heap_decrease_key(min_priority_queue* mpq, int i, node* n)
mpq->min_heap[i].freq = n->freq;
while (i >= 1 && mpq->min_heap[i].freq < mpq->min_heap[parent(i)].freq) }/*
* 插入元素
*/void heap_insert(min_priority_queue* mpq, node* n)
/* * 列印節點陣列
*/void print_node_array(node* n_arr, int max_index)
cout << endl;
}
形成小根堆耗時o(n),而在huffman(min_priority_queue* mpq,huffman_tree* t)中的n-1次for迴圈,每次for 都要做常數次維護小根堆性質的操作,每次的複雜度為o(lgn),所以總共是:o(n+n*lgn)=o(nlgn)。//哈夫曼編碼樹
struct huffman_tree
};/*
* 赫夫曼編碼,返回編碼樹的頭結點
*/void huffman(min_priority_queue* mpq,huffman_tree* t)
// return heap_extract_min(mpq);
t->root=heap_extract_min(mpq);}/*
* 中序遍歷編碼樹
*/void print_coded_tree(node* root) }/*
* 刪除編碼樹的節點
*/void delete_coded_tree(node* root)
}int main() ;
node node_arr[max_index + 1];
for (int i = 0; i < 12; i++)
//新建乙個最小優先佇列物件,應用上面的陣列
min_priority_queue* mpq = new min_priority_queue(node_arr, max_index);
huffman_tree* t = new huffman_tree();
huffman(mpq,t);
print_coded_tree(t->root); //10個內部節點和原來的11個葉子節點,一共21個節點
delete_coded_tree(t->root);
return 0;
}
小白高階之貪心演算法 赫夫曼編碼
赫夫曼編碼是根據字元的使用頻率對字元進行編碼的一種編碼方法,其用於壓縮資料的效果非常好。一般用二叉樹表示赫夫曼編碼,使用頻率越低的字元,其深度越大。其中q表示乙個按頻率從小到大排序的優先佇列。在赫夫曼編碼的表示中,通常用字首碼的表達方法。字首碼即沒有任何碼字是其他碼字的字首,其作用是簡化解碼過程。用...
貪心演算法 赫夫曼編碼問題(Huffman)
赫夫曼編碼是一種廣泛用於資料壓縮的問題,該演算法的主要優勢在於節約了儲存和傳輸成本。舉乙個例子 假設要傳輸的資料為 那麼傳輸成本就是 45 3 30 3 29 3 10 3 8 3 5 3 381個字元 先合併最小頻率的2個字元對應的子樹,計算合併後的子樹的頻率 重新排序各個子樹 重複步驟1 重複步...
演算法導論之貪心演算法
參考 下面請看示例題 有n個商品,每個商品的重量為wi,為 pi,現有乙個揹包,最多能裝 的重量 其中 0 i問 怎樣裝能使包中裝入的商品價值最高 對於每個商品可以只裝該商品的一部分 偽 引數分別為 n 物品數 m 揹包最多能裝的重量 v 價值陣列 w重量陣列 void knapsack int n...