Tải bản đầy đủ - 0 (trang)
§9. TÌM KIẾM (SEARCHING)

§9. TÌM KIẾM (SEARCHING)

Tải bản đầy đủ - 0trang

Cấu trúc dữ liệu và giải thuật



127



Nếu k[median] < X thì có nghĩa là đoạn từ k[inf] tới k[median] chỉ chứa tồn khố < X, ta

tiến hành tìm kiếm tiếp với đoạn từ k[median+1]tới k[sup].

Nếu k[median] > X thì có nghĩa là đoạn từ k[median] tới k[sup] chỉ chứa toàn khố > X, ta

tiến hành tìm kiếm tiếp với đoạn từ k[inf] tới k[median-1].

Nếu k[median] = X thì việc tìm kiếm thành cơng (kết thúc q trình tìm kiếm).

Q trình tìm kiếm sẽ thất bại nếu đến một bước nào đó, đoạn tìm kiếm là rỗng (inf > sup).

{Tìm kiếm nhị phân trên dãy khoá k[1] ≤ k[2] ≤ … ≤ k[n]; hàm này thử tìm xem trong dãy có khố nào = X khơng, nếu thấy nó

trả về chỉ số của khố ấy, nếu khơng thấy nó trả về 0}

function BinarySearch(X: TKey): Integer;

var

inf, sup, median: Integer;

begin

inf := 1; sup := n;

while inf ≤ sup do

begin

median := (inf + sup) div 2;

if k[median] = X then

begin

BinarySearch := median;

Exit;

end;

if k[median] < X then inf := median + 1

else sup := median - 1;

end;

BinarySearch := 0;

end;



Người ta đã chứng minh được độ phức tạp tính tốn của thuật tốn tìm kiếm nhị phân trong

trường hợp tốt nhất là O(1), trong trường hợp xấu nhất là O(lgn) và trong trường hợp trung

bình là O(lgn). Tuy nhiên, ta không nên quên rằng trước khi sử dụng tìm kiếm nhị phân, dãy

khố phải được sắp xếp rồi, tức là thời gian chi phí cho việc sắp xếp cũng phải tính đến. Nếu

dãy khố ln ln biến động bởi phép bổ sung hay loại bớt đi thì lúc đó chi phí cho sắp xếp

lại nổi lên rất rõ làm bộc lộ nhược điểm của phương pháp này.



9.4. CÂY NHỊ PHÂN TÌM KIẾM (BINARY SEARCH TREE - BST)

Cho n khố k[1..n], trên các khố có quan hệ thứ tự tồn phần. Cây nhị phân tìm kiếm ứng với

dãy khố đó là một cây nhị phân mà mỗi nút chứa giá trị một khoá trong n khoá đã cho, hai

giá trị chứa trong hai nút bất kỳ là khác nhau. Đối với mọi nút trên cây, tính chất sau ln

được thoả mãn:

Mọi khố nằm trong cây con trái của nút đó đều nhỏ hơn khố ứng với nút đó.

Mọi khố nằm trong cây con phải của nút đó đều lớn hơn khố ứng với nút đó



Lê Minh Hồng



128



Chun đề



4



2



1



6



3



5



7



9



Hình 38: Cây nhị phân tìm kiếm



Thuật tốn tìm kiếm trên cây có thể mơ tả chung như sau:

Trước hết, khố tìm kiếm X được so sánh với khố ở gốc cây, và 4 tình huống có thể xảy ra:

Khơng có gốc (cây rỗng): X khơng có trên cây, phép tìm kiếm thất bại

X trùng với khố ở gốc: Phép tìm kiếm thành cơng

X nhỏ hơn khố ở gốc, phép tìm kiếm được tiếp tục trong cây con trái của gốc với cách

làm tương tự

X lớn hơn khoá ở gốc, phép tìm kiếm được tiếp tục trong cây con phải của gốc với cách

làm tương tự

Giả sử cấu trúc một nút của cây được mô tả như sau:

type

PNode = ^TNode; {Con trỏ chứa liên kết tới một nút}

TNode = record {Cấu trúc nút}

Info: TKey; {Trường chứa khoá}

Left, Right: PNode; {con trỏ tới nút con trái và phải, trỏ tới nil nếu khơng có nút con trái (phải)}

end;

