DANH SÁCH LIÊN KẾT ĐƠN

Một phần của tài liệu Cấu trúc dữ liệu phân tích thuật toán và phát triển phần mềm (Trang 27 - 38)

Một trong những đặc trưng của danh sách là số phần tử không cố định mà thay đổi tuỳ thuộc vào thao tác trên nó. Điều này buộc cách tổ chức dữ liệu biểu diễn cho mô hình danh sách cũng phải có đặc trưng này, nghĩa là bộ nhớ phải được cấp phát động (dùng mảng không đáp ứng được yêu cầu này). Mặt khác, trong danh sách các phần tử được sắp xếp tuyến tính do đó việc tổ chức dữ liệu cấp phát động phải được tổ chức sao cho thể hiện được thứ tự tuyến tính của các phần tử trong danh sách, một trong những cách thường dùng là danh sách liên kết đơn. Trong danh sách liên kết đơn, mỗi phần tử phải quản lí địa chỉ ô nhớ lưu phần tử ngay sau nó. Để thuận lợi cho các thao tác với bộ nhớ cấp phát động, phần sau nhắc lại một số thao tác với bộ nhớ cấp phát động thông qua biến con trỏ của Pascal.

Liên kết đơn còn được gọi là mốc nối đơn

1.3.1. Cấp phát động, biến con trỏ và các thao tác

Ô nhớ cấp phát động là những ô nhớ được cấp phát và thu hồi bằng lệnh trong chương trình. Để quản lí các ô nhớ cấp phát động cần sử dụng biến kiểu con trỏ. Biến con trỏ chứa địa chỉ của ô nhớ động được cấp phát. Mỗi biến con trỏ chỉ có thể quản lí một ô nhớ cấp phát động có kiểu xác định.

Khai báo kiểu và biến con trỏ:

T y p e K i ể u _ C o n _ t r ỏ = ^ K i ể u _ d ữ _ l i ệ u ; V a r B i ế n _ c o n _ t r ỏ : ' ' K i ể u _ d ữ _ l i ệ u ;

Cấp phát ô nhớ cho biến con trỏ:

New ( b i ế n _ c o n _ t r ỏ ) ;

Thủ tục này cấp phát một ô nhớ đủ dùng để chứa dữ liệu của kiểu dữ liệu mà biến con trỏ trỏ đến và biến con trỏ trỏ đến ô nhớ này.

Sử dụng ô nhớ do biến con trỏ quản lí:

Mặc dù biến con trỏ chỉ quản lí địa chỉ của ô nhớ cấp phát động, tuy nhiên trong trường hợp cần thao tác với nội dung của ô nhớ ta có thể dùng cú pháp:

B i ế n _ c o n _ t r ỏ ^

Giả sử có biến con trỏ p trỏ đến một ô nhớ kiểu số nguyên {Var p\^lnteger\) và đã cấp phát ô nhớ cho con trỏ p (New(p)). Muốn thao tác với nội dung của ô nhớ này, ta dùng cú pháp vắ sử dụng như một biến nguyên thuần tuý. Chẳng hạn, để chứa số 5 vào ô nhố này ta có thể gán

Phép gán con trỏ

Ta có thể thực hiện thao tác gán cho biến con trỏ p:= q\ (p, q là hai biến con trỏ cùng kiểu). Khi âố pvầt.q sẽ cùng trỏ vào một ô nhớ do biến con trỏ q đang quản lí.

Hằng con trỏ NU dùng để gán cho con trỏ có kiểu bất kì, thường dùng khi chưa xác định địa chỉ mà biến con trỏ quản lí.

Thu hồi ô nhớ của một biến con trỏ

Khi cần thu hồi ô nhớ do bỉến con trỏ quản lí ta dùng thủ tục Dispose.

D i s p o s e ( b i ế n _ c o n _ t r ỏ ) ;

Khi đó vùng nhớ mà biến con trỏ p quản lí đã được thu hồi và có thể dùng cho việc khác. Sau khi thu hồi ô nhớ của một biến con trỏ thì nên gán cho nó giá trị NU để tránh những sai sót khi thao tác với biến con trỏ này.

