Phân tích thiết kế thuật toán a

Một phần của tài liệu Giáo trình Phân tích thiết kế thuật toán (Nghề Lập trình máy tính): Phần 1 - Tổng cục dạy nghề (Trang 30 - 49)

a1 a2 … an nil

Ðể cài đặt danh sách liên kết, ta dùng con trỏ để liên kết các phần tử của danh sách theo phương thức ai chỉ đến ai+1. Ðể một phần tử có thể chỉ đến một phần tử khác ta xem mỗi ơ là một Record gồm có 2 trường :

 Trường Elements để giữ nội dung của phần tử trong danh sách.

 Trường Next là một con trỏ giữ địa chỉ của ô kế tiếp.

 Phần tử cuối cùng của danh sách ta cho trỏ đến một giá trị đặc biệt Nil.

Ðể quản lý danh sách ta cần một biến chỉ đến phần tử đấu danh sách. Biến này được gọi là chỉ điểm đầu danh sách Header. Header là một ơ có cùng kiểu với kiểu phần tử trong danh sách. Nhưng trường Elements của ô header là rỗng, cịn trường Next của ơ Header thì giữ địa chỉ của phần tử đầu danh sách. Khai báo :

Type ElementType = ... ; {Kiểu phần tử của danh sách} Position

= ^ CellType; CellType = Record Elements: ElementType; Next : Position; End; List = Position;

Thủ tục khởi tạo danh sách rỗng :

Procedure MakenullList (Var Header : List); Begin

New (Header); Header.Next : = Nil; End;

Hàm kiểm tra danh sách rỗng :

Phân tích thiết kế thuật tốn

Begin

EmptyList: = (Header.Next = Nil); End;

Thủ tục duyệt các phần tử trong danh sách liên kết : Procedure Order (L : List);

Var p : Position; Begin p : = L^.Next; While p <> Nil do Begin Write (p^.Elements); p:= p^.Next; End; End;

Thủ tục xen thêm một phần tử vào danh sách liên kết :

Muốn xen một phần tử x vào danh sách ta cần biết vị trí p trước chỗ xen, rồi ta xen x vào vị trí sau p.

Procedure InsertList (x: ElementType; p : Position); Var Temp : Position;

Begin New (Temp); Temp^.Elements:=x; Temp^.Next:= p^.Next; p^.Next:= Temp; End;

Phân tích thiết kế thuật tốn

Muốn xóa một phần tử trong danh sách ta cũng cần biết vị trí p trước chỗ xóạ Procedure DeleteList (p : Position);

Var Temp : Position; Begin

Temp:= p^.Next; p^.Next:= Temp^.Next; Dispose(Temp);

End;

Thủ tục tìm kiếm một phần tử trong danh sách liên kết : Function Locate (x: ElementType; L:List) : Position;

Var p: Position; Found: Boolean; Begin

p:= L^.Next; found:= False; While (p <> Nil) and (not found) do

If p^.Elements = x then Found:= True Else p:= p^.Next; Locate:= p; End; So sánh 02 phương pháp cài đặt :

c. Cài đặt danh sách liên kết bằng con nháy (trên cơ sở mảng) :

Danh sách không phải là một cấu trúc dữ liệu tiền định trong đa số các ngơn ngữ lập trình. Vì vậy, người lập trình phải cài đặt bằng các cấu trúc tiền định khác có sẳn trong ngơn ngữ lập trình. Ví dụ : một số ngơn ngữ lập trình khơng có định nghĩa biến kiểu con trỏ. Trong trường hợp này ta sẽ giả con trỏ (dựa trên cơ sở cấu trúc tiền định mảng, được định nghĩa trong hầu hết các ngơn ngữ lập trình) để cài đặt danh sách liên kết.

Phân tích thiết kế thuật tốn

Ý tưởng: Là dùng một biến số nguyên (Integer) để lưu giữ chỉ số (vị trí) của các phần tử kế tiếp trong mảng. Như vậy để cài đặt danh sách bằng con nháy ta cần một mảng mà mỗi phần tử là một Record gồm 2 trường:

 Trường Elements: giữ nội dung của phần tử trong danh sách.

 Trường Next: là một số nguyên chỉ tới vị trí của phần tử kế tiếp.Ðể quản lý danh sách, ta dùng một con nháy (một biến số nguyên) để chỉ đến phần tử đầu danh sách. Phần tử cuối danh sách ta cho chỉ đến một phần tử đặc biệt Null (Giá trị Null có thể chọn là 0, nếu như mảng khơng có phần tử có giá trị 0).

