Tải bản đầy đủ (.pdf) (144 trang)

Cấu trúc dữ liệu và giải thuật Học viện bưu chính viễn thông

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 (1.18 MB, 144 trang )




HC VIN CÔNG NGH BU CHÍNH VIN THÔNG







CU TRÚC D LIU VÀ GII THUT

(Dùng cho sinh viên h đào to đi hc t xa)
Lu hành ni b









HÀ NI - 2007

LI NÓI U

Cu trúc d liu và gii thut là mt trong nhng môn hc c bn ca sinh viên ngành Công
ngh thông tin. Các cu trúc d liu và các gii thut đc xem nh là 2 yu t quan trng nht
trong lp trình, đúng nh câu nói ni ting ca Niklaus Wirth: Chng trình = Cu trúc d liu +


Gii thut (Programs = Data Structures + Algorithms). Nm vng các cu trúc d liu và các gii
thut là c s đ sinh viên tip cn vi vi
c thit k và xây dng phn mm cng nh s dng các
công c lp trình hin đi.
Cu trúc d liu có th đc xem nh là 1 phng pháp lu tr d liu trong máy tính
nhm s dng mt cách có hiu qu các d liu này. Và đ s dng các d liu mt cách hiu qu
thì cn phi có các thut toán áp dng trên các d liu đó. Do v
y, cu trúc d liu và gii thut là
2 yu t không th tách ri và có nhng liên quan cht ch vi nhau. Vic la chn mt cu trúc
d liu có th s nh hng ln ti vic la chn áp dng gii thut nào.
Tài liu “Cu trúc d liu và gii thut” bao gm 7 chng, trình bày v các cu trúc d liu
và các gii thut c bn nh
t trong tin hc.
Chng 1 trình bày v phân tích và thit k thut toán. u tiên là cách phân tích 1 vn đ,
t thc tin cho ti chng trình, cách thit k mt gii pháp cho vn đ theo cách gii quyt bng
máy tính. Tip theo, các phng pháp phân tích, đánh giá đ phc tp và thi gian thc hin gii
thut cng đc xem xét trong chng. Chng 2 trình bày v đ qui, mt khái nim rt c bn
trong toán hc và khoa hc máy tính. Vi
c s dng đ qui có th xây dng đc nhng chng
trình gii quyt đc các vn đ rt phc tp ch bng mt s ít câu lnh, đc bit là các vn đ
mang bn cht đ qui.
Chng 3, 4, 5, 6 trình bày v các cu trúc d liu đc s dng rt thông dng nh mng
và danh sách liên kt, ngn xp và hàng đi, cây, đ th. ó là các c
u trúc d liu cng rt gn
gi vi các cu trúc trong thc tin. Chng 7 trình bày v các thut toán sp xp và tìm kim.
Các thut toán này cùng vi các k thut đc s dng trong đó đc coi là các k thut c s
cho lp trình máy tính. Các thut toán đc xem xét bao gm các lp thut toán đn gin và c
các thut toán cài đt phc tp nhng có thi gian thc hin ti u.
Cui mi phn đu có các câu hi và bài tp đ sinh viên ôn luyn và t kim tra kin thc
ca mình. Cui tài liu có các ph lc hng dn tr li câu hi, mã ngun tham kho và tài liu

tham kho.
V nguyên tc, các cu trúc d liu và các gii thut có th đc biu din và cài đt bng
bt c ngôn ng lp trình hin đi nào. Tuy nhiên, đ có đc các phân tích sâu sc h
n và có kt
qu thc t hn, tác gi đã s dng ngôn ng lp trình C đ minh ho cho các cu trúc d liu và
thut toán. Do vy, ngoài các kin thc c bn v tin hc, ngi đc cn có kin thc v ngôn ng
lp trình C.
Cui cùng, mc dù đã ht sc c gng nhng chc chn không tránh khi các thiu sót. Tác
gi rt mong nhn
đc s góp ý ca bn đc và đng nghip đ tài liu đc hoàn thin hn.

Hà Ni, tháng 10/2007


3
CHNG 1
PHÂN TÍCH VÀ THIT K GII THUT


Chng 1 trình bày các khái nim v gii thut và phng pháp tinh chnh tng bc
chng trình đc th hin qua ngôn ng din đt gii thut. Chng này cng nêu phng pháp
phân tích và đánh giá mt thut toán, các khái nim liên quan đn vic tính toán thi gian thc
hin chng trình.
Trong mi phn đu có các minh ho c th. Phn đu đa ra ví d v bài toán nút giao
thông và phng pháp gii quyt bài toán t phân tích v
n đ cho đn thit k gii thut, tinh
chnh tng bc cho ti mc c th hn. Phn 2 đa ra mt ví d v phân tích và tính toán thi
gian thc hin gii thut sp xp ni bt.
 hc tt chng này, sinh viên cn nm vng phn lý thuyt và tìm các ví d tng t đ
thc hành phân tích, thit k, và đánh giá gii thut.


1.1 GII THUT VÀ NGÔN NG DIN T GII THUT
1.1.1 Gii thut
Trong thc t, khi gp phi mt vn đ cn phi gii quyt, ta cn phi đa ra 1 phng
pháp đ gii quyt vn đ đó. Khi mun gii quyt vn đ bng cách s dng máy tính, ta cn
phi đa ra 1 gii pháp phù hp vi vic thc thi bng các ch
ng trình máy tính. Thut ng
“thut toán” đc dùng đ ch các gii pháp nh vy.
Thut toán có th đc đnh ngha nh sau:
Thut toán là mt chui hu hn các lnh. Mi lnh có mt ng ngha rõ ràng và có th
đc thc hin vi mt lng hu hn tài nguyên trong mt khong hu hn thi gian.
Chng hn lnh x = y + z là mt lnh có các tính cht trên.
Trong mt thu
t toán, mt lnh có th lp đi lp li nhiu ln, tuy nhiên đi vi bt k b d
liu đu vào nào, thut toán phi kt thúc sau khi thc thi mt s hu hn lnh.
Nh đã nói  trên, mi lnh trong thut toán phi có ng ngha rõ ràng và có th đc thc
thi trong mt khong thi gian hu hn. Tuy nhiên, đôi khi mt lnh có ng ngha rõ ràng
đi vi
ngi này nhng li không rõ ràng đi vi ngi khác. Ngoài ra, thng rt khó đ chng minh
mt lnh có th đc thc hin trong 1 khong hu hn thi gian. Thm chí, k c khi bit rõ ng
ngha ca các lnh, cng khó đ có th chng minh là vi bt k b d liu đu vào nào, thut
toán s dng.
Tip theo, chúng ta s xem xét mt ví d v
 xây dng thut toán cho bài toán đèn giao
thông:
Gi s ngi ta cn thit k mt h thng đèn cho mt nút giao thông có nhiu đng giao
nhau phc tp.  xây dng tp các trng thái ca các đèn giao thông, ta cn phi xây dng mt
chng trình có đu vào là tp các ngã r đc phép ti nút giao thông (li đi thng cng đc
xem nh là 1 ngã r) và chia tp này thành 1 s ít nht các nhóm, sao cho tt c các ngã r trong
nhóm có th đc đi cùng lúc mà không xy ra tranh chp. Sau đó, chúng ta s gn trng thái ca

các đèn giao thông vi mi nhóm va đc phân chia. Vi cách phân chia có s nhóm ít nht, ta
s xây dng đc 1 h thng đèn giao thông có ít trng thái nht.


4
Chng hn, ta xem xét bài toán trên vi nút giao thông đc cho nh trong hình 1.1 
di. Trong nút giao thông trên, C và E là các đng 1 chiu, các đng còn li là 2 chiu. Có tt
c 13 ngã r ti nút giao thông này. Mt s ngã r có th đc đi đng thi, chng hn các ngã r
AB (t A r sang B) và EC. Mt s ngã r thì không đc đi đng thi (gây ra các tuyn giao
thông xung đt nhau), chng hn AD và EB. H thng đèn ti nút giao thông phi hot đ
ng sao
cho các ngã r xung đt (chng hn AD và EB) không đc cho phép đi ti cùng mt thi đim,
trong khi các ngã r không xung đt thì có th đc đi ti cng 1 thi đim.