Ta cũng có thể thu hồi một số ô nhớ cấp phát bằng cách dùng cặp thủ tục Markip)Releaseip). Thủ tục Markịp) (với p là một biến con trỏ) dùng để đánh dấu vị trí đầu của một vùng nhớ mà khi cần có thể thu hồi lại. Thủ tục Releaseip) thu hồi vùng nhớ bắt đầu từ vị trí được đánh dấu bằng lệnh Markip).

28

1.3.2. Khái niệm danh sách liên kết

Danh sách liên kết là một cách tổ chức dữ liệu cho mô hình danh sách trong đó các phần tử được móc nối với nhau nhờ vào vùng liên kết.

Danh sách liên kết sử dụng cơ chế cấp phát động nên thích hợp với các thao tác thêm vào, loại bỏ, ghép nhiều danh sách.

1.3.3. Tổ chức danh sách liên kết

Mỗi phần tử của danh sách liên kết gồm hai thành phần:

+ Phần Data chứa dữ liệu thực sự của từng phần tử trong danh sách.

+ Phần Link dùng để iiên kết một phần tử với phần tử ngay sau nó.

Data Link

Từ mỗi phần tử ta chỉ duy trì một liên kết đến phần tử ngay sau nó và danh sách liên kết được tổ chức như vậy được gọi là danh sách liên kết đơn. Trong phần này ta chỉ xét cấu trúc dữ liệu danh sách liên kết đơn và không gây nhầm lẫn khi ta gọi là danh sách liên kết.

Hỡnh ảnh một danh sỏch liờn kết biểu diễn danh sỏch L = (ô1, ô2- •••> ^ô) như sau:

Head a .1 — ^'h. ^ 2

Hình 2.4. Hình dnh danh sách liên kết đơn

Để quản lí danh sách biểu diễn bởi danh sách liên kết ta chỉ cần quản lí phần tử đầu tiên của danh sách. Từ phần tử này ta có thể thao tác được với các phần tử của danh sách nhờ liên kết giữa các phần tử. Khai báo danh sách liên kết có dạng:

T y p e E l e m e n t T y p e = . . . ; { Đ ị n h n g h ĩ a k i ể u p h ầ n t ử c ủ a d a n h s á c h } L i s t L i n k = ' ' C e l l ;

C e l l = R e c o r d

D a t a : E l e m e n t T y p e ; L i n k : L i s t L l n k ; E n d;

V a r H e a d : L i s t L i n k ;

Vi dụ. Tổ chức danh sách nhân viên kiểu danh sách liên kết như sau:

T y p e DSCB = ''HSCB;

HSCB = R e c o r d

H o _ T e n : s t r i n g [ 3 0 ] ; N a m S i n h : 1 9 0 0 . . 3 0 0 0 ;

Lk : DSCB;

E n d ; V a r d s l , d s 2 : DSCB;

í . 3.4. Các phép toán trên danh sách liên kết Khởi tạo danh sách liên kết

P r o c e d u r e I n i t ( v a r H e a d : L i s t L i n k ) ; B e g i n

H e a d : = N i l ; E n d ;

Thêm một phần tử vào danh sách

Thêm phần tử X vào sau phần tử ở vị trí p trong danh sách có phần tử đầu là Head.

Head p

Hình 2.5. Thêm một phán lử vào danh sách liên kết

Thủ tục thêm một phần tử vào vị trí sau p. Trong thủ tục ta xét trường hợp khi danh sách rỗng thì phần tử thêm vào chính là phần tử duy nhất của danh sách. Chi tiết thủ tục như sau:

P r o c e d u r e I n s e r t ( v a r H e a d : L i s t L i n k ; X : E l e m e n t T y p e ; p : L i s t L i n k ) ; v a r q : L i s t L i n k ;

B e g i n N e w ( q ) ;

. D a t a : = X ;

ỉ £ H e a d = N i l t h e n b e g i n

q'^ . L i n k : = n i l ; H e a d : = q ;

e n d e l s e

b e g i n

q ' ' . L i n k : = p ' ' . L i n k ; p ' ^ . L i n k : = q ;

e n d ; E n d ;

30

Loại bỏ một phân tử khỏi danh sách

Giả sử cần loại bỏ phần tử ngay sau phần tử b vị trí p trong danh sách có phần tử đầu là Head.

Hình 2.6. Xoá một phần tử trong danh sách liên kết

Thủ tục xoá một phần tử sau vị trí p trong danh sách:

P r o c e d u r e D e l e t e ( v a r H e a d : L i s t L i n k ; p v a r q : L i s t L i n k ;

B e g i n

q : = . L i n k ; i f q O n i l t h e n

b e g i n

p ' ^ . L i n k : = q ' ^ . L i n k ; D i s p o s e ( q ) ;

e n d ; E nd;

L i s t L i n k ) ;

Dễ thấy độ phức tạp của thao tác thêm và xoá trong danh sách liên kết là 0(1).

Tìm một phần tử trong danh sách liên kết

Cho danh sách liên kết có phần tử đầu là Head. Cần tìm một phần tử có khoá Key bằng một giá trị cho trước.

Với cách tổ chức dữ liệu của danh sách liên kết, việc truy xuất các phần tử là tuần tự nên thao tác tìm kiếm phải dùng thuật toán tìm tuần tự.

Thủ tục tìm khoá Xtrong danh sách Head, kết quả trả về qua giá trị f o u n d và vị trí của phần tử tìm được p.

P r o c e d u r e S e a r c h ( H e a d : L i s t L i n k ; x : K e y T y p e ;

v a r f o u n d : B o o l e a n ; v a r p : L i s t L i n k ) ; B e g i n

p : = H e a d ; f o u n d ; = f a l s e ;

W h i l e ( p ^ . l i n k O n i l ) a n d n o t f o u n d d o i f p ' ' . D a t a . K e y = X t h e n

f o u n d : = t r u e e l s e

p:=p'^ . l i n k ; E n d ;

Nối hai danh sách:

Cho hai danh sách có phần tử đầu tương ứng là HeadìHeadl. Ghép danh sách Head! vào sau danh sách H ea d l.

Để ghép được danh sách 2 sau danh sách 1 phải tìm được vị trí của phần tử cuối danh sách 1 (nếu có) và liên kết với phần tử đầu của danh sách thứ 2. Trong trường hợp danh sách 1 rỗng thì kết quả ghép là danh sách 2.

Hình 2.7. Ghép hai danh sách liên kết

P r o c e d u r e C o n c a t ( v a r H e a d l : L i s t L i n k ; H e a d 2 : L i s t L i n k ) ; v a r p : L i s t L i n k ;

B e g i n

I f H e a d l = n i l T h e n H e a d l : = H e a d 2 E l s e

B e g i n

p : = H e a d l ;

W h i l e p “' . L i n k O n i l Do p : = p ^ . L i n k ; p'' . L i n k ; = H e a d 2 ;

E n d ; E n d ;

1.3.5. So sánh cấu trúc dữ liệu danh sách liên kết đơn và mảng

Khi biểu diễn danh sách bằng mảng phải ước lượng số phần tử tối đa của danh sách.

Điều này có thể gây ra lãng phí bộ nhớ trong trường hợp danh sách có ít phần tử và sẽ không thêm phần tử vào danh sách được khi có nhiều phần tử. Trong khi đó biểu diễn danh sách bằng danh sách liên kết đơn sử dụng cơ chế cấp phát động nên bộ nhớ dùng cho danh sách được sử dụng đúng với số phần tử của danh sách tại mọi thời điểm do đó ít gây lãng phí ô nhớ và danh sách chỉ đầy khi không còn không gian nhớ để cấp phát. Tuy nhiên, danh sách liên kết phải dùng một phần bộ nhớ để liên kết các phần tử trong danh sách.

Trong danh sách tổ chức bằng mảng, thao tác truy xuất các phần tử là truy xuất trực tiếp nhưng các thao tác thêm một phần tử, xoá một phần tử có thời gian tỉ lệ với số phần tử của danh sách. Đối với danh sách liên kết đơn các thao tác thêm vào và xoá một phần tử được thực hiện với thời gian hằng trong khi thao tác với một phần tử là tuần tự. Tuỳ thuộc vào ứng dụng, cụ thể của danh sách với các phép toán thường dùng của nó mà lựa chọn cách tổ chức danh sách bằng mảng hay bằng danh sách liên kết.

32

1.3.6. Một sô' dạng danh sách liên kết khác

Một trong những hạn chế của danh sách liên kết đơn là phải quản lí được phần tử đầu tiên của danh sách và những thao tác với một phần tử phải biết phần tử ngay trước nó.

Những hạn chế này có thể được khắc phục phần nào bởi việc thay đổi kiểu liên kết. Trong phần này giới thiệu hai loại danh sách liên kết khác là liên kết vòng và liên kết kép.

