Ví dụ: Xét giải thuật Greedy áp dụng cho bài toán tô màu {2} For mỗi đỉnh của V chưa tô màu Do {3} If V không được nối với đỉnh nào trong NewClr Then Begin End ; End ; Chú ý : Trong
Trang 1CHƯƠNG 1 : CÁC KHÁI NIỆM CƠ BẢN
I TỪ BÀI TOÁN ĐẾN CHƯƠNG TRÌNH
1 Giải thuật
2 Ngôn ngữ già và tinh chế từng bước
3 Tóm tắt ba giai đoạn để giải một bài toán
II CÁC KIỂU DỮ LIỆU TRỪU TƯỢNG
1 ịnh nghĩaĐ
2 Ví dụ
III CÁC KIỂU DỮ LIỆU - CÁC CẤU TRÚC DỮ LIỆU - CÁC KIỂU DỮ LIỆU TRỪU TƯỢNG
IV THỜI GIAN CHẠY MỘT CHƯƠNG TRÌNH
1 o một thời gian chạy của một chương trìnhĐ
2 ộ phức tạp của giải thuậtĐ
3 Cách tính thời gian chạy của chương trình
I TỪ BÀI TOÁN ÐẾN CHƯƠNG TRÌNH
1 Giải thuật
TOP Giải thuật la ìmột chuỗi các chỉ thị, mỗi chỉ thị có ý nghĩa rõ ràng để có thể giải quyết bài toán trong một khoảng thời gian hữu hạn
Nói cách khác, giải thuật là một cách để giải bài toán nào đó, nhưng nó phải áp dụng được cho mọi bài toán cùng loại Nếu cách giải chỉ đúng cho một vài trường hợp đặc biệt thì
nó không phải là giải thuật
Có nhiều cách để thể hiện giải thuật như dùng lời, dùng lưu đồ, có một cách diễn đạt giải thuật được dùng rất phổ biến đó là dùng ngôn ngữ giả
Ngôn ngữ giả là sự kết hợp của ngôn ngữ tự nhiên và các cấu trúc của một ngôn ngữ lập trình nào đó
Ví dụ: Cho một ngã 5 như hình vẽ
Trang 2Giải: Chúng ta có thể mô hình hóa bài toán nói trên theo mô hình toán học đã biết đó
là đồ thị (Graph) Ðồ thị là tập hợp các điểm gọi là các đỉnh của đồ thị và một tập hợp các cạnh nối một số các cặp đỉnh với nhau 2 đỉnh có ít nhất 1 cạnh nối được gọi là 2 đỉnh kề nhau Tại ngả 5 này có 13 lối đi (AB, AC, AD, BA, BC, BD, DA, DB, DC, EA, EB, EC, ED)
Ta sẽ biểu diễn mỗi lối đi là một đỉnh của đồ thị và 2 lối đi không thể cùng đi đồng thời ta nối chúng bằng 1 cạnh Tóm lại, ta có:
• Ðỉnh là các tuyến đường đi cho phép
• Cạnh nối 2 đỉnh dùng để chỉ tuyến đường không thể lưu thông đồng thời
Ta có đồ thị hình 2 như sau :
Bài toán của chúng ta rõ ràng là bài toán tô màu cho đồ thị hình 2 Bài toán tô màu cho
đồ thị được phát biểu như sau: "Tô màu cho các đỉnh của đồ thị sao cho số màu được dùng là
ít nhất và 2 đỉnh kề nhau (có cung nối) không được tô cùng 1 màu
Trang 3Tuy nhiên, bài toán tô màu cho đồ thị không có giải thuật tốt Nói cách khác, giải thuật của bài toán tô màu là: "Thử tất cả các khả năng" Thực tế cách giải này khó có thể áp dụng đưọc vì vậy ta cần suy nghĩ cách khác để giải quyết vấn đề Nếu bài toán nhỏ ta có thể vét cạn các khả năng để tìm ra lời giải tối ưu
Một cách giải quyết dùng cho bài toán chúng ta là: "Cố gắng tô màu cho đồ thị bằng ít màu nhất" một cách nhanh chóng Một cách giải quyết như vậy gọi là một Heuricstic Một Heuricstic hợp lý cho bài toán tô màu đồ thị được gọi là giải thuật Greedy
Chọn 1 đỉnh chưa tô màu và tô màu cho nó Với các đỉnh còn lại mà không có cạnh chung với đỉnh đang xét thì tô các đỉnh đó cùng 1 màu với đỉnh đang xét
Duyệt danh sách các đỉnh chưa tô màu, lấy 1 đỉnh trong số chúng và tô bằng màu mới rồi quay lại bước 1
Ðây là giải thuật hiển nhiên và luôn đạt kết quả tốt (mặc dù có thể không là lời giải tối ưu)
Ví dụ : Xét đồ thị dưới đây và cách tô màu cho nó
Trang 4+ Xanh : AB, AC, AD, BA, DC, ED + Ðỏ : BC, BD, EA
+ Tím : DA, DB + Vàng : EB, EC Nhận xét:
Một đồ thị có k đỉnh mà mỗi cặp đỉnh bất kỳ đều được nối với nhau thì phải dùng k màu để tô
Một đồ thị trong đó có k đỉnh mà mỗi cặp đỉnh bất kỳ trong k đỉnh này đều được nối với nhau thì không thể dùng ít hơn k màu để tô cho đồ thị
Ðồ thị hình 2 có 4 đỉnh AC, DA, EB, BD mà mỗi cặp đỉnh bất kỳ trong số chúng đều được nối với nhau Vậy đồ thị hình 2 không thể tô với ít hơn 4 màu Ðều này khẳng định lời giải trên là lời giải tối ưu
2 Ngôn ngữ giả và tinh chế từng bước TOP
Một khi ta đã có mô hình thích hợp cho bài toán ta cần hình thức hóa một giải thuật trong thuật ngữ của mô hình đó Mở đầu ta viết những mệnh đề tổng quát rồi tinh chế dần thành những mệnh đề cụ thể hơn và cứ tiếp tục như thế Ðến cuối cùng ta được những chỉ thị thích hợp trong một ngôn ngữ lập trình
Ví dụ: Xét giải thuật Greedy áp dụng cho bài toán tô màu
{2} For mỗi đỉnh của V chưa tô màu Do
{3} If V không được nối với đỉnh nào trong NewClr Then
Begin
End ; End ;
Chú ý : Trong giải thuật bằng ngôn ngữ giả chúng ta đã dùng một số từ
khóa của Pascal (các từ được in đậm)
Graph và Set là các kiểu dữ liệu trừu tượng, chúng sẽ được xác định bằng phép định nghĩa kiểu của Pascal
Câu lệnh If {3} có thể được tinh chế một bước nữa như sau:
Procedure Greedy ( Var G : Graph ; Var NewClr : Set );
{3.3} If có cạnh nối giữa V với W Then
{3.4} Found : = True ; {3.5} If Found = False Then
Begin
Trang 5End;
End;
End ;
Ta có thể coi Graph và Set như các tập hợp Có nhiều cách để biểu diễn 1 tập hợp
trong ngôn ngữ lập trình Ðể đơn giản ta xem tập hợp như một danh sách (List) các số nguyên
là chỉ số của các đỉnh và kết thúc bằng 1 giá trị đặc biệt Null Giải thuật Greedy được tinh chế
1 bước nữa như sau:
Procedure Greedy ( Var G:Graph ; Var NewClr: Set );
Var Found : Boolean ;
Giai đoạn 1: Xây dựng mô hình toán thích hợp cho bài toán và tìm một giải thuật
giải quyết bài toán trên mô hình đó
Giai đoạn 2: Giải thuật được trình bày bằng ngôn ngữ giả dựa trên các kiểu dữ liệu trừu tượng
Giai đoạn 3: Chọn một cách cài đặt một kiểu dữ liệu trừu tượng và thay ngôn ngữ giả bằng các mã lệnh của 1 ngôn ngữ lập trình Kết quả là ta được 1 chương trình hoàn chỉnh có thể giải quyết được vấn đề đặt ra
II CÁC KIỂU DỮ LIỆU TRỪU TƯỢNG (Abstract Data Type)
Một kiểu dữ liệu trừu tượng (ADT) là một mô hình toán học cùng với một tập hợp các phép toán tác động trên mô hình đó
Trang 6Thông thường ta sẽ thiết kế giải thuật theo thuật ngữ của kiểu dữ liệu trừu tượng, nhưng để cài đặt được giải thuật trong một ngôn ngữ lập trình, ta phải tìm cách biểu diễn kiểu
dữ liệu trừu tượng qua các kiểu dữ liệu của ngôn ngữ
W= First (G): Hàm trả về phần tử đầu danh sách của G ( = Null nếu G rỗng)
W= Next (G): Hàm trả về phần tử kế tiếp trong G ( = Null nếu là phần tử cuối)
Insert (V ,G): Xen phần tử V vào đồ thị G
III CÁC KIỂU DỮ LIỆU (DATA TYPE) - CÁC CẤU TRÚC DỮ LIỆU (DATA
STRUCTURE) - CÁC KIỂU DỮ LIỆU TRỪU TƯỢNG (ABSTRACT DATA
Trong một ngôn ngữ lập trình, kiểu dữ liệu của một biến là tập hợp các giá trị mà biến
đó có thể nhận được Mỗi một ngôn ngữ lập trình đều có một số kiểu dữ liệu cơ sở khác nhau
Một kiểu dữ liệu được thành lập bằng sự tập hợp các kiểu dữ liệu khác nhau gọi là kiểu dữ liệu hợp thành hay kiểu dữ liệu có cấu trúc hay cấu trúc dữ liệu
Dễ hiểu, dễ viết chương trình
Giải thuật dùng các tài nguyên hiệu quả như dùng ít bộ nhớ, chạy càng nhanh càng tốt,
1 Ðo thời gian chạy của một chương trình TOP
Thời gian chạy của một chương trình phụ thuộc vào các yếu tố:
Dữ liệu đầu vào
Tốc độ của máy được dùng
Tính chất của trình biên dich được dùng
Ðộ phức tạp tính toán của giải thuật
Trong thực tế thời gian chạy của một chương trình có thể xem như là một hàm của dữ liệu vào Cụ thể, đó là kích thước của dữ liệu vào Vì vậy, ta gọi T(n) là thời gian chạy chương trình với dữ liệu vào có độ dài là n
Ðối với nhiều chương trình thì thời gian chạy chương trình còn phụ thuộc vào đặc tính của dữ liệu vào Nghĩa là dữ liệu vào có cùng độ dài nhưng thời gian chạy chương trình là khác nhau Vì vậy, ta có thể xem T(n) là thời gian chạy chương trình trong trường hợp xấu nhất trên dữ liệu vào có độ dài là n Hay nói cách khác T(n) là thời gian chạy chương trình với mọi dữ liệu vào có độ dài là n
Ðo thời gian chạy chương trình như thế nào ?
Trang 72 Ðộ phức tạp của giải thuật TOP
3 Cách tính thời gian chạy chương trình TOP
Cách tính thời gian chạy của một chương trình bất kỳ là một vấn đề không đơn giản Nhưng đối với nhiều chương trình ta có thể tính thời gian chạy của nó theo 2 quy tắc sau: ( áp dụng được cho tất cả các chương trình không đệ quy)
Chú ý :
Thời gian chạy của các lện gán, Read, Write là O(1)
Thời gian chạy của một chuỗi tuần tự các lệnh được xác định theo quy tắc cộng Như vậy thời gian này là thời gian thi hành một lệnh nào đó lâu nhất trong chuỗi lệnh (câu lệnh đó được gọi là Câu lệnh tích cực)
Thời gian chạy của cấu trúc If là thời gian lớn nhất để thực hiện các lệnh sau Then hoặc sau Else cộng với thời gian kiểm tra điều kiện Thường thời gian kiểm tra điều kiện
là O(1)
Thời gian thực hiện vòng lặp là tổng (trên tất cả các lần lặp) thời gian thực hiện từng vòng lặp Nói cách khác thời gian này được tính theo quy tắc nhân tức là tích của số lần lặp với thời gian thực hiện của thân vòng lặp
Thời gian gọi thủ tục hay hàm là thời gian thực hiện đoạn chương trình tương ứng với hàm hay thủ tục đó
Ví dụ 1: Câu lệnh: for i: = 1 to n do
for j: = 1 to n do x:= x + 1;
Trang 8Ví dụ 2: Procedure BubbleSort ( Var a: array [ 1 n ] of Integer);
Var i, j, Temp : Integer;
Begin
for i: = 1 to n do for j: = 1 to n do
If a[i] > a[j] then
Trong đó 5 dạng hàm đầu tiên gọi là hàm đa thức, còn 3 dạng hàm cuối gọi là hàm
mũ Một giải thuật mà thời gian thực hiện có dạng là một hàm đa thức thì có thể chấp nhận được
BÀI TẬP CUỐI CHƯƠNG I Bài 1 : Viết các thủ tục thực hiện các phép toán cộng và nhân 2 ma trận, rồi tính thời gian chạy
của các thủ tục này
Bài 2 : Hãy viết chương trình tính gần đúngĠ theo công thức :
Sau đó hãy tính thời gian chạy của chương trình
Bài 3 : Viết chương trình nhập vào một ma trận vuông cấp n Sau đó in ra các phần tử
thuộc các đường chéo song song với đường chéo chính
Phác họa giải thuật của chương trình và tính độ phức tạp của giải thuật
Bài 4 : Hãy tìm một giải thuật để tínhĠ sao cho độ phức tạp của giải thuật sẽ là O(n) và viết chương trình thể hiện giải thuật đã đề ra bằng ngôn ngữ Pascal
Bài 5 : Hãy viết chương trình đổi một số nguyên dương ở hệ thập phân (cơ số 10) sang hệ
nhị phân (cơ số 2) và đánh giá thời gian thực hiện của giải thuật
CHƯƠNG 2 : CÁC KIỂU DỮ LIỆU TRỪU TƯỢNG CƠ BẢN
Trang 9(BASIC ABSTRACT DATA TYPE)
I KHÁI NIỆM VỀ CON TRỎ
4 Cài đặt stack bằng con trỏ
IV CẤU TRÚC HÀNG ĐỢI
3 Thủ tục khởi tạo danh sách liên kết kép rổng
4 Hàm kiểm tra danh sách liên kết kép rổng
5 Thủ tục xen một phần tử vào danh sách liên kết kép
6 Thủ tục xóa một phần tử khỏi danh sách liên kết kép
Trang 10I KHÁI NIỆM VỀ CON TRỎ (Pointer)
Type Tên kiểu = ^ Kiểu dữ liệu;
Var Tên biến : Tên kiểu;
Khai báo trực tiếp :
Var Tên biến : ^ Kiểu dữ liệu;
Ví dụ 1: Type Ref = ^ Nhansu;
• P là biến kiểu con trỏ
• T là kiểu dữ liệu mà con trỏ trỏ tới
• P^ : Cho kết quả là giá trị kiểu T mà nó đang trỏ tới
• @ Chỉ địa chỉ của giá trị mà nó đang trỏ tới
Vậy muốn truy xuất nội dung tại vị trí mà p đang trỏ tới ta dùng P^
Ngoài ra, còn có một con trỏ tổng quát, nó không chỉ đến một kiểu dữ liệu xác định nào cả
Var p : Pointer;
Kiểu dữ liệu con trỏ chiếm 4 bytes trong bộ nhớ, 4 bytes này sẽ chứa địa chỉ
của kiểu dữ liệu mà nó trỏ tới còn bản thân biến mà nó trỏ tới thì chưa có trong bộ nhớ
Ví dụ : Var p : ^ Integer;
i : Integer;
Begin
i : = 5;
p : = @ i; {Hàm lấy địa chỉ của i và gán cho p}
Writeln ('Nội dung mà p đang trỏ tới :', p^);
Writeln ('Giá trị của biến i : ', i);
End
3 Một số hàm thường sử dụng ở biến con trỏ : TOP
a Cấp phát vùng nhớ : New (p)
Trang 11Thủ tục này sẽ cấp phát vùng nhớ động do con trỏ p quản lý và đồng thời gán cho
p địa chỉ đầu của vùng nhớ này
b Thu hồi vùng nhớ đã cấp phát : Dispose (p)
Thủ tục này sẽ hủy bỏ (giải phóng) vùng nhớ do p trỏ tới (quản lý)
c Cấp phát / thu hồi hàng loạt vùng nhớ :
Mark (HeapTop) Realease (HeapTop)
Thủ tục Mark (HeapTop) sẽ gán giá trị của địa chỉ của vùng Heap cho một con trỏ p nào đó Con trỏ p này được dùng như để đánh dấu vị trí đầu của vùng ô nhớ sẽ được giải phóng sau này Sau thủ tục Mark ta có thể dùng hàng loạt thủ tục New để tạo các biến động khác nhau và chúng sẽ chiếm ô nhớ bắt đầu từ vị trí đã được đánh dấu Nếu muốn lấy lại toàn
bộ vùng nhớ đã cấp phát ta chỉ cần dùng thủ tục Realease (HeapTop) Tất nhiên là sau lệnh Realease ta không còn có thể sử dụng các biến động được tạo ra bằng thủ tục New nằm trong vùng ô nhớ vừa được giải phóng
d Thủ tục GetMem (p, k)
Thủ tục này sẽ cấp phát k bytes vùng nhớ do con trỏ p quản lý
e Thủ tục FreeMem (p, k) Thủ tục này sẽ thu hồi vùng nhớ đã được cấp phát bởi thủ tục GetMem
f Các hàm cho biết tình trạng của vùng nhớ :
Hàm MaxAvail : Longint : Hàm cho biết vùng nhớ lớn nhất còn trống trong Heap
Hàm MemAvail : Longint : Hàm cho biết tổng số bytes còn lại trên Heap
Hàm SizeOf (Biến ) : Longint : Cho biết số bytes được cấp phát / thu hồi bởi biến
Ví dụ : P là con trỏ trỏ đến kiểu RecordType nào đó
If MaxAvail >= SizeOf (RecType) then New(p) Else Writeln('Không đủ vùng nhớ cấp phát');
g Các hàm cho biết địa chỉ của một đối tượng trong bộ nhớ :
Hàm Add (x) : Pointer : Cho biết địa chỉ tổng quát của biến x
Hàm Seg (x) : Word : Cho biết địa chỉ segment của biến x
Hàm Ofs (x) : Word : Cho biết địa chỉ Offset của biến x
Hàm Ptr (Seg, Ofs) : Pointer : Trỏ tới địa chỉ seg : Ofs
Writeln (' Nội dung của a là : ', a);
Writeln (' Nội dung p đang trỏ tới là : ', p^);
Trang 12k : Word;
Begin
Writeln ('Vùng nhớ ban đầu',MemAvail,' Bytes trống');
New (p); p : = 'Khoa Su Pham';
k : = SizeOf (p);
Writeln ('Vùng nhớ sau khi khởi động',MemAvail,' Bytes');
GetMem(q, SizeOf(q));
P^ : = q^;
Writeln ('p có kích thưóc là : ',SizeOf(p^),');
Writeln ('q có kích thưóc là : ',SizeOf(q^),');
Writeln ('Nội dung của q là : ',q^,');
Mô hình toán học của danh sách là một tập hợp các phần tử có cùng một kiểu mà
ta gọi là kiểu phần tử (ElementType)
Một số phép toán trên danh sách :
a Hàm MakeNullList ( Var L : List ) - Hàm tạo một danh sách L rỗng
b Hàm EmptyList (L : List) : Boolean - Hàm kiểm tra danh sách rỗng( Hàm cho kết quả là True nếu danh sách rỗng
( Hàm cho kết quả là False nếu ngược lại
c Hàm First ( L : List ) : Position - Hàm cho kết quả là vị trí của phần tử đứng
đầu danh sách L Nếu danh sách rỗng thì ta có First (L) = EndList (L)
d Hàm Next ( p : Position ; L : List ) : Position - Hàm cho kết quả là vị trí của phần tử đứng sau phần tử thứ p trong danh sách L Nếu p là phần tử cuối danh sách thì ta có Next (p, L) = EndList (L)
e Hàm Previous ( p : Position ; L : List ) : Position - Hàm cho kết quả là vị trí của phần tử đứng trước phần tử thứ p trong danh sách L Nếu p là phần tử đầu danh sách thì hàm không xác định
f Hàm Retrieve (p : Position ; L : List ) : ElementType - Hàm cho kết quả là nội dung của phần tử thứ p trong danh sách L Nếu vị trí p không tồn tại thì hàm không xác
định
Trang 13g Thủ tục InsertList (x : ElementType ; p : Position ; Var L : List ) - Thủ tục thực hiện việc xen phần tử x vào vị trí p trong danh sách L
h Thủ tục DeleteList (p : Position ; Var L : List ) - Thủ tục thực hiện việc xóa phần tử tại vị trí p trong danh sách L
i Hàm Locate (x : ElementType ; L : List ) : position - Hàm tìm phần
tử x trong danh sách L Hàm trả về vị trí đầu tiên của phần tử x nếu tìm thấy Nếu không tìm thấy hàm trả về EndList (L)
j Thủ tục PrintList (L : List ) - In danh sách các phần tử trong danh sách L theo thứ tự xuất hiện của các phần tử
Ví dụ 1 : Viết giải thuật sắp xếp một danh sách theo thứ tự tăng dần giả sử thủ tục Swap ( p, q) thực hiện việc đổi chỗ 2 phần tử tại vị trí p và vị trí q
Procedure BubbleSort ( Var L : List );
If Retrieve(p) > Retrieve(q) then
Swap(p, q) Else
Ví dụ 2 : Lặp danh sách các hồ sơ và loại bỏ các hồ sơ trùng lặp
Procedure LoaiBo ( Var L : List );
If Retrieve(p) = Retrieve(q) then DeleteList(q, L) Else
Trang 14Cài đặt danh sách bằng mảng nghĩa là ta dùng một mảng (array) để lưu trữ liên tiếp các phần tử của danh sách bắt đầu từ vị trí đầu tiên của mảng Dĩ nhiên là ta phải ước lượng
số phần tử của danh sách để khai báo số phần tử của mảng thích hợp và nói chung là còn thừa chỗ trống trong mảng, vì vậy ta phải lưu giữ độ dài hiện tại của danh sách Danh sách này cho biết mảng có bao nhiêu phần tử và phần tử nào của mảng còn trống
Elements : array [1 Maxlength] of ElementType;
Last : integer; {Ðộ dài thật sự của danh sách}
End;
Thủ tục khởi tạo danh sách rỗng :
Procedure MakenullList ( Var L : List);
Begin
L.Last : = 0;
End;
Hàm kiểm tra danh sách rỗng :
Function EmptyList ( L : List ) : Boolean;
Begin
EmptyList : = ( L.Last = 0 );
End;
Hàm kiểm tra danh sách đầy :
Function FullList ( L : List ) : Boolean;
Begin
FullList : = ( L.Last > = Maxlenght );
End;
Thủ tục nhập giá trị cho các phần tử của danh sách :
Procedure ReadList (Var L : List);
Var i : position;
Begin
For i := 1 to L Last do
Begin Write (' Nhập phần tử thứ ', i ,' của danh sách : ');
Readln (L.Elements [i]);
End;
End;
Thủ tục xen một phần tử vào danh sách :
Khi xen một phần tử vào danh sách, ta có thể gặp các trường hợp sau đây :
Trang 15Mảng đầy ( độ dài của danh sách = độ dài của mảng ) thì thủ tục sẽ bị lỗi
Ngược lại nếu vị trí p không tồn tại (p < 1 hay p > L Last + 1) thì thủ tục cũng
bị lỗi
Ngược lại nếu vị trí p là hợp lệ thì ta thực hiện các bước sau :
Dời các phần tử từ vị trí p đến cuối danh sách xuống 1 đơn vị
Ðộ dài của danh sách tăng lên 1
Xen phần tử mới vào vị trí p của danh sách
Procedure InsertList (x:ElementType; p:position; Var L:List);
for i:=L.Last downto p do
L.Elements [i+1]:=L.Elements [i];
L.Last:= L.last + 1;
L.Elements[p]:=x;
End;
End;
Thủ tục xóa một phần tử khỏi danh sách :
Khi xóa một phần tử khỏi danh sách, ta có thể gặp các trường hợp sau đây :
Danh sách rỗng thì thủ tục sẽ bị lỗi
Ngược lại nếu vị trí p không tồn tại (p < 1 hay p > L Last + 1) thì thủ tục cũng
bị lỗi
Ngược lại nếu vị trí p là hợp lệ thì ta thực hiện các bước sau :
Dời các phần tử từ vị trí p + 1 đến cuối danh sách lên 1 đơn vị
Ðộ dài của danh sách giảm đi 1 đơn vị
Procedure DeleteList(p : position; Var L : List);
Hàm tìm kiếm một phần tử trong danh sách :
Hàm Locate( x, L ) tìm phần tử x trong danh sách L và cho kết quả là phần tử
đầu tiên nếu tìm thấy, còn nếu không tìm gûặp thì hàm cho kết quả là EndList (L) (tức là Last + 1)
Giải thuật đơn giản như sau : Bắt đầu từ phần tử đầu tiên, rồi lần lượt duyệt qua các phần
tử tiếp theo cho đến khi tìm được phần tử cần tìm (đầu tiên) Trả kết quả về cho hàm là vị trí của của phần tử được tìm thấy
Function Locate ( x : ElementType; L : List ) : position;
Var i : position;
Begin
i : = 1;
Trang 16While (i <= L Last) and (L.Elements [i]<> x) do
i : = i + 1;
Locate : = i;
End;
Thủ tục hiển thị các phần tử của danh sách :
Procedure WriteList ( L : List);
Mô tả danh sách liên kết :
Trang 17Ðể 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
Elementss 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
• 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 :
Function EmptyList( Header : List): Boolean;
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 );
Trang 18• 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;
Trang 19Muố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óa
Procedure DeleteList ( p : Position );
Var Temp : Position;
• 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
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
Trang 20Ý 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ử thứ 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áy
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à Available 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ày Khi xóa 1 phần tử, ta cho ô bị xóa nối vào đầu Available
ArrayOfNodes = Array [1 Maxlenght] of NodeType;
Var Node: ArrayOfNode;
Available: CursorType; {Chỉ đến nút tự do đầu tiên} List: CursorType; { Chỉ đến nút đầu danh sách }
• 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
Trang 21Node[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);
• 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 );
Trang 22• 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ày Hàm cho kết quả kiểu Boolean tùy theo việc chuyển thành công hay thất bại 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;
• Thủ tục xen một phần tử vào danh sách :
Muốn xen một phần tử vào danh sách, ta cần biết vị trí trước chỗ xen rồi ta chuyển ô đầu Available vào vị trí cần xen (sau p) Tuy nhiên, nếu ta xen vào đầu danh sách thì không
Trang 23có vị trí 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 Ở đây ta cho p = 0
Procedure InsertList (x : ElementType; p : CursorType; Var L : CursorType);
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 Available 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);
If Move (Node[p].Next, Available) then
Writeln ('Da xoa')
Else Writeln ('Loi')
End;
III CẤU TRÚC CHỒNG / NGĂN XẾP (STACK)
1 Ðịnh nghĩa :
TOP
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)
2 Các phép toán trên Stack
TOP
a Thủ tục MakeNullStack (Var S : Stack) : Tạo một Stack rỗng
b Hàm/thủ tục Top (S : Stack) : Trả về kết quả là phần tử tại đỉnh của Stack
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
e 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ại
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ại
Trang 24Ví 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 giải thuật cho thủ tục này theo 2 cách sau:
Repeat Read (c);
If c < > '@' then Push (c, S);
Const Maxlength = .; { Ðộ dài mảng }
Type ElementType = ; { Kiểu phần tử }
Elements : array[1 Maxlength] of ElementType;
End;
• Thủ tục khởi tạo Stack rỗng :
Procedure MakeNullStack ( Var S : Stack);
Begin
Trang 25S.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;
• Thủ tục xóa một phần tử khỏi Stack :
Procedure Pop ( Var S: Stack);
• Thủ tục thêm một phần tử vào Stack :
Procedure Push (x:ElementsType; Var S:Stack);
Trang 264 Cài đặt Stack bằng con trỏ : TOP
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;
• Thủ tục xóa một phần tử khỏi Stack :
Procedure Pop ( Var S: StackType; Var x: ElementType);
Var TempPtr : PointerType;
• 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;
Trang 27Stack := TempPtr;
End;
• Hàm cho phần tử đầu Stack :
Function Top (Stack : StackType) : ElementType;
{ 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);
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
a 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
Trang 28d Thủ tục DeQueue (Var Q : Queue) : Xóa một phần tử đầu hàng
e 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ại
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ại
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
luôn 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ử }
Elements : array[1 Maxlength] of ElementType;
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
EmptyQ := (Q.Front = 0);
End;
• Hàm kiểm tra Queue đầy :
Function FullQ (Q : Stack):Boolean;
Begin
FullQ := (( Q.rear - Q.Front + 1) = Maxlenght);
End;
Trang 29• Hàm ütruy xuất phần tử đầu hàng :
Function Front ( Q: Queue ) : ElementsType;
Begin
Front := (Q.Elements [Q.Front]);
• 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 day'); End;
• Hàm kiểm tra Queue đầy :
Function FullQ (Q : Stack):Boolean;
Begin
FullQ := (( Q.rear - Q.Front + 1) mod Maxlenght = 0);
End;
Trang 30• 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 day');
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
Trang 31Type ElementType = ; { Kiểu phần tử }
Cell = Record Elements : ElementType;
• Thủ tục khởi tạo Queue rỗng :
• Hàm kiểm tra Queue rỗng :
Function EmptyQ (Q : Queue) : Boolean;
• Thủ tục xen một phần tử vào cuối hàng :
Procedure EnQueue (x : ElementType; Var Q: Queue);
Trang 32Procedure DeQueue ( Var Q: Queue);
Var Temp : CellType;
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 danh sách là một ô Ở mỗi ô ta dùng 2 con trỏ ở 2 đầu :
Con trỏ Next chỉ đến nút tiếp theo
Con trỏ Prevous chỉ đến nút liền trước
Ngoài ra còn có một trường dữ liệu dùng để lưu trữ dữ liệu của ô đang xét
Trang 332 Khai báo: TOP
Type ElementType = ; { Kiểu phần tử trong danh sách }
Var List = DList;
3 Thủ tục khởi tạo danh sách liên kết kép rỗng :
5 Thủ tục xen một phần tử vào danh sách liên kết kép : TOP
Procedure InsertList ( x : ElementsType; p : DList; var List : DList);
Var Temp : DList;
Begin
New (Temp);
Temp^.Elements := x; Temp^.Next := p^.Next;
If p^.Next < > Nil then
Trang 34Procedure DeleDlist (p : DList; Var List : Dlist);
p^.Previous^.Next := p^.Next; Dispose (p);
End Else Writeln ('Danh sach rong'); End;
BÀI TẬP CUỐI CHƯƠNG II Bài 1 : Hãy giải thích vì sao thời gian chạy của thủ tục xóa một phần tử trong danh sách
(cài đặt bằng mảng) trong trường hợp tốt nhất là O(1) và xấu nhất là O(n)
Bài 2 : Viết chương trình nhập vào một danh sách các số nguyên, in danh sách theo yêu
cầu : In danh sách ra màn hình theo thứ tự như lúc nhập và thứ tự ngược lúc nhập
Bài 3 : Viết thủ tục sắp xếp một danh sách các số nguyên trong các trường hợp :
1 Danh sách được cài đặt bằng mảng
2 Danh sách được cài đặt bằng con trỏ
Bài 4 : Viết các thủ tục thực hiện yêu cầu sau :
1 Thêm một phần tử vào một danh sách đã có thứ tự (tăng hoặc giảm) sao cho ta được một danh sách mới vẫn bảo đảm thứ tự
2 Xóa một phần tử trong danh sách liên kết đã có thứ tự
Bài 5 : Viết thủ tục trộn 2 danh sách đã có thứ tự (tăng hoặc giảm) để được một danh
sách mới vẫn bảo đảm thứ tự ban đầu
Bài 6 : Viết thủ tục nhập vào một danh sách các số nguyên, loại bỏ các phần tử trùng
nhau Sau đó sắp xếp danh sách theo thứ tự tăng (hoặc giảm) In danh sách sau khi đã được sắp xếp
Bài 7 : Ða thứcĠ được lưu trữ trong máy tính dưới dạng một danh sách liên kết mà mỗi
phần tử của danh sách là một Record gồm có 3 trường : lưu trữ hệ số, số mũ và trường Next là một con trỏ chỉ đến phần tử kế tiếp Chú ý rằng cách lưu trữ đảm bảo thứ tự số mũ giảm dần của từng hạng tử trong đa thức Hãy viết chương trình thực hiện các công việc sau :
1 Lưu trữ đa thức
2 Cộng 2 đa thức
3 Tính độ phức tạp tính toán của giải thuật của thủ tục cộng 2 đa thức
Bài 8 : Viết chương trình nhập vào một xâu ký tự, xếp chúng vào một danh sách liên kết,
dùng phím $ để báo hết xâu Duyệt qua danh sách và in danh sách theo thứ
tự đảo ngược lại lúc nhập
Bài 9 : Viết chương trình nhập vào một chuỗi dấu ngoặc và kiểm tra xem chuỗi dấu
ngoặc đó đúng hay sai theo quan điểm toán học
Bài 10 : Một danh sách liên kết các ký tự được tạo ra bằng cách dùng mảng như sau:
Trang 35Bài 11 : Hình dưới đây biểu diễn một mảng Space gồm có 10 phần tử , dùng để diễn tả danh sách bằng con nháy vad 2 danh sách L1, L2 đã có trong mảng :
Bài 12 : Ðể tiện việc truy nhập vào danh sách, người ta tổ chức một danh sách liên kết
có dạng như hình vẽ sau :
Gọi là danh sách liên kết vòng Hãy cài đặt một danh sách nối kết vòng như vậy
CHƯƠNG 3 : CẤU TRÚC CÂY
I ỊNH NGHĨA VÀ CÁC KHÁI NIỆM CƠ BẢNĐ
1 ịnh nghĩaĐ
2 Các khái niệm cơ bản
3 Các phép duyệt cây quan trọng
4 Cây có nhản và cây biểu thức
Trang 365 Quy tắc biểu diễn một biểu thức toán học trên cây
II KIỂU DỮ LIỆU TRỪU TƯỢNG CÂY
1 Các phép toán trên cây
2 Cài đặt cây
III CÂY NHỊ PHÂN
1 ịnh nghĩaĐ
2 Tính chất
3 Cài đặt cây nhị phân
IV GIẢI THUẬT MÃ HÓA HUFFMAN
1 Đặt vấn đề
2 Giải thuật mã hóa HuffMan
V CÂY TÌM KIẾM NHỊ PHÂN
1 ịnh nghĩaĐ
2 Cài đặt cây tìm kiếm nhị phân
I ÐỊNH NGHĨA VÀ CÁC KHÁI NIỆM CƠ BẢN
Cây (Trees) là một tập hợp hữu hạn các phần tử gọi là nút cây (Node), trong đó có
một nút đặc biệt gọi là nút gốc (Root) Trên tập hợp các nút này có một quan hệ phân cấp gọi
là quan hệ "cha - con"
Một nút có thể có kiểu bất ký, ta thường biểu diễn nút bằng tên nút Tên nút có thể là
một ký tự, một số hay một chuỗi và có thể được ghi trong một vòng tròn Ta quy ước biểu diễn cây như sau: Ta viết nút cha ở dòng trên, các nút con ở dòng dưới và quan hệ "cha-con" được biểu diễn bằng một đoạn thẳng nối liền 2 nút
Trang 37Ngoài ra ta có thể định nghĩa cây một cách đệ qui như sau :
Một nút (đơn độc) là một cây và nút đó cũng là nút gốc của cây
Nếu ta có n là một nút và T1, T2, , Tk là các cây với n1, n2, , nk lần lượt là các nút gốc của các cây con thì ta có thể xây dựng một cây mới bằng cách cho n trở thành cha của các nút n1, n2, , nk; Nghĩa là trên cây mới này n là nút gốc còn các cây T1, T2, , Tk là các cây con của nút n
Ðể tiện việc quản lý, người ta cho phép tồn tại một cây không có nút nào, mà
ta gọi là cây rỗng (Null tree)
Trang 38Anh em ruột: Các nút con của cùng một nút cha được gọi là các nút anh em ruột
Ðường đi: Cho một dãy các nút n1, n2, , nk sao cho ni là nút cha của ni+1 thì
ta nói n1 ( n2 ( ( nk là một đường đi từ nút n1 ( nk Ðộ dài của đường đi bằng số nút trên đường đi trừ 1 hay bằng số cạnh trên đường đi
Nút gốc (Root): Là nút không có nút cha
Nút lá (leaf): Là nút không có nút con
Chiều cao của một nút: Là độ dài đường đi từ nút đó đến nút lá xa nhất
Ðộ sâu của một nút (mức của một nút): Là chiều dài đường đi từ nút gốc đến nút đó
Chiều cao của một cây: Là chiều cao của nút gốc
Bậc của một nút: Là số nút con của nút đó
Bậc của một cây: Là bậc cao nhất của các nút trong cây
+ Cây có bậc n được gọi là cây n - phân
+ Rừng là một tập hợp hữu hạn các cây phân biệt
Nếu ta phân biệt thứ tự các nút con của một cây thì ta gọi cây đó là cây có thứ tự Ngược lại là cây không có thứ tự
Thứ tự của các nút trong một cây có thứ tự được quy ước từ trái sang phải và
từ trên xuống dưới
Nếu A và B là 2 nút anh em ruột và A ở bên trái của B thì các nút con cháu của
A là nút bên trái tất cả các nút con cháu của B
Ðể xác định nút trái (phải) của một nút n, ta vẽ một đường đi từ nút gốc đến nút n Nút nào nằm bên trái của đường đi thì sẽ là nút trái của nút đó, nút nào nằm bên phải của đường đi thì sẽ là nút phải của nút đó
Trang 39Danh sách liệt kê các nút theo thứ tự xử lý được gọi là danh sách duyệt cây
Duyệt tiền tự (PreOrder)
Duyệt trung tự (InOrder)
Duyệt hậu tự (PostOder)
Ðịnh nghĩa các phép duyệt cây: Ta có thể định nghĩa phép duyệt cây tổng quát bằng đệ quy như sau :
Cây rỗng : Danh sách duyệt tiền tự, trung tự, hậu tự là danh sách rỗng
Cây có một nút : Danh sách duyệt tiền tự, trung tự, hậu tự chính là nút đó
Trang 40Ví dụ : Cho một cây như hình sau :
Ta thường lưu trữ kết hợp một nhản (Label) hoặc một giá trị (value) với một nút của cây Như vậy, nhản của một nút không phải là tên của nút mà là giá trị được lưu tại nút đó Nhản của một nút còn được gọi là khóa của nút
Ví dụ : Cây sau đây sẽ biểu diễn cho biểu thức (a + b) * (a - c)
Trong cây trên thì n1, n2, , n7 là các tên nút còn *, +, -, a, b, c là các nhản của nút
5 Quy tắc biểu diễn một biểu thức toán học trên cây TOP
Mỗi nút lá sẽ biểu diễn cho một toán hạng đơn độc
Mỗi nút trung gian sẽ biểu diễn cho một toán tử Giả sử nút n biểu diễn cho toán tử 2 ngôi, nút con bên trái biểu diễn cho biểu thức E1, nút con bên phải biểu diễn cho biểu thức E2 thì nút n sẽ biểu diễn cho biểu thức Eı E2