Gốc của cây được lưu trong con trỏ Root. Cây rỗng thì Root = nil



Thuật tốn tìm kiếm trên cây nhị phân tìm kiếm có thể viết như sau:

{Hàm tìm kiếm trên BST, nó trả về nút chứa khố tìm kiếm X nếu tìm thấy, trả về nil nếu khơng tìm thấy}

function BSTSearch(X: TKey): PNode;

var

p: PNode;

begin

p := Root; {Bắt đầu với nút gốc}

while p ≠ nil do

if X = p^.Info then Break;

else

if X < p^.Info then p := p^.Left

else p := p^.Right;

BSTSearch := p;

end;



Thuật tốn dựng cây nhị phân tìm kiếm từ dãy khố k[1..n] cũng được làm gần giống quá

trình tìm kiếm. Ta chèn lần lượt các khoá vào cây, trước khi chèn, ta tìm xem khố đó đã có

trong cây hay chưa, nếu đã có rồi thì bỏ qua, nếu nó chưa có thì ta thêm nút mới chứa khố

cần chèn và nối nút đó vào cây nhị phân tìm kiếm tại mối liên kết vừa rẽ sang khiến quá trình

tìm kiếm thất bại.

ĐHSPHN 1999-2004



Cấu trúc dữ liệu và giải thuật



129



{Thủ tục chèn khoá X vào BST}

procedure BSTInsert(X);

var

p, q: PNode;

begin

q := nil; p := Root; {Bắt đầu với p = nút gốc; q là con trỏ chạy đuổi theo sau}

while p ≠ nil do

begin

q := p;

if X = p^.Info then Break;

else {X ≠ p^.Info thì cho p chạy sang nút con, q^ ln giữ vai trò là cha của p^}

if X < p^.Info then p := p^.Left

else p := p^.Right;

end;

if p = nil then {Khố X chưa có trong BST}

begin

New(p); {Tạo nút mới}

p^.Info := X; {Đưa giá trị X vào nút mới tạo ra}

p^.Left := nil; p^.Right := nil; {Nút mới khi chèn vào BST sẽ trở thành nút lá}

if Root = nil then Root := NewNode {BST đang rỗng, đặt Root là nút mới tạo}

else {Móc NewNode^ vào nút cha q^}

if X < q^.Info then q^.Left := NewNode

else q^.Right := NewNode;

end;

end;



Phép loại bỏ trên cây nhị phân tìm kiếm khơng đơn giản như phép bổ sung hay phép tìm kiếm.

Muốn xố một giá trị trong cây nhị phân tìm kiếm (Tức là dựng lại cây mới chứa tất cả những

giá trị còn lại), trước hết ta tìm xem giá trị cần xố nằm ở nút D nào, có ba khả năng xảy ra:

Nút D là nút lá, trường hợp này ta chỉ việc đem mối nối cũ trỏ tới nút D (từ nút cha của D)

thay bởi nil, và giải phóng bộ nhớ cấp cho nút D (Hình 39).

4



4



2



1



6



3



5



2



7



1



9



6



3



7



9



Hình 39: Xóa nút lá ở cây BST



Nút D chỉ có một nhánh con, khi đó ta đem nút gốc của nhánh con đó thế vào chỗ nút D,

tức là chỉnh lại mối nối: Từ nút cha của nút D không nối tới nút D nữa mà nối tới nhánh

con duy nhất của nút D. Cuối cùng, ta giải phóng bộ nhớ đã cấp cho nút D (Hình 40)



Lê Minh Hồng



130



Chun đề



4



4



2



5



2

7



1



3



7



1



6



3



6



9



9



Hình 40. Xóa nút chỉ có một nhánh con trên cây BST



Nút D có cả hai nhánh con trái và phải, khi đó có hai cách làm đều hợp lý cả:

Hoặc tìm nút chứa khoá lớn nhất trong cây con trái, đưa giá trị chứa trong đó sang nút D,

rồi xố nút này. Do tính chất của cây BST, nút chứa khố lớn nhất trong cây con trái

chính là nút cực phải của cây con trái nên nó khơng thể có hai con được, việc xố đưa

về hai trường hợp trên (Hình 41)

4



3



2



5



1



2



3



7



5



1



6



7



9



6



9



Hình 41: Xóa nút có cả hai nhánh con trên cây BST thay bằng nút cực phải của cây con trái