Danh sách liên kết vòng

Danh sách liên kết vòng là danh sách liên kết đofn với một thay đổi là phần tử cuối của danh sách liên kết với phần tử đầu tiên. Hình ảnh của danh sách liên kết vòng như hình sau:

t

Hình 2.8. Danh sách liên kết vòng

Với cách tổ chức như trên, trong trường hợp không quan tâm đến thứ tự trước-sau của các phần tử, để quản lí danh sách ta chỉ cần quản lí phần tử bất kì trong danh sách mà không nhất thiết phải quản lí phần tử đầu tiên vì từ vị trí bất kì ta đều có thể truy xuất đến những phần tử còn lại của danh sách bằng cách duyệt tuần tự.

Về tổ chức dữ liệu, danh sách liên kết vòng có tổ chức hoàn toàn giống danh sách liên kết đơn. Để quản lí danh sách liên kết vòng ta dùng một biến con trỏ quản lí một phần tử bất kì, phần tử này gọi là phần tử hiện tại của danh sách. Trên danh sách liên kết vòng thường thực hiện các thao tác sau:

Thêm một phần tử vào ngay sau phần tử hiện tại của danh sách.

Thêm một/phần tử vào ngay trước phần tử hiện tại của danh sách.

Xoá phần tử ngay sau phần tử hiện tại.

Duyệt danh sách.

Cách tổ chức dữ liệu cho danh sách liên kết vòng giống như danh sách liên kết đơn.

Các thủ tục trên danh sách liên kết vòng được mô tả như sau:

Thêm vào sau

a) Thêm vào danh sách rỗng L

b) Thêm vào danh sách khác rỗng

Hình 2.9. Thêm vào sau phần tử hiện tại trong danh sách lién kết vòng

P r o c e d u r e I n s e r t A f t e r ( v a r L : L i s t L i n k ; x : E l e m e n t T y p e ) ; v a r p : L i s t L i n k ;

B e g i n

N e w ( p ) ; p ' ^ . D a t a : = x ; I f L = n i l t h e n

b e g i n

L : = p ; L ^ . l i n k : = L ; e n d

e l s e b e g i n

p ^ . l i n k : = L ^ . l i n k ; L ^ . l i n k : = p ; e n d ;

E n d ;

Thêm vào trước

Hình 2.10. Thêm vào trước phần tử hiện tại trong danh sách liên kết vòng

Thủ tục thêm vào trước được thực hiện bằng cách thêm vào sau và chuyển phần tử hiện tại của danh sách là phần tử vừa thêm vào.

P r o c e d u r e I n s e r t B e f o r e ( v a r L: L i s t L i n k ; X : E l e m e n t T y p e ) ; B e g i n

I n s e r t A f t e r ( L , x ) ; L : = L - ' . l i n k ;

E n d ;

ỵoá phẩn tử sau

a) Xoá danh sách có 1 phần tử

b)Xoá danh sách nhiều hơn 1 phần tử L

Hình 2.11. ỉừ>á phẩn tử sau phần tử hiện tại trong daìih sách liên kết vòng

34 5CT0L PrPMÉM.B

P r o c e d u r e D e l e t e A f t e r ( v a r L ; L i s t L i n k ) ; v a r p : L i s t L i n k ;

B e g i n

i f L O n i l t h e n b e g i n

p : = L ' ' . l i n k ;

i f L ' ^ . l i n k = L t h e n L : = n i l e l s e L ^ . l i n k : = p ^ . l i n k ; d i s p o s e ( p ) ;

e n d ; E nd;

Duyệt danh sách

Thao tác duyệt danh sách liên kết vòng được thực hiện tương tự như thao tác duyệt danh sách liên kết đơn với chú ý vị trí của phần tử xuất phát để tránh duyệt lại danh sách.

P r o c e d u r e T r a v e r s e (L : L i s t L i n k ) ; v a r p : L i s t L i n k ;

B e g i n

i f L O n i l t h e n b e g i n

p : = L ; r e p e a t

w r i t e ( p ' ' . d a t a : 4) ; p : = p ^ . l i n k ;

u n t i l P = L ; e n d ;

End;

