BÀI 1.TỔNG QUAN VỀ GIẢI THUẬT VÀ CẤU TRÚC DỮ LIỆU121.Thuật Toán122.Một số quy ước133.Độ Phức Tạp Của Thuật Toán14BÀI 2. DANH SÁCH ĐẶC (MẢNG – ARRAY LIST)191.Khái niệm:192.Các thao tác xử lí trên mảng193.Ưu khuyết điểm của mảng25BÀI 3. DANH SÁCH LIÊN KẾT (LIST)271.Danh sách liên kết đơn271.1.Khái niệm:271.2.Các thao tác xử lí cơ bản trên DSLK291.3.Ưu khuyết điểm của DSLK482.Danh sách liên kết kép (double list)482.1.Khái niệm:482.2.Các thao tác xử lí cơ bản trên DSLK kép503.Danh sách liên kết vòng (circular list)503.1.Khái niệm danh sách liên kết vòng:503.2.Các thao tác xử lí cơ bản trên DSLK vòng50BÀI 4. STACK – QUEUE (Ngăn Xếp – Hàng Đợi)511.Stack511.1.Khái niệm511.2.Stack (Danh sách đặc)521.2.1.Các thao tác xử lý trên stack521.3.Stack (Danh sách liên kết)561.4.Ứng dụng562.Queue592.1.Khái niệm:592.2.Queue (Danh sách đặc)602.2.1.Các thao tác xử lý trên queue602.2.2.Hàng đợi vòng (Circular Queue)652.3.Dùng danh sách liên kết tạo queue702.4.Ứng dụng70BÀI 5. ĐỆ QUI (Recursion)711.Khái quát về đệ qui712.Thi hành hàm tính giai thừa723.Phân loại đệ qui734.Khử đệ qui785.Bài toán tháp Hà Nội81BÀI 6. TÌM KIẾM – SẮP XẾP (Search Sort)831.Tìm Kiếm (Search)831.1.Tìm kiếm tuyến tính841.2.Tìm kiếm nhị phân( Binary Search)862.Sắp xếp (Sort)892.1.Đổi chỗ trực tiếp – Interchange Sort892.2.Nổi bọt – Bubble Sort942.3.Chọn trực tiếp – Selection Sort982.4.Chèn trực tiếp – Insertion Sort1002.5.Chèn nhị phân – Binary Insertion Sort1032.6.Shaker Sort1042.7.Shell Sort1092.8.Heap Sort1132.9.Quick Sort1182.10.Merge Sort1232.11.Radix Sort ( Sắp xếp theo cơ số)1292.12.Sắp xếp ngoại (External Sort)139BÀI 7. CÂY (TREE) CÂY NHỊ PHÂN (BINARY TREE) CÂY NHỊ PHÂN TÌM KIẾM (BINARY SEARCH TREE) CÂY NHỊ PHÂN CÂN BẰNG AVL (ADELSON – VELSKII – LANDIS)1431.Cây (Tree)1432.Cây nhị phân1453.Cây Nhị Phân Tìm Kiếm (Binary Search Tree)1614.Cây Nhị Phân (tìm kiếm) Cân Bằng AVL (Adelson – Velskii – Landis)1715.Cây đỏ đen (red black tree)1856.Cây tập hợp1867.Cây biểu thức187BÀI 8. BẢNG BĂM (Hash Function Hash Table)1891.Hàm băm (Hash Function) Bảng băm (Hash Table)1892.Giải quyết sự đụng độ trên bảng băm191BÀI 9. ĐỒ THỊ (Graph)1941.Các khái niệm cơ bản1942.Đồ thị Euler và đồ thị Hamilton2033.Biểu diễn đồ thị2044.Các thao tác trên đồ thị2085.Cây2196.Tô màu đồ thị2257.Topological Sorting226TÀI LIỆU THAM KHẢO228
Trang 1BỘ NÔNG NGHIỆP VÀ PHÁT TRIỂN NÔNG THÔN
TRƯỜNG CAO ĐẲNG NGHỀ CÔNG NGHỆ VÀ NÔNG LÂM NAM BỘ
KHOA CÔNG NGHỆ THÔNG TIN
Địa chỉ: QL 1K, Phường Bình An, TX Dĩ An, Tỉnh Bình
Dương Email: it.svoctaf@gmail.com
Trang 2Bài 1.
TỔNG QUAN VỀ GIẢI THUẬT VÀ CẤU TRÚC DỮ
LIỆU
1 Thuật Toán
Một dãy hữu hạn các chỉ thị có thể thi hành để đạt mục tiêu đề ra
Ví dụ: Thuật toán tính tổng tất cả các số nguyên dương nhỏ hơn n:
Bước 1: S=0, i=0;
Bước 2: nếu i<n thì s=s+i;
Ngược lại: qua bước 4;
Biểu Diễn Bằng Mã Giả
» Ngôn ngữ tựa ngôn ngữ lập trình:
Trang 33 Độ Phức Tạp Của Thuật Toán
• Một thuật toán hiệu quả:
Trang 4 Thời gian sử dụng CPU thấp, …
• Phân tích độ phức tạp thuật toán:
Phương pháp thực nghiệm
Phương pháp xấp xỉ toán học
Phương Pháp Thực Nghiệm
• Cài thuật toán rồi chọn các bộ dữ liệu thử nghiệm
• Thống kê các thông số nhận được khi chạy các bộ dữ liệu đó
• Ưu điểm: Dễ thực hiện.
• Nhược điểm:
Chịu sự hạn chế của ngôn ngữ lập trình
Ảnh hưởng bởi trình độ của người lập trình
Chọn được các bộ dữ liệu thử đặc trưng cho tất cả tập các dữ liệu vào củathuật toán: khó khăn và tốn nhiều chi phí
Phụ thuộc vào phần cứng
Phương Pháp Xấp Xỉ
• Đánh giá giá thuật toán theo hướng xấp xỉ qua các khái niệm O()
• Ưu điểm: Ít phụ thuộc môi trường cũng như phần cứng hơn.
• Nhược điểm: Phức tạp.
• Các trường hợp độ phức tạp quan tâm:
Đơn vị đo thời gian thực hiện:
Đơn vị của T(n) không phải là đơn vị đo thời gian bình thường như giờ, phút giây mà thường được xác định bởi số các lệnh được thực hiện trong một máy tính lý tưởng
- Trường hợp tốt nhất (phân tích chính xác)
- Trường hợp xấu nhất (phân tích chính xác)
- Trường hợp trung bình (mang tích dự đoán)
Trang 5Cách tính độ phức tạp:
Gồm có qui tắc cộng, qui tắc nhân, qui tắc tổng quát để phân tích một chươngtrình, độ phức tạp của chương trình có gọi chương trình con không đệ qui, độ phức tạpcủa chương trình có đệ qui
Qui tắc cộng:
Nếu T1(n) và T2(n) là thời gian thực hiện của hai đoạn chương trình P1 và P2;
và T1(n)=O(f(n)), T2(n)=O(g(n) thì thời gian thực hiện của đoạn hai chương trình
đó nối tiếp nhau là T(n)=O(max(f(n),g(n)))
Ví dụ:
- Lệnh gán x:=15 có O(1)
- Lệnh đọc dữ liệu READ(x) có O(1)
Vậy thời gian thực hiện cả hai lệnh trên nối tiếp nhau là O(max(1,1))=O(1)
Qui tắc nhân:
Nếu T1(n) và T2(n) là thời gian thực hiện của hai đoạn chương trình P1và P2 và
T1(n) = O(f(n)), T2(n) = O(g(n)) thì thời gian thực hiện của đoạn hai đoạn chương
trình đó lồng nhau là T(n) = O(f(n).g(n))
Qui tắc tổng quát để phân tích một chương trình:
-Thời gian thực hiện của mỗi lệnh gán, READ, WRITE là O(1)
- Thời gian thực hiện của một chuỗi tuần tự các lệnh được xác định bằng qui 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
- Thời gian thực hiện cấu trúc IF là thời gian lớn nhất thực hiện lệnh sau THEN hoặcsau 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:Thời gian thực hiện vòng lặp là tổng (tất cả các lần lặp)thời gian thực hiện thân vòng lặp Nếu thời gian thực hiện thân vòng lặp không đổi thìthời gian thực hiện vòng lặp là tích của số lần lặp với thời gian thực hiện thân vòng lặp
Ví dụ: Tính thời gian thực hiện của đoạn chương trình
procedure Bubble (var a: array[1 n] of integer);
var i,j,temp: integer;
begin
{1} for i:=1 to n-1 do
{2} for j:=n downto i+1 do
{3} if a[j-1]>a[j] then begin
Trang 6{4} temp:=a[j-1];
{5} a[j-1]:=a[j];
{6} a[j]:=temp; end;
end;
- Cả ba lệnh đổi chỗ {4} {5} {6} tốn O(1) thời gian, do đó lệnh {3} tốn O(1).
- Vòng lặp {2} thực hiện (n-i) lần, mỗi lần O(1) do đó vòng lặp {2} tốn
O((n-i).1)=O(n-i).
- Vòng lặp {1} lặp (n-1) lần vậy độ phức tạp của giải thuật là:
Độ phức tạp của chương trình có gọi chương trình con không đệ qui
Giả sử ta có một chương trình gọi các chương trình con theo sơ đồ sau:
Chương trình A gọi hai chương trình con là B và C, chương trình B gọi hai
chương trình con là B1 và B2, chương trình B1 gọi hai chương trình con là B11 và B12 Để tính thời gian thực hiện của A, ta tính theo các bước sau:
-Tính thời gian thực hiện của C, B2, B11 và B12
-Tính thời gian thực hiện của B1
-Tính thời gian thực hiện của B
-Tính thời gian thực hiện của A
Phân tích các chương trình đệ quy
Có ba phương pháp giải phương trình đệ quy:
- Phương pháp truy hồi
Thành lập phương trình đệ quy rồi giải phương trình đệ
quy
Trang 7- Phương pháp đoán nghiệm.
- Lời giải tổng quát của lớp các phương trình đệ quy
Trang 9Trình tự thực hiện
1 Tạo phương thức khoi_tao
void khoi_tao(int a[], int n){
}
Dùng hàm không kiểu, đối số là mảng 1 chiều, biến n là số phần
tử trong mảng
2 Mã hóa thân phương thức
for(int i=0;i<n;i++){
printf(“Phan tu thu[ %d]: \n”, i);
scanf(“%d”,&a[i])
;}
Biến chạy i, câu lệnh lặp, hàm xuất printf, hàm nhập scanf
Xuất mảng
Duyệt tất cả các phần tử có được trong mảng một chiều
void khoi_tao(int a[], int n)
{
for(int i=0;i<n;i++)
{
Trang 10Trình tự thực hiện
1 Tạo phương thức duyệt
mảng xuat. void xuat(int a[],int n)
{}
Dùng hàm không kiểu, đối số là mảng 1 chiều, biến n là số phần
Trang 11Dùng hàm không kiểu, đối số là mảng 1 chiều, biến n là số phần
tử trong mảng
2 Mã hóa thân phương thức
int x, k;
printf(“Phan tu can chen:\n”);
Xóa một phần tử của mảng
void xoa(int a[], int &n)
{
int k;
Trang 12Trình tự thực hiện
1 Tạo phương thức xóa
mảng xoa. void xoa(int a[], int &n)
{}
Dùng hàm không kiểu, đối số là mảng 1 chiều, biến n là số phần
Tìm kiếm một phần tử trong mảng (while)
12
Nếu xóa một phần tử tại vị trí thứ k(0 ≤ k < n) thì các phần tử từ a[k+1] đến a[n-1] được dichuyển về trước một vị trí
Tìm xem phần tử x đầu tiên có nằm trong mảng
a kích thước n hay không?
Trang 13Dùng hàm có kiểu dữ liệu trả
về , đối số là mảng 1 chiều, biến n là số phần
tử trong mảng và giâ trị tìm kiếm x
2 Mã hóa thân phương thức
else
Biến chạy i, câu lệnh lặp while, câu lệnh điều kiện if
Trang 14Dùng hàm có kiểu dữ liệu trả
về , đối số là mảng 1 chiều, biến n là số phần
tử trong mảng và giâ trị tìm kiếm x
2 Mã hóa thân phương thức
for (int i = 0; i < n; i++)
if (a[i] == x) return i;
Biến chạy i, câu lệnh lặp for, câu lệnh điều kiện if
int TimKiem_for(int a[], int n, int x)
Trang 15return -1;
Sắp xếp mảng thành tăng dần
Trình tự thực hiện
1 Tạo phương thức SapXep
void SapXep(int a[], int n) {
}
Dùng hàm không
có kiểu dữ liệu trả về , đối số là mảng 1 chiều, biến n là số phần
Trang 162 Mã hóa thân phương thức
for (int i = 0; i < n – 1; i++) {
for (int j = i + 1; j < n; j++) {
if (a[i] > a[j]) // “<“ neu sap xep giam
HoanVi(a[i], a[j]);
Biến chạy i, câu lệnh lặp ngoài, câu lệnh lặp lồng,câu lệnh điều kiện if và thủ tục hoán vị
3 Ưu - khuyết điểm của mảng
Ưu điểm :
Dễ cài đặt và truy xuất các phần tử dữ liệu
Có khả năng truy xuất ngẫu nhiên
Khuyết điểm:
Trước khi sử dụng cần phải xác định trước số phần tử mảng Không phù hợp
cho các bài toán có số lượng phần tử thay đổi
Khó khăn trong các thao tác chèn và xóa một
phần tử bất kỳ trong mảng
(*) Cài đặt và chạy chương trình
Trang 17Bài 3.
DANH SÁCH LIÊN KẾT (LIST)
1. Danh sách liên kết đơn
Trang 18Cấu trúc dữ liệu (CTDL) của 1 nút
Trình tự thực hiện
1 Tạo cấu trúc Node
struct Node {
};
Dùng cấu trúcstruct
2 Thành phần cấu trúc
int key;
Node *next;
Thành phần dữliệu và thànhphần con trỏ
Trang 19Trình tự thực hiện
1 Tạo cấu trúc danh sách
{ };
Dùng cấu trúcstruct
2 Thành phần cấu trúc
Node *head, *tail; Dùng 2 thànhphần con trỏ đầu
và cuối
1.2 Các thao tác xử lí cơ bản trên DSLK
Khởi tạo danh sách
Trình tự thực hiện
void Initial( List &l) {
l.head = l.tail = NULL;
Trang 20{ }
trả về , đối số là một danh sách liên kết
2 Mã hóa thân phương thức
l.head = l.tail = NULL; Dùng con trỏ đầu,con trỏ cuối
Kiểm tra danh sách có rỗng hay không
Trình tự thực hiện
1 Tạo phương thức isEmpty
int isEmpty(List l) {
}
Dùng hàm có kiểu dữ liệu trả
về , đối số là một danh sách liên kết
2 Mã hóa thân phương thức
return (l.head==NULL); Dùng con trỏ đầu.int isEmpty(List l)
{
Trang 21Thêm 1 nút vào đầu danh sách
void Head_Insert (List &l, int x)
Trang 22l.head= p;
l.tail= p;
}
Dùng toán tử new, con trỏ đầu, con trỏ cuối, biến con trỏ tạm p
3 Khi danh sách liên kết
không rỗng p->next= l.head;
l.head= p;
Dùng toán tử new, con trỏ đầu, biến con trỏ tạm p
Thêm 1 nút vào cuối danh sách
Trang 23void Tail_Insert (List &l, int x)
Trang 24l.head = l.tail= p;
}
Dùng toán tử new, con trỏ đầu, con trỏ cuối, biến con trỏ tạm p
3 Khi danh sách liên kết không rỗng l.tail->next= p;
l.tail= p;
Dùng toán tử new, con trỏ cuối,biến con trỏ tạm p
Thêm một phần tử vào sau một phần tử được trỏ bởi q
Trang 25Dùng hàm không
có kiểu dữ liệu trả về , đối số là một danh sách liên kết, giá trị chèn, con trỏ phụ
2 Khi danh sách liên kết
rỗng p->key= x;
p->next= NULL;
if(isEmpty(l)) {
l.head= l.tail= p;
}
Dùng toán tử new, con trỏ đầu, con trỏ cuối, biến con trỏ tạm p
3 Khi danh sách liên kết
không rỗng p->next=q->next; Dùng toán tử new, con trỏ cuối,
biến con trỏ tạm
Trang 26if(q==l.tail) // nut q
la nut taill.tail= p; //cap nhatlai tail
Thêm một phần tử vào sau một phần tử có khóa k
void Insert_After_K (List &l, int k, int x)
{
Node *p;
for( p=l.head; p!= NULL; p=p->next)
Khi DSLK rỗng hay không rỗng
Trang 27Dùng hàm không
có kiểu dữ liệu trả về , đối số là một danh sách liên kết, giá trị chèn, giá trị phụ k
Tạo danh sách liên kết
void Build_List (List &l)
{
int x;
char tiep;
do
Trang 28Dùng hàm không
có kiểu dữ liệu trả về , đối số là một danh sách liên kết
printf(“ Muon nhap lieu cho
Dùng chuỗi phụ tiếp, vòng lặp, hàm nhập xuất printf, scanf, hàmtiếp nhận kí tự getch(), thủ tục chèn đầu
- Xây dựng DSLK dựa trên các thao tác chèn: Chèn đầu hoặc chèn cuối hoặc chèn giữa.
- Số lượng phần tử danh sách phụ thuộc vào biến “tiep”.
Kết nối từng node danh sách
Trang 29danh sach nua khong?\n”); tiep= getch();
} while((tiep==‘Y’)||(tiep==‘y’));
In (duyệt) danh sách (DSLK) ra màn hình
TH: DSLK rỗng
TH: DSLK không rỗng
(Kĩ thuật: Dùng p làm con trỏ chạy lần lượt
void Print( List l) {
if (isEmpty(l)) {
if(isEmpty(l))
printf(“ DSLK rong”);
}
Trang 30Trình tự thực hiện
TIỆN
1 Tạo phương thức Print
void Print( List l) {
}
Dùng hàm không có kiểu
dữ liệu trả về , đối số là một danh sách liên kết
2 Khi danh sách liên kết
printf(“ DSLK rong”);
Dùng hàm kiểmtra rỗng, lệnh xuất
3 Khi danh sách liên kết
while(p!=NULL) {
printf(“Gia tri khoa: %d\n”, p->key);
p=p->next;
Dùng con trỏ đầu, con trỏ tạm p
Trang 31- Khi DSLK không rỗng (Trong trường
void Head_Delete(List &l)
Trang 321 Tạo phương thức
Head_Delete void Head_Delete(List &l)
{ }
Dùng hàm không
có kiểu dữ liệu trả về , đối số là một danh sách liên kết
2 Khi danh sách liên kết
return;
Dùng hàm kiểm tra rỗng
3 Khi danh sách liên kết
l.head=l.head->next;
p->next=NULL;
if(l.head==NULL)l.tail= NULL;
delete p;
Dùng con trỏ đầu,cuối, con trỏ tạm p
Xóa 1 nút ở cuối danh sách
Trang 332 Khi danh sách liên kết
return;
Dùng hàm kiểm tra rỗng
Có 2 trường hợp:
- Khi DSLK rỗng
- Khi DSLK không rỗng (Trong trường
hợp này nếu DSLK chỉ có một nút thì ta xóa riêng)
void Tail_Delete(List &l)
Trang 343 Khi danh sách liên kết
delete p;
l.head=l.tail=NULL;
}
Dùng con trỏ đầu,cuối, con trỏ tạm
l.tail=p;
p->next=NULL;
delete q;
Dùng con trỏ cuối, con trỏ tạm
Trang 35Trình tự thực hiện
TT
BƯỚC
PHƯƠNGTIỆN
1 Tạo phươngthức
Delete_After
void Delete_After(List &l, Node *q)
{ }
Dùng hàm không có kiểu dữ liệu trả về, đối số là một danh
void Delete_After(List &l, Node *q)
Trang 36kết, con trỏ q.
Dùng hàm kiểm tra rỗng
3 Khi danh
sách liên kết
không rỗng
if(q!=NULL){
p=q->next; // p nut can xoaif(p!=NULL)
Dùng con trỏ cuối, con trỏ tạm p, q, toán tử delete
Xóa 1 nút có khóa k
Trang 37- Khóa x được tìm thấy tại nút đầu.
- Khóa x được tìm thấy không phải tại nút đầu
void Delete_K(List &l, int x)
{
Node *p=l.head, *q;
while((p!=NULL)&&(p->key!=x)) {
q=p; p=p->next;
}
Trang 38{ }
một danh sách liên kết
2 Node cần xóa là node
3 Node cần xóa không phải
là node đầu Node *p=l.head, *q;
while((p!=NULL)&&(p->key!=x)) {
q=p; p=p->next;
} if(l.head->key != x ) {
Delete_After(l, q);
}
Dùng con trỏ đầu, con trỏ tạm
p, q, toán tử delete, phương thức xóa sau
Hủy toàn bộ danh sách
Trang 39Dùng hàm không
có kiểu dữ liệu trả về , đối số là một danh sách liên kết
2 Khi danh sách liên kêt
return;
Dùng hàm kiểm tra rỗng
3 Khi danh sách liên kết
không rỗng while(l.head!=NULL)
{ Head_Delete(l);
}
Dùng con trỏ đầu,phương thức xóa đầu, vòng lặp
Tìm kiếm phần tử trong DSLK (tìm kiếm tuần tự_Linear Search)
void Delete_List (List &l)
(Lần lượt xóa đầu cho đến khi con trỏ đầu NULL)
Trang 40Trình tự thực hiện
1 Tạo phương thức Search
Node *Search (List l, int x)
{ }
Dùng hàm có kiểu dữ liệu trả
về , đối số là một danh sách liên kết, giá trị tìm kiếm
2 Khi danh sách liên kêt
return NULL;
Dùng hàm kiểm tra rỗng
TH: DSLK rỗng Không tìm thấy, nên hàm Searchnhận NULL
Trang 413 Khi danh sách liên kết
while ((p!=
NULL)&&(p->key!=
x)){
p=p->next;
} return p;
Dùng con trỏ đầu,vòng lặp, con trỏ tạm p
Sắp xếp các phần tử trong DSLK (tăng dần)
Trình tự thực hiện
1 Tạo phương thức Sort
void Sort (List &l) Dùng hàm không có kiểu dữ liệu
void Sort (List &l)
{
Node *p, *q;
if (isEmpty(l))
return ;
for (p=l.head; p!= l.tail; p=p->next)
for(q=p->next; q!= NULL; q=q->next)
TH:DSLK rỗng Có cần sắp xếp không?TH: DSLK không rỗng
(Kĩ thuật: So sánh lần lượt các cặp phần
Trang 42{ }
liên kết
2 Khi danh sách liên kêt
if (isEmpty(l)) return ;
Dùng hàm kiểm tra rỗng
3 Khi danh sách liên kết
không rỗng for (p=l.head; p!= l.tail;
p=p->next)
for(q=p->next; q!=
NULL; q=q->next){
p, q, vòng lặp, , lệnh rẽ nhánh, phương thức hoánvị
Đếm phần tử có trong danh sách
TH: DSLK rỗng
TH DSLK không rỗng: Dùng vòng lặp để
đếm
Trang 43Trình tự thực hiện
1 Tạo phương thức Count
int Count (List l){
}
Dùng hàm có kiểu dữ liệu trả
về , đối số là một danh sách liên kết
2 Khi danh sách liên kêt
int dem=0;
if(isEmpty(l))return 0;
Dùng hàm kiểm tra rỗng
3 Khi danh sách liên kết
không rỗng for( p= l.head; p!
=NULL;p=p-> next)
Dùng con trỏ đầu,vòng lặp, con trỏ
int Count (List l)
{
Node *p;
int dem=0;
if(isEmpty(l)) return 0;
Trang 44return dem;
Cài đặt và chạy chương trình
1.3 Ưu - khuyết điểm của DSLK
Ưu điểm :
- Phù hợp với các bài toán có các phần tử chưa xác định trước
- Dễ dàng trong việc xóa, chèn các phần tử trong DS (Tính linh động của DSLK).
-Cấp phát bộ nhớ động, sử dụng hiệu quả bộ nhớ.
Khuyết điểm:
- Khó cài đặt và truy nhập đến các phần tử trong DS
- Tốc độ truy nhập của danh sách chậm
2 Danh sách liên kết kép (double list)
2.1 Khái niệm:
Mỗi phần tử liên kết với phần tử đứng trước và sau nó trong danh sách