Hoặc tìm nút chứa khố nhỏ nhất trong cây con phải, đưa giá trị chứa trong đó sang nút

D, rồi xố nút này. Do tính chất của cây BST, nút chứa khố nhỏ nhất trong cây con

phải chính là nút cực trái của cây con phải nên nó khơng thể có hai con được, việc xố

đưa về hai trường hợp trên.

4



2



5



5



2

7



1



3



7



6



1



3



6



9



9



Hình 42: Xóa nút có cả hai nhánh con trên cây BST thay bằng nút cực trái của cây con phải



ĐHSPHN 1999-2004



Cấu trúc dữ liệu và giải thuật



131



{Thủ tục xoá khoá X khỏi BST}

procedure BSTDelete(X: TKey);

var

p, q, Node, Child: PNode;

begin

p := Root; q := nil; {Về sau, khi p trỏ sang nút khác, ta luôn giữ cho q^ luôn là cha của p^}

while p ≠ nil do {Tìm xem trong cây có khố X khơng?}

begin

if p^.Info = X then Break; {Tìm thấy}

q := p;

if X < p^.Info then p := p^.Left

else p := p^.Right;

end;

if p = nil then Exit; {X khơng tồn tại trong BST nên khơng xố được}

if (p^.Left ≠ nil) and (p^.Right ≠ nil) then {p^ có cả con trái và con phải}

begin

Node := p; {Giữ lại nút chứa khoá X}

q := p; p := p^.Left; {Chuyển sang nhánh con trái để tìm nút cực phải}

while p^.Right ≠ nil do

begin

q := p; p := p^.Right;

end;

Node^.Info := p^.Info; {Chuyển giá trị từ nút cực phải trong nhánh con trái lên Node^}

end;

{Nút bị xố giờ đây là nút p^, nó chỉ có nhiều nhất một con}

{Nếu p^ có một nút con thì đem Child trỏ tới nút con đó, nếu khơng có thì Child = nil}

if p^.Left ≠ nil then Child := p^.Left

else Child := p^.Right;

if p = Root then Root := Child; {Nút p^ bị xoá là gốc cây}

else {Nút bị xố p^ khơng phải gốc cây thì lấy mối nối từ cha của nó là q^ nối thẳng tới Child}

if q^.Left = p then q^.Left := Child

else q^.Right := Child;

Dispose(p);

end;



Trường hợp trung bình, thì các thao tác tìm kiếm, chèn, xố trên BST có độ phức tạp là O(lgn).

Còn trong trường hợp xấu nhất, cây nhị phân tìm kiếm bị suy biến thì các thao tác đó đều có

độ phức tạp là O(n), với n là số nút trên cây BST.

Nếu ta mở rộng hơn khái niệm cây nhị phân tìm kiếm như sau: Giá trị lưu trong một nút lớn

hơn hoặc bằng các giá trị lưu trong cây con trái và nhỏ hơn các giá trị lưu trong cây con phải.

Thì chỉ cần sửa đổi thủ tục BSTInsert một chút, khi chèn lần lượt vào cây n giá trị, cây BST sẽ

có n nút (có thể có hai nút chứa cùng một giá trị). Khi đó nếu ta duyệt các nút của cây theo

kiểu trung thứ tự (inorder traversal), ta sẽ liệt kê được các giá trị lưu trong cây theo thứ tự

tăng dần. Phương pháp sắp xếp này người ta gọi là Tree Sort. Độ phức tạp tính tốn trung

bình của Tree Sort là O(nlgn).

Phép tìm kiếm trên cây BST sẽ kém hiệu quả nếu như cây bị suy biến, người ta có nhiều cách

xoay xở để tránh trường hợp này. Đó là phép quay cây để dựng cây nhị phân cân đối AVL,

hay kỹ thuật dựng cây nhị phân tìm kiếm tối ưu. Những kỹ thuật này ta có thể tham khảo

trong các tài liệu khác về cấu trúc dữ liệu và giải thuật.



Lê Minh Hoàng



132



Chuyên đề



9.5. PHÉP BĂM (HASH)

Tư tưởng của phép băm là dựa vào giá trị các khoá k[1..n], chia các khoá đó ra thành các

nhóm. Những khố thuộc cùng một nhóm có một đặc điểm chung và đặc điểm này khơng