Hình 1.1 Nút giao thông

Chúng ta có th mô hình hóa vn đ này bng mt cu trúc toán hc gi là đ th (s đc
trình bày chi tit  chng 5).  th là mt cu trúc bao gm 1 t
p các đim gi là đnh và mt
tp các đng ni các đim, gi là các cnh. Vn đ nút giao thông có th đc mô hình hóa bng

mt đ th, trong đó các ngã r là các đnh, và có mt cnh ni 2 đnh biu th rng 2 ngã r đó
không th đi đng thi. Khi đó, đ th ca nút giao thông  hình 1.1 có th đc biu din nh 
hình 1.2.













Hình 1.2  th ngã r

A
B
C
D
E
ACAB AD
BCBA BD
DBDA DC
EBEA EC
ED



5
Ngoài cách biu din trên, đ th còn có th đc biu din thông qua 1 bng, trong đó phn
t  hàng i, ct j có giá tr 1 khi và ch khi có 1 cnh ni đnh i và đnh j.

AB AC AD BA BC BD DA DB DC EA EB EC ED
AB 1 1 1 1
AC 1 1 1 1 1
AD 1 1 1
BA
BC 1 1 1
BD 1 1 1 1 1
DA 1 1 1 1 1
DB 1 1 1
DC
EA 1 1 1
EB 1 1 1 1 1
EC 1 1 1 1
ED

Bng 1.1 Biu din đ th ngã r bng bng

Ta có th s dng đ th trên đ gii quyt vn đ thit k h thng đèn cho nút giao thông
nh đã nói.
Vic tô màu mt đ th là vic gán cho mi đnh ca đ th mt màu sao cho không có hai
đnh đc ni bi 1 cnh nào đó li có cùng mt màu. D thy rng v
n đ nút giao thông có th
đc chuyn thành bài toán tô màu đ th các ngã r  trên sao cho phi s dng s màu ít nht.
Bài toàn tô màu đ th là bài toán đã xut hin và đc nghiên cu t rt lâu. Tuy nhiên, đ
tô màu mt đ th bt k vi s màu ít nht là bài toán rt phc tp.  gii bài toán này, ngi ta
thng s dng phng pháp “vét cn” đ th tt c các kh nng có th

. Có ngha, đu tiên th
tin hành tô màu đ th bng 1 màu, tip theo dùng 2 màu, 3 màu, v.v. cho ti khi tìm ra phng
pháp tô màu tho mãn yêu cu.
Phng pháp vét cn có v thích hp vi các đ th nh, tuy nhiên đi vi các đ th phc
tp thì s tiêu tn rt nhiu thi gian thc hin cng nh tài nguyên h thng. Ta có th tip cn
vn đ theo hng c gng tìm ra mt gii pháp
đ tt, không nht thit phi là gii pháp ti u.
Chng hn, ta s c gng tìm mt gii pháp tô màu cho đ th ngã r  trên vi mt s màu khá ít,
gn vi s màu ít nht, và thi gian thc hin vic tìm gii pháp là khá nhanh. Gii thut tìm các
gii pháp đ tt nhng cha phi ti u nh vy gi là các gii thut tìm theo “cm tính”.
i vi bài toán tô màu
đ th, mt thut toán cm tính thng đc s dng là thut toán
“tham n” (greedy). Theo thut toán này, đu tiên ta s dng mt màu đ tô nhiu nht s đnh có
th, tho mãn yêu cu bài toán. Tip theo, s dng màu th 2 đ tô các đnh cha đc tô trong
bc 1, ri s dng đn màu th 3 đ tô các đnh cha đc tô trong bc 2, v.v.


6
 tô màu các đnh vi màu mi, chúng ta thc hin các bc:
- La chn 1 đnh cha đc tô màu và tô nó bng màu mi.
- Duyt qua các đnh cha đc tô màu. Vi mi đnh dng này, kim tra xem có cnh
nào ni nó vi mt đnh va đc tô bi màu mi hay không. Nu không có cnh nào
thì ta tô màu đnh này bng màu mi.
Thut toán này đc gi là “tham n” vì ti mi bc nó tô màu t
t c các đnh có th mà
không cn phi xem xét xem vic tô màu đó có đ li nhng đim bt li cho các bc sau hay
không. Trong nhiu trng hp, chúng ta có th tô màu đc nhiu đnh hn bng 1 màu nu
chúng ta bt “tham n” và b qua mt s đnh có th tô màu đc trong bc trc.
Ví d, xem xét đ th  hình 1.3, trong đó đnh 1 đã đc tô màu đ. Ta thy rng hoàn toàn
có th tô c

2 đnh 3 và 4 là màu đ, vi điu kin ta không tô đnh s 2 màu đ. Tuy nhiên, nu ta
áp dng thut toán tham n theo th t các đnh ln dn thì đnh 1 và đnh 2 s là màu đ, và khi
đó đnh 3, 4 s không đc tô màu đ.

















Hình 1.3  th

Bây gi ta s xem xét thut toán tham n đc áp dng trên đ th các ngã r  hình 1.2 nh
th nào. Gi s ta bt đu t đnh AB và tô cho đnh này màu xanh. Khi đó, ta có th tô cho đnh
AC màu xanh vì không có cnh ni đnh này vi AB. AD cng có th tô màu xanh vì không có
cnh ni AD vi AB, AC. nh BA không có cnh ni ti AB, AC, AD nên cng có th đc tô
màu xanh. Tuy nhiên, đnh BC không tô đc màu xanh vì tn ti cnh ni BC và AB. Tng t
nh vy, BD, DA, DB không th tô màu xanh vì tn ti cnh ni chúng ti mt trong các đnh đã
tô màu xanh. Cnh DC thì có th
tô màu xanh. Cui cùng, cnh EA, EB, EC cng không th tô

màu xanh trong khi ED có th đc tô màu xanh.


1 5
3
4
2
a)  th ban đu
1 5
3
4
2
b) Tô màu theo thut toán tham n
1 5
3
4
2
c) Mt cách tô màu tt hn


7















Hình 1.4 Tô màu xanh cho các đnh ca đ th ngã r

Tip theo, ta s dng màu đ đ tô các đnh cha đc tô màu  bc trc. u tiên là
BC. BD cng có th tô màu đ, tuy nhiên do tn ti cnh ni DA vi BD nên DA không đc tô
màu đ. Tng t nh vy, DB không tô đc màu đ còn EA có th tô màu đ. Các đnh cha
đc tô màu còn li đ
u có cnh ni ti các đnh đã tô màu đ nên cng không đc tô màu.













Hình 1.5 Tô màu đ trong bc 2
Bc 3, các đnh cha đc tô màu còn li là DA, DB, EB, EC. Nu ta tô màu đnh DA là
màu lc thì DB cng có th tô màu lc. Khi đó, EB, EC không th tô màu lc và ta chn 1 màu
th t là màu vàng cho 2 đnh này.



ACAB AD
BCBA BD
DBDA DC
EBEA EC
ED
ACAB AD
BCBA BD
DBDA DC
EBEA EC
ED


8














Hình 1.6 Tô màu lc và màu vàng cho các đnh còn li


Nh vy, ta có th dùng 4 màu xanh, đ, lc, vàng đ tô màu cho đ th ngã r  hình 1.2
theo yêu cu nh đã nói  trên. Bng tng hp màu đc mô t nh sau:

Màu Ngã r
Xanh AB, AC, AD, BA, DC, ED
 BC, BD, EA
Lc DA, DB
Vàng EB, EC

Bng 1.2 Bng tng hp màu

