Tải bản đầy đủ (.doc) (59 trang)

Chương 5 cấu trúc cây (tree)

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (278.68 KB, 59 trang )

209

Chương 5
Cấu trúc Cây (TREE)
Trong các chương trước đây, chúng ta đã
nghiên cứu các loại cấu trúc dữ liệu tuyến tính .
Trong chương này chúng ta nghiên cứu một loại cấu
trúc dữ liệu phi tuyến là cây và đồ thị Đây là một
loại cấu trúc dữ liệu có nhiều ứng dụng trong thiết kế
giải thuật.
5.1 Một số khái niệm cơ bản về cây

Trước hết , chúng ta sẽ xem xét các khái niệm
cơ bản về cây, khái niệm cây nhị phân, các phương
pháp “quét” một cây nhị phân . Chúng ta cũng
nghiên cứu các giải thuật bổ sung ,loại bỏ đối với
cấu trúc dữ liệu cây và ứng dụng loại cáu trúc dữ liệu
này trong các bài toán thực tế .
Định nghĩa
Cây là một tập hợp hữu hạn các nút có mối quan
hệ phân cấp xuất phát từ một nút gọi là gốc
( Root)
209


210

 Root

A


B

E

C

F

1

D

G

H

2

3

Hình 5.1 - Cấu trúc cây
Ta thấy trong thực tiễn có rất nhiều ví dụ cho
loại cấu trúc dữ liệu cây. Sau đây là một ví dụ minh
hoạ.
Tổng công ty Điện lực Việt nam có 3 bộ phận
Tổn ở
ba vùng lãnh thổ khác nhau : khu vực 1( Phía g
Bắc),
khu vực 3( Miền trung), khu vực 2( Phía Nam) .côn
Mỗi

ty
khu vực quản lý Sỏ điện lực của các tỉnh,vùn
thànhgvùn
phố
trong khu vực của mình. Mỗi Sở điện lựcg của
tỉnh,
1 g
2 vùn
thành phố lại có Sở điện lực huyện, Quận . Như vậy g 3
cấu trúc bộ máy tổ chức của Tổng công ty điện lực
là hình ảnh của một cấu trúc dữ liệu loại cây tổng
quát.
Một đặc điểm rất quan trọng của cấuTỉntrúcTỉn
cây Tỉn
h1 h2 h3
là tính chất phân cấp theo thứ bậc của các nút .Các
nút được phân cấp theo mức của chúng. Chúng ta
210

H

H

H


ện ện ện
1
2
3

211

qui ước nút gốc luôn luôn có mức 1, còn các nút con
của nút gốc sẽ có mức tăng dần. Giả sử ,nút thứ i có
mức là k thì các nút con của nút i sẽ có mức là k+1.
Bây giờ ta xét vấn đề “quét” một cấu trúc
hình cây.
Chúng ta gọi phép “quét” cây là quá trình thâm nhập
vào các nút của nó một cách có hệ thống sao cho
mỗi nút chỉ được thâm nhập một lần .
5. 2

cây nhị phân (Binary tree)

Cây nhị phân là một trường hợp riêng của
cây tổng quát và có rất nhiều ứng dụng trong thiết kế
giải thuật.
Trong trường hợp này, mỗi nút của cây chỉ có
2 nút con gọi là nút con trái ( LChild ) và nút con
phải (RChild).
Hình ảnh của một cây nhị phân biểu diễn trong hình
vẽ sau đây.
Trong hình vẽ này , A là nút gốc( Root).
LChild của A là B còn RChild của A là C. Đến lượt
mình, nút B lại có hai nút con : LChild của B là D
còn RChild của B là E ... vv

211



212

Root
A
B

C

D

H

E

I

K

G

L

R

M

Q

X


N

Hình 5.2 Cây nhị phân
Đối với một cây nhị phân ta có các kết quả sau đây
[2]:
1 - Số lượng tối đa các nút ở mức I của cây là:
2 I+1 (I>= 1)
212


213

2 - Số lượng tối đa các nút trên một cây nhị phân có
chiều cao h (h là số mức lớn nhất của nút có trên
cây) được xác định bằng công thức:
2 h - 1 (h>=1)
phương pháp Lưu trữ cây nhị phân

Đối với cây nhị phân người ta thường dùng 2
phương pháp lưu trữ:
Phương pháp lưu trữ kế tiếp bằng mảng
Phương pháp lưu trữ bằng danh sách liên kết
Phương pháp lưu trữ kế tiếp bằng mảng

Xét cây nhị phân mà giá trị mỗi nút là một chữ số

213


214

Root
1

A

2 B
4
8
H

D

3
5

9
I

E

6 G

10
K

C

11 12
L


R

7 M
13
Q

14 15
X

N