có trong các nhóm khác. Khi có một khố tìm kiếm X, trước hết ta xác định xem nếu X thuộc

vào dãy khố đã cho thì nó phải thuộc nhóm nào và tiến hành tìm kiếm trên nhóm đó.

Một ví dụ là trong cuốn từ điển, các bạn sinh viên thường dán vào 26 mảnh giấy nhỏ vào các

trang để đánh dấu trang nào là trang khởi đầu của một đoạn chứa các từ có cùng chữ cái đầu.

Để khi tra từ chỉ cần tìm trong các trang chứa những từ có cùng chữ cái đầu với từ cần tìm.

A B



Z



Một ví dụ khác là trên dãy các khố số tự nhiên, ta có thể chia nó là làm m nhóm, mỗi nhóm

gồm các khố đồng dư theo mơ-đun m.

Có nhiều cách cài đặt phép băm:

Cách thứ nhất là chia dãy khoá làm các đoạn, mỗi đoạn chứa những khoá thuộc cùng một

nhóm và ghi nhận lại vị trí các đoạn đó. Để khi có khố tìm kiếm, có thể xác định được

ngay cần phải tìm khố đó trong đoạn nào.

Cách thứ hai là chia dãy khố làm m nhóm, Mỗi nhóm là một danh sách nối đơn chứa các

giá trị khoá và ghi nhận lại chốt của mỗi danh sách nối đơn. Với một khố tìm kiếm, ta xác

định được phải tìm khố đó trong danh sách nối đơn nào và tiến hành tìm kiếm tuần tự trên

danh sách nối đơn đó. Với cách lưu trữ này, việc bổ sung cũng như loại bỏ một giá trị khỏi

tập hợp khoá dễ dàng hơn rất nhiều phương pháp trên.

Cách thứ ba là nếu chia dãy khố làm m nhóm, mỗi nhóm được lưu trữ dưới dạng cây nhị

phân tìm kiếm và ghi nhận lại gốc của các cây nhị phân tìm kiếm đó, phương pháp này có

thể nói là tốt hơn hai phương pháp trên, tuy nhiên dãy khố phải có quan hệ thứ tự tồn

phần thì mới làm được.



9.6. KHỐ SỐ VỚI BÀI TỐN TÌM KIẾM

Mọi dữ liệu lưu trữ trong máy tính đều được số hố, tức là đều được lưu trữ bằng các đơn vị

Bit, Byte, Word v.v… Điều đó có nghĩa là một giá trị khố bất kỳ, ta hồn tồn có thể biết

được nó được mã hoá bằng con số như thế nào. Và một điều chắc chắn là hai khoá khác nhau

sẽ được lưu trữ bằng hai số khác nhau.

Đối với bài toán sắp xếp, ta khơng thể đưa việc sắp xếp một dãy khố bất kỳ về việc sắp xếp

trên một dãy khoá số là mã của các khoá. Bởi quan hệ thứ tự trên các con số đó có thể khác

với thứ tự cần sắp của các khoá.

ĐHSPHN 1999-2004



Cấu trúc dữ liệu và giải thuật



133



Nhưng đối với bài tốn tìm kiếm thì khác, với một khố tìm kiếm, câu trả lời hoặc là “Khơng

tìm thấy” hoặc là “Có tìm thấy và ở chỗ …” nên ta hồn tồn có thể thay các khố bằng các

mã số của nó mà khơng bị sai lầm, chỉ lưu ý một điều là: hai khoá khác nhau phải mã hố

thành hai số khác nhau mà thơi.

Nói như vậy có nghĩa là việc nghiên cứu những thuật tốn tìm kiếm trên các dãy khố số rất

quan trọng, và dưới đây ta sẽ trình bày một số phương pháp đó.



9.7. CÂY TÌM KIẾM SỐ HỌC (DIGITAL SEARCH TREE - DST)

Xét dãy khoá k[1..n] là các số tự nhiên, mỗi giá trị khố khi đổi ra hệ nhị phân có z chữ số nhị

phân (bit), các bit này được đánh số từ 0 (là hàng đơn vị) tới z - 1 từ phải sang trái.

Ví dụ:

bit



3



2



1



0



11 =



1



0



1



1



(z = 4)



Hình 43: Đánh số các bit



