b. Cài đặt bằng con trỏ liờn kết.
5.6. Vớ dụ về cõy nhị phõ n mó hoỏ Huffman (Đọc thờm)
Giả sử ta cú một thụng bỏo đú là một chuỗi cỏc kớ tự, trong đú mỗi kớ tự xuất hiện độc lập với cựng một xỏc suất tại bất kỳ vị trớ nào trong thụng bỏo. Mục đớch của chỳng ta muốn mó hoỏ chuỗi thụng bỏo này thành một chuỗi cỏc bớt 0,1. Tất nhiờn việc mó hoỏ phải đảm bảo cú thể giải mó trở
lại được chuỗi thụng bỏo ban đầu. Ta thấy cú một tớnh chất mà phương phỏp mó hoỏ phải thoả món đú là: mó hoỏ của mỗi kớ tự khụng được là tiền tố của chuỗi mó hoỏ của kớ tự khỏc, chẳng hạn a được mó thành 10, b
được mó thành 1011 là khụng thể được vỡ khi giải mó sẽ nhầm. Ta gọi tớnh
chất này là tớnh chất tiền tố. Hơn nữa ta phải tỡm bộ mó tiết kiệm nhất, tức là thụng bỏo được mó thành ớt bớt nhất cú thể được.
Vớ dụ:
Thụng bỏo gồm 5 kớ tự a, b, c, d, e xuất hiện với xỏc suất tương ứng là: 0.10, 0.40, 0.12, 0.18, 0.20. Cú thể mó hoỏ như sau:
ký hiệu xỏc suất mó cỏch 1 mó cỏch 2 a .10 000 000 b .40 001 11 c .12 010 01 d .18 011 001 e .20 100 10
Với cỏch mó húa 1 ta dễ dàng giải mó cho một chuổi cỏc bớt 0,1. chẳng hạn chuỗi 000001010 sẽ cho kết quả abc.
Ta cũng dễ dàng kiểm tra cỏch mó hoỏ 2 là hợp lệ (thoả món tớnh chất tiền tố). Cỏch mó hoỏ 1 cho độ dài mó trung bỡnh của một kớ tự là 3, cỏch mó hoỏ 2 cho độ dài trung bỡnh mó 2.24 (vỡ 3*0.10 + 2*0.40 + 2*0.12 + 3*0.18 + 2* 0.20 = 2.24)
Cú cỏch mó hoỏ nào cho độ dài mó trung bỡnh nhỏ hơn 2.24 khụng? Một phương phỏp để tỡm lời giải tối ưu gọi là giải thuật Huffman:
* Giải thuật Huffman
Thành lập cõy nhị phõn từ tập hợp cỏc kớ hiệu trong thụng bỏo, mỗi kớ hiệu là một nỳt lỏ của cõy. Cỏch thành lập cõy như sau:
Chọn hai nỳt a,b cú xỏc suất nhỏ nhất trong tập hợp cỏc nỳt, giả sử xỏc suất nỳt a nhỏ hơn hoặc bằng xỏc suất nỳt b. Thành lập cõy nhị phõn cú nỳt gốc x, con trỏi là a, con phải là b. Nỳt x cú xỏc suất bằng tổng xỏc suất của a và b.
Tập hợp cỏc nỳt bõy giờ là cỏc nỳt cũn lại (đó loại bỏ a,b) và nỳt x. Lặp lại một cỏch đệ qui quỏ trỡnh trờn tập hợp đang xột cho đến khi tập này chỉ cũn lại một nỳt.
Mó của a, b sẽ tỡm được bằng cỏch lấy mó của x nối thờm 0 cho a và 1 cho b. Mó của nỳt gốc là rỗng.
Như vậy thực chất quỏ trỡnh trờn là ta xõy dựng một cõy nhị phõn từ tập hợp cỏc ký tự muốn mó hoỏ, cuối cựng ta được một cõy nhị phõn cú lỏ là cỏc ký tự đú. Mó của một ký tự là một đường đi trờn cõy từ gốc đến lỏ chứa kớ tự, với 0 đi sang trỏi cũn 1 đi sang phải. í tưởng của giải thuật mó hoỏ cũng hết sức đơn giản, ta tỡm bộ mó cho cỏc kớ tự sao cho cỏc kớ tự cú tần suất xuất hiện cao (xỏc suất xuất hiện là lớn) sẽ được mó ngắn (gần với gốc) để độ dài trung bỡnh để mó hoỏ một kớ tự là nhỏ nhất.
* Cài đặt giải thuật Huffman
Dưới đõy là một chương trỡnh hoàn chỉnh để cỏc bạn cú thể cài đặt và chạy thử. Ở đõy, chỳng tụi cài đặt bằng mảng (dựng con nhỏy) nhưng cỏc bạn cú thể dể dàng chuyển sang cài đặt bằng con trỏ.
const maxnode=100;
Type
TREE= array [1..maxnode] of record
leftchild: integer; rightchild: integer; parent: integer;
end;
ALPHABET= array[1..maxnode] of record
symbol: char;
probability: real;
leaf: integer; {mỗi kớ tự kết hợp với một nỳt lỏ của cõy}
end;
FOREST= array[1..maxnode] of record
weight: real; root: integer;
end;
var
A:ALPHABET; F:FOREST;
i,lastnode,lasttree,n,root:integer; s:string;
procedure initialize;{ Khởi tạo giỏ trị ban đầu }
var i: integer;
begin
write('Cho so nut ban dau?'); readln(lasttree);
for i:=1 to lasttree do begin
write('ky hieu cua nut thu ',i,' = '); readln(A[i].symbol);
write('xac suat cua nut thu ',i,' = '); readln(A[i].probability);
A[i].leaf:=i;
{hiện tại, mỗi nỳt là một cõy trong forest}
F[i].weight:=a[i].probability; F[i].root:=i; T[i].leftchild:=0; T[i].rightchild:=0; T[i].parent:=0; end; end;
{ Tỡm hai cõy cú trọng lượng bộ nhất}
procedure lightOnes( var least,second: integer);
var i:integer; begin if F[1].weight<=F[2].weight then begin least:=1; second:=2; end else begin least:=2;
second:=1;
end;
for i:=3 to lasttree do
if F[i].weight< F[least].weight then begin
second:=least; least:=i;
end
else
if F[i].weight<F[second].weight then second:=i;
end;
{ tạo cõy mới từ hai cõy cú trọng lượng bộ nhất }
Function create(lefttree,righttree: integer): integer;
begin
{cấp phỏt gốc mới}
lastnode:=lastnode+1;
{cho hai con trỏ của nỳt gốc trỏ hai nỳt gốc của hai cõy con}
T[lastnode].leftchild:=F[lefttree].root; T[lastnode].rightchild:=F[righttree].root;
{con trỏ trỏ nỳt cha của nỳt gốc = null}
T[lastnode].parent:=0;
{nỳt gốc mới là nỳt cha của hai nỳt gốc của hai cõy con}
T[F[lefttree].root].parent:=lastnode; T[F[righttree].root].parent:=lastnode; create:=lastnode;
end;
{ Giải thuật HUFFMAN }
procedure huffman(var root:integer);
var i,j:integer; newroot:integer; begin {lặp cho đến khi chỉ cũn một nỳt} while lasttree>1 do begin
lightones(i,j);{tỡm hai nỳt bộ nhất} newroot:=create(i,j);
{tạo cõy từ hai nỳt bộ nhất}
{trọng số nỳt mới bằng tổng trọng số của hai nỳt con}
F[i].weight:=F[i].weight+F[j].weight; F[i].root:=newroot; F[j]:=F[lasttree]; lasttree:=lasttree-1; end; root:=newroot; end; { Mó hoỏ một ký tự thành cỏc bit 0,1 } function encodeOneChar(ch:char):string; var i,j,l:integer; S_encode:string; begin
{khởi tạo chuỗi mó rỗng} S_encode:='';
i:=0;
{tỡm kớ tự cần mó hoỏ trong bảng Alphabet}
repeat
i:=i+1
until (i>n) or (A[i].symbol=ch);{n là số kớ tự trong bảng}
{nếu tỡm thấy}
if i<=n then
{lần ngược từ lỏ chứa kớ tự này lờn gốc cõy để tỡm chuỗi bớt 0,1 nếu nỳt nằm bờn trỏi thỡ thờm 0, nằm bờn phải thỡ thờm 1}
repeat
j:=T[i].parent; { lấy nỳt cha của nỳt i}
if T[j].leftchild=i then
S_encode:=’0’ + s_encode
else S_encode:=’1’+ S_encode;
i:=j;
else
begin
{khụng tỡm thấy kớ tự muốn mó hoỏ trong bảng kớ tự} writeln(' ký tự ',ch,' sai ');
S_encode:=S_encode+'*';
end;
encodeOneChar:=S_encode;
end;
{ Mó hoỏ một chuỗi ký tự thành chuỗi bớt 0,1 }
function encode(s:string):string;
var s_encode:string;
begin
s_encode:='';
for i:=1 to length(s) do
s_encode:=s_encode+encodeOneChar(s[i]); encode:=S_encode;
end;
{ Giải mó một chuỗi bớt 0,1 thành chuỗi ký tự thụng thường a,b,c}
function decode(s:string):string;
var S_decode: string; i,curr:integer; err:boolean; begin i:=1; S_decode:=''; Err:=false;
while (i<= length(s)) and ( not Err ) do
begin
curr:=root;
while ((T[curr].leftchild<>0) or
(T[curr].rightchild<>0))and (not Err) do begin
if s[i]='0' then
curr:=T[curr].leftchild
else Err:=true
else if s[i]='1' then
If t[curr].rightchild<>0
then
curr:=T[curr].rightchild
else Err:=true else Err:=true; i:=i+1 end; S_decode:=S_decode+A[curr].symbol end; if Err then begin
writeln(' Lỗi trong chuỗi muốn giải mó '); S_decode:=''; end; decode:=S_decode; end; {chương trỡnh chớnh } BEGIN initialize; lastnode:=lasttree; n:=lasttree;
{xõy dựng cõy Huffman} huffman(root);
{kiểm tra giải thuật mó hoỏ}
repeat
write('cho chuỗi ký tự muốn mó hoỏ '); readln(s);
writeln(encode(s));
write('cho chuỗi bớt 0,1 để giải mó '); readln(s);
until s='';
END.