Khi xen một phần tử vào danh sách, ta lấy một ô trống trong mảng để chứa phần tử mới này và nối kết lại các con nháỵ Tương tự, khi xóa một phần tử khỏi danh sách, ta nối kết lại các con nháy để loại bỏ phần tử đó. Ðiều này kéo theo số phần tử trong mảng tăng lên 1. Ðể quản lý các ô trống, ta liên kết tất cả các ô trống vào một danh sách đặc biệt gọi là Availablẹ Khi xen một phần tử vào danh sách ta lấy ô trống đầu Available để chứa phần tử mới nàỵ Khi xóa 1 phần tử, ta cho ơ bị xóa nối vào đầu Availablẹ

Khai báo :

Const Maxlenght = ......... ; { Ðộ dài cực đại của mảng } Type ElementType = ... ; {Kiểu phần tử của danh sách}

CursorType = 0 .. Maxlenght ; { Kiểu con nháy }

NodeType = Record

Elements: ElementType; Next : CursorType;

End;

ArrayOfNodes = Array [1.. Maxlenght] of NodeType;

Var Node: ArrayOfNode;

Phân tích thiết kế thuật tốn

Thủ tục khởi tạo vùng lưu trữ các nút tự do : Procedure Initialize; Var i: Integer; Begin for i: = 1 to Maxlenght - 1 do Node[i].Next := i + 1; Node[Maxlenght].Next := 0; Available := 1; End;

Thủ tục lấy nút tự do đầu tiên :

{ Thủ tục này trả con nháy p chỉ đến nút tự do, nếu khơng cịn nút tự do, P trả về giá trị Null (0) }

Procedure Getnode (Var P:CursorType); Begin

p := Available; If p < > 0 then

Available := Node[p].Next

Else Writeln('Vung luu tru rong'); End;

Thủ tục khởi tạo danh sách rỗng :

Procedure Create (Var List: CursorType); Begin

List : = 0; {Null} End;

Hàm kiểm tra danh sách rỗng :

Function EmptyList(List : cursorType): Boolean; Begin

EmptyList: = (List = 0); End;

Thủ tục duyệt các phần tử trong danh sách liên kết : Procedure Linker (List: CursorType);

Var CrrPtr : CursorType; Begin

Phân tích thiết kế thuật toán CrrPtr := List; CrrPtr := List; While CrrPtr < > 0 do Begin Write(Node[CrrPtr].Elements); CrrPtr := Node[CrrPtr].Next; End; End;

Hàm chuyển 1 phần tử từ danh sách này sang danh sách khác :

Ta thấy thực chất của việc xen hay xóa một phần tử là thực hiện công việc chuyển một ô từ danh sách này sang danh sách khác. Vậy ta cần phải viết một hàm Move thực hiện thao tác nàỵ Hàm cho kết quả kiểu Boolean tùy theo việc chuyển thành công hay thất bạị Ta viết hàm Move thực hiện việc chuyển ô được chỉ bởi con nháy p vào ô trước ô được chỉ bởi con nháy q.

Function Move (var p, q: Integer) : Boolean; Var Temp : Integer;

Begin If p = 0 then Move := false Else Begin Temp := q; q := p; p := Node[p].Next; Node[p].Next := Temp; Move := True; End; End;

Thủ tục xen một phần tử vào danh sách :

Phân tích thiết kế thuật tốn

thực sự trước đó. Ta sẽ cho p một giá trị đặc biệt để thủ tục xử lý trong trường hợp nàỵ Ở đây ta cho p = 0

Procedure InsertList(x:ElementType;p:CursorType;Var L:CursorType); Begin

If p = 0 then { Xen vào đầu danh sách } Begin

If Move (Available, L) then Node[L].Elements := x; End

Else

If Move (Available, Node[p].Next) then Node [Node [p].Next].Elements := x; End;

Thủ tục xóa một phần tử khỏi danh sách :

Muốn xóa một phần tử khỏi danh sách ta cũng cần biết vị trí trước chỗ xóa rồi ta chuyển ơ cần xóa này vào đầu Availablẹ Tương tự như phép xen vào, muốn xóa phần tử đầu danh sách ta cho p = 0.

Procedure DeleteList (p : CursorType; Var L : CursorType); Begin

If p = 0 then { Xóa phần tử đầu danh sách } If Move (L, Available) then Writeln ('Da xoá) Else Writeln ('Loí)

Else

If Move(Node[p].Next, Available) then Writeln('Da xoá) Else Writeln ('Loí) End;

.7. Cấu trúc chồng / ngăn xếp (STACK) 2.3.1Ðịnh nghĩa :