Cây tìm kiếm số học chứa các giá trị khố này có thể mơ tả như sau: Trước hết, nó là một cây

nhị phân mà mỗi nút chứa một giá trị khố. Nút gốc có tối đa hai cây con, ngồi giá trị khố

chứa ở nút gốc, tất cả những giá trị khố có bit cao nhất là 0 nằm trong cây con trái, còn tất cả

những giá trị khố có bit cao nhất là 1 nằm ở cây con phải. Đối với hai nút con của nút gốc,

vấn đề tương tự đối với bit z - 2 (bit đứng thứ nhì từ trái sang).

So sánh cây tìm kiếm số học với cây nhị phân tìm kiếm, chúng chỉ khác nhau về cách chia hai

cây con trái/phải. Đối với cây nhị phân tìm kiếm, việc chia này được thực hiện bằng cách so

sánh với khoá nằm ở nút gốc, còn đối với cây tìm kiếm số học, nếu nút gốc có mức là d thì

việc chia cây con được thực hiện theo bit thứ d tính từ trái sang (bit z - d) của mỗi khoá.

Ta nhận thấy rằng những khoá bắt đầu bằng bit 0 chắc chắn nhỏ hơn những khố bắt đầu bằng

bit 1, đó là điểm tương đồng giữa cây nhị phân tìm kiếm và cây tìm kiếm số học: Với mỗi nút

nhánh: Mọi giá trị chứa trong cây con trái đều nhỏ hơn giá trị chứa trong cây con phải (Hình

44).

6

0



1



5

0



8

1



2



7

0

4



Hình 44: Cây tìm kiếm số học

Lê Minh Hồng



0

10



1

12



1

11



6

5

2

7

8

10

12

11

4



=

=

=

=

=

=

=

=

=



0110

0101

0010

0111

1000

1010

1100

1011

0100



134



Chun đề



Giả sử cấu trúc một nút của cây được mô tả như sau:

type

PNode = ^TNode; {Con trỏ chứa liên kết tới một nút}

TNode = record {Cấu trúc nút}

Info: TKey; {Trường chứa khoá}

Left, Right: PNode; {con trỏ tới nút con trái và phải, trỏ tới nil nếu khơng có nút con trái (phải)}

end;

Gốc của cây được lưu trong con trỏ Root. Ban đầu nút Root = nil (cây rỗng)



Với khố tìm kiếm X, việc tìm kiếm trên cây tìm kiếm số học có thể mơ tả như sau: Ban đầu

đứng ở nút gốc, xét lần lượt các bit của X từ trái sang phải (từ bit z - 1 tới bit 0), hễ gặp bit

bằng 0 thì rẽ sang nút con trái, nếu gặp bit bằng 1 thì rẽ sang nút con phải. Quá trình cứ tiếp

tục như vậy cho tới khi gặp một trong hai tình huống sau:

Đi tới một nút rỗng (do rẽ theo một liên kết nil), quá trình tìm kiếm thất bại do khố X

khơng có trong cây.

Đi tới một nút mang giá trị đúng bằng X, quá trình tìm kiếm thành cơng

{Hàm tìm kiếm trên cây tìm kiếm số học, nó trả về nút chứa khố tìm kiếm X nếu tìm thấy, trả về nil nếu khơng tìm thấy. z

là độ dài dãy bit biểu diễn một khoá}

function DSTSearch(X: TKey): PNode;

var

b: Integer;

p: PNode;

begin

b := z; p := Root; {Bắt đầu với nút gốc}

while (p ≠ nil) and (p^.Info ≠ X) do {Chưa gặp phải một trong 2 tình huống trên}

begin

b := b - 1; {Xét bit b của X}

if then p := p^.Left {Gặp 0 rẽ trái}

else p := p^.Right; {Gặp 1 rẽ phải}

end;

DSTSearch := p;

end;



Thuật tốn dựng cây tìm kiếm số học từ dãy khố k[1..n] cũng được làm gần giống q trình

tìm kiếm. Ta chèn lần lượt các khoá vào cây, trước khi chèn, ta tìm xem khố đó đã có trong

cây hay chưa, nếu đã có rồi thì bỏ qua, nếu nó chưa có thì ta thêm nút mới chứa khố cần