Trong một số trường hợp ta có thể tổ chức danh sách liên kết vòng tốt hơn bằng cách dùng một phần tử đặc biệt để đánh dấu phần tử đầu tiên. Với cách tổ chức này danh sách iiên kết vòng luôn khác rỗng vì luôn có phần tử đặc biệt.

Danh sách liên kết kép

Khi làm việc với những danh sách mà việc thao tác trên một phần tử của nó liên quan đến cả phần tử ngay trước và ngay sau nó thì việc tổ chức danh sách bằng liên kết đoìi không đáp ứng được. Trong các trường hợp như vậy, mỗi phần tử của danh sách thường được dùng hai liên kết đến phần tử ngay trước và ngay sau nó, danh sách như vậy gọi là danh sách liên kết kép.

left \

right

Hình 2.12. Danh sách liên kết kép

Vì danh sách liên kết kép có thể duyệt theo cả hai chiểu nên cần quản lí cả hai phần tử đầu và cuối danh sách. Khai báo dữ liệu cho danh sách liên kết kép như sau:

T y p e

D L i n k = ^ C e l l 2 L ; C e l l 2 L = R e c o r d

D a t a : E l e m e n t T y p e ; L L i n k , R L i n k : D L i n k ; E n d ;

L i s t 2 L i n k = R e c o r d

L e f t , R i g h t : D L i n k ; E n d ;

V a r L : L i s t 2 L i n k ;

Các thao tác cơ bản trên danh sách liên kết kép:

Khởi tạo

P r o c e d u r e I n i t ( v a r L ; L i s t 2 L i n k ) ; B e g i n

L . L e f t : = n i l ; L . R i g h t ; = n i l ; E n d ;

Thêm một phần tử vào trước phần tử ở vị trí p

left I..Ĩ O I P ...!

I . 1 \ ị p

right

Hình 2.13. Thêm một phần tử vào trước phần tử ỏ vị trí p trong danh sách lìén kết kép P r o c e d u r e I n s e r t B e f o r e ( v a r L : L i s t 2 L i n k ; p : D L i n k ; X :E l e m e n t T y p e ) ;

v a r q , p l : D L i n k ; B e g i n

New (q) ;

q'' . D a t a : = X ; { d a n h s á c h r ỗ n g )

i f ( L . L e f t = n i l ) a n d ( L . R i g h t = n i l ) t h e n

36

b e g i n

. L L i n k : = n i l ; . R L i n k : = n i l ; L . L e f t : = q ; L . R i g h t : = q ; e n d

e l s e

i f L . L e f t = p t h e n { t h ê m v à o đ ầ u } b e g i n

. LLink : = n i l ; . R L i n k : =p ; . L L i n k : = q ; L . L e f t : = q ; e n d

e l s e

b e g i n

p i :=p'^ . L L i n k ; . L L i n k := p l ; p l ^ . R l i n k : = q ; q ^ . R L i n k : = p ;

. L L i n k : = q ; e n d ;

End;

Xỡá phần tử tại vị trí p left

Hình 2.14. ỵoá phẩn tử tại vị trí p trong danh sách liên kết kép P r o c e d u r e D e l e t e ( v a r L : L i s t 2 L i n k ; p : D L i n k ) ;

B e g i n

i f L . L e f t = p t h e n { x o á p h ầ n t ử đ ầ u } L . L e f t : = p ^ . R L i n k

e l s e

i f L . R i g h t = p t h e n { x o á p h ầ n t ử c u ố i }

L . R i g h t : =p'' . L L i n k e l s e

b e g i n

p ' ' . L L i n k ^ . R L i n k : =p^ . R L i n k ; p'' . RLink"^ . L L i n k : =p'*'. L L i n k ; e n d ;

d i s p o s e ( p ) ; E nd ;

Duyệt danh sách liên kết kép

Có thể duyệt từ trái sang phải hoặc ngược lại. Thủ tục duyệt từ trái sang phải được thực hiện như sau:

P r o c e d u r e T r a v e r s e (L ; L i s t 2 L i n k ) ; v a r p : D L i n k ;

B e g i n

p : = L . L e f t ;

w h i l e p O n i l d o b e g i n

V i s i t ( p ) ; p : = p ^ . R L i n k ; e n d ;

_________E n d ; ______________________________

Một phần của tài liệu Cấu trúc dữ liệu phân tích thuật toán và phát triển phần mềm (Trang 27 - 38)

Tải bản đầy đủ (PDF)

(297 trang)