Hình 5.3
Ta lần lượt đánh số cho các nút từ mức 1 trở đi, hết
mức này sang mức khác và từ trái sang phải từ 1 đến
15
Như vậy nút con của nút thứ I sẽ là nút thứ 2i và 2i
+1.
Ta sẽ lưu trữ cây nhị phân trong một mảng gồm 15
thành phần theo qui tắc:
Nút thứ i của cây sẽ được lưu trữ ở thành phần  i-1
của mảng. Đây chính là phương pháp lưu trữ kế tiếp.
Hình ảnh lưu trữ cây nhị phân trên đây bằng mảng
như sau ( Hình 5.4 ) :
214


215

§ Þa chØ


Vï ng nhí

PhÇn tö m¶ng

 1

Base (B)+0
Base (B)+1
Base (B)+2
Base (B)+3
Base (B)+4
Base (B)+5

...........

.................
Base(B)+8
Base (B)+9
Base (B)+14

11001010111001
 15

Hình 5.4
Khi biết địa chỉ của một nút, chúng ta có thể
tính được địa chỉ các nút con của nó. Ví dụ: Nút C
chứa ở nút E  3 có địa chỉ là 3. Hai nút con của nó
có địa chỉ là:3 x 2 = 6 và 3 x 2 +1 = 7 .
Dễ dàng thấy rằng trong trường hợp lưu trữ
như trên đây thì đối với cây nhị phân không đầy đủ,

có nhiều cây con rỗng thì ta phải lưu trữ nhiều phần
tử rỗng ( mà ta qui ước ký hiệu là phần tử  ) .
Phương pháp lưu trữ kế tiếp, trong các trường hợp
215


216

cây nhị phân lớn, không đầy đủ sẽ dẫn đến tốn rất
nhiều ô nhớ ( Vì phải chứa tất cả các thành phần
rỗng).
Chẳng hạn xét cây nhị phân không đầy đủ
sau đây:

1 12
2 20
4 60

8

2

3 7
5 2

9

7

6


10

11

9

7

11

65

6

Hình 5.5
Rõ ràng là trong trường hợp này chúng ta
phải lưu trữ thêm 4 nút trống. Đó là RChild của nút
4 , RChild của nút 5, RChild của nút 6 và RChild
của nút 7. Nếu cây nhị phân có kích thước lớn thì
216


217

việc phải lưu trữ nhiều phần tử rỗng một cách vô ích
như vậy là rất tốn kém bộ nhớ và không hiệu quả
trong giải thuật xử lý.
Phương pháp Lưu trữ bằng danh sách liên kết


Trong trường hợp này mỗi nút sẽ được lưu trữ theo
cấu trúc sau đây:
LChi

Data

RChild

ld
Trường Data chứa dữ liệu của nút.
Trường LChild là con trỏ, trỏ tới cây con trái của
nút.
Trường RChild là con trỏ, trỏ tới cây con phải của
nút.
Ví dụ : Cho cây nhị phân như trong hình vẽ dưới
đây

217


218

1 12
2 20
4 60

8

2


3 7
5 21

9

7

6

10

11

9

7

11

65

6

Hình 5.6
Cây này sẽ được lưu trữ theo qui tắc sau đây:
Nếu một nút P nào đó là tận cùng không có nút con
thì:
P^. Child = NIL
P ^. Child = NIL.
Ta dùng con trỏ T ,trỏ tới gốc của cây nhị phân để

truy nhập cây từ gốc,nếu cây rỗng thì T = nil.

218


219
Root
12

20

7

60

21

11

65

2

7

9

6

Hình 5.7

5.3 phương pháp quét cây nhị phân

Việc quét một cây nhị phân một cách đệ qui
đòi hỏi ba bước cơ bản mà chúng ta qui ước ký hiệu
là N,L,R ( N-thăm nút,L-thăm nút trái, R- thăm nút
phải) . Trong thực tế có thể có 6 trình tự khác nhau :
LNR,NLR,LRN,NRL,RNL,RLN

219


220

Ba thứ tự đầu tiên là những thứ tự quan trọng
nhất trong 6 cách quét và chúng được gọi một cách
phổ biến bằng các tên khác:
LNR Trung tự (Inorder)
NLR Tiền tự ( Preorder)
HI
LRN Hậu tự (Postorder)
Phương pháp quét theo thứ tự tiền tự được định
nghĩa một cách đệ qui như sau:
DE F
1 - Thăm gốc
B C
2 - quét cây con trái theo thứ tự tiền tự
A
3 - quét cây con phải theo thứ tự tiền tự
Quét theo trung tự:
1 - Quét cây con trái theo thứ tự trung tự

