Tuy nhieân, vôùi caùch toå chöùc naøy coù theå xuaát hieän tình huoáng laø ñeán moät luùc naøo ñoù khoâng theå boå sung tieáp phaàn töû naøo nhöng khoâng gian nhôù cuûa maûng Q vaãn coø[r]
(1)CHƯƠNG 1: PHÂN TÍCH, THIẾT KẾ GIẢI THUẬT 1.1 Giải thuật cấu trúc liệu:
Theo Niklaus Wirth: Giải thuật + Cấu trúc liệu = Chương trình Điều hàm ý cấu trúc liệu giải thuật có mối quan hệ mật thiết với chương trình Do việc nghiên cứu cấu trúc liệu sau đôi với việc xác lập giải thuật xử lý cấu trúc
1.2 Một số vấn đề liên quan:
Lựa chọn cấu trúc liệu thích hợp để tổ chức liệu vào sở xây dựng giải thuật xử lý hữu hiệu nhằm đưa tới kết mong muốn cho toán khâu quan trọng
- Cần phân biệt loại quy cách liệu:
+ Quy cách biểu diễn hình thức: Cịn gọi cấu trúc logic liệu Đối với ngơn ngữ lập trình xác định có cấu trúc logic liệu Một liệu thuộc loại cấu trúc cần phải có mơ tả kiểu liệu Ví dụ: Trong Pascal có loại liệu: Array, Record, File
+ Quy cách lưu trữ: cách biểu diễn cấu trúc liệu nhớ Ví dụ: Cấu trúc liệu mảng lưu trữ nhớ theo quy tắc lưu trữ Có quy cách lưu trữ:
Lưu trữ trong: ví dụ RAM Lưu trữ ngồi: ví dụ đĩa (disk)
1.3 Ngôn ngữ diễn đạt giải thuật: Chọn ngôn ngữ tựa Pascal
Đặc điểm: Gần giống với Turbo Pascal, dễ dàng việc chuyển chương trình viết ngôn ngữ tựa Pascal sang ngôn ngữ Pascal
1.3.1 Cấu trúc chương trình chính: Program <Tên chương trình>
Begin S1; S2; S3;
End; Báo kết thúc chương trình Nhận xét:
- Chương trình khơng cần viết khai báo Tuy nhiên mơ tả trước chương trình ngơn ngữ tự nhiên
- Phần thuyết minh đặt dấu { }
- Nếu chương trình gồm nhiều bước đánh số thứ tự bước kèm lời thuyết minh
Ví dụ:
Program Tinhgiaithua Begin
(2)1 Readln(n); { nhaäp n} { Tính p = n! }
p:=1;
for i:=1 to n p:= p * i; Writeln (p); {In kết quả}
End
1.3.2 Các ký tự: Tương tự Pascal: - Các ký tự số học: +, -, *, /,… - Quan hệ: >, <, ≥, ≤, ≠
1.3.3 Các câu lệnh: - Lệnh gán: V := E;
Trong đó: V biến (variable), E biểu thức (expression) Lưu ý: Có thể dùng phép gán chung Ví dụ: a:=b:=1;
- Lệnh ghép: Begin S1; S2; ; Sn; End;
coi câu lệnh (trong Si câu lệnh) - Lệnh If: Tương tự lệnh If ngôn ngữ Pascal - Lệnh Case:
Case
B1: S1; B2: S2; B3: S3; Bn: Sn; [Else: Sn+1;] End Case;
- Lệnh lặp: For, While, Repeat: Tương tự lệnh lặp Pascal - Lệnh nhảy: goto n (n: số hiệu / nhãn bước chương trình) - Lệnh vào ra: Write, Read, : giống Pascal
Ví dụ: Chương trình chuyển đổi số thứ tuần, với số nhập từ bàn phím (Ví dụ: nhập số 1: in hình chu nhat; số 2: in hình thu hai;…)
Program Vidu_ve_Case; Var n:byte;
Bengin
Readln(n); Case n
1: Write(‘chu nhat’); 2: Write(‘thu hai’); 3: Write(‘thu ba’); 4: Write(‘thu tu’); 5: Write(‘thu nam’); 6: Write(‘thu sau’); 7: Write(‘thu bay’);
Else: Write(‘khong phai thu tuan’); End Case;
(3)End
1.3.4 Chương trình con: Có dạng: a) Dạng hàm:
Function <Tên hàm>(<danh sách tham số>) S1;
S2; S3;
Return Báo kết thúc chương trình
Lưu ý: Các lệnh bên chương trình có câu lệnh gán mà tên hàm nằm bên trái Khi gọi hàm xuất biểu thức chương trình gọi
Ví dụ: Viết chương trình dạng hàm NamNhuan(x) Cho kết số x năm nhuận có giá trị True, ngược lại có giá trị False; chẳng hạn: NamNhuan(1996) cho giá trị True, NamNhuan(1997) cho giá trị False Biết x gọi năm nhuận x chia hết cho x không chia hết cho 100 x chia hết cho 400
Caùch 1:
Function NamNhuan(x)
If ((x mod = 0) and (x mod 100 <> 0)) or (x mod 400 = 0) then NamNhuan:=True Else
NamNhuan:=False; Return
Caùch 2:
Function NamNhuan(x)
NamNhuan:=((x mod = 0) and (x mod 100 <> 0)) or (x mod 400 =
0); Return
b) Dạng thủ tục:
Procedure <Tên thủ tục>(<danh sách tham số>); S1; S2; ; Sn;
Return
Lưu ý: Lời gọi thủ tục có dạng: Call <Tên thủ tục>(<danh sách tham số thực>) khơng cần ghi từ khóa Call
Ví dụ: Viết thủ tục HoanDoi(a, b) để hoán đổi giá trị biến số a b cho Cách 1: Procedure HoanDoi(a, b);{a b tham biến}
tam:=a; a:=b; b:=tam; Return
Caùch 2: Procedure HoanDoi(a, b);
a:= a+b; b:= a-b; a:= a-b; Return
(4)trình con), Halt (thốt khỏi chương trình chính) 1.4 Thiết kế phân tích giải thuật:
1.4.1 Module hoá giải thuật:
Các toán ngày đa dạng phức tạp Do giải thuật mà ta đề xuất có quy mơ lớn việc viết chương trình cần có lực lượng lập trình đông đảo Muốn làm việc , người ta phân chia toán lớn thành toán nhỏ (module) Và dĩ nhiên module chia nhỏ thành module khác nữa, việc tổ chức lời giải thể theo cấu trúc phân cấp
Ví dụ:
A
B C D
E F G H I
Q trình module hố tốn xem nguyên lý “chia để trị” (divide and conquer) hay gọi thiết kế từ đỉnh xuống (top-down) thiết kế từ khái quát đến chi tiết (specialization)
Việc module hố lập trình thể ở:
Các chương trình Chẳng hạn, Procedure, Function Pascal Cụm chương trình xung quanh cấu trúc liệu Chẳng hạn, Unit Pascal
Nhận xét:
- Việc module hố làm cho toán định hướng rõ ràng
- Bằng cách này, người ta phân chia cơng việc cho đội ngũ lập trình - Đây công việc nhiều thời gian
1.4.2 Phương pháp tinh chỉnh bước:
Phương pháp tinh chỉnh bước phương pháp thiết kế giải thuật gắn liền với lập trình Nó phản ánh tinh thần q trình module hố thiết kế giải thuật theo kiểu top-down
Xuất phát từ ngôn ngữ tự nhiên giải thuật, giải thuật chi tiết hoá cuối công việc xử lý thay dần câu lệnh (của ngơn ngữ lập trình đó) Q trình để trả lời câu hỏi: What? (làm gì?), How (làm nào?)
Ví dụ: Giải hệ phương trình: a11x1 + a12x2 + … = b1 A21x1 + a22x2 + … = b2 ………
an1x1 + an2x2 + … = bn
- Nhập hệ số: Nhập bi (i = 1, n ), nhập aij (i = 1, n ; j = 1, z )
(5)For i:=1 to n Begin
Readln(b[i]); For j:=1 to i
Readln(a[i, j]);
End;
- Từ phương trình ta suy ra:
x[1]:=b[1]/a[1, 1]; For i:=2 to n
Begin
s:=0;
For j:=1 to i-1 s:=s+a[i, j]*x[j];
x[i]:=(b[i]-s)/a[i,i]; Writeln(x[i]);
End;
1.4.3 Phân tích giải thuật: Chất lượng chương trình hay giải thuật bao gồm: - Tính đắn
- Tính đơn giản (dễ hiểu, dễ quản lý, dễ lập)
- Tính tối ưu (hiệu quả) mặt thời gian khơng gian nhớ * Tính đắn:
Đây yêu cầu phân tích quan trọng cho giải thuật Thông thường, người ta thử nghiệm (test) nhờ số liệu chạy chương trình so sánh kết thử nghiệm với kết mà ta biết Tuy nhiên, theo Dijkstra: “Việc thử nghiệm chương trình chứng minh có mặt lỗi khơng chứng minh vắng mặt lỗi”
Ngày nay, với cơng cụ tốn học người ta chứng minh tính đắn giải thuật
* Mâu thuẫn tính đơn giản tính hiệu quả:
Một giải thuật đơn giản (dễ hiểu) chưa hẳn tối ưu thời gian nhớ Đối với chương trình dùng vài lần tính đơn giản coi trọng chương trình sử dụng nhiều lần (ví dụ, phần mềm) thời gian thực rõ ràng phải ý
u cầu thời gian khơng gian có giải pháp trọn vẹn 1.5 Độ phức tạp thuật toán:
Thời gian thực giải thuật phụ thuộc vào nhiều yếu tố:
Kích thước liệu đưa vào (dung lượng) Nếu gọi n kích thước liệu vào thời gian thực giải thuật, ký hiệu T(n)
Tốc độ xử lý máy tính, nhớ (RAM) Ngơn ngữ để viết chương trình
(6)n lớn, thời gian thực giải thuật tối ưu so với giải thuật
Cách đánh giá thời gian thực giải thuật theo kiểu gọi đánh giá thời gian thực giải thuật theo “độ phức tạp tính tốn giải thuật”
1.5.1 Độ phức tạp tính tốn giải thuật:
Nếu thời gian thực giải thuật T(n) = Cn2 (C: hằng), ta nói rằng: Độ phức tạp tính tốn giải thuật có cấp n2 ta ký hiệu T(n) = O(n2)
Tổng quát: T(n) = O(g(n)) ta nói độ phức tạp giải thuật có cấp g(n) 1.5.2 Xác định độ phức tạp giải thuật:
Việc xác định độ phức tạp tính tốn giải thuật nói chung phức tạp Tuy nhiên, thực tế độ phức tạp giải thuật xác định từ độ phức tạp phần giải thuật Cụ thể, ta có số quy tắc sau:
- Quy tắc tính tổng:
Nếu chương trình P phân tích thành phần: P1, P2 độ phức tạp P1 T1(n) = O(g1(n)) độ phức tạp P2 T2(n) = O(g2(n)) độ phức tạp P là: T(n) = O(g1(n) + g2 (n)) = O(max(g1(n), g2(n)))
Ví dụ: g1(n) = n2, g2(n) = n3 Suy ra: T(n) = O(n3)
* Lưu ý: Nếu g1(n) ≥ g2(n), ∀n≥ n0thì O(g1(n) + g2(n)) = O(g2(n)) Ví dụ: O(n + log2n) = O(n)
- Quy tắc nhân:
Nếu độ phức tạp P1 O(g1(n)), độ phức tạp P2 O(g2(n)) độ phức tạp tính tốn P1 lồng P2 O(g1(n).g2(n))
* Lưu ý:
Câu lệnh gán, Read, Write, If có thời gian thực số C = O(1)
Câu lệnh lặp vòng g(n) lần có thời gian thực O(g(n)) O(Cg(n)) = O(g(n)) (C: hằng)
Ví dụ:
1) Câu lệnh: For i:=1 to n { O(n) }
P:=P*i; { O(1) }
Có thời gian thực là: O(n*1) = O(n)
2) For i:=1 to n { O(n) }
For j:=1 to n { O(n) }
x:=x+1; { O(1) }
Có thời gian thực là: O(n*n*1) = O(n2)
(7)• Có thời gian thực giải thuật phụ thuộc vào đặc điểm liệu Bấy T(n) trường hợp thuận lợi khác T(n) trường hợp xấu Tuy nhiên, thông thường người ta đánh giá độ phức tạp tính tốn giải thuật thơng qua T(n) trường hợp xấu
Ví dụ: Cho dãy gồm có n phần tử mảng: V[1], V[2], , V[n] X giá trị cho trước
Procedure
TimKiem(X); Found:=False; i:=1;
While (i<= n) and (not Found) If V[i]=X then
Begin
Writeln(i); Found:=True; End;
Else i:=i+1;
If not Found then Write(‘khong co’); Return
Khi đó: T(n) thuận lợi = O(1) (với X = V[1]) T(n) xấu = O(n) (với X ≠ V[i], ∀ i) Nên: T(n) = O(n)
(8)CHƯƠNG 2: GIẢI THUẬT ĐỆ QUY 2.1 Khái niệm đệ qui:
- Chương trình đệ quy chương trình gọi đến
Ví dụ: Một hàm đệ quy hàm định nghĩa dựa vào nó.
- Trong lý thuyết tin học, người ta thường dùng thủ thuật đệ quy để định nghĩa đối tượng
Ví dụ: Tên biến Pascal chuẩn định nghĩa sau: - Mỗi chữ tên
- Nếu t tên biến t <chữ cái>, t <chữ số> tên biến
- Một chương trình đệ quy định nghĩa đệ quy khơng thể gọi đến mãi mà phải có điểm dừng đến trường hợp đặc biệt đó, mà ta gọi trường hợp suy biến (degenerate case)
Ví dụ: Cho số tự nhiên n, ta định nghĩa n! sau:
= =
1 0!
1)! -(n * n n!
Lời giải đệ quy: Nếu lời giải tốn T thực lời giải tốn T' có dạng giống T, theo nghĩa T' "nhỏ hơn" T T' có khuynh hướng ngày tiếp cận với trường hợp suy biến
Ví dụ: Cho dãy phần tử mảng V[1], V[2], , V[n] xếp theo thứ tự tăng dần, gọi X giá trị Viết giải thuật tìm kiếm để in vị trí phần tử mảng có giá trị X (nếu có) Ngược lại, thơng báo khơng có
Procedure TimKiem(d, c, X)
If d>c then Writeln('Khong co') Else Begin
g:=(d+c) div 2;
If X=V[g] then Writeln(g)
Else If X<V[g] then TimKiem(d, g-1, X) Else TimKiem(g+1, c, X);
End; Return;
Nhận xét:
Bài tốn tìm kiếm ban đầu tách thành tốn tìm kiếm với phạm vi nhỏ gặp phải trường hợp suy biến Chính việc phân tích đó, người ta xem giải thuật đệ quy giải thuật thể phương pháp "chia để trị"
Nếu thủ tục hàm chứa lời gọi đến (ví dụ trên) gọi đệ quy trực tiếp Ngược lại, có thủ tục chứa lời gọi đến thủ tục khác mà thủ tục chứa lại lời gọi đến gọi đệ quy gián tiếp, hay gọi đệ quy tương hỗ (trong Pascal, dùng từ khóa Forward)
(9)Program VD_Forward;
Procedure Ong(n: Byte); Forward; Procedure Ba(n: Byte);
Begin
Writeln(n, 'Ba');
If n>0 then Ong(n-1); End;
Procedure Ong(n: Byte); Begin
Writeln(n, 'Ong'); If n>0 then Ba(n-1); End;
BEGIN Ong(3); END
Kết quả: Ong Ba Ong Ba
2.2 Phương pháp để thiết kế giải thuật đệ quy: - Tham số hoá tốn
- Phân tích trường hợp chung (đưa tốn dạng tốn loại có phạm vi giải nhỏ theo nghĩa tiến đến trường hợp suy biến) - Tìm trường hợp suy biến
Ví dụ:
Lập hàm GT(n) = n!
Function GT(n); If n=0 then
GT:=1 Else
GT:=n*GT(n-1); Return;
2) Dãy số Fibonaci: F1 = F2 = 1;
Fn = Fn-1 + F n-2 (n ≥ 3) Function F(n);
If n ≤ then F:=1
Else F:= F(n-1)+F(n-2); Return;
Nhận xét:
(10)- Việc sử dụng giải thuật đệ quy có:
Ưu điểm Khuyết điểm
Thuận lợi cho việc biểu diễn toán
Có khơng tối ưu thời gian
Gọn (đối với chương trình) Có thể gây tốn nhớ
- Chính vậy, lập trình người ta cố tránh sử dụng thủ tục đệ quy thấy khơng cần thiết
Ví dụ: Bài toán tháp Hà nội:
Cho ba cọc A, B, C; có n đĩa khác xếp theo thứ tự nhỏ lớn nằm cọc A Yêu cầu: Chuyển chồng đĩa từ cọc A sang cọc C với điều kiện:
Mỗi lần chuyển đĩa
- Khơng có trường hợp đĩa lớn đặt đĩa nhỏ - Có thể dùng cọc B làm cọc trung gian
Tham số hoá toán: HaNoi(n, A, B, C) { A, B, C: char} Trong đó: n: Số đĩa
A: Cọc nguồn cần chuyển đóa B: Cọc trung gian
C: Cọc đích để chuyển đĩa đến Chương trình sau:
Program Thap_HN; Readln(n); A:= 'A'; B:= 'B'; C:= 'C';
HaNoi(3, A, B, C); End
Giải thuật đệ quy:
- Trường hợp suy biến:
Nếu n = chuyển đĩa từ cọc A qua cọc C { Writeln(A, '->', C) } - Trường hợp chung (n ≥ 2):
Thử với n=2: + Chuyển đĩa thứ từ A sang B + Chuyển đĩa thứ từ A sang C + Chuyển đĩa thứ từ B sang C
->Tổng quát: + Chuyển (n -1) đĩa từ A sang B (C làm trung gian) + Chuyển đĩa từ A sang C (B: trung gian)
+ Chuyển (n -1) đĩa từ B sang C (A: trung gian) Suy giải thuật đệ quy:
Procedure HaNoi(n, A, B, C);
If n=1 then Write(A, '->', C) Else Begin
(11)HaNoi(1, A, B, C); {II} HaNoi(n -1, B, A, C); {III} End;
(12)BÀI TẬP:
1) Viết hàm luỹ thừa lt(x: real; n: byte): real; cho giá trị xn
2) Viết chương trình nhập vào số ngun đảo ngược số lại (không dùng phương pháp chuyển số thành xâu)
3) Viết chương trình cho phép sản sinh hiển thị tất số dạng nhị phân độ dài n (có n chữ số)
(13)CHƯƠNG 3: MẢNG VÀ DANH SÁCH TUYẾN TÍNH 3.1 Mảng cấu trúc lưu trữ mảng:
- Mảng cấu trúc liệu đơn giản thông dụng nhiều ngơn ngữ lập trình - Mảng tập có thứ tự gồm số cố định phần tử có quy cách
Ví dụ: Trong Pascal, để khai báo dãy số nguyên phần tử: a1, a2, , an (với n≤ 100), ta khai báo mảng A sau:
Var A: array [1 100] of integer;
Lúc này, việc truy xuất thông qua phần tử mảng, ký hiệu: a[1], a[2], a[100]
- Ma trận mảng chiều
Ví dụ: Var B: array [1 10, 10] of real;
Khi đó, B[i, j] phần tử ma trận B Trong i hàng j cột - Tương tự ta có mảng chiều, mảng chiều
Cấu trúc lưu trữ:
Cách lưu trữ mảng thông thường (đối với ngơn ngữ lập trình) lưu trữ theo kiểu
Ví dụ: Gọi a mảng chiều gồm có n phần tử, phần tử có độ dài d
(chiếm d byte) lưu trữ hình đây:
d d
a1 a2 an
Loc (a1): địa phần tử a1 {Loc: Locate) Suy địa phần tử thứ ai:
Loc (ai) = Loc (a1) + d*(i-1) Lưu ý:
- Nếu số mảng số j (ví dụ, a: array[5 10]) thì: Loc (ai) = Loc (aj) + d*(i-j)
- Đối với mảng nhiều chiều, việc tổ chức lưu trữ thực tương tự: Ví dụ: a: array[1 3, 2] of byte;
a11 a12 a21 a22 a31 a32
Địa phần tử aij:
Loc (a[i, j]) = Loc (a[1,1]) + d*((i-1)*n + (j-1)) Trong đó, n số cột ma trận
Baøi tập:
(14)+ Procedure Xoa(i): Xố phần tử thứ i mảng
+ Procedure ChenSau(i, x): Chèn sau phần tử thứ i phần tử có giá trị x Lưu ý: Do phần tử mảng thường lưu trữ nên việc truy nhập vào chúng nhanh, đồng với phần tử (ưu điểm) Trong lúc đó, nhược điểm việc lưu trữ mảng là:
+ Phải khai báo số tối đa, có trường hợp gây lãng phí nhớ + Khó khăn việc thực phép xoá / chèn phần tử mảng 3.2 Danh sách tuyến tính (Linear list):
* Định nghóa:
Danh sách tuyến tính dãy có thứ tự a1, a2, , an với n≥0 Nếu n=0 gọi danh sách rỗng Ngược lại: a1 gọi phần tử đầu tiên, an gọi phần tử cuối cùng, n gọi chiều dài danh sách
- Đối với danh sách tuyến tính, với phần tử (i = 1, n - 1) có phần tử ai+1 với phần tử ai (i =2, n ) có phần tử đứng trước ai-1 Danh sách tuyến tính khác với mảng chiều chỗ kích thước danh sách khơng cố định phép bổ sung phép loại bỏ thường xuyên tác động lên danh sách Ví dụ: Stack
- Có nhiều cách để lưu trữ danh sách tuyến tính: + Lưu trữ theo địa mảng chiều
+ Lưu trữ địa trỏ (sử dụng danh sách móc nối) + Lưu trữ file (sử dụng nhớ ngồi)
- Với danh sách tuyến tính, ngồi phép bổ sung loại bỏ cịn có số phép sau: + Phép ghép nhiều danh sách thành danh sách (xem tập, làm mảng)
1 m n
1 n
+ Phép tách (tách danh sách thành danh sách)
+ Sao chép danh sách nhiều danh sách (2 danh sách) + Cập nhật sửa đổi nội dung phần tử danh sách + Sắp xếp phần tử danh sách theo thứ tự ấn định trước
+ Tìm kiếm phần tử danh sách thoả mãn điều kiện cho trước 3.3 Ngăn xếp (Stack):
3.3.1 Định nghóa:
Stack kiểu danh sách tuyến tính đặc biệt, phép bổ sung loại bỏ thực đầu gọi đỉnh Stack (đầu gọi đáy Stack)
(15)S[n] … S[T]
… S[2] S[1] 3.3.2 Lưu trữ Stack mảng:
Vì Stack danh sách tuyến tính nên sử dụng mảng chiều để tổ chức Stack Chẳng hạn: sử dụng mảng S để lưu trữ dãy phần tử: S[1], S[2], , S[n] (n gọi số phần tử cực đại mảng S)
Gọi T số phần tử đỉnh Stack T sử dụng để theo dõi vị trí đỉnh Stack nên sử dụng danh sách móc nối để tổ chức Stack T xem trỏ vào vị trí đỉnh Stack
Giá trị T tăng lên đơn vị bổ sung phần tử vào danh sách giảm bớt loại bỏ phần tử khỏi Stack
Lưu ý:
- Khi T = n khơng thể bổ sung thêm (hay nói cách khác Stack đầy) - Khi T = khơng thể loại bỏ phần tử Stack rỗng (hay Stack cạn) Giải thuật bổ sung phần tử x vào Stack S có đỉnh T:
Procedure Push(S, T, X); {T: tham bieán} If T=n then
Write(khong bo sung duoc'); Else
Begin T:=T+1; S[T]:=X; End;
Return;
Giải thuật loại bỏ khỏi Stack S phần tử đỉnh T gán giá trị phần tử cho tham biến X:
Procedure Pop(S, T, X); { T, X: tham bieán} If T=0 then
Write('Stack can'); Else
Begin
X:=S[T]; T:=T-1; End; Return;
(16) Bài tập:
Giả sử ta có mảng S dùng để lưu trữ đồng thời hai Stack hình đây:
Stack Stack
1 n-1 n
T1 T2
Trong đó:
+ T1 dùng để theo dõi vị trí đỉnh Stack (đây phần tử số mảng S). + T2 dùng để theo dõi vị trí đỉnh Stack (đây phần tử n mảng S)
Nếu T2 = T1 + lúc hai Stack đầy Viết thủ tục sau:
1) Procedure Bosung(i, x);
Dùng để bổ sung vào Stack i (i = 1, ) phần tử x 2) Procedure Loaibo (i, x);
Dùng để loại bỏ phần tử khỏi Stack i (i = 1, ) trả phần tử cho tham biến x
3.3.3 Các ví dụ:
Ví dụ 1: Viết chương trình đổi số hệ thập phân thành số hệ nhị phân.
Procedure Chuyen;
1 Readln (n); T:=0;
2 While n<>0 Begin
r:= n mod 2;
Push(S, T, r); {Push(S, T, n mod 2) r tham trò} n:= n div 2;
End;
3 While T<>0 Begin
Pop(S, T, r); Write(r);
End;
Return; { Lưu ý: S: array [1 100] of }
Ví dụ 2: Viết chương trình tính giá trị biểu thức hậu tố (tức ký pháp Ba Lan đảo)
* Moät số khái niệm:
- Biểu thức số học mà ta thường sử dụng gọi biểu thức trung tố Ở đây, ta coi thành phần (token) có biểu thức trung tố bao gồm: số (toán hạng), toán tử (+, -, *, /), dấu ngoặc: (, )
- Biểu thức số học biểu diễn dạng ký pháp hậu tố (biểu thức hậu tố) ký pháp tiền tố (biểu thức tiền tố)
Ví dụ: + biểu thức trung tố
(17)+ 3 biểu thức tiền tố (các toán tử trước toán hạng) - Với ký pháp (ký pháp Ba Lan), dấu ngoặc không cần thiết
Ví dụ: + * * +
7 * + 7 * +
7 * (3 + 5) 7 + *
(1 + 5) * ( - (4 - 1)) 1 + - - *
- Cách tính giá trị biểu thức hậu tố: Biểu thức cần tính đọc từ trái sang phải tìm tốn tử Hai tốn hạng đọc gần (trước toán tử này) thực với tốn tử để thay tốn hạng Q trình lại tiếp tục có kết cuối
Giải thuật: 1) T:=0; 2) Repeat
Đọc token X biểu thức;
If <X số> then Push(S, T, X); If <X toán tử> then
Begin
Pop(S, T, Y); Pop(S, T, Z);
Tác động toán tử X vào Y Z gán kết cho W
Push(S, T, W); End;
Until <Khơng cịn token nữa>; 3) Pop(S, T, X);
4) Write(X); Baøi tập:
1) Chuyển giải thuật thành chương trình Pascal
2) Nhập xâu có nội dung biểu thức hậu tố, token cách ô trống Viết chương trình tính kết biểu thức vừa nhập
Ví dụ 3: Viết chương trình để chuyển biểu thức trung tố sang hậu tố Giải thuật:
1) Khởi tạo Stack chứa ký tự toán tử có đỉnh T:
T:=0; { S: array [1 100] of char +, -, *, /, ( } Khởi tạo xâu biểu thức hậu tố:
Xau:=’’;
2) Repeat
<Đọc token biểu thức trung tố từ trái sang phải;>
Case
<Token toán hạng>:
{ Cộng vào bên phải xâu (kèm khoảng trắng) }
Xau:=Xau + Token + ‘ ‘;
<Token toán tử>:
(18)Begin
{ So sánh token với toán tử đỉnh Stack; }
If <Token > toán tử đỉnh Stack> then
<Push Token naøy>;
Else Begin
<Lặp việc Pop toán tử đỉnh Stack cộng vào bên phải xâu (trừ dấu “ ( “ ) Token > toán tử đỉnh Stack rỗng>;
<Push Token naøy;> End;
End;
<Token laø “(“>: <Push Token naøy>; <Token “)”>:
<Lặp việc Pop tốn tử đỉnh Stack cộng vào bên phải xâu (trừ dấu ngoặc mở) gặp dấu ngoặc mở.>
End Case;
Until <Hết chuỗi biểu thức trung tố>;
3) Pop phần tử lại Stack vào bên phải xâu Stack rỗng rối đưa vào bên phải xâu
4) Write(Xaâu);
Bài thực hành số 1:
Nhập xâu có nội dung biểu thức trung tố Tính kết biểu thức Chú ý: Các toán tử: *, /, +, -, (, ) Dùng hàm trả thứ tự ưu tiên để so sánh:
* hay / trả + hay - trả ( hay ) trả
Lời giải tham khảo chương trình tính kết biểu thức hậu tố: Program Tinh_bieu_thuc_hau_to;
uses crt;
var a:array[1 200]of REAL; s,s1,STRI:string; n:byte; function uutien(x:char):byte;
begin
if (x in ['(',')']) then uutien:=0;
else if (x='*')or(x='/') then uutien:=2;
else
uutien:=1; end;
function laytu(s2:string;var i:byte):string; var j:byte;
(19)while s2[j]<>' ' inc(j); laytu:=copy(s2,i,j-i);
i:=j; end;
procedure thuchien;
var x:string;t:byte;kt,y,z:REAL;er:INTEGER; begin
t:=0; n:=0; s:=s+' '; repeat
inc(n);
x:=laytu(s,n); val(x,y,er); if er=0 then
begin inc(t);
val(x,a[t],er); end;
else begin
y:=a[t]; dec(t); z:=a[t]; dec(t);
if x='+' then kt:=y+z; if x='-' then
kt:=z-y; if x='*'then
kt:=y*z; if x='/' then
kt:=z / y; inc(t);
a[t]:=kt; end;
until n=length(s); y:=a[t];
writeln(y:5:5); end;
begin
CLRSCR;
WRITELN('NHAP VAO XAU HAU TO:'); READLN(s); thuchien;
readln ;
(20) Lời giải tham khảo chương trình tính kết biểu thức trung tố: Program Tinh_bieu_thuc_trung_to;
uses crt;
var a:array[1 200]of real; s,s1,STRI:string;
n:byte;
function uutien(x:char):byte; begin
if (x in ['(',')']) then uutien:=0 else if (x='*')or(x='/') then uutien:=2 else
uutien:=1; end;
function laytu(s2:string;var i:byte):string; var j:byte;
begin j:=i;
while (s2[j]<>' ')do inc(j); laytu:=copy(s2,i,j-i);
i:=j; end;
procedure chuyen;
var t:byte;stack:array[1 100]of char; token:string;
er:integer;ktt:real; begin
write('Nhap vao xau trung to:'); readln(s1); token:='';
{s1:='1 + * - * ( + )'; }
s1:=s1+' ';
t:=0; STRI:=''; n:=0; repeat
inc(n);
token:=laytu(s1,n); val(token,ktt,er);
if er=0 then STRI:=STRI+token+' ' ; else if n>length(s1) then
begin
writeln('bieu thuc sai, dung chuong trinh'); readln; halt;
end; if
(token='*')or(token='+')or(token='')or(token='/') then
begin
if t=0 then begin
inc(t);
(21)begin
if uutien(token[1])> then
uutien(stack[t]) beg
in
inc(t); stack[t]:=token[1]; end
els e
begin
repeat {WHILE
T<>0)AND(UUTIEN(TOKEN[1])<=UUTIEN(STACK[T]))DO} BEGIN
if stack[t]<>'(' then
STRI:=STRI+stack[t]+' '; dec(t);
END;
until (t=0)or
(uutien(token[1])>uutien(stack[t])) ;
inc(t);
stack[t]:=token[1]; end;
end; end;
if token='(' then begin
inc(t);
stack[t]:=token[1]; end; if token=')' then
repeat
if stack[t]<>')' then STRI:=STRI+stack[t]+' ';
dec(t);
until stack[t]='('; until n=length(s1);
{======================= -} repeat
if stack[t]<>'(' then STRI:=STRI+stack[t]+' '; dec(t);
until t<=0; writeln(STRI); end;
{ -} procedure thuchien;
(22)t:=0; n:=0; s:=s+' '; repeat
inc(n);
x:=laytu(s,n); val(x,p,er); if er=0 then
begin inc(t);
val(x,p,er);a[t]:=p end;
else begin
y:=a[t]; dec(t); z:=a[t]; dec(t); if x='+'then kt:=y+z;
if x='-'then kt:=z-y; if x='*'then kt:=y*z; if x='/'then kt:=z / y;
inc(t); a[t]:=kt; end;
until n=length(s); y:=a[t];
writeln('KET QUA LA: ',y:5:4);
end; BEGIN
CLRSCR;
writeln('CHUONG TRINH TINH MOT BIEU THUC:');
WRITELN('YEU CAU PHAI NHAP SAO CHO DUNG BIEUTHUC:'); textcolor(green);
writeln(' Nhap bieu thuc cho cac so,dau,ngoac phai cach nhau');
writeln(' mot dau cach moi thuc hien duoc, doi voi so am thi');
writeln(' viet dau tru dang truoc so do'); textcolor(white);
chuyen; s:=stri; thuchien; readln END
3.4 Hàng đợi (Queue): 3.4.1 Định nghĩa:
Hàng đợi danh sách tuyến tính mà phép bổ sung thực đầu (gọi lối vào/lối sau: rear) phép loại bỏ thực đầu (lối ra/lối trước: front)
Nhận xét: Cơ cấu Queue giống hàng đợi: vào trước - trước Do Queue cịn gọi danh sách kiểu FIFO (First In First Out)
3.4.2 Lưu trữ Queue mảng:
(23)Để xử lý Q ta dùng biến:
+ Biến R để theo dõi vị trí lối sau Q + Biến F để theo dõi vị trí lối trước Q Lưu ý:
- Khi Q (Queue) rỗng ta quy ước: F = R =
- Khi phần tử bổ sung R tăng lên (R:=R+1) Khi lấy bớt phần tử F tăng lên (F:=F+1)
1
Lối Lối vào
F R
Tuy nhiên, với cách tổ chức xuất tình đến lúc bổ sung tiếp phần tử không gian nhớ mảng Q chỗ Để khắc phục, ta xem Q mảng vòng tròn, nghĩa xem Q[1] đứng sau Q[n]
Với cách tổ chức ta có:
- Giải thuật bổ sung vào hàng đợi Queue (có vị trí lối trước F vị trí lối sau R) phần tử x:
Procedure Insert_Queue(Q, F, R, X) {Q, R, F: tham bieán} If (R mod n)+1=F then
{ Tương đương: if ((R<n) and (R+1=F)) or ((R=n) and (F=1)) }
Write(“Hang doi da day”) Else Begin
R:=(R mod n) +1; Q[R]:=X;
If F=0 then
F:=1; End; Return;
- Giải thuật loại bỏ phần tử từ hàng đợi Queue (lối trước F, lối sau R) phần tử loại bỏ gán cho biến X:
Procedure Delete_Queue(Q, F, R, X) {Q, F, R, X: tham bieán} If F=0 then Write(“Hang doi dang can”)
Else Begin
X:=Q[F];
If F=R then F:=R:=0 Else F:=(F mod n)+1; End;
Return; Bài tập:
(24)- Đọc token từ trái sang phải, tất đưa vào hàng đợi Ví dụ: 11 + * 3:
11 + *
Lần lượt lấy để bỏ vào danh sách thứ hai Nhớ rằng: gặp phải tốn tử * / lấy hàng đợi phần tử danh sách thứ lấy lại phần tử để thực phép toán này, kết lại bỏ vào danh sách thứ
11 +
2 *
2) Giống tập 1, token có dấu ‘(‘ dấu ‘)’, phương pháp sau:
Ví dụ: + (2 * (3 + 4))
1 ( * ( +
- Lần lượt đọc token từ trái sang phải để push vào Stack gặp dấu ‘)’ lấy phần tử Stack để bỏ vào danh sách thứ hai (bỏ vào từ phía trái) gặp dấu ‘(‘
3 +
- Lúc ta xử lý danh sách thứ (tính) dựa vào thủ tục xây dựng tập 1) Được kết lại cho vào Stack ban đầu
1 + ( *
- Rồi lại tiếp tục hết biểu thức + 14
- Lúc ta coi Stack hàng đợi để sử dụng thủ tục tập mà xử lý
(25)CHƯƠNG 4: DANH SÁCH MÓC NỐI 4.1 Danh sách móc nối đơn:
4.1.1 Tổ chức danh sách nối đơn:
- Mỗi phần tử danh sách gọi nút (node), ghi gồm phần: Phần thông tin (Info): Chứa thông tin phần tử (có thể có nhiều trường)
Phần liên kết (Next): Đây trường chứa địa phần tử sau (là trỏ) Trường có kiểu liệu trỏ
- Các nút nằm rải rác nhớ
- Để truy cập đến phần tử danh sách, ta phải truy nhập vào nút Do phải có trỏ First để trỏ vào phần tử danh sách Từ nút đầu tiên, thông qua trường Next ta đến nút thứ hai ta duyệt hết phần tử danh sách
- Phần tử cuối danh sách có trường Next khơng chứa địa phần tử mà ta gọi Nil
- Khi danh sách rỗng, ta quy ước First = Nil - Ta ký hiệu:
New(p) (hay: p <= Avail): thủ tục nhằm tạo vùng nhớ trống để chứa nút nút trỏ trỏ p (p chứa địa nút này)
Dispose(p) (hay p => Avail): thủ tục để giải phóng vùng nhớ nút trỏ trỏ p khỏi nhớ
- Nếu ta ký hiệu p trỏ, p^ xem Record Ví dụ:
Type ConTro = ^BanGhi; Banghi = Record Name: String[10]; Age: Byte;
Next: ConTro; End;
Var p: ConTro;
4.1.2 Một số phép toán danh sách nối đơn:
4.1.2.1 Chèn nút có nội dung X vào danh sách sau nút trỏ p:
Procedure Insert_Node(First, p, X); {First: tham bieán} New(Tam);
Tam^.Info:=X; If First=Nil then
Begin
First:=Tam; First^.Next:=Nil ; Exit;
End;
(26)4.1.2.2 Loại bỏ nút trỏ p khỏi danh saùch:
Procedure Delete_Node(First, p); {First: tham biến} If First=Nil then
Begin
Write(‘Danh sach
rong’); Exit; End;
If First=p then Begin
First:=First^.Next; Dispose(p);
Exit; End;
q:=First;
While q^.Next<>p q:=q^.Next; q^.Next:=p^.Next
; Dispose(p); Return;
4.1.2.3 Ghép danh sách trỏ first1 first2 thành danh sách trỏ first1:
Procedure Combine(First1, First2); {First1: tham biến} If First1=Nil then
Begin
First1:=First2; Exit; End;
If First2=Nil then Exit; p:=First1;
While p^.Next<>Nil p:=p^.Next; p^.Next:=First2; Return;
4.2 Danh sách nối kép: 4.2.1 Tổ chức:
Tương tự danh sách nối đơn nối vòng, danh sách nối kép bao gồm nút nằm rãi rác nhớ liên kết với Nhưng điều khác biệt nút có hai trường chứa địa nút đứng trước nút đứng sau (con trỏ), nghĩa nút có dạng:
Lúc danh sách có dạng sau:
First Last
Nhận xét:
- Danh sách sử dụng biến trỏ First Last để trỏ tới nút nút cuối Trong trường before nút trường Next nút cuối có giá trị Nil
- Khi danh sách rỗng: First = Last = Nil
Previous Info Next
(27)4.2.2 Một số phép toán danh sách nối kép:
- Chèn phần tử có trường Info X vào sau nút trỏ p:
First p Last
X
Produre Insert_Node(First, Last, p, X); New(Tam);
Tam^.Info:=X;
If First=Nil then Begin
First:=Tam;
First^.Next:=First^.Prev:=Nil ;
Last:=First; Exit;
End;
Tam^.Next:=p^.Next; Tam^.Prev:=p; p^.Next:=Tam; If p <> Last then
Tam^.Next^.Prev:=Tam Else
Last:=Tam; Return;
- Loại bỏ nút trỏ p khỏi danh sách:
Procedure Delete_Node(First, Last, p); If First=Nil then
Begin
Write(‘Danh sach rong’); Exit; End;
If First=Last then First:=Last:=Nil Else If p=First then
Begin
First:=First^.Next; First^.Prev:=Nil; End;
Else If p=last then Begin
Last:=Last^.Prev; Last^.Next:=Nil; End; Else
Begin
P^.Prev^.Next:=p^.Next; P^.Next^.Prev:=p^.Prev; End; Dispose(p);
Return;
(28)* Ví dụ việc sử dụng danh sách móc nối:
- Biểu diễn đa thức danh sách móc nối đơn
- Đa thức biểu diễn danh sách nối đơn mà nút (lưu đơn thức) có dạng sau:
Hệ số Mũ Tiếp
Bài tốn: Tính tổng đa thức:
- Giả sử đa thức A(x), B(x) biểu diễn danh sách móc nối đơn trỏ A B
- Vấn đề đặt ra: Tạo danh sách nối đơn thứ trỏ C để biểu diễn cho đa thức: C(x) = A(x) + B(x)
- Trước hết ta viết thủ tục để ghép thêm nút vào cuối danh sách C có nội dung trường hệ số XX, trường mũ YY Giả sử danh sách C có nút cuối trỏ R
C R
Procedure Ghep(C, R, XX, YY); New(Tam);
Tam^.Heso:=XX; Tam^.Mu:=YY; Tam^.Tiep:=Nil;
R^.Tiep:=Tam; R:=Tam;
Return;
Procedure Cong_Da_Thuc(A, B, C); New(C); R:=C;
p:=A; q:=B;
2 While (p<>Nil) and (q<>Nil) If p^.Mu=q^.Mu then
Begin
XX:=p^.Heso+q^.Heso;
If XX<>0 then Ghep(C, R, XX, p^.Mu); p:=p^.Tiep;
q:=q^.Tiep; End
Else If p^.Mu<q^.Mu then Begin
Ghep(C, R, q^.Heso, q^.Mu); q:=q^.Tiep;
End; Else
Begin
(29)p:=p^.Tiep; End;
3 While p<>Nil Begin
Ghep(C, R, q^.Heso, q^.Mu); q:=q^.Tiep;
End;
While q<>Nil Begin
Ghep(C, R, p^.Heso, p^.Mu); p:=p^.Tiep;
End; Tam:=C; C:=C^.Tiep; Dispose(Tam); Return;
4.3 Stack Queue móc nối:
Đối với Stack, việc truy nhập thực đầu nên việc cài đặt danh sách Stack móc nối tự nhiên Chẳng hạn với danh sách nối đơn có nút đầu trỏ First coi First đỉnh Stack
Bổ sung phần tử vào Stack bổ sung nút vào danh sách để nút trở thành nút danh sách Loại bỏ phần tử danh sách loại bỏ phần tử Đối với danh sách móc nối, khơng cần kiểm tra tượng tràn Stack Stack dùng danh sách móc nối khơng bị giới hạn kích thước dùng mảng (mà giới hạn nhớ toàn phần)
Thủ tục chèn vào đầu danh sách phần tử:
Procedure Push(First, X);
New(p);
p^.Info:=X; p^.Tiep:=First; First:=p;
Return;
Thủ tục lấy phần tử đầu danh sách: Procedure Pop(First, Var X);
If First=Nil then Begin
Write(‘Stack caïn’);
Exit; End;
X:=First^.Info; p:=First;
First:=First^.Tiep; Dispose(p);
Return;
(30)Nhưng dùng danh sách móc nối đơn cần biến trỏ First Last
Bổ sung phần tử vào Queue bổ sung phần tử sau Last Và loại bỏ phần tử khỏi Queue loại bỏ phần tử (First)
Thủ tục chèn vào đầu danh sách phần tử:
Procedure Insert_Queue(First, Last, X); New(p);
p^.Info:=X; p^.Tiep:=Nil;
If Last=Nil then First:=Last:=p Else
Begin
Last^.Next:=p; Last:=p;
End; Return;
(31)CHƯƠNG 5: CÂY (TREE) 5.1 Định nghóa khái niệm:
5.1.1 Định nghóa:
- Một nút Đó gốc
- Nếu n nút T1, T2, , Tk k với gốc n1, n2, , nk thành lập cách cho nút n trở thành cha n1, n2, ,nk (được gọi gốc n)
Ví dụ: Cho biểu thức dạng trung tố: x + y * (z - t) + u / v Biểu thức biểu diễn dạng sau:
5.1.2 Caùc khaùi niệm liên quan: a Cấp (bậc - degree):
- Cấp nút số nút Suy nút có cấp gọi (leaf), ngược lại gọi nhánh (branch)
- Cấp cấp cao nút b Mức (level):
- Gốc có mức
- Một nút có mức i nút có mức i + Lưu ý:
- Mức lớn gọi chiều cao
(32)1) dãy gọi đường từ n1 đến nk, k gọi độ dài đường
c Cây có thứ tự:
- Là mà thứ tự coi trọng
Ví dụ: Nếu A A khác có thứ tự
B C C B
Lưu ý: Thường thứ tự của nút tính từ trái sang phải.Ví dụ, biểu thức u / v biểu diễn sau: /
u v
d Rừng (forest): Là tập hợp phân biệt 5.2 Cây nhị phân:
5.2.1 Định nghóa tính chất:
- Cây nhị phân mà nút có tối đa Nh
ậ n xét: A Gốc
- Cây nhị phân không thiết cấp Ví dụ: B - Một cấp nhị phân
- Cây nhị phân có thứ tự C
Số lượng tối đa nút mức i nhị phân là: 2i-1 Số lượng tối đa nút nhị phân có chiều cao h là: 2h-1 Lưu ý:
- Một gọi hoàn chỉnh số nút mức đạt tối đa trừ mức cuối
- Một gọi đầy đủ số nút mức đạt tối đa
(Cây không đầy đủ) Cây đầy đủ (Cây hoàn chỉnh) Cây hoàn chỉnh 5.2.2 Biểu diễn nhị phân:
5.2.2.1 Lưu trữ kế tiếp:
(33)Ví dụ: 1
4
Nh
ậ n xét : Lúc nút thứ i có số thứ tự là:
Tuy nhiên khơng đầy đủ (ví dụ suy biến) việc tổ chức lưu trữ theo kiểu tỏ lãng phí (cho ví dụ)
5.2.2.2 Lưu trữ móc nối:
- Ta biểu diễn nút có phần sau:
Left Info Right
Trong đó:
+ Info có nhiều trường
+ Left, Right: trường kiểu trỏ để trỏ tới trái phải Ví dụ:
A A Biểu diễn
B C
B C
D E D E
- Để truy nhập vào nút cây, cần có trỏ T trỏ tới nút gốc
- Người ta quy ước nhị phân rỗng T = Nil Nhận xét:
- Tại nút lá, trường Left Right có giá trị Nil
- Nếu nút khơng có bên trái trường Left = Nil (tương tự trường Right)
5.2.3 Phép duyệt nhị phân:
Phép duyệt phép qua nút nút qua lần (thăm lần) Có phép duyệt dựa vào thứ tự duyệt
Duyệt theo thứ tự trước, thứ tự thứ tự sau tuỳ thuộc vào nút gốc (N), 2i
(34)con trái (L), phải (R), thành phần duyệt trước thành phần duyệt sau Chẳng hạn:
- Duyệt theo thứ tự trước có nghĩa gốc duyệt trước (NLR) - Duyệt theo thứ tự (LNR)
- Duyệt theo thứ tự sau (LRN) Ví dụ 1: Cho nhị phân sau:
A T
B C
D E
F 5.2.3.1 Duyệt theo thứ tự trước:
Procedure DuyetTruoc(T); If T<>Nil then
Begin
Write(T^.Info);
DuyetTruoc(T^.Left); DuyetTruoc(T^.Right) ;
End; Return;
- Đối với ví dụ trên, ta có kết sau duyệt là: ABDEFC - Ta khử đệ quy thủ tục thủ tục sau:
Procedure DuyetTruoc(T); Top:=0;
Push(S, T); Repeat
Pop(S, pt); Write(pt^.Info);
If pt^.Right<>Nil then Push(S, pt^.Right); If pt^.Left<>Nil then
Push(S, pt^.Left); Until top=0;
Return;
5.2.3.2 Duyệt theo thứ tự giữa:
Procedure DuyetGiua(T); If T<>Nil then
Begin
(35)End; Return;
Đối với ví dụ trên, ta có kết sau duyệt là: DBFEAC 5.2.3.3 Duyệt theo thứ tự sau:
Procedure DuyetSau(T); If T<>Nil then
Begin
Duyetsau(T^.Left); Duyetsau(T^.Right); Write(T^.Info);
End; Return;
Đối với ví dụ trên, ta có kết sau duyệt là: DFEBCA Ví dụ 2: Xét nhị phân:
+
+ /
x * u v
y
z t Thứ tự trước: + x + * y - z + / u v Thứ tự giữa: x + y * z - t + u / v Thứ tự sau: x y z t - * u v / + +
Bài tập:
1) Tạo (sử dụng biểu diễn móc nối) hình ảnh sau: T
A
B C
D
(36)BanGhi = Record Info: Char;
Left, Right: ConTro; End;
Var T: ConTro;
Procedure TaoNut(Var p: ConTro; X: Char); Begin
New(p); P^.Info:=X;
p^.Left:=p^.Right:=Nil; End;
Procedure TaoCay; Begin
TaoNut(T, ‘A’); TaoNut(T, ‘B’); T^.Left:=p; TaoNut(T, ‘C’); T^.Right:=p; TaoNut(T, ‘D’); T^.Left^.Right:=p; End;
BEGIN TaoCay; END
2) Giả sử có nhị phân mà gốc trỏ T Nhập xâu từ bàn phím gồm chữ R, L để đường dẫn đến nút từ gốc T (R: rẽ phải, L: rẽ trái) Từ in nội dung trường Info nút
Readln(Xau); p:=T;
i:=1; Kt:=True;
While Kt and (i<=Length(Xau)) Begin
If p<>Nil then
If Xau[i]=’L’ then p:=p^.Left Else
p:=p^.Right Else Kt:=False; i:=i+1;
End;
(37)CHƯƠNG 6: CÁC GIẢI THUẬT SẮP XẾP VAØ TÌM KIẾM 6.1 Đặt vấn đề:
Sắp xếp q trình bố trí lại phần tử tập đối tượng theo thứ tự định Yêu cầu xếp thường xuyên xuất tin học nhằm giúp quản lý liệu dễ dàng
Các giải thuật xếp phân chia thành nhóm chính:
+ Sắp xếp trong: Toàn liệu xếp đưa vào nhớ Do liệu khơng nhiều ngược lại thời gian xếp nhanh
+ Sắp xếp ngoài: Một phần liệu cần xếp đưa vào nhớ trong, phần lại lưu trữ nhớ ngồi Do xếp liệu với khối lượng lớn tiến trình xếp chậm
Nói chung, liệu xuất nhiều dạng khác Ở ta quy ước tập đối tượng xếp tập ghi Tuy nhiên, xếp, thường người ta quan tâm đến giá trị trường (gọi trường khố) việc xếp tiến hành dựa vào trường khoá Để đơn giản, ta xem ghi chứa trường liệu có kiểu số thứ tự xếp theo chiều tăng
6.2 Một số phương pháp xếp đơn giản: 6.2.1 Sắp xếp kiểu lựa chọn:
Nguyên tắc:
Tại bước i ta chọn dãy ai+1, , an phần tử lớn ai, sau thực hốn đổi vị trí chúng với (sao cho có giá trị nhỏ (i = 1, n ))
Giải thuật:
Procedure Selection_Sort(a, n); For i:=1 to n-1
For j:=i+1 to n If a[i]>a[j] then
<Đổi chổ a[i] a[j]>; Return;
6.2.2 Sắp xếp kiểu chèn:
Ngun tắc: Tương tự tiến lên Chia làm trường hợp: - Kết hợp việc xếp với việc nhập liệu:
Procedure SapXep(a, n); For i:=1 to n
Begin
Readln(a[i]); Chen(a[i]); End;
Return;
Trong đó, ta có thủ tục Chen(X) sau: Procedure Chen(X);
(38)j:=1;
While (a[j]<=X) and (j<=i-1) j:=j+1;
If j<i then Begin
For k:=i downto j+1 a[k]:=a[k-1];
a[j]:=X; End;
End; Return;
- Sắp xếp từ mảng có liệu:
Procedure Insert_Sort(a, n); Const VoCuc = 106;
1 a[0]:= -VoCuc; For i:=2 to n
Begin
X:=a[i]; j:=i-1;
While x<a[j] Begin
a[j+1]:=a[j]; j:=j-1;
End; a[j+1]:=X; End;
Return;
Lưu ý: Ý tưởng giải thuật thực dễ dàng sử dụng danh sách móc nối để lưu dãy số
6.2.3 Sắp xếp kiểu bọt:
Procedure Bubble_Sort(a, n); For i:=1 to n-1
For j:=n downto i+1 If a[j]<a[j-1] then
<Đổi chỗ a[j] a[j-1]>; Return;
Nhận xét: Cả giải thuật có độ phức tạp tính tốn O(n2) 6.3 Sắp xếp kiểu phân đoạn (Sắp xếp nhanh - quick sort):
Nguyên tắc:
Chọn ngẫu nhiên phần tử X dãy (chẳng hạn phần tử đầu tiên) cố gắng phân chia dãy thành dãy liên tiếp nhau:
+ Dãy 1: Gồm phần tử nhỏ X + Dãy 2: Gồm phần tử X + Dãy 3: Gồm phần tử lớn X
Sau áp dụng lại giải thuật cho dãy thứ dãy thứ ba (dãy có số phần tử lớn 1)
Procedure Quick_Sort(a, Be, Lon); If Be<Lon then
(39)1 i:=Be+1; j:=Lon; X:=a[Be]; Repeat
While (a[i]<X) and (i<=Lon) i:=i+1;
If i>Lon then Begin
<Đổi chổ a[Be] a[Lon]>; Quick_Sort(a, Be, Lon-1); Exit;
End;
While a[j]>X j:=j-1;
If j=Be then Begin
Quick_Sort(a, Be+1, Lon); Exit;
End;
If i<=j then Begin
<Đổi chổ a[j] a[i]>; i:=i+1;
j:=j-1; End;
Until i>j;
< Đổi chổ a[Be] a[j]>; If Be<j then
Quick_Sort (a, Be, j-1); If Lon>i then
Quick_Sort (a, i, Lon); End;
Return;
Lưu ý: Tại chương trình chính, để xếp mảng a từ phần tử thứ đến phần tử thứ n ta gọi thủ tục xếp sau: Quick_Sort (a, 1, n);