chèn và nối nút đó vào cây tìm kiếm số học tại mối nối rỗng vừa rẽ sang khiến quá trình tìm

kiếm thất bại



ĐHSPHN 1999-2004



Cấu trúc dữ liệu và giải thuật



135



{Thủ tục chèn khố X vào cây tìm kiếm số học}

procedure DSTInsert(X: TKey);

var

b: Integer;

p, q: PNode;

begin

b := z;

p := Root;

while (p ≠ nil) and (p^.Info ≠ X) do

begin

b := b - 1; {Xét bit b của X}

q := p; {Khi p chạy xuống nút con thì q^ ln giữ vai trò là nút cha của p^}

if then p := p^.Left {Gặp 0 rẽ trái}

else p := p^.Right; {Gặp 1 rẽ phải}

end;

if p = nil then {Giá trị X chưa có trong cây}

begin

New(p); {Tạo ra một nút mới p^}

p^.Info := X; {Nút mới tạo ra sẽ chứa khoá X}

p^.Left := nil; p^.Right := nil; {Nút mới đó sẽ trở thành một lá của cây}

if Root = nil then Root := p {Cây đang là rỗng thì nút mới thêm trở thành gốc}

else {Khơng thì móc p^ vào mối nối vừa rẽ sang từ q^}

if then q^.Left := p

else q^.Right := p;

end;

end;



Muốn xố bỏ một giá trị khỏi cây tìm kiếm số học, trước hết ta xác định nút chứa giá trị cần

xố là nút D nào, sau đó tìm trong nhánh cây gốc D ra một nút lá bất kỳ, chuyển giá trị chứa

trong nút lá đó sang nút D rồi xoá nút lá.

{Thủ tục xoá khoá X khỏi cây tìm kiếm số học}

procedure DSTDelete(X: TKey);

var

b: Integer;

p, q, Node: PNode;

begin

{Trước hết, tìm kiếm giá trị X xem nó nằm ở nút nào}

b := z;

p := Root;

while (p ≠ nil) and (p^.Info ≠ X) do

begin

b := b - 1;

q := p; {Mỗi lần p chuyển sang nút con, ta luôn đảm bảo cho q^ là nút cha của p^}

if then p := p^.Left

else p := p^.Right;

end;

if p = nil then Exit; {X không tồn tại trong cây thì khơng xố được}

Node := p; {Giữ lại nút chứa khoá cần xoá}

while (p^.Left ≠ nil) or (p^.Right ≠ nil) do {chừng nào p^ chưa phải là lá}

begin

q := p; {q chạy đuổi theo p, còn p chuyển xuống một trong 2 nhánh con}

if p^.Left ≠ nil then p := p^.Left

else p := p^.Right;

end;

Node^.Info := p^.Info; {Chuyển giá trị từ nút lá p^ sang nút Node^}

if Root = p then Root := nil {Cây chỉ gồm một nút gốc và bây giờ xoá cả gốc}

else {Cắt mối nối từ q^ tới p^}

if q^.Left = p then q^.Left := nil

else q^.Right := nil;

Dispose(p);

end;

Lê Minh Hoàng



136



Chuyên đề



Về mặt trung bình, các thao tác tìm kiếm, chèn, xố trên cây tìm kiếm số học đều có độ phức

tạp là O(min(z, lgn)) còn trong trường hợp xấu nhất, độ phức tạp của các thao tác đó là O(z),

bởi cây tìm kiếm số học có chiều cao khơng q z + 1.



9.8. CÂY TÌM KIẾM CƠ SỐ (RADIX SEARCH TREE - RST)

Trong cây tìm kiếm số học, cũng như cây nhị phân tìm kiếm, phép tìm kiếm tại mỗi bước phải

so sánh giá trị khoá X với giá trị lưu trong một nút của cây. Trong trường hợp các khố có cấu

trúc lớn, việc so sánh này có thể mất nhiều thời gian.

Cây tìm kiếm cơ số là một phương pháp khắc phục nhược điểm đó, nội dung của nó có thể

tóm tắt như sau:

Trong cây tìm kiếm cơ số là một cây nhị phân, chỉ có nút lá chứa giá trị khố, còn giá trị chứa

trong các nút nhánh là vơ nghĩa. Các nút lá của cây tìm kiếm cơ số đều nằm ở mức z + 1.