Thut toán tham n không đm bo cho ra kt qu ti u là s màu ít nht đc dùng. Tuy
nhiên, ngi ta có th dùng mt s tính cht ca đ th đ đánh giá kt qu thu đc.
Tr li vi vn đ nút giao thông, t kt qu tô màu trên, ta có th thit k h thng đèn
giao thông theo bng tng hp màu trên, trong đó m
i trng thái ca h thng đèn tng ng vi
1 màu. Ti mi trng thái, các ngã r nm ti hàng tng ng vi màu đó đc cho phép đi, các
ngã r còn li b cm.
1.1.2 Ngôn ng din đt gii thut và k thut tinh chnh tng bc
Sau khi đã xây dng đc mô hình toán hc cho vn đ cn gii quyt, tip theo, ta có th
hình thành mt thu
t toán cho mô hình đó. Phiên bn đu tiên ca thut toán thng đc din t
di dng các phát biu tng đi tng quát, và sau đó s đc tinh chnh dn tng bc thành
chui các lnh c th, rõ ràng hn. Ví d trong thut toán tham n  trên, ta mô t bc thc hin
 mc tng quát là “La chn 1 đnh cha đc tô màu”. Vi phát biu nh vy, ta hy vng rng
ngi đc có th nm đc ý tng thc hin thao tác. Tuy nhiên, đ chuyn các phát biu đó
ACAB AD
BCBA BD
DBDA DC
EBEA EC

ED


9
thành chng trình máy tính, cn phi qua 1 s bc tinh chnh cho ti khi đt đn mc các phát
biu đu có th đc chuyn đi trc tip sang các lnh ca ngôn ng lp trình.
Tr li ví d v bài toán tô màu đ th bng thut toán tham n. Ta s xem xét vic mô t
thut toán t mc tng quát cho ti mt s mc c th hn. Ti bc nào đó, gi s ta có đ th G
có 1 s đnh đã đc tô màu theo quy tc đã nói  trên. Th tc Tham_an di đây s xác đnh 1
tp các đnh cha đc tô màu thuc G mà có th cùng đc tô bi 1 màu mi. Th tc này s
đc gi đi gi li nhiu ln cho ti khi tt c các đnh ca G đã đc tô màu.  mc tng quát,
th t
c đc mô t nh sau:

void Tham_an(GRAPH: G, SET: Mau_moi)
{
Mau_moi = Tp rng;
For mi đnh v cha đc tô màu thuc G
If v không đc ni ti đnh nào trong tp Mau_moi
{
Tô màu mi cho đnh v;
a v vào tp Mau_moi;
}
}

Trong th tc trên, ta s dng mt ngôn ng din đt gii thut ta nh ngôn ng lp trình
C. Trong ngôn ng này, các lnh đc mô t di dng ngôn ng t nhiên nhng vn tuân theo cú
pháp ca ngôn ng lp trình.
Ta nhn thy rng các phát biu trong th tc trên còn rt tng quát, và cha tng ng vi
các lnh trong ngôn ng lp trình, chng hn các điu kin ki

m tra trong câu lnh For và If 
mc mô t hin ti là không thc hin đc trong C.  th tc có th thc thi đc, ta cn phi
tinh chnh mt s bc đ có th chuyn đi v chng trình trong ngôn ng lp trình C thông
thng.
u tiên, ta xem xét lnh If  trên.  kim tra xem đnh v có ni ti mt đnh nào đó trong
tp Mau_moi hay không, ta xem xét tng đnh w trong Mau_moi và s dng đ th G
đ kim tra
xem có tn ti cnh ni v à w không.  lu gi kt qu kim tra, ta s dng mt bin ton_tai.
Khi đó, th tc đc tinh chnh nh sau:
void Tham_an(GRAPH: G, SET: Mau_moi)
{
int ton_tai;
Mau_moi = Tp rng;
For mi đnh v cha đc tô màu thuc G
{
ton_tai = 0;
For mi đnh w thuc Mau_moi
If tn ti cnh ni v và w trong G
ton_tai = 1;
If ton_tai = = 1
{


10
Tô màu mi cho đnh v;
a v vào tp Mau_moi;
}
}
}


Nh vy, ta có th thy rng điu kin kim tra trong phát biu If đã đc mô t c th hn
bng các phát nh hn,và các phát biu này có th d dàng chuyn thành các lnh c th trong C.
Tip theo, ta s tinh chnh các vòng lp For đ duyt qua các đnh thuc G và thuc Mau_moi. 
làm điu này, tt nht là ta thay For bng mt vòng lp While, bin v ban đu đc gán là phn t
đu tiên cha tô màu trong tp G, và ti mi bc lp, bin v s đc thay bng phn t cha tô
màu tip theo trong G. Vòng lp F bên trong có th thc hin tng t.
Void Tham_an(GRAPH: G, SET: Mau_moi)
{
int ton_tai;
int v, w
Mau_moi = Tp rng;
v = đnh cha tô màu đu tiên trong G ;
While v != NULL
{
ton_tai = 0;
w = đnh đu tiên trong Mau_moi;
While w != NULL{
If tn ti cnh ni v và w trong G
ton_tai = 1;
w = đnh tip theo trong Mau_moi ;
}
If ton_tai = = 1
{
Tô màu mi cho đnh v;
a v vào tp Mau_moi;
}
v = đnh cha tô màu tip theo trong G;
}
}


Nh vy, ta thy các phát biu trong th tc đã khá c th, tuy nhiên, đ chuyn đi thành
chng trình trong ngôn ng C thì cn ti vài bc tinh chnh na. Bn đc hãy xem nh đó là
bài tp và t gii đ hiu rõ v ngôn ng din đt gii thut cng nh k thut tinh chnh tng
bc.
1.2 PHÂN TÍCH THUT TOÁN
Vi mi vn đ c
n gii quyt, ta có th tìm ra nhiu thut toán khác nhau. Có nhng thut
toán thit k đn gin, d hiu, d lp trình và sa li, tuy nhiên thi gian thc hin ln và tiêu tn


11
nhiu tài nguyên máy tính. Ngc li, có nhng thut toán thit k và lp trình rt phc tp,
nhng cho thi gian chy nhanh hn, s dng tài nguyên máy tính hiu qu hn. Khi đó, câu hi
đt ra là ta nên la chn gii thut nào đ thc hin?
i vi nhng chng trình ch đc thc hin mt vài ln thì thi gian chy không phi là
tiêu chí quan trng nht. i vi bài toán ki
u này, thi gian đ lp trình viên xây dng và hoàn
thin thut toán đáng giá hn thi gian chy ca chng trình và nh vy nhng gii thut đn
gin v mt thit k và xây dng nên đc la chn.
i vi nhng chng trình đc thc hin nhiu ln thì thi gian chy ca chng trình
đáng giá hn rt nhiu so vi thi gian đc s dng
đ thit k và xây dng nó. Khi đó, la chn
mt gii thut có thi gian chy nhanh hn (cho dù vic thit k và xây dng phc tp hn) là mt
la chn cn thit. Trong thc t, trong giai đon đu ca vic gii quyt vn đ, mt gii thut
đn gin thng đc thc hin trc nh là 1 nguyên mu (prototype), sau đó nó s
đc phân
tích, đánh giá, và ci tin thành các phiên bn tt hn.
1.2.1 c lng thi gian thc hin chng trình
Thi gian chy ca 1 chng trình ph thuc vào các yu t sau:
- D liu đu vào

- Cht lng ca mã máy đc to ra bi chng trình dch
- Tc đ thc thi lnh ca máy
-  phc tp v thi gian c
a thut toán
Thông thng, thi gian chy ca chng trình không ph thuc vào giá tr d liu đu vào
mà ph thuc vào kích thc ca d liu đu vào. Do vy thi gian chy ca chng trình nên
đc đnh ngha nh là mt hàm có tham s là kích thc d liu đu vào. Gi s T là hàm c
lng thi gian chy ca chng trình, khi đó vi d liu đu vào có kích thc n thì th
i gian
chy ca chng trình là T(n). Ví d, đi vi mt s chng trình thì thi gian chy là an hoc
an
2
,