2 - Thăm gốc
3 - Quét cây con phải theo thứ tự trung tự.
Quét theo hậu tự :
1 - Quét cây con trái theo thứ tự hậu tự

220


221

1

A

2 B
4 E

3
5

F

6

G

C
7

H


Hình 5.8
2 - Quét cây con phải theo thứ tự hậu tự
3 - Thăm gốc
Xét cây nhị phân biểu diễn ở trên:
Nếu quét cây theo tiền tự, trình tự các nút sẽ thăm
là:
A B E FC G H
Trình tự quét cây thứ tự trung tự:
EB F A G C H
Trình tự quét cây theo thứ tự hậu tự:
E F

B G H C A

Giải thuật quét một cây nhị phân
221


222

Để quét một cây nhị phân ta dùng một Stack để
nạp địa chỉ các nút cần thiết khi “đi xuống” theo một
nhóm nào đó và lấy các địa chỉ đó ra khỏi Stack để
xác định đường đi lên [2]
Cho T là con trỏ, trỏ tới gốc một cây, S là một
Stack tổ chức theo kiểu kế tiếp với Top trỏ tới đỉnh.
Con trỏ P dùng để trỏ tới nút hiện đang được xem
xét.
1 - Giải thuật quét theo tiền tự

Trong trường hợp này khi thăm gốc xong thì địa
chỉ gốc cây con phải của nút được lưu trữ vào Stack.
Giải thuật như sau :
Procedure
VisitTreePreorder (T
PointerType );
Var
Top: PointerType;
S: StackType;
P: ElementType;
begin
If T= nil then
Begin Write (‘cây rỗng’)

:

Else
222


223

(S,Top );
Stack )

begin
Top : = 0;
InsertStack ( S,Top,T);
End;
(Bắt đầu duyệt cây)

While Top >0 do
Begin
P : = DeleteStack
(Lấy một phần tử khỏi
(Thăm nút rồi xuống con

trái)

RChild);

While P < > nil do
Begin
Write (P^. Data);
if P^. Rptr < > nil Then
insertStack (S,Top, p^.
(Lưu trữ địa chỉ gốc cây con

phải)

End;

P : = P^. LChild; (xuống gốc trái)
End;
end;

223


224


2 - Giải thuật quét theo thứ tự hậu tự
Trong trường hợp này nút gốc chỉ được thăm
sau khi đã duyệt xong hai nút con của nó. Như vậy
chỉ khi từ nút con phải đi lên gốc mới được thăm (đi
từ cây con trái lên gốc không được thăm). Để phân
biệt, ta đưa thêm dấu âm vào địa chỉ phải để đánh
dấu đường đi lên từ cây con phải.
Procedure
VisitTreePostorder
PointerType );
Var
P,Top: PointerType;
Begin
If T= nil then
Write (‘cây rỗng’);
Else
Begin
Top : =0;
P : = T;
End;
(Bắt đầu duyệt cây)
While P < > nil do
Begin
InsertStack ( S,Top,p);

(T

:

224



225

P : = P^. LChild;
End;
(Thăm nút mà con trái và con phải đã
duyệt)

rỗng’);

While S Top < 0 do
Begin
P : = InsertStack (S,Top);
Write (P^. Data);
If Top = 0 then write(‘ cây
End;
(Xuống nút con phải và đánh dấu)
P : = S Top ^ . RChild;

End;
Chúng ta có thể dùng giải thuật đệ qui để biễu
diễn ba phương pháp quét cây nhị phân như sau:
1 - Giải thuật quét theo tiền tự - NLR
Procedure
VisitTreepreorder ( T :
PointerType );
begin
If T < > nil then
225



226

Begin
Write (T^. Data);
VisitTreepreorder ( T^.
LChild);
RChild);

