1
CHNG V. CU TRÚC D LIU
a bit rng vic lp trình ph thuc phn ln vào cách mô hình hoá d liu ca bài toán
cn gii. Mi quan h gia thut toán và cu trúc d liu trong lp trình đã đc
Niclaus Wirth, tác gi ngôn ng lp trình Pascal, đa ra mt công thc rt ni ting :
T
Cu trúc d liu + Thut toán = Chng trình
(Data structure + Algorithms = Programs)
Nh đã thy, thut toán mi ch phn ánh các thao tác cn x lý, còn đi tng đ x lý
trong máy tính li là d liu. Nói đn thut toán là nói đn thut toán đó tác đng lên d liu
nào. Còn nói đn d liu là nói đn d liu y cn đc tác đng bi thut toán nào đ đa
đn kt qu mong mun. D liu biu din thông tin cn thit đ gii bài toán, gm d liu
đa vào, d liu đa ra và d liu tính toán trung gian. Mt cu trúc d liu liên quan đn ba
yu t : kiu d liu, các phép toán tác đng lên d liu và cách biu din d liu trong b
nh ca máy tính tu theo công c lp trình.
Trong chng này, chúng ta s trình bày mt s cu trúc d liu tiêu biu nh tp hp,
ngn xp, danh sách móc ni, cây...
V.1 Tp hp
Trong toán hc, tp hp (set) là mt nhóm hay mt b su tp các đi tng
1
phân bit
{x
1
, x
2
, …, x
n
}, đc gi là các phn t (elements) ca tp hp. Do mi phn t ca mt tp
hp ch đc lit kê mt ln và không đc sp xp th t nên ngi ta không nói đn phn
t th nht, phn t th hai, v.v... Ví d hai tp hp sau đây là đng nht :
{ a, b, d, a, d, e, c, d } = { a, b, c, d }
Do tp hp cng là mt danh sách, ngi ta có th s dng cu trúc danh sách đ biu
din tp hp trong Scheme. Nh vy, mt tp hp rng là mt danh sách rng. so sánh hai
tp hp có bng nhau không, ta có th s dng v t equal? nh sau :
(define (setequal? E1 E2)
(cond ; hai tp hp rng thì bng nhau
((and (null? E1) (null? E2)) #t)
; hai tp hp có hai phn t đu tiên bng nhau
1
Khái nim đi tng ca tp hp có tính trc giác, do nhà toán hc ngi c G. Cantor đa ra t nm 1985.
n nm 1902, nhà trit hc ngi Anh B. Russell đã ch ra nhng nghch lý toán hc (paradox) hay nhng
mâu thun lôgic trong lý thuyt tp hp.
147
148 LP TRÌNH HÀM
((equal? (car E1) (car E2))
(setequal? (cdr E1) (cdr E2)))
; hai tp hp có hai phn t đu tiên khác nhau thì khác nhau !!!
(else #f)))
(setequal? ’(1 3 4 5 6) ’(1 3 4 5 6))
--> #t
hoc :
(define E1 ’(a b c d e))
(define E2 E1)
(setequal? E1 E2)
--> #t
ý rng trong v t setequal? trên đây, ta cha x lý hai tp hp có các phn t
ging y nhau và có cùng s phn t, nhng không đc sp xp th t nh nhau :
(setequal? '(1 5 4 3 6) '(1 3 4 5 6))
--> #f
Sau đây ta s s dng thng xuyên hàm member đ x lý tp hp. Hàm này kim tra
mt phn t có thuc mt danh sách đã cho hay không :
; Trng hp phn t kim tra có kiu đn gin
(member ’c ’(a b c d e))
--> ’(c d e)
; Trng hp phn t kim tra có kiu phc hp
(member (list ’a) ’(b (a) c))
--> ’((a) c)
V t in? kim tra mt phn t có thuc mt tp hp đã cho hay không ?
(define (in? x E)
(cond ((null? E) #f) ; danh sách rng
((member x E) #t) ; x là phn t kiu đn gin
(else (in? x (cdr E))))) ; x là phn t kiu phc hp
(in? ’c E1)
--> #t
xây dng mt tp hp t mt danh sách, ngi ta phi loi b các phn t trùng lp.
Hàm list->set sau đây s dng hàm member ln lt kim tra các phn t ca danh
sách đã cho. Kt qu tr v ch gi li phn t cui cùng đi vi nhng phn t trùng nhau và
cha sp xp li các phn t theo th t (xem phng pháp sp xp nhanh cui chng).
(define (list->set L)
(cond ((null? L) ’())
((member (car L) (cdr L))
(list->set (cdr L)))
(else (cons (car L)
(list->set (cdr L)))))
(list->set ’(a b d a d e c d))
--> ’(b a e c d)
(define (union2 E1 E2)
(cond ((null? E1) E2)
CU TRÚC D LIU 149
((member (car E1) E2) (union2 (cdr E1) E2))
(else (cons (car E1) (union2 (cdr E1) E2)))))
1. Phép hp trên các tp hp
Gi s cho hai tp hp E
1
và E
2
, ta cn tìm kt qu ca phép hp ca hai tp hp E
1
∪E
2
là
mt tp hp nh sau :
(define (union2 E1 E2)
(cond ; tp hp th nht là rng thì kt qu là tp hp th hai
((null? E1) E2)
; nu tp hp th nht có phn t thuc tp hp th hai thì b qua
((member (car E1) E2) (union2 (cdr E1) E2))
; tip tc sau khi gim kích thc tp hp th nht
(else (cons (car E1) (union2 (cdr E1) E2)))))
(union2 ’(1 2 3 7) ’(2 3 4 5 6))
--> ’(1 7 2 3 4 5 6)
M rng phép hp ca hai tp hp, ta xây dng phép hp trên các tp hp bt k bng
cách s dng hàm list-it đã đc đnh ngha chng trc :
(define (union . Lset)
(list-it union2 Lset ’()))
(union ’(1 2 3 4) ’(2 3 4 5 6) ’(4 5 6 7) ’(6 7 8))
--> ’(1 2 3 4 5 6 7 8)
2. Phép giao trên các tp hp
Tng t cách xây dng phép hp trên các tp hp bt k, trc tiên ta xây dng phép
giao ca hai tp hp E
1
∩E
2
nh sau :
(define (intersection2 E1 E2)
(cond ; nu mt tp hp là rng thì kt qu cng rng
((null? E1) E1)
; chn ra phn t nm c hai tp hp
((member (car E1) E2) (cons (car E1)
(intersection2 (cdr E1) E2)))
; tip tc sau khi gim kích thc tp hp th nht
(else (intersection2 (cdr E1) E2))))
(intersection2 ’(1 2 3 4)’(2 3 4 5 6))
--> ’(2 3 4)
M rng phép giao ca hai tp hp, ta xây dng phép giao trên các tp hp bt k bng
cách s dng hàm apply đã đc đnh ngha mc Error! Reference source not found. :
(define (intersection . Lset)
(cond ; giao ca các tp hp rng cng là rng
((null? Lset) ’())
; giao ca mt tp hp là chính nó
((null? (cdr Lset)) (car Lset))
; đa v thc hin phép giao ca hai tp hp
(else (intersection2
(car Lset)
(apply intersection (cdr Lset))))))
150 LP TRÌNH HÀM
(intersection ’(1 2 3 4) ’(2 3 4 5 6) ’(4 5 6 7))
--> ’(4)
3. Phép hiu ca hai tp hp
Cho hai tp hp E
1
và E
2
, ta âënh nghéa phép hiu ca hai tp hp E
1
\E
2
nh sau :
(define (difference E1 E2)
(cond ; nu E
2
rng thì kt qu là E
1
((null? E2) E1)
; nu E
1
rng thì kt qu là rng
((null? E1) ’())
; nu E
1
có phn t thuc E
2
thì b qua
((member (car E1) E2)
(difference (cdr E1) E2))
; tip tc sau khi gim kích thc tp hp E
1
(else (cons (car E1)
(difference (cdr E1) E2)))))
(difference ’(1 2 3 4 5) ’(1 2))
--> ’(3 4 5)
4. Tìm các tp hp con ca mt tp hp
Cho trc tp hp E, ta cn tìm tp hp tt c các tp hp con (sub-set) ca E, ký hiu 2
E
.
Chng hn cho E={ a, b, c } thì 2
E
={
∅
, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}} có tt c
8 phn t bao gm tp hp rng và bn thân tp hp E.
Nu tp hp E rng, thì 2
E
cng rng. Nu E khác rng, xét mt phn t a
∈
E, khi đó có
các tp hp con cha a và các tp hp con không cha a. u tiên xây dng tp hp E\{a},
sau đó, chèn a vào tt c các tp hp con ca E\{a}. Ta có hàm subset nh sau :
(define (subset E)
(if (null? E)
(list ’())
(let ((Lremain (subset (cdr E))))
(append Lremain
(map (lambda (L)
(cons (car E) L))
Lremain)))))
(subset ’(a b c))
--> ’(() (c) (b) (b c) (a) (a c) (a b) (a b c))
V.2 Ngn xp
Danh sách kiu «ngn xp» (stack), còn đc gi là «chng» (tng t mt chng đa,
mt bng đn...), là mt cu trúc d liu mà phép b sung hay loi b mt phn t luôn luôn
đc thc hin mt đu gi là đnh (top). Nguyên tc hot đng «vào sau ra trc» ca
ngn xp đã dn đn mt tên gi khác là danh sách kiu LIFO (Last In First Out).
Ngn xp đc s dng rt ph bin trong tin hc. Hình V.1. minh ho hot đng ca mt
ngn xp khi thc hin th tc đ quy tính n!.
CU TRÚC D LIU 151
V.2.1. Kiu d liu tru tng ngn xp
Sau đây là đc t cu trúc d liu tru tng kiu ngn xp :
Types Stack(T)
functions
empty-stack : → Stack(T)
empty-stack? : Stack(T) → boolean
push-stack : T × Stack(T) → Stack(T)
pop-stack : Stack(T) -/→ Stack(T)
top-stack : Stack(T) -/→ T
preconditions
pop-stack(s: Stack(T))
ch xác đnh khi và ch khi (not empty-stack?(s))
top-stack(s: Stack(T))
ch xác đnh khi và ch khi (not empty-stack?(s))
axioms
var x: T, s : Stack(T)
empty-stack?(empty-stack) = true
empty-stack?(push-stack (x, S)) = false
pop-stack(push-stack (x, S)) = S
top-stack(push-stack (x, S)) = x
fac(3)= ? fac(3)=3*fac(2) fac(2)=2*fac(1) fac(1)=1*fac(0)
fac(1)= 1 fac(2)=2*1=2 fac(3)= 3*2 fac(3)= 6
fac(3)=?
3*fac(2)
2*fac(1)
1*fac(0)
fac(0)=1
3*fac(2)
2*fac(1)
fac(1)=?
3*fac(2)
fac(2)=?
3*fac(2)
2*fac(1)
1*1
3*fac(2)
2*1
6
3*2
Hình V.1. Hot đng ca ngn xp thc hin th tc đ quy tính n!.
Ta cn xây dng các hàm Scheme thao tác trên ngn xp nh sau :
(empty-stack? (empty-stack))
-- #t
152 LP TRÌNH HÀM
(empty-stack? (push-stack x S))
-- #f
(pop-stack (push-stack x S))
--> S
(top-stack (push-stack x S)
--> x
V.2.2. Xây dng ngn xp
Có nhiu cách đ biu din cu trúc d liu kiu ngn xp trong Scheme. Phng pháp t
nhiên hn c là biu din ngn xp di dng mt danh sách mà các thao tác b sung và loi
b mt phn t đc thc hin mt đu danh sách. Trong Scheme, mi ln b sung mt
phn t vào danh sách kéo theo vic to ra mt b đôi (dotted pair) mi.
S ...
$
a)
b)
S
$
Hình V.2. Hot đng b sung (a) và loi b (b) mt phn t ca ngn xp.
Ta có các hàm Scheme nh sau :
(define (empty-stack) ’())
(define empty-stack? null?)
(define (push-stack x S)
(cons x S))
(define (pop-stack S)
(if (empty-stack? S)
(display ”ERROR: stack is empty!”)
(cdr S)))
(define (top-stack S)
(if (empty-stack? S)
(display ”ERROR: stack is empty!”)
(car S)))
Ta thy các thao tác trên ngn xp tng t đi vi danh sách. Sau đây là mt s ví d
minh ho thao tác trên ngn xp s dng các hàm đã có phù hp vi ca1c tiên đ trong đc t
trên đây :
(empty-stack? (push-stack 'a (empty-stack)))
--> #f
(pop-stack (push-stack ’a ’(1 2 3)))
--> ’(1 2 3)
CU TRÚC D LIU 153
(pop-stack (push-stack ’a ’(1 2 3)))
--> ’(1 2 3)
(top-stack (push-stack ’a ’(1 2 3)))
--> ’a
(top-stack (pop-stack
(push-stack 1 (pop-stack
(push-stack 2 (push-stack 3 (pop-stack
(push-stack 4 (push-stack 5
(empty-stack))))))))))
--> 3
V.2.3. Xây dng trình son tho vn bn
Sau đây là mt ví d đn gin minh ho ng dng ngn xp đ xây dng mt trình son
tho đn gin cho phép thc hin vào ra tng dòng vn bn mt. Hot đng nh sau : hàm
nhn dòng vào là mt tham bin đ lu gi trong mt vùng nh trung gian (buffer). Dòng vào
cha các ký t hn hp mà mt ký t nào đó có th là mt lnh son tho (edit command).
Có bn loi ký t nh sau :
• Các ký t khác ba ký t #, $ và newline đu là ký t vn bn đ đc lu gi trong
vùng nh trung gian.
• Ký t # dùng đ xoá ký t đng trc trong vùng nh trung gian.
• Ký t $ dùng đ xoá tt c ký t trong vùng nh trung gian.
• Ký t qua dòng newline đ kt thúc mt dòng vào và đa ni dung vùng nh trung
gian lên màn hình.
Ta s s dng mt ngn xp đ biu din vùng nh trung gian. Trong chng trình có s
dng mt hàm cc b loop đ đc các ký t liên tip t dòng vào. Ch s i là ký t đang đc
th i.
(define (lineeditor str)
(let ((L (string-length in-str)))
(letrec ((loop
(lambda (i buffer)
(if (< i L)
(let ((readchar (string-ref in-str i)))
(case readchar
((#\#) (loop (+ i 1) (pop-stack buffer)))
((#\$) (loop (+ i 1) (empty-stack)))
((#\.) (map (display (reverse buffer)))
(else (loop (+ i 1)
(begin
(display "ERROR: dot is the end of
command!")
(newline))))))
(loop 0 (empty-stack)))))
(lineeditor "XY$abce#def.")
--> abcdef
154 LP TRÌNH HÀM
Ngi đc có th phát trin trình son tho trên đây đ x lý các tình hung son tho vn
bn phc tp hn.
V.2.4. Ngn xp đt bin
Ta mun rng hot đng ca ngn xp gn gi vi các chng (pile) hn, ngha là vic b
sung loi b phn t ch liên quan đn mt chng đ ta có th gán cho bin. Ta xây dng kiu
d liu tru tng ngn xp đt bin (mutable stack) Stack!(T) (sau tên có mt du chm
than) nh sau :
Types Stack!(T)
functions
empty-stack! : → Stack!(T)
empty-stack!? : Stack!(T) → boolean
push-stack! : T × Stack!(T) → Stack!(T)
pop-stack! : Stack!(T) -/→ T
top-stack! : Stack!(T) -/→ T
phân bit vi các ngn xp đã xét, ta thêm vào sau các tên kiu và hàm mt du chm
than ! tng t các đt bin. im hot đng khác bit ca ngn xp đt binlà sau khi loi
b mt phn t thì hàm tr v phn t trên đnh.
Trong chng trc, khi xét môi trng làm vic ca Scheme, ta thy rng không th s
dng lnh set! đ thay đi ni dung ca ngn xp chng hn nh :
(define (push-stack x s)
(set! s (cons x s)) s)
Ta phi áp dng tính cht đt bin ca các danh sách, bng cách biu din ngn xp rng
(empty stack) bi mt danh sách khác rng đ sau đó thay đi bi cdr.
(define (empty-stack!) (list ’stack!))
(define (empty-stack!? S)
(and (pair? S)
(null? (cdr S))
(eq? ’stack! (car S))))
(define S1 (empty-stack!))
S1 ; xem ni dung ca ngn xp
--> ’(stack!)
(empty-stack!? S1)
--> #t
b sung (push) và loi b (pop) trên ngn xp S, ta gi thit rng bin S luôn luôn tr
đn cùng mt b đôi. Tt c các thao tác vt lý đc thc hin nh lnh cdr nh minh ho
trong hình di đây.
Lúc ngn xp rng, danh sách S ch cha phn t ’stack!. Sau khi b sung phn t
’bob thì phn t này đng th hai (ngay sau ’stack!) trong danh sách S.
Ta xây dng hàm nh sau :
(define (push-stack! x S)
(set-cdr! S (cons x (cdr S))))
CU TRÚC D LIU 155
(push-stack! ’ann S1)
(push-stack! ’bob S1)
(push-stack! ’jim S1)
Hình V.3. Hot đng b sung mt phn t vào ngn xp.
’stack!
’bob
S ...
$
Ta tip tc xây dng hàm xem và loi b phn t đnh ca ngn xp :
(define (top-stack! S)
(if (empty-stack!? S)
(begin
(display "ERROR: stack! is empty!")
(newline))
(cadr S)))
(top-stack! S1)
--> 'jim
(define (pop-stack! S)
(if (empty-stack!? S)
(begin
(display "ERROR: stack! is empty!") (newline))
(let ((top (cadr S)))
(set-cdr! S (cddr S))
top)))
(pop-stack! S1)
--> 'jim
(top-stack! S1)
--> ’bob
Thao tác vt lý phép loi b đc minh ho nh sau :
Hình V.4. Hot đng loi b mt phn t khi ngn xp.
’stack!
S ...
$
Sau đây là mt cách khác s dng k thut truyn thông đip đ xây dng các hàm x lý
cu trúc d liu kiu ngn xp trong Scheme.
(define (make-empty-stack!)
(let ((content '()))
(lambda (message . Largt)
(case message
156 LP TRÌNH HÀM
((empty-stack!?) (null? content))
((push-stack!)
(set! content
(cons (car Largt) content)) content)
((pop-stack!)
(if (null? content)
(begin
(display "ERROR: stack is empty!")
(newline))
(let ((top (car content)))
(set! content (cdr content))
top)))
((top-stack!)
(if (null? content)
(begin
(display "ERROR: stack is empty!")
(newline))
(car content)))))))
; Kim tra ngn xp rng nh thông đip ’empty-stack!?
(define (empty-stack!? S) (S ’empty-stack!?))
; B sung mt phn t vào ngn xp nh thông đip ’push-stack!
(define (push-stack! x S) (S ’push-stack! x))
; Loi b mt phn t khi ngn xp nh thông đip ’pop-stack!
(define (pop-stack! S) (S ’pop-stack!))
; X lý phn t đnh ca ngn xp nh thông đip ’top-stack!
(define (top-stack! S) (S ’top-stack!))
; nh ngha mt ngn xp mi
(define S2 (make-empty-stack!))
(empty-stack!? S2)
--> #t
(push-stack! ’ann S2)
--> ’(ann)
(push-stack! ’bob S2)
--> ’(bob ann)
(push-stack! ’jim S2)
--> ’(jim bob ann)
(top-stack! S2)
--> ’jim
(pop-stack! S2)
--> ’jim
(top-stack! S2)
--> ’bob
V.2.5. Tính biu thc s hc dng hu t
Mt biu thc s hc thng đc vit di dng trung t, chng hn :
CU TRÚC D LIU 157
(9 2) * 52+
Tuy nhiên, trong b nh, các biu thc đc biu din dng cây (ta s xét cu trúc cây
trong mc tip theo). Khi tính toán, biu thc s hc dng cây nh phân (cho các phép toán
hay hàm, gi chung là toán t, có ti đa hai toán hng) đc đa ra thành mt chui ký t
dng hu t (dng ký pháp Ba Lan) nh sau :
9 2 52 * +
Lúc này các du ngoc không còn na do các biu thc dng hu t thng không nhp
nhng (un-ambiguous). Khi đc biu thc t trái qua phi, ta gp các toán hng trc khi gp
mt toán t. Nu áp dng toán t này cho các toán hng trc đó, ta nhn đc mt kt qu
là mt toán hng mi và quá trình tip tc theo đúng ch đ «vào trc ra sau». Do vy,
ngi ta s dng mt ngn xp đ mô phng quá trình tính toán biu thc : các toán hng
(đc đc t biu thc hu t, ln lt t trái qua phi) đc «đy vào» ngn xp cho đn khi
gp toán t. Thc hin phép toán vói các toán hng «ly ra» t ngn xp ri li đy kt qu
vào ngn xp cho đn khi ht biu thc và ly kt qu nm đnh ngn xp.
Sau đây ta xây dng hàm tính mt biu thc s hc dng hu t (ngi đc có th tìm đc
các giáo trình «Cu trúc d liu và thut toán» đ hiu chi tit hn). Chng trình s dng
ngn xp đt bin x lý các phép toán s hc +, -, *, / và hàm sqrt :
(define (evaluation postfix-expr)
(let ((pile (make-empty-stack!)))
(letrec ((compute
(lambda (exp)
(if (null? exp)
(top-stack! pile)
(let ((s (car exp)))
(if (number? s)
(push-stack! s pile)
(begin
(case s
((sqrt)
(let ((v (pop-stack! pile)))
(push-stack! (sqrt v) pile)))
((+)
(let ((v2 (pop-stack! pile))
(v1 (pop-stack! pile)))
(push-stack! (+ v1 v2) pile)))
((*)
(let ((v2 (pop-stack! pile))
(v1 (pop-stack! pile)))
(push-stack! (* v1 v2) pile)))
((-)
(let ((v2 (pop-stack! pile))
(v1 (pop-stack! piler)))
(push-stack! (- v1 v2) pile)))
((/)
(let ((v2 (pop-stack! pile))
(v1 (pop-stack! pile)))
(push-stack! (/ v1 v2) pile))))
(compute (cdr exp)))))))))
(compute postfix-expr))))
158 LP TRÌNH HÀM
(evaluation ’(9 2 + 52 sqrt *))
--> 79.3221
V.3 Tp
V.3.1. Cu trúc d liu tru tng kiu tp
Cu trúc d liu kiu tp (file) mô phng mt hàng đi (queue) có ch đ hot đng theo
kiu «vào trc ra trc» FIFO (First In First Out). Lúc này phép b sung đc thc hin
cui hàng (đuôi), và phép loi b đc thc hin đu hàng :
Loi b B sung
u hàng Cui hàng
Hình V.5. Mô phng cu trúc d liu kiu tp tng t mt hàng đi.
Ta xây dng File(T) cu trúc d liu tru tng kiu tp có các phn t kiu T bt k ca
Scheme nh sau :
Types File(T)
functions
empty-file : → File(T)
empty-file? : File(T) → boolean
push-file : T × File(T) → File(T)
pop-file : File(T) → File(T)
top-file : File(T) → T
axioms
var s: T, F: File(T)
empty-file?(empty-file) = true
empty-file?(push-file (s, F)) = false
top-file(push-file (s, F)) =
if empty-file?(F) then s else top-file(F))
pop-file(push-file(s, F) =
if empty-file?(F)
then empty-file
else push-file(s, pop-file(F))
Sau đây ta xây dng các hàm x lý tp s dng kiu danh sách ca Scheme. Vic b sung
phn t đc thc hin cui danh sách và vic loi b thc hin đu danh sách.
(define (empty-file) ’())
(define empty-file? null?)
(define (push-file s F)
(append F (list s)))
(define (pop-file F)
(if (empty-file? F)
CU TRÚC D LIU 159
(begin
(display "ERROR: file is empty!")
(newline))
(cdr F)))
(define (top-file F)
(if (empty-file? F)
(begin
(display "ERROR: file is empty!")
(newline))
(car F)))
Ta nhn thy rng vic s dng danh sách là không hp lý khi kích thc tp tng lên và
lnh appenf trong hàm push-file s gây ra chi phí tính toán ln.
V.3.2. Ví d áp dng tp
Sau đây ta xét mt ví d áp dng cu trúc d liu kiu tp va xây dng. Gi s cho trc
mt danh sách L gm các s nguyên tng ngt và mt s nguyên N, ta cn tính s lng các
cp s (x, x + N) vi x và N có mt trong danh sách.
Chng hn nu L = (0 2 3 4 6 7 8 9 10 12 13 14 16 20) và N = 8, thì ta tìm đm đc bn
cp s tho mãn nh sau : (2, 10), (4, 12), (6, 14) và (12, 20).
Phng pháp đn gin nht có tính trc quan là vi mi s x ly t L, tin hành tìm kim
các s x + N trong L . Tuy nhiên phng pháp này có chi phí ln vì ta phi duyt qua duyt
li nhiu ln danh sách L. Sau đây là phng pháp ch cn duyt mt ln danh sách L.
Ta s dng tp F có đ dài < N và kim tra mi phn t x ca F :
• Nu tp F rng thì ta ch vic thêm vào phn t x.
• Nu top là phn t đu tp và x > top + N, ta chc chn rng ta không th còn tìm đc
phn t y nào ca F mà y = top + N và do vy ta phi loi b F.
• Nu x = top + N, ta loi b x khi L đ thêm x vào F và đm cp tho mãn (x, top).
• Nu x < top + N, ta ch loi b x khi L đ thêm x vào F.
Chng trình Scheme nh sau :
(define (pair-count L N)
(letrec ((count
(lambda (L result F)
(cond ((null? L) result)
((empty-file? F)
(count
(cdr L)
result
(push-file (car L) F)))
(else
(let
((difference (- (car L) (top-file F))))
(cond
((< N difference)
(count L result (pop-file F)))
((= N difference)
160 LP TRÌNH HÀM
(count
(cdr L)
(+ result 1)
(pop-file (push-file (car L) F))))
(else
(count
(cdr L)
result
(push-file (car L) F))))))))))
(count L 0 (empty-file))))
(pair-count '(0 2 3 4 6 7 9 10 12 13 14 16 20) 8)
--> 4
V.3.3. Tp đt bin
Gi s gi File !(T) cu trúc d liu kiu tp đt bin (mutable file), ta đc t kiu tru
tng nh sau :
Types File!(T)
functions
empty-file : → File!(T)
empty-file!? : File!(T) → boolean
push-file! : T × File!(T) → File!(T)
pop-file! : File!(T) → File!(T)
top-file! : File!(T) -→ T
axioms
var s: T, F: File!(T)
empty-file!?(empty-file!) = true
empty-file!?(push-file! (s, F)) = false
top-file!(push-file! (s, F)) =
if empty-file!?(F) then s else top-file!(F))
if empty-file!?(F)
then pop-file!(push-file!(s, F) = s
if not (empty-file!?(F))
then pop-file!(push-file!(s, F) =
push-file!(s, pop-file!(F))
Ta có th áp dng k thut xây dng các ngn xp đt bin cho cu trúc d liu kiu tp
đt bin. Ta s không s dng lnh append đ b sung phn t mi vào tp mà s dng mt
con tr tr đn đuôi ca tp. Vi cách xây dng này, đ dài tp s không bit gíi hn.
Nh vy, cu trúc tp có dng mt b đôi (dotted pair) mà thành phn car tr đn danh
sách các phn t là ni dung tp, còn thành phn cdr tr đn phn t b đôi cui cùng ca
danh sách này.
(define (empty-file!)
(cons '() '()))
(define (empty-file!? file)
(null? (car file)))
CU TRÚC D LIU 161
(define (push-file! s file)
(let ((doublet (cons s '())))
(if (null? (car file))
(set-car! file doublet)
(set-cdr! (cdr file) doublet))
(set-cdr! file doublet)))
u tp uôi tp
file file
a) Ni dung tp ’(ann bob jim) b) Tp rng
’ann ’bob ’jim
Hình V.6. Hot đng loi b mt phn t khi ngn xp.
(define (top-file! file)
(if (empty-file!? file)
(begin
(display "ERROR: file is empty!")
(newline))
(caar file)))
(define (pop-file! file)
(if (empty-file!? file)
(begin
(display "ERROR: file is empty!")
(newline))
(begin (set-car! file (cdar file)))))
Sau đây là vn dng xây dng tp có ni dung ’(ann bob jim) :
(define F (empty-file!))
(empty-file!? F)
#t
(push-file! ’jim F)
(push-file! ’bob F)
(push-file! ’ann F)
(top-file! F)
--> ’jim
(pop-file! F)
(top-file! F)
--> ’bob
162 LP TRÌNH HÀM
V.4 Cây
Cây (tree) gm mt tp hp nút (node) đc t chc theo kiu phân cp. Nu cây không
có nút nào thì đc gi là cây rng (empty tree).
Khi cây khác rng, nút trên cùng, cao nht, là nút gc (root node). Phía di nút gc có
th có nhiu nút con (child node) ni vi nó bi các cnh (edge). Lúc này, nút gc là nút cha
(father node). Mi nút con li có th là mt nút cha có nhiu nút con ni vi nó, và c th tip
tc. Phn cây to ra t mt nút con bt k là cây con (subtree).
Nhng nút mc thp nht không có nút con nào ni vi nó là lá (leaf) hay nút ngoài
(exterior node). Mt nút không phi là lá là nút trong (interior node). Các nút con có cùng cha
là các nút anh em (siblings). Mi nút ca cây có mt cha duy nht (tr nút gc), là hu du
hay con cháu (descendant) ca nút gc và là t tiên (ancestor) ca các nút lá. Mt nhánh
(branch) hay mt đng đi (path) là tp hp các nút và các nhánh xut phát t mt nút đn
mt nút khác.
Trong toán hc, cây là mt đ th (graph) liên thông không có chu trình. Trong mt cây,
luôn luôn tn ti duy nht mt đng đi t nút gc đn mt nút bt k, hoc gia hai nút nào
đó đã cho. Nh vy, mt cây có th rng, hoc ch có mt nút gc, hoc gm mt nút gc và
mt hoc nhiu cây con.
Cu trúc cây đc ng dng nhiu trong tin hc : t chc th mc ca các h điu hành
Unix, MS-DOS, ..., mt chng trình Scheme, cây gia ph, ...
Gc
Cnh
Nút trong
Lá
(nút ngoài)
Cây con gc b
a
b c d e
f g h
ng đi
t a đn h,
qua e
Hình V.7. Hình nh cây.
Sau đây ta s xét cách Scheme x lý các cu trúc d liu dng cây, trc tiên là cây nh
phân (binary tree), loi cây mà mi nút cha ch có th có ti đa hai nút con.
V.4.1. Cây nh phân
V.4.1.1. Kiu tru tng cây nh phân
Cho trc mt kiu d liu c s T là mt kiu d liu bt k. Cây nh phân kiu T đc
đnh ngha nh sau :
1. 0 (s không) là mt cây nh phân kiu T, đc gi là cây rng.
2. Nu E là mt phn t kiu T, A1 và A2 là nhng cây nh phân kiu T,
thì b ba (E, A1, A2) cng là nhng cây nh phân kiu T .
3. Ch nhng đi tng đc đnh ngha bi hai quy tc trên đây mi là các cây nh phân
kiu T.
CU TRÚC D LIU 163
Nu A = (E, A1, A2) là cây nh phân, thì E là nút gc, A1 là cây con bên trái, A2 là cây
con bên phi ca A. Nu A là cây suy bin ch gm mt nút, thì nút đó luôn luôn là nút gc
ca mt cây con. Ngi ta còn gi A là cây đc gn nhãn (labelled tree, hay tag tree) bi
các giá tr trong T. Hình di đây minh ho hai cây nh phân, mt cây biu din biu thc s
hc x+(y-3*z)/4 và mt cây biu din mt s-biu thc nh sau :
a) x + (y
−
3*z)/4 b) (define L (cons ’a ’b))
Hình V.8. Hai cây nh phân biu din :
a) biu thc s hc ; b) biu thc Scheme.
Chú ý s lch đi xng ca đnh ngha : nu A không phi là cây rng, thì cây (E, A, 0)
khác vi cây (E, 0, A). Cn phân bit mt nút vi mt giá tr gn cho nút đó. Các nút luôn
luôn ri (phân bit) nhau, còn các giá tr thì có th ging nhau. Ví d, cây biu din s-biu
thc ca Scheme trong hình V.9.(b) trên có 7 nút, trong khi đó ch có 6 giá tr phân bit
nhau gán cho chúng. Ngi ta đa ra nm loi cây nh phân đc bit nh sau :
x /
+
define
L cons
quote quote
a b
3 z
y *
− 4
1. Cây suy thoái (degenerated hay filiform tree) mi nút ch có đúng mt con không rng.
2. Cây hình lc trái (left comb) có mi con bên phi là mt lá.
Cây hình lc phi (right comb) có mi con bên trái là mt lá.
3. Cây đy đ (complete tree) có mi mc ca cây đu đc làm đy
4. Cây hoàn thin (perfect tree) mi mc ca cây đu đc làm đy, tr mc cui cùng,
nhng các lá là bên trái nht có th.
5. Cây đy đ đa phng mi nút có t 0 đn 2 con.
Gi s BinTree (T) cu trúc d liu kiu cây nh phân, ta đc t kiu tru tng nh sau :
Types BinTree (T)
functions
empty-tree : BinTree(T)
empty-tree? : BinTree(T) → boolean
create-tree : T × BinTree(T) × BinTree(T) → BinTree(T)
root : BinTree(T) → T
left : BinTree(T) → BinTree(T)
right : BinTree(T) → BinTree(T)
leaf? : BinTree(T) → boolean (có th không cn)
preconditions
root(A) ch đc xác đnh nu và ch nu A ≠ empty-tree
left(A) ch đc xác đnh nu và ch nu A ≠ empty-tree
right(A) ch đc xác đnh nu và ch nu A ≠ empty-tree
leaf?(A) ch đc xác đnh nu và ch nu A ≠ empty-tree
axioms
empty-tree?(empty-tree) = true
164 LP TRÌNH HÀM
empty-tree?(create-tree(E, A1, A2)) = false
root(create-tree(E, A1, A2)) = E
left(create-tree(E, A1, A2)) = A1
right(create-tree(E, A1, A2)) = A2
leaf?(A) <=> empty-tree?(left(A)) và empty-tree?(right(A))
V.4.1.2. Biu din cây nh phân
1. Biu din tit kim s dng hai phép cons
; nh ngha cây rng :
(define empty-tree ()) ; hoc
(define empty-tree (list))
; nh ngha v t kim tra cây có rng không :
(define empty-tree? null?)
; nh ngha cây khác rng (E, A1, A2) :
(define (create-tree E A1 A2)
(cons E (cons A1 A2)))
; nh hàm tr v nút gc :
(define root car)
; nh hàm tr v cây con trái :
(define left cadr)
; nh hàm tr v cây con phi :
(define right cddr)
; nh ngha v t kim tra có phi nút lá không :
(define (leaf? A)
(and (null? (cadr A)) (null? (cddr A))))
; Ví d to mt cây nh phân có 3 nút :
(create-tree 1
(create-tree 2 empty-tree empty-tree)
(create-tree 3 empty-tree empty-tree))
--> ’(1 (2 ()) 3 ())
Ví d : Cây A gm 5 nút đc cho nh hình sau đây :
2 3
1
4 5
Hình V.10. Cây nh phân có 5 nút.
đc đnh ngha nh sau :
(create-tree 1
(create-tree 2
(create-tree 4 empty-tree empty-tree)
(create-tree 5 empty-tree empty-tree))
(create-tree 3 empty-tree empty-tree))
--> ’(1 (2 (4 ()) 5 ()) 3 ())
CU TRÚC D LIU 165
2. Biu din dng đy đ
d dàng x lý khi lp trình, thông thng ngi ta biu din cây dng danh sách, đy
đ các nút, s dng 3 phép cons :
(define empty-tree ())
(define empty-tree? null?)
(define (create-tree E A1 A2)
(list E A1 A2))
(define root car)
(define left cadr)
(define right caddr)
(define (leaf? A)
(and (null? (cadr A)) (null? (caddr A))))
; Ví d to mt cây nh phân có 3 nút :
(create-tree 1 (create-tree 2 empty-tree empty-tree)
(create-tree 3 empty-tree empty-tree))
--> ’(1 (2 () ()) (3 () ()))
Cây A trong Hình V.10. trên đây đc đnh ngha nh sau :
(create-tree 1
(create-tree 2
(create-tree 4 empty-tree empty-tree)
(create-tree 5 empty-tree empty-tree))
(create-tree 3 empty-tree empty-tree))
--> (1 (2) (4 () ()) (5 () ())) (3 () ()))
Trong dng đy đ, mi nút lá A đu đc biu din dng (A () ()).
3. Biu din đn gin
Khi kiu các phn t không cha các b đôi, k c không cha danh sách, mt lá có th
đc biu din đn gin bi giá tr ca nút, mà không cn cons. Ta đnh ngha li nh sau :
(define empty-tree ())
(define empty-tree? null?)
(define (create-tree E A1 A2)
(if (and (null? A1) (null? A2))
E
(list E A1 A2)))
; hay (cons E (cons A1 A2)) tit kim hn
(define (root A)
(if (pair? A)
(car A )
A))
(define (left A)
(if (pair? A)
(cadr A)
()))
(define (right A)
166 LP TRÌNH HÀM
(if (pair? A)
(caddr A)
;hay (cadr A) trong trng hp biu din tit kim
()))
(define (leaf? A)
(not (pair? A)))
; Ví d to mt cây nh phân có 3 nút :
(create-tree 1
(create-tree 2 empty-tree empty-tree)
(create-tree 3 empty-tree empty-tree))
--> ’(1 2 3)
Cây A trong Hình V.10. trên đây đc đnh ngha nh sau :
(create-tree 1
(create-tree 2
(create-tree 4 empty-tree empty-tree)
(create-tree 5 empty-tree empty-tree))
(create-tree 3 empty-tree empty-tree))
--> ’(1 (2 4 5) 3)
Trong dng đn gin này, các nút ca cây A đc biu din bi giá tr ca nút ln lt
theo th t duyt cây gc
−
trái
−
phi.
V.4.1.3. Mt s ví d lp trình đn gin
1. m s lng các nút có trong mt cây
Xây dng hàm size có dng hàm nh sau :
; size : BinTree(T) → Integer
(define (size A)
(if (empty-tree? A)
0
(+ 1 (size (left A)) (size (right A)))))
; m s lng các nút có trong cây A Hình V.10
````:
(size A)
--> 5
2. Tính đ cao ca mt cây
cao ca mt cây khác rng là s cnh ti đa ni gc ca cây vi các lá ca nó.
Theo quy c, đ cao ca mt cây rng có th là −1. Xây dng hàm height có dng nh sau :
; height : BinTree(T) → Integer
(define (height A)
(if (empty-tree? A)
-1
(+ 1 (max (height (left A)) (height (right A))))))
; cao ca cây A Hình V.10.:
CU TRÚC D LIU 167
(height A)
--> 2
Kích thc n và đ cao h ca cây nh phân thng đc dùng đ đo đ phc tp tính toán.
Ta có :
[log
2
n] ≤ h ≤ n−1 (ký hiu [x] đ ch phn nguyên ca x)
Cây đy đ : n = 2
h+1
− 1
Cây suy thoái : n = h + 1
Có tt c
2
( 1)
n
n
C
n +
cây nh phân kích thc n,
vi là t hp chp p (cách chn p phn t) ca n (trong s n phn t).
n
p
C
C hai hàm size và height trên đu có chi phí tuyn tính theo n.
V.4.1.4. Duyt cây nh phân
Cây đc s dng ch yu đ t chc lu tr d liu nên khi cn khai thác ngi ta phi
duyt (traverse) hay thm (visit) ni dung ca cây. Có rt nhiu chin lc duyt cây. Do cây
đc xây dng đ quy, mt cách t nhiên, chng trình duyt cây có th s dng đ quy. Mi
chin lc duyt cây đu tr v kt qu là danh sách các nút đã đc duyt qua.
Ta s xét sau đây chin lc duyt cây nh phân theo chiu sâu. Trong trng hp tng
quát, thut toán có dng nh sau :
(define (run-around A)
(if (empty-tree? A)
<result>
(f (root A) ; duyt nút gc
(run-around (left A )) ; duyt cây con trái
(run-around (right A)))))) ; duyt cây con phi
Trong đó, <result> là kt qu tr v (thng là mt danh sách rng) và f là hàm x lý
mt phn t ca cây A (liên quan đn ni dung hay nhãn ca nút va đc duyt). Th t
duyt cây trên đây (gc-trái-phi) là th t trc hay còn đc gi là th t tin t (prefixe
order). Nu đi li th t duyt trên đây thì ta nhn đc :
− Th t gia hay trung t (infixe order) nu nút gc đc x lý gia các li gi đ quy
(trái-gc-phi).
− Th t sau hay hu t (postfixe order) nu nút gc đc x lý sau các li gi đ quy
(trái-phi-gc).
Tuy nhiên, ta cng có th có thêm ba th t duyt cây theo hng ngc li là : gc-phi-
trái, phi-gc-trái và phi-trái-gc.
Ví d hàm nodes sau đây duyt cây nh phân đ tr v danh sách tt c các nút ca cây
đó. Do hàm s dng lnh append nên có chi phí bc hai đi vi kích thc ca cây.
; nodes: BinTree(T) → List(T)
; Duyt cây nh phân theo th t trc :
(define (pre-nodes A)
168 LP TRÌNH HÀM
(if (empty-tree? A)
’()
(append (list (root A))
(pre-nodes (left A))
(pre-nodes (right A)))))
; Duyt cây nh phân theo th t gia :
(define (inf-nodes A)
(if (empty-tree? A)
’()
(append (inf-nodes (left A))
(list (root A))
(inf-nodes (right A)))))
; Duyt cây nh phân theo th t sau :
(define (post-nodes A)
(if (empty-tree? A)
’()
(append (post-nodes (left A))
(post-nodes (right A))
(list (root A)))))
minh ho các thut toán duyt cây trên đây, gi s A là cây đc cho nh hình sau :
Hình V.11. Cây nh phân A có 7 nút
Cây A đc đnh ngha theo kiu đn gin :
(define A
(create-tree 1
(create-tree 2
(create-tree 4 empty-tree empty-tree)
(create-tree 5 empty-tree empty-tree))
(create-tree 3
(create-tree 6 empty-tree empty-tree)
(create-tree 7 empty-tree empty-tree)))
--> ’(1 (2 4 5) (3 6 7))
(pre-nodes A)
--> ’(1 2 4 5 3 6 7) ; th t trc
(inf-nodes A)
--> ’(4 2 5 1 6 3 7) ; th t gia
(post-nodes A)
--> ’(4 5 3 6 7 3 1) ; th t sau
1
2 3
4 5 6 7
CU TRÚC D LIU 169
V.4.2. Cu trúc cây tng quát
Trong trng hp cây tng quát, mi nút đu đc gn nhãn và có s lng cây con tu ý
không hn ch. Mt cách trc giác, cây có gn nhãn là mt cu trúc d liu gm mt giá tr
nhãn và mt danh sách có th rng các cây con gn nhãn. Nh vy cây không bao gi rng,
nhng danh sách các cây con có th rng. Trong trng hp cây ch có mt con, thì không có
khái nim cây con trái hay cây con phi. Ta cng cn chú ý rng cu trúc cây nh phân không
phi là trng hp riêng ca cây tng quát.
Ta có đnh ngha đ quy nh sau :
1. Mt danh sách các cây kiu T là mt rng (forest) kiu T.
2. Nu E là mt phn t kiu T và F là mt rng kiu T, thì cp (E, F) là mt cây kiu T.
3. Ch nhng đi tng đc đnh ngha quy tc 1 là rng kiu T.
Ch nhng đi tng đc đnh ngha quy tc 2 là cây kiu T.
Cho trc tp hp các cây kiu T. Vi mi cây T, các cây T
1
, ..., T
n
đc gi là con ca T.
Mt nhãn e nào đó là gc ca T. S con mi nút là bc (degree) ca nút đó. Mt nút có có
bc 0 là mt lá
V.4.2.1. Kiu tru tng cây tng quát
Ta xây dng kiu tru tng ca cây tng quát gm Tree(T) và Forest(T)=List(Tree(T))
nh sau :
types Tree(T), Forest(T)
functions
() : Forest(T)
null? : Forest(T) → boolean
cons : Tree(T)×Forest(T) → Forest(T)
car : Forest(T) → Tree(T)
cdr : Forest(T) → Forest(T)
create-tree : T × Forest(T) → Tree(T)
root : Tree(T) → T
forest : Tree(T) → Forest(T)
leaf? : Tree(T) → boolean
var F: Forest(T); A, E: Tree(T)
preconditions
car(F) và cdr(F) ch xác đnh khi và ch khi F ≠ ()
axioms (các tiên đ liên quan đn list không nhc li đây)
root(create-tree(E, F)) = E
forest(create-tree(E, F)) = F
leaf?(A) and forest(A)= ()
V.4.2.2. Biu din cây tng quát
Ta đa ra hai cách biu din cây tng quát nh sau :
1. Biu din cây nh mt b đôi
(define create-tree cons)
(define root car)