Stack là một danh sách đặc biệt mà phép thêm vào hoặc loại bỏ một phần tử chỉ thực hiện tại một đầu gọi là đỉnh (Top) của Stack. Như vậy, Stack là một cấu trúc có tính chất vào trước, ra sau (FILO - First In Last Out).

.7.1 Các phép toán trên Stack

ạ Thủ tục MakeNullStack (Var S : Stack) : Tạo một Stack rỗng.

Phân tích thiết kế thuật tốn

c. Thủ tục Pop (Var S : Stack) : Loại bỏ phần tử tại đỉnh của Stack.

d. Thủ tục Push (x : ElementsType; Var S : Stack) : Ðặt phần tử x vào đỉnh của Stack.

ẹ Hàm EmptyS (S : Stack) : Boolean : Kiểm tra xem một stack có rỗng hay khơng? Hàm trả về giá trị True nếu Stack rỗng và False nếu ngược lạị

f. Hàm FullS (S : Stack) : Boolean : Kiểm tra xem một stack có đầy hay khơng? Hàm trả về giá trị True nếu Stack đầy và False nếu ngược lạị

Ví dụ : Viết thủ tục nhập từ bàn phím một dãy ký tự cho đến khi gặp ký tự '@' thì ngưng. In dãy ký tự theo thứ tự ngược lại lúc nhập.

Ta sẽ trình bày thuật tốn cho thủ tục này theo 2 cách sau: Procedure Edit; Var S : Stack; C : Char; Begin MakeNullStack (S); Repeat Read (c); Push (c, S); Until c = '@'; Repeat Write (Top(S)); Pop (S); Until EmptyS (S); End; Procedure Edit1; Var S : Stack; C : Char; Begin MakeNullStack (S); Repeat Read (c); If c < > '@' then Push (c, S); Until c = '@';

While not EmptyS (S) do Begin Write (Top(S)); Pop (S); End; End; .7.2 Cài đặt Stack bằng mảng Khai báo :

Const Maxlength = .......; { Ðộ dài mảng } Type ElementType = ......; { Kiểu phần tử }

Stack = Record

Elements : array[1..Maxlength] of ElementType; Top : integer;

Phân tích thiết kế thuật toán

End;

Thủ tục khởi tạo Stack rỗng :

Procedure MakeNullStack (Var S : Stack); Begin

S.Top :=Maxlenght + 1; End;

Hàm kiểm tra Stack rỗng :

Function EmptyS (S : Stack) : Boolean; Begin

EmptyS := (S.Top = Maxlenght + 1); End;

Hàm kiểm tra Stack đầy :

Function FullS (S : Stack):Boolean; Begin

FullS := (S.Top = 1); End;

Hàm cho phần tử ở đầu Stack :

Function Top (S : Stack) : ElementType; Begin

If not EmptyS (S) then Top:=S.Elements[S.Top] Else Writeln('Stack rong'); End;

Nếu Elements khơng thể là kiểu kết quả của hàm thì ta sẽ chuyển thành thủ tục như sau : Procedure Top (S : Stack; Var x : ElementType);

Begin

If not EmptyS(S) then x:=S.Elements[S.Top] Else Writeln('Stack rong'); End;

Thủ tục xóa một phần tử khỏi Stack : Procedure Pop(Var S: Stack); Begin

Phân tích thiết kế thuật tốn

S.Top:=S.Top+1

Else Writeln('Stack rong'); End;

Thủ tục thêm một phần tử vào Stack :

Procedure Push(x:ElementsType; Var S:Stack); Begin

If not FullS(S) then Begin

S.Top:=S.Top -1; S.Elements[S.Top]:=x; End