VisitTreepreorder (T^.
End;

end;
2 - Thủ tục quét cây theo trung tự - LNR
Procedure
VisitTreeInorder(
T
:
PointerType);
begin
If T < > nil then
Begin
VisitTreeInorder ( T^.
LChild);
Write ( T^. Data);
VisitTreeInorder ( T^.
RChild);
End;
end;

3 - Thủ tục quét theo hậu tự - LRN
226


227

Procedure VisitTreeInorder ( T : PointerType);
begin
If T < > nil then
Begin
VisitTreeInorder ( T^.
Lchild);
VisitTreeInorder ( T^.
RChild);
Write ( T^. Data);
End;
end;
Nhận xét
Thủ tục đệ qui biểu diễn một cách rất ngắn gọn ba
phép duyệt cây nhị phân so với giải thuật không
dùng đệ qui đồng thời nó cho ta hình dung rất rõ
ràng trình tự của qui trình duyệt cây.
5. 4 Cây nhị phân tìm kiếm ( Binary Search Tree )

Trong phần này chúng ta sẽ nghiên cứu một loại cấu
trúc nhị phân đặc biệt có nhiều ứng dụng trong thực
tiễn thiết kế giải thuật đó là cây nhị phân tìm kiếm
[1,2, 8]
227



228

5.4.1 Định nghĩa
Cây nhị phân tìm kiếm là một cây nhị phân mà mỗi
nút của nó luôn luôn thoả mãn hai điều kiện sau đây:
1 - Giá trị của nút con trái nhỏ hơn giá trị của chính
nút đó
2 - Giá trị của nút con phải lớn hơn giá trị của chính
nút đó
Chú ý : Khái niệm thứ tự ở đây được hiểu như sau :
đối với số thì thứ tự là tăng dần, còn đối với chữ thì
theo trình tự A,B,C ... vv
Ví dụ 1:
Cho cây nhị phân tìm kiếm mà mỗi giá trị mỗi nút là
một số sau đây ( Hình 5.9 )

228


229

1

23

2 12
4 6

5


3 34
21

6 31

7 56

Hình 5.9
Ví dụ 2
Sau đây là hình ảnh một cây nhị phân tìm kiếm mà
giá trị mỗi nút là một chữ cái ( Hình 5.10 ) :

229


230

1

D

2 E
4 G

3
5

C


6

L

B
7 A

Hình 5.10
5.4.2 Giải thuật tạo lập cây nhị phân tìm kiếm
Trước hết chúng ta xem xét một ví dụ số về tạo
lập cây nhị phân tìm kiếm . Cho dãy hoá đơn thanh
toán trong một Quỹ tiết kiệm ,số hiệu của chúng như
sau :
24
17

5
32

26
45

35
67

8
32

Bây giờ chúng ta dùng cấu trúc cây nhị phân để
lưu trữ dãy hoá đơn trên đây.

- Trước hết nút gốc sẽ nhận giá trị 24
230


231

- Nút con có giá trị 5 sẽ là nút con trái của gốc vì
5 < 24
- Nút con có giá trị 26 là nút phải của gốc vì 26 >
24
Quá trình cứ tiếp diễn như vậy cho đến hết dãy số
theo nguyên tắc nếu số nhỏ hơn gốc thì nó là nút con
trái, còn nếu nó lớn hơn gốc thì sẽ là nút con phải.
Chúng ta có giải thuật tạo lập cây nhị phân như sau
[3] :
Type

Pointer = ^ Element;
Element = Record;
node : integer;
LChild,Rchild : Pointer;
end;

Var

k : integer;
root, p : pointer;
Procedure CreateBynaryChearTree ( n1 : pointer;
Var n2 : pointer);
Begin

if n2 = nil then n2 : = n1;
else
Begin
231


232

if n1 ^ . node < n2 ^ . node then
CreateBynaryChearTree( n1,n2^. LChild);
else
CreateBynaryChearTree (n1,n2^. RChild);
end;
end;
Begin
Write (' Nạp số =?'); Readln(k);
New(root);
Root ^ . node : = k;
root ^ . LChild : = nil;
root^ . RChild : = nil;
While .not. eof( input) do
Begin
Write ( ' Nạp số = ?');
Readln(k);
New ( p );
p^.node : = k;
P^ . LChild : = nil;
p^. RChild : = nil;
CreateBynaryChearTree ( p, root);
end;

end;
5. 5 giải thuật bổ xung trên cây nhị phân tìm kiếm
232


233

Việc tổ chức cây nhị phân tìm kiếm tạo điều
kiện thuận lợi cho phép tìm kiếm trên cây nhị phân.
Chẳng hạn, để xác định xem một nút có giá
trị X nào đó có phải là nút của cây hay không ta thực
hiện như sau [2]:
So sánh X với giá trị ở gốc, 4 trường hợp sau
đây có thể xẩy ra:
1 - Không có gốc (X không có trên cây)
2 - X trùng với khoá ở gốc.
3 - X nhỏ hơn khoá ở gốc: Tiếp tục xét nút trái của
gốc.
4 - X lớn hơn khoá ở gốc: Tiếp tục xét nút phải của
gốc.
Cấu trúc của mỗi nút trên cây nhị phân tìm kiếm như
sau:
LChild và RChild là con trỏ, trỏ tới cây con trái và
gốc cây con phải, key là khoá của nút , Data là các
trường khác ngoài khoá.
Cho cây nhị phân tìm kiếm mà gốc được trỏ bởi
T. Lập giải thuật tìm một nút có giá trị bằng X. Nếu
tìm thấy thì đưa con trỏ Q trỏ tới nút ấy, nếu không
tìm thấy thì bổ sung nút ấy vào T và đưa ra con trỏ
Q trỏ tới nút mới bổ sung này.

233


×