trong đó a là hng s. n v ca hàm T(n) là không xác đnh, tuy nhiên ta có th xem nh
T(n) là tng s lnh đc thc hin trên 1 máy tính lý tng.
Trong nhiu chng trình, thi gian thc hin không ch ph thuc vào kích thc d liu
vào mà còn ph thuc vào tính cht ca nó. Khi tính cht d liu vào tho mãn mt s đc đim
nào đó thì thi gian thc hin chng trình có th là ln nht ho
c nh nht. Khi đó, ta đnh ngha
thi gian thc hin chng trình T(n) trong trng hp xu nht hoc tt nht. ó là thi gian
thc hin ln nht hoc nh nht trong tt c các b d liu vào có kích thc n. Ta cng đnh
ngha thi gian thc hin trung bình ca chng trình trên mi b d liu vào kích thc n. Trong
thc t, c l
ng thi gian thc hin trung bình khó hn nhiu so vi thi gian thc hin trong
trng hp xu nht hoc tt nht, bi vì vic phân tích thut toán trong trng hp trung bình
khó hn v mt toán hc, đng thi khái nim “trung bình” không có ý ngha thc s rõ ràng.
Yu t cht lng ca mã máy đc to bi chng trình dch và tc đ thc thi lnh ca
máy cng nh h
ng ti thi gian thc hin chng trình cho thy chúng ta không th th hin

thi gian thc hin chung trình di đn v thi gian chun, chng hn phút hoc giây. Thay vào
đó, ta có th phát biu thi gian thc hin chng trình t l vi n hoc n
2
v.v. H s ca t l là 1
hng s cha xác đnh, ph thuc vào máy tính, chng trình dch, và các nhân t khác.
Ký hiu O(n)
 biu th cp đ tng ca hàm, ta s dng ký hiu O(n). Ví d, ta nói thi gian thc hin
T(n) ca chng trình là O(n
2
), có ngha là tn ti các hng s dung c và n
0
sao cho T(n) ≤ cn
2

vi n ≥ n
0
.


12
Ví d, xét hàm T(n) = (n+1)
2
.
Ta có th thy T(n) là O(n
2
) vi n
0
= 1 và c = 4, vì ta có T(n)
= (n+1)
2

< 4n
2
vi mi n ≥ 1. Trong ví d này, ta cng có th nói rng T(n) là O(n
3
), tuy nhiên,
phát biu này “yu” hn phát biu T(n) là O(n
2
).
Nhìn chung, ta nói T(n) là O(f(n)) nu tn ti các hng s dng c và n
0
sao cho T(n) <
c.f(n) vi n ≥ n
0
. Mt chng trình có thi gian thc hin là O(f(n)) thì đc xem là có cp đ
tng f(n).
Vic đánh giá các chng trình có th đc thc hin qua vic đánh giá các hàm thi gian
chy ca chng trình,b qua các hng s t l. Vi gi thit này, mt chng trình vi thi gian
thc hin là O(n
2
) s tt hn chng trình vi thi gian chy O(n
3
). Bên cnh các yu t hng s
xut phát t chng trình dch và máy, còn có thêm hng s t bn thân chung trình. Ví d, trên
cùng mt chng trình dch và cùng 1 máy, chng trình đu tiên có thi gian thc hin là 100n
2
,
trong khi chung trình th 2 có thi gian thc hin là 5n
3
. Vi n nh, có th 5n
3

nh hn 100n
2
,
tuy nhiên vi n đ ln thì 5n
3
s ln hn 100n
2
đáng k.
Mt lý do na đ xem xét cp đ tng v thi gian thc hin ca chng trình là nó cho
phép ta xác đnh đ ln ca bài toán mà ta có th gii quyt. Mc dù máy tính có tc đ ngày càng
cao, tuy nhiên, vi nhng chng trình có thi gian thc hin có cp đ tng ln (t n
2
tr lên),
thì vic tng tc đ ca máy tính to ra s khác bit không đáng k v kích thc bài toán có th
x lý bi máy tính trong mt khong thi gian c đnh.
1.2.2 Tính toán thi gian thc hin chng trình
 tính toán đc thi gian thc hin chng trình, ta cn chú ý mt s nguyên tc cng và
nhân cp đ tng ca hàm nh sau :
Gi s T
1
(n) và T
2
(n) là thi gian chy ca 2 đon chng trình P
1
và P
2
, trong đó T
1
(n) là
O(f(n)) và T

2
(n) là O(g(n)). Khi đó, thi gian thc hin ca 2 đon chng trình trình ni tip P
1
,
P
2
là O(max(f(n), g(n))).
Nguyên tc cng trên có th s dng đ tính thi gian thc hin ca chng trình bao gm
1 s tun t các bc, mi bc có th là 1 đon chng trình bao gm 1 s vòng lp và r nhánh.
Ví d, gi s ta có 3 bc thc hin chng trình ln lt có thi gian chy là O(n
2
), O(n
3
),
O(nlog n). Khi đó, thi gian chy ca 2 đon chng trình đu là O(max(n
2
, n
3
)) = O(n
3
), còn thi
gian chy ca c 3 đon chng trình là O(max(n
3
, nlog n)) = O(n
3
).
Nguyên tc nhân cp đ tng ca hàm nh sau: Vi gi thit v T
1
(n) và T
2