Else Writeln('Stack daý); End;

.7.3 Cài đặt Stack bằng con trỏ

Khai báo :

Type ElementType = ......; { Kiểu phần tử } PointerType = ^ StackNode; StackNode = Record Elements : ElementType; Next : PointerType; End; StackType = PointerType;

Var Stack : StackType; { Chỉ đến phần tử ở đỉnh Stack } Thủ tục khởi tạo Stack rỗng :

Procedure MakeNullStack(Var S : StackType); Begin

Stack := Nil; { Stack rỗng } End;

Hàm kiểm tra Stack rỗng :

Function EmptyS(S : StackType) : Boolean; Begin

EmptyS := (Stack := Nil); End;

Phân tích thiết kế thuật toán

Var TempPtr : PointerType; Begin If EmptyS (S) then Writeln('Stack rong') Else Begin x := Stack^.Elements; TempPtr := Stack; Stack := Stack^.Next; Dispose (TempPtr); End; End;

Thủ tục thêm một phần tử vào Stack :

Procedure Push(Var Stack : StackType; x:ElementType); Var TempPtr : PointerType;

Begin New (TempPtr); TempPtr^.Elements :=x; TempPtr^.Next := Stack; Stack := TempPtr; End;

Hàm cho phần tử đầu Stack :

Function Top(Stack : StackType) : ElementType; Begin

Top := Stack^.Elements; End;

Ví dụ: Dùng cấu trúc Stack để viết chương trình đổi một số nguyên ở dạng thập phân thành dạng nhị phân.

Program Nhi_phan;

Type ElementType = integer; { Kiểu phần tử } PointerType = ^ StackNode; StackNode = Record Elements : ElementType; Next : PointerType; End; StackType = PointerType;

Phân tích thiết kế thuật toán

Number, du : Integer;

{Các thủ tục cần thiết cho chương trình } Procedure MakeNullStack (Var S : StackType); Function EmptyS (S : StackType) : Boolean;

Procedure Push(Var Stack : StackType; x:ElementType); Procedure Pop(Var S: StackType; Var x: ElementsType); BEGIN { Chương trình chính }

Write ('Nhap so can doi : '); Readln (Number); MakeNullStack(Stack);

While Number < > 0 do Begin

Du := Number mod 2; Push(Stack, du);

Number := Number Div 2; End;

While not EmptyS(Stack) do Begin Pop(Stack, du); Write(Du : 1); End; Readln; END. { Chương trình chính } .8. Cấu trúc hàng đợi 2.4.1Ðịnh nghĩa

Hàng là một danh sách đặc biệt mà phép thêm vào chỉ thực hiện ở một đầu của danh sách gọi là cuối hàng (Rear). Phép loại bỏ lại được thực hiện ở một đầu kia của danh sách gọi là đầu hàng (Front). Như vậy hàng là một cấu trúc dữ liệu có tính chất "Vào trước - ra trước" - FIFO : First In First Out.

.8.1 Các phép toán cơ bản trên hàng

ạ Thủ tục MakeNullQueue (Var Q : Queue) : Tạo một hàng rỗng.

b. Hàm Front (Q : Queue) : ElementType : Trả về kết quả là phần tử đầu hàng.

c. Thủ tục EnQueue (x : ElementType; Var Q : Queue) : Ðặt phần tử x vào cuối của hàng. d. Thủ tục DeQueue (Var Q : Queue) : Xóa một phần tử đầu hàng.

Phân tích thiết kế thuật tốn

ẹ Hàm EmptyQ (Q : Queue) : Boolean : Kiểm tra xem một Queue có rỗng hay khơng? Hàm trả về giá trị True nếu Queue rỗng và False nếu ngược lạị

f. Hàm FullQ (Q : Queue) : Boolean : Kiểm tra xem một Queue có đầy hay khơng? Hàm trả về giá trị True nếu Queue đầy và False nếu ngược lạị

.8.2 Cài đặt hàng

ạ Cài đặt hàng bằng mảng: Ta dùng một mảng để chứa các phần tử của hàng. Phần tử đầu hàng xếp vào vị trí thứ nhất của mảng, phần tử thứ hai xếp vào vị trí thứ 2,... Giả sử mảng có n phần tử ta có Front = 1 và Rear = n. Khi xóa một phần tử Front tăng lên 1, khi thêm một phần tử vào hàng Rear tăng lên 1. Như vậy, hàng có khuynh hướng đi xuống và đến một lúc nào đó ta khơng thể thêm vào hàng được nữa dù rằng mảng còn nhiều chỗ trống. Ta gọi là hàng bị tràn. Trong trường hợp toàn bộ mảng đã chứa các phần tử ta gọi là hàng bị đầỵ

Cách khắc phục hàng bị tràn:

 Dời toàn bộ hàng lên Front - 1 vị trí. Cách này gọi là di chuyển tịnh tiến và ta ln có Front < Rear.

 Xem mảng như là một vòng tròn nghĩa là khi hàng bị tràn nhưng chưa đầy, ta thêm phần tử mới vào vị trí thứ nhất của mảng, thêm phần tử thứ 2 vào vị trí thứ 2 của mảng, ... (nếu có thể). Cách này gọi là mảng vịng. * Cài đặt hàng bằng phương pháp di chuyển tịnh tiến:

Khai báo :

Const Maxlength = .......; { Ðộ dài mảng } Type ElementType = ......; { Kiểu phần tử }

Queue = Record

Elements : array[1..Maxlength] of ElementType; Front, Rear : integer;

End;

Thủ tục khởi tạo Queue rỗng :

Procedure MakeNullQueue(Var Q : Queue); Begin

Q. Front := 0; Q. Rear := 0; End;

Hàm kiểm tra Queue rỗng :

Function EmptyQ(Q : Queue) : Boolean; Begin

Phân tích thiết kế thuật tốn

End;

Hàm kiểm tra Queue đầy :

Function FullQ(Q : Stack):Boolean; Begin

FullQ := ((Q.rear - Q.Front + 1) = Maxlenght); End;

Hàm ütruy xuất phần tử đầu hàng :

Function Front(Q: Queue) : ElementsType;

Begin

Front := (Q.Elements [Q.Front]); End;

Thủ tục xen một phần tử vào cuối hàng :

Procedure EnQueue(x : ElementsType; Var Q: Queue); Var i : integer;

Begin

If not FullQ (Q) then Begin

If EmptyQ (Q) then Q.Front := 1; If Q.Rear = Maxlenght then

Begin

For i := Q.Front to Q.Rear do

Q.Elements [i - Q.Front + 1] := Q.Elements[i]; Q.Rear := Maxlenght - Q.Front + 1;

End;

Q.Rear := Q.Rear + 1; Q.Elements [Q.Rear] := x;

End

Else Write ('Hang daý); End;

Thủ tục xóa một phần tử đầu hàng : Procedure DeQueue(Var Q: Queue);

Begin

If not EmptyQ(Q) then Begin

Q.Front := Q.Front + 1; If Q.Front > Q.Rear then

Phân tích thiết kế thuật toán

MakeNullQueue (Q); End

Else Write ('Queue rong'); End;

* Cài đặt hàng bằng mảng vòng:

Hàm kiểm tra Queue đầy :

Function FullQ (Q : Stack):Boolean; Begin

FullQ := ((Q.rear - Q.Front + 1) mod Maxlenght = 0); End;

Thủ tục xen một phần tử vào cuối hàng :

Procedure EnQueue (x : ElementsType; Var Q: Queue); Begin

If not FullQ (Q) then Begin

If EmptyQ (Q) then

Q.Front := 1;

Q.Rear := (Q.rear mod Maxlenght) + 1; Q.Elements [Q.Rear] := x;

End Else

Write ('Hang daý); End;

Thủ tục xóa một phần tử đầu hàng : Procedure DeQueue (Var Q: Queue);

Begin

If not EmptyQ (Q) then

If Q.Front=Q.Rear then MakeNullQueue (Q) Else

Q.Front := (Q.Front mod Maxlenght) + 1 Else

Write ('Queue rong'); End;

Phân tích thiết kế thuật tốn

b. Cài đặt hàng bằng con trỏ:

Ta sẽ dùng 2 con trỏ Front và Rear để quản lý hàng (Front chỉ đến phần tử đầu hàng, Rear chỉ đến phần tử cuối hàng).

Khai báo :

Type ElementType = ......; { Kiểu phần tử } CellType = ^ Cell; Cell = Record Elements : ElementType; Next: CellType; End; Queue = Record

Front, Rear : CellType; End;

Thủ tục khởi tạo Queue rỗng :

Hàm kiểm tra Queue rỗng :

Function EmptyQ (Q : Queue) : Boolean; Begin

EmptyQ := (Q.Front = Q.Rear); End;

Phân tích thiết kế thuật toán

Procedure EnQueue (x : ElementType; Var Q: Queue); Begin If EmptyQ (Q) then Begin New (Q.front); Q.Front^.Elements := x; Q.Front^.Next := Nil; Q.Rear := Q.Front; End Else Begin New (Q.rear^.Next); Q.rear := Q.rear^.Next; Q.rear^.Elements :=x; Q.rear^.Next := Nil; End; End; Thủ tục xóa một phần tử đầu hàng :

Procedure DeQueue (Var Q: Queue); Var Temp : CellType;

Begin

If not EmptyQ (Q) then Begin Temp := Q.Front; Q.front := Q.Front^.Next; Dispose (Temp); End Else

Phân tích thiết kế thuật tốn

Write ('Queue rong'); End;

.9. Danh sách liên kết kép (Double Link List) .9.1 Nhận xét

Ðối với danh sách liên kết đơn, khi biết được một nút ta chỉ có thể truy xuất đến nút đứng sau nó. Nếu muốn truy xuất đến nút đứng trước thì phải quay về đầu danh sách (Ta cần tạo một danh sách liên kết kép để có thể truy xuất các phần tử một cách dễ dàng hơn. Ta xem mỗi phần tử của

Một phần của tài liệu Giáo trình Phân tích thiết kế thuật toán (Nghề Lập trình máy tính): Phần 1 - Tổng cục dạy nghề (Trang 30 - 49)

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

(109 trang)