Đối với nút gốc của cây tìm kiếm cơ số, nó có tối đa hai nhánh con, mọi khoá chứa trong nút

lá của nhánh con trái đều có bit cao nhất là 0, mọi khố chứa trong nút lá của nhánh con phải

đều có bit cao nhất là 1.

Đối với hai nhánh con của nút gốc, vấn đề tương tự với bit thứ z - 2, ví dụ với nhánh con trái

của nút gốc, nó lại có tối đa hai nhánh con, mọi khố chứa trong nút lá của nhánh con trái đều

có bit thứ z - 2 là 0 (chúng bắt đầu bằng hai bit 00), mọi khoá chứa trong nút lá của nhánh con

phải đều có bit thứ z - 2 là 1 (chúng bắt đầu bằng hai bit 01)…

Tổng quát với nút ở mức d, nó có tối đa hai nhánh con, mọi nút lá của nhánh con trái chứa

khố có bit z - d là 0, mọi nút lá của nhánh con phải chứa khố có bit thứ z - d là 1 (Hình 45).



0



0



0



0



0010



0



1



0



1



1



2



1



1



1



1



1



0



0



0



0



1



0



4



5



7



8



10



11



12



0100



0101



0111



1000



1010



1011



1100



Hình 45: Cây tìm kiếm cơ số



Khác với cây nhị phân tìm kiếm hay cây tìm kiếm số học. Cây tìm kiếm cơ số được khởi tạo

gồm có một nút gốc, và nút gốc tồn tại trong suốt q trình sử dụng: nó khơng bao giờ bị

xoá đi cả.



ĐHSPHN 1999-2004



Cấu trúc dữ liệu và giải thuật



137



Để tìm kiếm một giá trị X trong cây tìm kiếm cơ số, ban đầu ta đứng ở nút gốc và duyệt dãy

bit của X từ trái qua phải (từ bit z - 1 đến bit 0), gặp bit bằng 0 thì rẽ sang nút con trái còn gặp

bit bằng 1 thì rẽ sang nút con phải, cứ tiếp tục như vậy cho tới khi một trong hai tình huống

sau xảy ra:

Hoặc đi tới một nút rỗng (do rẽ theo liên kết nil) quá trình tìm kiếm thất bại do X khơng có

trong RST

Hoặc đã duyệt hết dãy bit của X và đang đứng ở một nút lá, quá trình tìm kiếm thành cơng

vì chắc chắn nút lá đó chứa giá trị đúng bằng X.

{Hàm tìm kiếm trên cây tìm kiếm cơ số, trả về nút lá chứa khố tìm kiếm X nếu tìm thấy, trả về nil nếu khơng tìm thấy. z

là độ dài dãy bit biểu diễn một khoá}

function RSTSearch(X: TKey): PNode;

var

b: Integer;

p: PNode;

begin

b := z; p := Root; {Bắt đầu với nút gốc, đối với RST thì gốc ln có sẵn}

repeat

b := b - 1; {Xét bit b của X}

if then p := p^.Left {Gặp 0 rẽ trái}

else p := p^.Right; {Gặp 1 rẽ phải}

until (p = nil) or (b = 0);

RSTSearch := p;

end;



Thao tác chèn một giá trị X vào RST được thực hiện như sau: Đầu tiên, ta đứng ở gốc và

duyệt dãy bit của X từ trái qua phải (từ bit z - 1 về bit 0), cứ gặp 0 thì rẽ trái, gặp 1 thì rẽ phải.

Nếu quá trình rẽ theo một liên kết nil (đi tới nút rỗng) thì lập tức tạo ra một nút mới, và nối

vào theo liên kết đó để có đường đi tiếp. Sau khi duyệt hết dãy bit của X, ta sẽ dừng lại ở một

nút lá của RST, và công việc cuối cùng là đặt giá trị X vào nút lá đó.

Ví dụ:



0



0



1



1



1



0



0



0



1



0



1



1



0



0



1



1



2



4



5



2



4



5



7



010



101



101



010



101



101



111



Hình 46: Với độ dài dãy bit z = 3, cây tìm kiếm cơ số gồm các khố 2, 4, 5 và sau khi thêm giá trị 7



Lê Minh Hoàng



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

§9. TÌM KIẾM (SEARCHING)

Tải bản đầy đủ ngay(0 tr)

×