(n) nh trên, nu
2 đon chng trình P
1
và P
2
không đc thc hin tun t mà lng nhau thì thi gian chy tng
th s là T
1
(n).T
2
(n) = O(f(n).(g(n)).
 minh ho cho vic phân tích và tính toán thi gian thc hin ca 1 chng trình, ta s
xem xét mt thut toán đn gian đ sp xp các phn t ca mt tp hp, đó là thut toán sp xp
ni bt (bubble sort).
Thut toán nh sau :
void buble (int a[n]){
int i, j, temp;
1. for (i = 0; i < n-1; i++)
2. for (j = n-1; j>= i+1; j )
3. if (a[j-1] > a[j]{
// i ch cho a[j] và a[j-1]
4. temp = a[j-1];
5. a[j-1] = a[j];


13
6. a[j] = temp;
}
}


Trong thut toán này, mi ln duyt ca vòng lp trong (bin duyt j) s làm “ni” lên trên
phn t nh nht trong các phn t đc duyt.
D thy rng kích thc d liu vào chính là s phn t đc sp, n. Mi lnh gán s có
thi gian thc hin c đnh, không ph thuc vào n, do vy, các lnh 4, 5, 6 s có thi gian thc
hin là O(1), tc thi gian thc hi
n là hng s. Theo quy tc cng cp đ tng thì tng thi gian
thc hin c 3 lnh là O(max(1, 1, 1)) = O(1).
Tip theo ta s xem xét thi gian thc hin ca các lnh lp và r nhánh. Lnh If kim tra
điu kin đ thc hin nhóm lnh gán 4, 5, 6. Vic kim tra điu kin s có thi gian thc hin là
O(1). Ngoài ra, chúng ta cha bit đc là điu kin có tho mãn hay không, tc là không bi
t
đc nhóm lnh gán có đc thc hin hay không. Do vy, ta gi thit trng hp xu nht là tt
c các ln kim tra điu kin đu tho mãn, và các lnh gán đc thc hin. Nh vy, toàn b lnh
If s có thi gian thc hin là O(1).
Tip tc xét t trong ra ngoài, ta xét đn vòng lp trong (bin duyt j). Trong vòng lp này,
ti mi bc lp ta cn thc hi
n các thao tác nh kim tra đã gp điu kin dng cha và tng
bin duyt lên 1 nu cha dng. Nh vy, mi bc lp có thi gian thc hin là O(1). S bc
lp là n-i, do đó theo quy tc nhân cp đ tng thì tng thi gian thc hin ca vòng lp này là
O((n-i)x1) = O(n-i).
Cui cùng, ta xét vòng lp ngoài cùng (bin duyt i). Vòng lp này đc thc hin (n-1) ln,
do đó, t
ng thi gian thc hin ca chng trình là:
∑ (n-i) = n(n-1)/2 = n
2
/2- n/2 = O(n
2
)
Nh vy, thi gian thc hin gii thut sp xp ni bt là t l vi n
2

.
Mt s quy tc chung trong vic phân tích và tính toán thi gian thc hin chng trình
- Thi gian thc hin các lnh gán, đc, ghi .v.v, luôn là O(1).
- Thi gian thc hin chui tun t các lnh đc xác đnh theo quy tc cng cp đ tng.
Có ngha là thi gian thc hin ca c nhóm lnh tun t đc tính là thi gian thc
hin ca lnh ln nht.
- Th
i gian thc hin lnh r nhánh If đc tính bng thi gian thc hin các lnh khi
điu kin kim tra đc tho mãn và thi gian thc hin vic kim tra điu kin. Thi
gian thc hin vic kim tra điu kin luôn là O(1).
- Thi gian thc hin 1 vòng lp đc tính là tng thi gian thc hin các lnh  thân
vòng lp qua tt c các bc l
p và thi gian đ kim tra điu kin dng (thng là
O(1)). Thi gian thc hin này thng đc tính theo quy tc nhân cp đ tng s ln
thc hin bc lp và thi gian thc hin các lnh  thân vòng lp. Các vòng lp phi
đc tính thi gian thc hin mt cách riêng r.
1.3 TÓM TT CHNG 1
Các kin thc cn nh trong chng 1:
- Thut toán là mt chui hu hn các lnh. Mi lnh có mt ng ngha rõ ràng và có th
đc thc hin vi mt lng hu hn tài nguyên trong mt khong hu hn thi gian.


14
- Thut toán thng đc mô t bng các ngôn ng din đt gii thut gn vi ngôn ng
t nhiên. Các mô t này s đc tnh chnh dn dn đ đt ti mc ngôn ng lp trình.
- Thi gian thc hin thut toán thng đc coi nh là 1 hàm ca kích thc d liu đu
vào.
- Thi gian thc hin thut toán th
ng đc tính trong các trng hp tt nht, xu nht,
hoc trung bình.

-  biu th cp đ tng ca hàm, ta s dng ký hiu O(n). Ví d, ta nói thi gian thc
hin T(n) ca chng trình là O(n
2
), có ngha là tn ti các hng s dung c và n
0
sao
cho T(n) ≤ cn
2
vi n ≥ n
0
.
- Cp đ tng v thi gian thc hin ca chng trình cho phép ta xác đnh đ ln ca bài
toán mà ta có th gii quyt.
- Quy tc cng cp đ tng: Gi s T
1
(n) và T
2
(n) là thi gian chy ca 2 đon chng
trình P
1
và P
2
, trong đó T
1
(n) là O(f(n)) và T
2
(n) là O(g(n)). Khi đó, thi gian thc hin
ca 2 đon chng trình trình ni tip P
1
, P

2
là O(max(f(n), g(n))).
- Quy tc nhân cp đ tng: Vi gi thit v T
1
(n) và T
2
(n) nh trên, nu 2 đon chng
trình P
1
và P
2
không đc thc hin tun t mà lng nhau thì thi gian chy tng th s
là T
1
(n).T
2
(n) = O(f(n).(g(n)).
1.4 CÂU HI VÀ BÀI TP
1. Trình bày khái nim thut toán? Các đc đim ca thut toán?
2. Trong mt gii vô đch bóng đá có 6 đi bóng đá vòng tròn. Các đi là A, B, C, D, E, F.
i A đã đá vi B và C. i B đã đá vi D và F. i E đã đá vi C và F. Mi đi đá
mi tun 1 trn. Hãy lp 1 lch cho các đi bóng sao cho tt c các đi đu đá đ s trn
quy đnh trong 1 s tun va phi. Thc hin phân tích, thit k thut toán. Biu din
thut toán bng ngôn ng din đt gii thut, sau đó tinh chnh v dng ngôn ng lp
trình C.
3. Thi gian thc hin mt chng trình thng ph thuc vào các yu t nào? Phân tích
c th tng yu t.
4. Nói thi gian thc hin chng trình là T(n) = O(f(n)) có ngha là gì? Cho ví d minh
ho.
5. Nêu quy tc cng và nhân cp đ tng ca hàm. Có ví d minh ho.

6. Nêu các quy tc chung trong vic phân tích và đánh giá thi gian thc hin chng
trình.










15
CHNG 2
 QUI


Chng 2 trình bày các khái nim v đnh ngha đ qui, chng trình đ qui. Ngoài vic
trình bày các u đim ca chng trình đ qui, các tình hung không nên s dng đ qui cng
đc đ cp cùng vi các ví d minh ho.
Chng này cng đ cp và phân tích mt s thut toán đ qui tiêu biu và kinh đin nh
bài toán tháp Hà ni, các thut toán quay lui .v.v
 hc tt chng này, sinh viên cn nm vng ph
n lý thuyt. Sau đó, nghiên cu k các
phân tích thut toán và thc hin chy th chng trình. Có th thay đi mt s đim trong
chng trình và chy th đ nm k hn v thut toán. Ngoài ra, sinh viên cng có th tìm các bài
toán tng t đ phân tích và gii quyt bng chng trình.

2.1 KHÁI NIM
 qui là mt khái nim c bn trong toán hc và khoa hc máy tính. Mt đi tng đc

g
i là đ qui nu nó hoc mt phn ca nó đc đnh ngha thông qua khái nim v chính nó. Mt
s ví d đin hình v vic đnh ngha bng đ qui là:
1- nh ngha s t nhiên:
- 0 là s t nhiên.
- Nu k là s t nhiên thì k+1 cng là s t nhiên.
Nh vy, bt đu t phát biu “0 là s t nhiên”, ta suy ra 0+1=1 là s t nhiên. Ti
p
theo 1+1=2 là s t nhiên, v.v.
2- nh ngha xâu ký t bng đ qui:
- Xâu rng là 1 xâu ký t.
- Mt ch cái bt k ghép vi 1 xâu s to thành 1 xâu mi.
T phát biu “Xâu rng là 1 xâu ký t”, ta ghép bt k 1 ch cái nào vi xâu rng đu
to thành xâu ký t. Nh vy, ch cái bt k có th coi là xâu ký t. Tip tc ghép 1 ch
cái bt k vi 1 ch cái bt k
 cng to thành 1 xâu ký t, v.v.
3- nh ngha hàm giai tha, n!.
- Khi n=0, đnh ngha 0!=1
- Khi n>0, đnh ngha n!=(n-1)! x n
Nh vy, khi n=1, ta có 1!=0!x1 = 1x1=1. Khi n=2, ta có 2!=1!x2=1x2=2, v.v.
Trong lnh vc lp trình, mt chng trình máy tính gi là đ qui nu trong chng trình có
li gi chính nó. iu này, thot tiên, nghe có v hi vô lý. Mt chng trình không th gi mãi
chính nó, vì nh vy s to ra mt vòng lp vô hn. Trên thc t, mt chng trình đ
qui trc
khi gi chính nó bao gi cng có mt thao tác kim tra điu kin dng. Nu điu kin dng tha
mãn, chng trình s không gi chính nó na, và quá trình đ qui chm dt. Trong các ví d 
trên, ta đu thy có các đim dng. Chng hn, trong ví d th nht, nu k = 0 thì có th suy ngay
k là s t nhiên, không cn tham chiu xem k-1 có là s t nhiên hay không.
Nhìn chung, các chng trình đ qui đu có các đc đ
im sau:



16
- Chng trình này có th gi chính nó.
- Khi chng trình gi chính nó, mc đích là đ gii quyt 1 vn đ tng t, nhng nh
hn.
- Vn đ nh hn này, cho ti 1 lúc nào đó, s đn gin ti mc chng trình có th t
gii quyt đc mà không cn gi ti chính nó na.
Khi chng trình gi ti chính nó, các tham s, hoc kho
ng tham s, thng tr nên nh
hn, đ phn ánh 1 thc t là vn đ đã tr nên nh hn, d hn. Khi tham s gim ti mc cc
tiu, mt điu kin so sánh đc kim tra và chng trình kt thúc, chm dt vic gi ti chính
nó.
u đim ca chng trình đ qui cng nh đnh ngha bng đ qui là có th
 thc hin mt
s lng ln các thao tác tính toán thông qua 1 đon chng trình ngn gn (thm chí không có
vòng lp, hoc không tng minh đ có th thc hin bng các vòng lp) hay có th đnh ngha
mt tp hp vô hn các đi tng thông qua mt s hu hn li phát biu. Thông thng, mt
chng trình đc vit di dng đ qui khi vn đ cn x
lý có th đc gii quyt bng đ qui.
Tc là vn đ cn gii quyt có th đa đc v vn đ tng t, nhng đn gin hn. Vn đ này
li đc đa v vn đ tng t nhng đn gin hn na .v.v, cho đn khi đn gin ti mc có
th trc tip gii quy
t đc ngay mà không cn đa v vn đ đn gin hn na.
2.1.1 iu kin đ có th vit mt chng trình đ qui
Nh đã nói  trên, đ chng trình có th vit di dng đ qui thì vn đ cn x lý phi
đc gii quyt 1 cách đ qui. Ngoài ra, ngôn ng dùng đ vit chng trình phi h tr đ qui.
 có th vi
t chng trình đ qui ch cn s dng ngôn ng lp trình có h tr hàm hoc th tc,
nh đó mt th tc hoc hàm có th có li gi đn chính th tc hoc hàm đó. Các ngôn ng lp

trình thông dng hin nay đu h tr k thut này, do vy vn đ công c đ to các chng trình
đ qui không phi là vn đ cn phi xem xét. Tuy nhiên, c
ng nên lu ý rng khi mt th tc đ
qui gi đn chính nó, mt bn sao ca tp các đi tng đc s dng trong th tc này nh các
bin, hng, các th tc con, .v.v. cng đc to ra. Do vy, nên hn ch vic khai báo và s dng
các đi tng này trong th tc đ qui nu không cn thit nhm tránh lãng phí b nh, đc bit
đi vi các l
i gi đ qui đc gi đi gi li nhiu ln. Các đi tng cc b ca 1 th tc đ qui
khi đc to ra nhiu ln, mc dù có cùng tên, nhng do khác phm vi nên không nh hng gì
đn chng trình. Các đi tng đó s đc gii phóng khi th tc cha nó kt thúc.
Nu trong mt th tc có li gi đn chính nó thì ta gi đó là đ qui tr
c tip. Còn trong
trng hp mt th tc có mt li gi th tc khác, th tc này li gi đn th tc ban đu thì
đc gi là đ qui gián tip. Nh vy, trong chng trình khi nhìn vào có th không thy ngay s
đ qui, nhng khi xem xét k hn thì s nhn ra.
2.1.2 Khi nào không nên s dng đ qui
Trong nhiu trng hp, mt chng trình có th vit di dng đ
qui. Tuy nhiên, đ qui
không hn đã là gii pháp tt nht cho vn đ. Nhìn chung, khi chng trình có th vit di dng
lp hoc các cu trúc lnh khác thì không nên s dng đ qui.
Lý do th nht là, nh đã nói  trên, khi mt th tc đ qui gi chính nó, tp các đi tng
đc s dng trong th tc này nh các bin, hng, cu trúc .v.v s đc to ra. Ngoài ra, vic
chuyn giao đi
u khin t các th tc cng cn lu tr các thông s dùng cho vic tr li điu
khin cho th tc ban đu.
Lý do th hai là vic s dng đ qui đôi khi to ra các tính toán tha, không cn thit do
tính cht t đng gi thc hin th tc khi cha gp điu kin dng ca đ qui.  minh ha cho


17

điu này, chúng ta s xem xét mt ví d, trong đó c đ qui và lp đu có th đc s dng. Tuy
nhiên, ta s phân tích đ thy s dng đ qui trong trng hp này gây lãng phí b nh và các tính
toán không cn thit nh th nào.
Xét bài toán tính các phn t ca dãy Fibonaci. Dãy Fibonaci đuc đnh ngha nh sau:
- F(0) = 0
- F(1) =1
- Vi n >1 thì F(n) = F(n-1) + F(n-2)
Rõ ràng là nhìn vào mt đnh ngha đ qui nh trên, ch
ng trình tính phn t ca dãy
Fibonaci có v nh rt phù hp vi thut toán đ qui. Phng thc đ qui đ tính dãy này có th
đc vit nh sau:
int Fibonaci(int i){
if (i==0) return 0;
if (i==1) return 1;
return Fibonaci(i-1) + Fibonaci (i-2)
}
Kt qu thc hin chng trình không có gì sai. Tuy nhiên, chú ý rng mt li gi đ qui
Fibonaci (n) s dn ti 2 li gi đ qui khác ng vi n-1 và n-2. Hai li gi này li gây ra 4 li gi
na .v.v, c nh vy s li gi đ qui s tng theo cp s m. iu này rõ ràng là không hiu qu
vì trong s các li gi đ qui đó có rt nhiu li g
i trùng nhau. Ví d li gi đ qui Fibonaci (6)
s dn đn 2 li gi Fibonaci (5) và Fibonaci (4). Li gi Fibonaci (5) s gi Fibonaci (4) và
Fibonaci (3). Ngay ch này, ta đã thy có 2 li gi Fibonaci (4) đc thc hin. Hình 2.1 cho thy
s các li gi đc thc hin khi gi th tc Fibonaci (6).












Hình 2.1 Các li gi đ qui đc thc hin khi gi th tc Fibonaci (6)

Trong hình v trên, ta th
y đ tính đc phn t th 6 thì cn có ti 25 li gi ! Sau đây, ta
s xem xét vic s dng vòng lp đ tính giá tr các phn t ca dãy Fibonaci nh th nào.
u tiên, ta khai báo mt mng F các s t nhiên đ cha các s Fibonaci. Vòng lp đ tính
và gán các s này vào mng rt đn gin nh sau:
F[0]=0;
F[1]=1;
for (i=2; i<n-1; i++)
6
5
4
3
3 2
2 1 1
0
1
0
2 1
1
0
4
3 2
2 1 1

0
1
0


18
F[i] = F[i-1] + F [i-2];
Rõ ràng là vi vòng lp này, mi s Fibonaci (n) ch đc tính 1 ln thay vì đc tính toán
chng chéo nh  trên.
Tóm li, nên tránh s dng đ qui nu có mt gii pháp khác cho bài toán. Mc dù vy, mt
s bài toán t ra rt phù hp vi phng pháp đ qui. Vic s dng đ qui đ gii quyt các bài
toán này hiu qu và rt d hiu. Trên thc t, tt c các gii thut đ qui đu có th
 đc đa v
dng lp (còn gi là “kh” đ qui). Tuy nhiên, điu này có th làm cho chng trình tr nên phc
tp, nht là khi phi thc hin các thao tác điu khin stack đ qui (bn đc có th tìm hiu thêm
k thut kh đ qui  các tài liu tham kho khác), dn đn vic chng trình tr nên rt khó hiu.
Phn tip theo s trình bày mt s thut toán đ
qui đin hình.
2.2 THIT K GII THUT  QUI
2.2.1 Chng trình tính hàm n!
Theo đnh ngha đã trình bày  phn trc, n! = 1 nu n=0, ngc li, n! = (n-1)! * n.
int giaithua (int n){
if (n==0) return 1;
else return giaithua(n-1) * n;
}
Trong chng trình trên, đim dng ca thut toán đ qui là khi n=0. Khi đó, giá tr ca
hàm giaithua(0) có th tính đc ngay lp tc mà không cn gi hm đ qui khác. Nu điu kin
dng không tha mãn, s có mt li gi đ qui hàm giai tha vi tham s là n-1, nh hn tham s
ban đu 1 đn v (tc là bài toán tính n! đã đc qui v bài toán đn gin hn là tính (n-1)!).
2.2.2 Thut toán Euclid tính c s

 chung ln nht ca 2 s nguyên dng
c s chung ln nht (USCLN) ca 2 s nguyên dng m, n là 1 s k ln nht sao cho m
và n đu chia ht cho k. Mt phng pháp đn gin nht đ tìm USCLN ca m và n là duyt t s
nh hn trong 2 s m, n cho đn 1, ngay khi gp s nào đó mà m và n đu chia ht cho nó thì đó
chính là USCLN ca m, n. Tuy nhiên, phng pháp này không phi là cách tìm USCLN hiu qu.
Cách đây hn 2000 nm, Euclid đã phát minh ra mt gi
i thut tìm USCLN ca 2 s nguyên
dng m, n rt hiu qu. Ý tng c bn ca thut toán này cng tng t nh ý tng đ qui, tc
là đa bài toán v 1 bài toán đn gin hn. C th, gi s m ln hn n, khi đó vic tính USCLN
ca m và n s đc đa v bài toán tính USCLN ca m mod n và n vì USCLN(m, n) = USCLN(m
mod n, n).
Thut toán đc cài đt nh sau:
int USCLN(int m, int n){
if (n==0) return m;
else return USCLN(n, m % n);
}
im dng ca thut toán là khi n=0. Khi đó đng nhiên là USCLN ca m và 0 chính là
m, vì 0 chia ht cho mi s. Khi n khác 0, li gi đ qui USCLN(n, m% n) đc thc hin. Chú ý
rng ta gi s m >= n trong th tc tính USCLN, do đó, khi gi đ qui ta gi USCLN (n, m% n)
đ đm bo th t các tham s vì n bao gi cng ln hn phn d ca phép m cho n. Sau mi ln
gi đ qui, các tham s ca th tc s nh d
n đi, và sau 1 s hu hn li gi tham s nh hn s
bng 0. ó chính là đim dng ca thut toán.


19
Ví d, đ tính USCLN ca 108 và 45, ta gi th tc USCLN(108, 45). Khi đó, các th tc
sau s ln lt đc gi:
USCLN(108, 45) 108 chia 45 d 18, do đó tip theo gi
USCLN(45, 18) 45 chia 18 d 9, do đó tip theo gi

USCLN(18, 9) 18 chia 9 d 0, do đó tip theo gi
USCLN(9, 0) tham s th 2 = 0, do đó kt qu là tham s th nht, tc là 9.
Nh vy, ta tìm đc USCLN ca 108 và 45 là 9 ch sau 4 ln gi th t
c.
2.2.3 Các gii thut đ qui dng chia đ tr (divide and conquer)
Ý tng c bn ca các thut toán dng chia đ tr là phân chia bài toán ban đu thành 2
hoc nhiu bài toán con có dng tng t và ln lt gii quyt tng bài toán con này. Các bài
toán con này đc coi là dng đn gin hn ca bài toán ban đu, do vy có th s dng các li
gi đ qui đ gii quyt. Thông thng, các thut toán chia đ tr chia b
d liu đu vào thành 2
phn riêng r, sau đó gi 2 th tc đ qui đ vi các b d liu đu vào là các phn va đc chia.
Mt ví d đin hình ca gii thut chia đ tr là Quicksort, mt gii thut sp xp nhanh. Ý
tng c bn ca gii thut này nh sau:
Gii s ta cn sp xp 1 dãy các s theo chiu tng d
n. Tin hành chia dãy đó thành 2 na
sao cho các s trong na đu đu nh hn các s trong na sau. Sau đó, tin hành thc hin sp
xp trên mi na này. Rõ ràng là sau khi mi na đã đc sp, ta tin hành ghép chúng li thì s
có toàn b dãy đc sp. Chi tit v gii thut Quicksort s đc trình bày trong chng 7 - Sp
xp và tìm kim.
Tip theo, chúng ta s xem xét mt bài toán cng rt đin hình cho l
p bài toán đc gii
bng gii thut đ qui chia đ tr.
Bài toán tháp Hà ni
Có 3 chic cc và mt b n chic đa. Các đa này có kích thc khác nhau và mi đa đu
có 1 l  gia đ có th xuyên chúng vào các cc. Ban đu, tt c các đa đu nm trên 1 cc,
trong đó, đa nh hn bao gi cùng nm trên đa ln hn.








Hình 2.2 Bài toán tháp Hà n
i

Yêu cu ca bài toán là chuyn b n đa t cc ban đu A sang cc đích C (có th s dng
cc trung gian B), vi các điu kin:
- Mi ln chuyn 1 đa.
- Trong mi trng hp, đa có kích thc nh hn bao gi cng phi nm trên đa có
kích thc ln hn.
Vi n=1, có th thc hin yêu cu bài toán bng cách chuyn tr
c tip đa 1 t cc A sang
cc C.
Cc A
Cc B
Cc C


20
Vi n=2, có th thc hin nh sau:
- Chuyn đa nh t cc A sang cc trung gian B.
- Chuyn đa ln t cc A sang cc đích C.
- Cui cùng, chuyn đa nh t cc trung gian B sang cc đích C.
Nh vy, c 2 đa đã đc chuyn sang cc đích C và không có tình hung nào đa ln nm
trên đa nh.
Vi n > 2, gi
s ta đã có cách chuyn n-1 đa, ta thc hin nh sau:
- Ly cc đích C làm cc trung gian đ chuyn n-1 đa bên trên sang cc trung gian B.
- Chuyn cc di cùng (cc th n) sang cc đích C.

- Ly cc ban đu A làm cc trung gian đ chuyn n-1 đa t cc trung gian B sang cc
đích C.
Có th minh ha quá trình chuyn này nh sau:
Trng thái ban đu:








Bc 1:
Chuyn n-1 đa bên trên t cc A sang cc B, s dng cc C làm cc trung gian.







Bc 2:
Chuyn đa di cùng t cc A thng sang cc C.









Bc 3:
Chuyn n-1 đa t cc B sang cc C s dng cc A làm cc trung gian.

Cc A
Cc B
Cc C
Cc A
Cc B
Cc C
Cc A
Cc B
Cc C


21







Nh vy, ta thy toàn b n đa đã đc chuyn t cc A sang cc C và không vi phm bt
c điu kin nào ca bài toán.
 đây, ta thy rng bài toán chuyn n cc đã đc chuyn v bài toán đn gin hn là
chuyn n-1 cc. im dng ca thut toán đ qui là khi n=1 và ta chuyn thng cc này t cc
ban
đu sang cc đích.
Tính cht chia đ tr ca thut toán này th hin  ch: Bài toán chuyn n đa đc chia làm

2 bài toán nh hn là chuyn n-1 đa. Ln th nht chuyn n-1 đa t cc a sang cc trung gian b,
và ln th 2 chuyn n-1 đa t cc trung gian b sang cc đích c.
Cài đt đ qui cho thut toán nh sau:
- Hàm chuyen(int n, int a, int c) thc hin vic chuyn đa th n t cc a sang c
c c.
- Hàm thaphanoi(int n, int a, int c, int b) là hàm đ qui thc hin vic chuyn n đa t cc
a sang cc c, s dng cc trung gian là cc b.
Chng trình nh sau:
void chuyen(int n, char a, char c){
printf(‘Chuyen dia thu %d tu coc %c sang coc %c
\n”,n,a,c);
return;
}
void thaphanoi(int n, char a, char c, char b){
if (n==1) chuyen(1, a, c);
else{
thaphanoi(n-1, a, b, c);
chuyen(n, a, c);
thaphanoi(n-1, b, c,a);
}
return;
}
Hàm chuyen thc hin thao tác in ra 1 dòng cho bit chuyn đa th my t cc nào sang
cc nào.
Hàm thaphanoi kim tra nu s đa bng 1 thì thc hin chuyn trc tip đa t cc a sang
cc c. Nu s đa ln hn 1, có 3 lnh đc thc hin:
1- Li gi đ qui thaphanoi(n-1, a, b, c) đ chuyn n-1 đa t cc a sang cc b, s dng cc
c làm cc trung gian.
2-
Thc hin chuyn đa th n t cc a sang cc c.

Cc A
Cc B
Cc C


22
3- Li gi đ qui thaphanoi(n-1, b, c, a) đ chuyn n-1 đa t cc b sang cc c, s dng cc
a làm cc trung gian.
Khi chy chng trình vi s đa là 4, ta có kt qu nh sau:


Hình 2.3 Kt qu chy chung trình tháp Hà ni vi 4 đa

 phc tp ca thut toán là 2
n
-1. Ngha là đ chuyn n cc thì mt 2
n
-1 thao tác chuyn.
Ta s chng minh điu này bng phng pháp qui np toán hc:
Vi n=1 thì s ln chuyn là 1 = 2
1
-1.
Gi s gi thit đúng vi n-1, tc là đ chuyn n-1 đa cn thc hin 2
n-1
-1 thao tác chuyn.
Ta s chng minh rng đ chuyn n đa cn 2
n
–1 thao tác chuyn.
Tht vy, theo phng pháp chuyn ca gii thut thì có 3 bc. Bc 1 chuyn n-1 đa t
cc a sang cc b mt 2

n-1
-1 thao tác. Bc 2 chuyn 1 đa t cc a sang cc c mt 1 thao tác. Bc
3 chuyn n-1 đa t cc b sang cc c mt 2
n-1
-1 thao tác. Tng cng ta mt (2
n-1
-1) + (2
n-1
-1) + 1 =
2*2
n-1

-1 = 2
n
–1 thao tác chuyn. ó là điu cn chng minh.
Nh vy, thut toán có cp đ tng rt ln. Nói v cp đ tng này, có mt truyn thuyt vui
v bài toán tháp Hà ni nh sau: Ngày tn th s đn khi các nhà s  mt ngôi chùa thc hin
xong vic chuyn 40 chic đa theo quy tc nh bài toán va trình bày. Vi đ phc tp ca bài
toàn va tính đc, nu gi
 s mi ln chuyn 1 đa t cc này sang cc khác mt 1 giây thì vi
2
40
-1 ln chuyn, các nhà s này phi mt ít nht 34.800 nm thì mi có th chuyn xong toàn b
s đa này !
Di đây là toàn b mã ngun chng trình tháp Hà ni vit bng C:

#include<stdio.h>
#include<conio.h>
void chuyen(int n, char a, char c);
void thaphanoi(int n, char a, char c, char b);




23
void chuyen(int n, char a, char c){
printf("Chuyen dia thu %d tu coc %c sang coc %c \n", n,
a, c);
return;
}
void thaphanoi(int n, char a, char c, char b){
if (n==1) chuyen(1, a, c);
else{
thaphanoi(n-1, a, b, c);
chuyen(n, a, c);
thaphanoi(n-1, b, c,a);
}
return;
}
void main(){
int sodia;
clrscr();
printf("Cho biet so dia can chuyen: ");
scanf("%d",&sodia);
printf("\nCac buoc chuyen nhu sau:\n\n");
thaphanoi(sodia, 'a', 'c', 'b');
getch();
return;
}

2.2.4 Thut toán quay lui (backtracking algorithms)

Nh chúng ta đã bit, các thut toán đc xây dng đ giái quyt vn đ thng đa ra 1
quy tc tính toán nào đó. Tuy nhiên, có nhng vn đ không tuân theo 1 quy tc, và khi đó ta phi
dùng phng pháp th - sai (trial-and-error) đ gii quyt. Theo phng pháp này, quá trình th -
sai đc xem xét trên các bài toán đn gin hn (thng ch là 1 phn ca bài toán ban đu). Các
bài toán này thng đc mô t di dng đ qui và thng liên quan đn vic gi
i quyt mt s
hu hn các bài toán con.
 hiu rõ hn thut toán này, chúng ta s xem xét 1 ví d đin hình cho thut toán quay
lui, đó là bài toán Mã đi tun.
Cho bàn c có kích thc n x n (có n
2
ô). Mt quân mã đc đt ti ô ban đu có to đ x
0
, y
0

và đc phép dch chuyn theo lut c thông thng. Bài toán đt ra là t ô ban đu, tìm mt chui
các nc đi ca quân mã, sao cho quân mã này đi qua tt c các ô ca bàn c, mi ô đúng 1 ln.
Nh đã nói  trên, quá trình th - sai ban đu đc xem xét  mc đn gin hn. C th,
trong bài toán này, thay vì xem xét vic tìm kim chui nc đi ph khp bàn c, ta xem xét vn
đ đn gin hn là tìm ki
m nc đi tip theo ca quân mã, hoc kt lun rng không còn nc đi
k tip tha mãn. Ti mi bc, nu có th tìm kim đc 1 nc đi k tip, ta tin hành ghi li
nc đi này cùng vi chui các nc đi trc đó và tip tc quá trình tìm kim nc đi. Nu ti
bc nào đó, không th tìm nc đi k tip th
a mãn yêu cu ca bài toán, ta quay tr li bc


24
trc, hy b nc đi đã lu li trc đó và th sang 1 nc đi mi. Quá trình có th phi th ri

quay li nhiu ln, cho ti khi tìm ra gii pháp hoc đã th ht các phng án mà không tìm ra
gii pháp.
Quá trình trên có th đc mô t bng hàm sau:
void ThuNuocTiepTheo;
{
Khi to danh sách các nc đi k tip;
do{
La chn 1 nc đi k ti
p t danh sách;
if Chp nhn đc
{
Ghi li nc đi;
if Bàn c còn ô trng
{
ThuNuocTiepTheo;
if Nc đi không thành công
Hy b nc đi đã lu  bc trc
}
}
}while (nc đi không thành công) && (vn còn nc đi)
}
 th hin hàm 1 cách c th hn qua ngôn ng C, trc ht ta phi đnh ngha các cu
trúc d liu và các bin dùng cho quá trình x lý.
u tiên, ta s d
ng 1 mng 2 chiu đ mô t bàn c:
int Banco[n][n];
Các phn t ca mng này có kiu d liu s nguyên. Mi phn t ca mng đi din cho 1
ô ca bàn c. Ch s ca phn t tng ng vi ta đ ca ô, chng hn phn t Banco[0][0]
tng ng vi ô (0,0) ca bàn c. Giá tr ca phn t cho bit ô đ
ó đã đc quân mã đi qua hay

cha. Nu giá tr ô = 0 tc là quân mã cha đi qua, ngc li ô đã đc quân mã đã đi qua.
Banco[x][y] = 0: ô (x,y) cha đc quân mã đi qua
Banco[x][y] = i: ô (x,y) đã đc quân mã đi qua ti nc th i.
Tip theo, ta cn phi thit lp thêm 1 s tham s.  xác đnh danh sách các nc đi k
tip, ta cn ch ra ta đ hin ti ca quân mã, t đó theo lut c
thông thng ta xác đnh các ô
quân mã có th đi ti. Nh vy, cn có 2 bin x, y đ biu th ta đ hin ti ca quân mã.  cho
bit nc đi có thành công hay không, ta cn dùng 1 bin kiu boolean.
Nc đi k tip chp nhn đc nu nó cha đc quân mã đi qua, tc là nu ô (u,v) đc
chn là nc đi k tip thì Banco[u][v] = 0 là điu kin đ chp nh
n. Ngoài ra, hin nhiên là ô đó
phi nm trong bàn c nên 0 ≤ u, v < n.
Vic ghi li nc đi tc là đánh du rng ô đó đã đc quân mã đi qua. Tuy nhiên, ta cng
cn bit là quân mã đi qua ô đó ti nc đi th my. Nh vy, ta cn 1 bin i đ cho bit hin ti
đang th  nc đi th my, và ghi li nc đi thành công bng cách gán giá tr Banco[u][v]=i.


25
Do i tng lên theo tng bc th, nên ta có th kim tra xem bàn c còn ô trng không bng
cách kim tra xem i đã bng n
2
cha. Nu i<n
2
tc là bàn c vn còn ô trng.
 bit nc đi có thành công hay không, ta có th kim tra bin boolean nh đã nói  trên.
Khi nc đi không thành công, ta tin hành hy nc đi đã lu  bc trc bng cách cho giá tr
Banco[u][v] = 0.
Nh vy, ta có th mô t c th hn hàm  trên nh sau:
void ThuNuocTiepTheo(int i, int x, int y, int *q)
{

int u, v, *q1;
Khi to danh sách các nc đi k tip;
do{
*q1=0;
Chn nc đi (u,v) trong danh sách n
c đi k tip;
if ((0 <= u) && (u<n) && (0 <= v) && (v<n) && (Banco[u][v]==0))
{
Banco[u][v]=i;
if (i<n*n)
{
ThuNuocTiepTheo(i+1, u, v, q1)
if (*q1==0) Banco[u][v]=0;
} else *q1=1;
}
}while ((*q1==0) && (Vn còn nc đi))
*q=*q1;
}
Trong đon chng trình trên vn còn 1 thao tác cha đc th hin bng ngôn ng lp
trình, đó là thao tác khi to và chn nc đi k tip. Bây gi, ta s xem xét xem t ô (x,y), quân
mã có th đi ti các ô nào, và cách tính v trí tng đi ca các ô đó so vi ô (x,y) ra sao.
Theo lut c thông thng, quân mã t ô (x,y) có th
đi ti 8 ô trên bàn c nh trong hình v:

3 2
4 1


5 8
6 7



Hình 2.4 Các nc đi ca quân mã

x
y

×