Professional Documents
Culture Documents
Slides - KTLT - T NG H P
Slides - KTLT - T NG H P
(PROGRAMMING TECHNIQUES)
6
KỸ THUẬT LẬP TRÌNH
(PROGRAMMING TECHNIQUES)
Chương 1:
3. Xử lý ngoại lệ
3. Xử lý ngoại lệ
3. Xử lý ngoại lệ
Code bẩn (Messy Code) sẽ làm cho công việc chậm lại một cách đáng kể. Lúc đầu,
team thực hiện rất nhanh khi bắt đầu dự án. Mỗi lần thay đổi, lại phá vỡ vài phần
khác của Code. Mỗi lần thêm hoặc sửa, hệ thống lại “lộn xộn, xoáy vào nhau”, ...
Qua thời gian thì sự lộn xộn đó lớn dần lên, và không có cách nào dọn sạch nó.
Năng suất theo đó giảm dần và tiệm cận về 0 (Năng suất giảm Management thêm
nhân viên Nhân viên mới không hiểu hệ thống Làm và làm Áp lực kết quả
không đạt yêu cầu.
Ví dụ:
int x;
#define MAX_SIZE 100
const float PI = 3.14;
class NhanVien
Phân loại:
lowerCamelCase UpperCamelCase
Ví dụ:
- isEmpty - IsEmpty
- hoTen - HoTen
Phân loại:
• arru8NumberList: biến là một mảng các • rwPosition: biến thể hiện một dòng
số nguyên 8 bit không dấu (“rw” – row)
(“arru8” – array of unsigned 8-bit integers) • strName: biến thể hiện một chuỗi
• szName: biến là một chuỗi có ký tự kết thúc chứa một tên (“str” – string).
(“sz” – zero-terminated string)
Ví dụ:
int SoLonNhat(int a[], int n); // Không rõ hành động
int TimSoLonNhat(int a[], int n); // Sai quy tắc đặt tên
int timSoLonNhat(int a[], int n); // OK
int tim_so_lon_nhat(int a[], int n); // OK
Lưu ý: Tránh những thay đổi bất chợt do sử dụng goto hay continue.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 27
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 28
2.2. Quy tắc trình bày tổng thể chương trình
//Neu cap phat thanh cong thi gan du lieu cho node
if (p != NULL)
{
p->info = x;
p->next = NULL;
}
return p;
}
Lưu ý: Một số vấn đề cần xác định khi viết chú thích:
+ Mục đích của hàm là gì?
+ Các điều kiện ràng buộc của các biến đầu vào (nếu có)?
Nguyên tắc KISS: đơn giản là mục tiêu trong thiết kế và lập
trình, tránh sự phức tạp không cần thiết.
Nên viết biểu thức điều kiện mang tính tự nhiên: biểu thức nên
viết dưới dạng khẳng định. Vì việc viết biểu thức dạng phủ
định sẽ làm khó hiểu.
Hạn chế dùng biến toàn cục: Xu hướng chung là nên hạn chế sử dụng biến toán cục. Khi
nhiều hàm cùng sử dụng một biến toàn cục, việc thay đổi giá trị biến toàn cục của một hàm
nào đó có thể dẫn đến những thay đổi không mong muốn ở các hàm khác. Biến toàn cục sẽ
làm cho các hàm trong chương trình không độc lập với nhau.
3. Xử lý ngoại lệ
Trong quá trình thực thi, chương trình có thể phát sinh lỗi:
1. Lỗi do mã nguồn (lập trình viên).
2. Lỗi do nhập sai (người sử dụng).
3. Do những điều không lường trước được.
Khi xảy ra lỗi, chương trình sẽ dừng lại và gửi một thông điệp lỗi
=> Hay còn gọi là ném ra ngoại lệ (throw an exception)
}
Vậy nên “ném ra” (throw) những ngoại lệ nào?
và “bắt lại” (catch) ra sao?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 48
3. Xử lý ngoại lệ
Trường hợp 1: Ngoại lệ là giá trị đang gây ra lỗi.
try {
int age = 15;
if (age > 18) {
printf("Access granted - you are old enough.“);
}
else {
throw (age);
}
}
catch (int myNum) { // bắt lấy giá trị lỗi được ném ra, đó là age
printf("Access denied - You must be at least 18 years old.\n“);
printf("Age is: %d“, myNum);
}
* Nữ: 60 tuổi
* Nam: 62 tuổi
3. Xử lý ngoại lệ
Phân loại:
◦ Tập tin kiểu văn bản (ứng với stream văn bản).
◦ Tập tin kiểu nhị phân (ứng với stream nhị phân).
Mở tập tin có tên (đường dẫn) là chứa trong fileName với kiểu mở mode.
Ví dụ:
Ví dụ:
int i;
FILE* fp = fopen(“file.txt”, “rt”);
if (fp != NULL)
fscanf(fp, “%d”, &i);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 71
4.3. Thao tác trên tập tin
int fscanf(FILE *fp, char *fnt, …)
Đọc chuỗi thông tin phức hợp
%[chuỗi]: đọc cho đến khi không gặp ký tự nào trong chuỗi thì dừng.
%[^chuỗi]: đọc cho đến khi gặp một trong những ký tự trong chuỗi thì dừng.
Ví dụ: Một tập tin chứa nhiều dòng, mỗi dòng là thông tin 1 sinh viên
theo định dạng sau:
<MSSV>, <HoLot>-<Tên>(<Khoa>) tab <ĐTB>
Ví dụ: 02345, Nguyen Van-An(CNTT) 8.5
fscanf(fp, "%[^, ], %[^-]-%[^(](%[^)])\t%.1f",
&sv.mssv, &sv.hoLot, &sv.ten, &sv.khoa, &sv.diemTB);
Ví dụ:
int a[] = {1, 2, 3};
FILE* fp = fopen(“file.dat”, “wb”);
if(fp != NULL)
fwrite(a, sizeof(int), 3, fp);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 73
4.3. Thao tác trên tập tin
int fread(void *buf, int size, int count, FILE *fp)
Đọc count mẫu tin có kích thước mỗi mẫu tin là size (byte) vào
vùng nhớ buf từ stream fp (theo kiểu nhị phân).
• Thành công: trả về số lượng mẫu tin (không phải số lượng byte) đã đọc.
• Thất bại: số lượng nhỏ hơn count khi kết thúc stream fp hoặc gặp lỗi.
Ví dụ:
int a[5];
FILE* fp = fopen(“file.dat”, “rb”);
if (fp != NULL)
fread(a, sizeof(int), 3, fp);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 74
4.3. Thao tác trên tập tin
int fclose(FILE *fp)
Đóng stream fp.
Dữ liệu trong stream fp sẽ được ghi hết lên đĩa trước khi đóng.
• Thành công: trả về 0.
• Thất bại: trả về EOF.
Ví dụ:
FILE* fp = fopen(“file.txt”, “rt”);
…
fclose(fp);
2. Cho mảng arr - mỗi phần tử lưu thông tin một Sinh viên, n là số phần tử
của mảng. Thông tin một sinh viên gồm: mã số sinh viên, họ tên và điểm
trung bình. Hãy viết hàm:
a. Ghi mảng arr ra thành tập tin nhị phân.
b. Đọc dữ liệu từ tập tin nhị phân vào mảng arr.
Ví dụ:
void readTextFile(int* arr[], int& n) {
Đọc tập tin // mo file de doc
văn bản FILE* fi = fopen(, "rt");
//doc du lieu
fscanf(fi, "%d", &n); //doc so luong n
for (int i = 0; i < n; i++) {
fscanf(fi, "%d", &arr[i]); //doc tung phan tu
}
//dong file
fclose(fi);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 77
4.3. Thao tác trên tập tin
Ví dụ: void writeTextFile(int arr[], int& n, char* fileName)
{
Ghi tập tin văn bản // mo file de doc
FILE* fo = fopen(fileName, “wt");
file.txt
//doc du lieu
5 fprintf(fo, "%d\n", &n); //ghi so luong phan tu n
21345 for (int i = 0; i < n; i++) {
//gan gia tri phan tu cho mang arr
fprintf(fo, "%d ", &arr[i]);
}
//dong file
fclose(fo);
}
Ví dụ:
FILE* fp = fopen(“file.txt”, “w+”);
fprintf(fp, “123456789”);
rewind(fp);
fprintf(fp, “aaa”);
Ví dụ:
FILE* fp = fopen(“a.txt”, “w+”);
fseek(fp, 0L, SEEK_SET); // rewind(fp);
fseek(fp, 0L, SEEK_END); // cuối tập tin
fseek(fp, -2L, SEEK_CUR);// lùi lại 2 vị trí
3. Xử lý ngoại lệ
Khi gặp một biểu thức với nhiều kiểu khác nhau?
◦ C tự động chuyển đổi kiểu (ép kiểu).
◦ Người sử dụng tự chuyển đổi kiểu.
Ví dụ:
int i;
float f = 1.23;
i = f; // f tạm thời thành int
f = i; // i tạm thời thành float
Ví dụ:
int x1 = 2, x2 = 4;
float f1 = x1 / x2; // f1 = 0.0
float f2 = (float)x1 / x2; // f2 = 0.5
float f3 = (float)(x1 / x2);// f3 = 0.0
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… 3412 0B …
a int a = 3412; p int *p = &a;
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 100
Biến con trỏ - Pointer
Con trỏ là biến dùng để lưu địa chỉ những biến khác hay địa chỉ
vùng nhớ hợp lệ (hay gọi là trỏ đến địa chỉ của biến khác).
Con trỏ NULL là con trỏ đặc biệt và không trỏ vào đâu cả.
Toán tử để lấy địa chỉ của biến vào con trỏ: &
<tên biến con trỏ> = &<tên biến>;
Toán tử truy xuất đến ô nhớ mà con trỏ trỏ đến: *
*<tên biến con trỏ>
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 101
Thao tác trên con trỏ
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 102
Biến Con trỏ
Ví dụ:
void main() {
int* p;
int a = 1, b; Nếu bỏ chú thích, chương trình sẽ lỗi vì:
// *p = 8; - biến p chưa được khởi gán.
- gán *p = 8 lỗi vì p giữ địa chỉ không hợp lệ.
p = &a;
*p = 11; a đổi thành 11
p = &b;
b sẽ được gán giá trị là 22
*p = 22;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 103
Cấp phát vùng nhớ cho con trỏ (động)
Ngôn ngữ C Ngôn ngữ C++
Cấp phát malloc new
calloc
realloc
Giải phóng free delete
Ví dụ:
int *p = (int *)malloc(10*sizeof(int)); int *a = new int; delete a;
int *p2 = (int *)calloc(10, sizeof(int)); int *p2 = new int[10]; delete []p2;
p = (int *)realloc(p, 20*sizeof(int));
free(p); free(p2);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 104
Con trỏ và mảng một chiều
Mảng một chiều
int a[3];
◦ Tên mảng a là một con trỏ hằng
không thể thay đổi giá trị địa chỉ mà a chứa.
◦ Giá trị của a là địa chỉ phần tử đầu tiên của mảng
a == &a [0]
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
a
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 105
Con trỏ và mảng một chiều
Con trỏ đến mảng một chiều
int a[3], *p;
p = a; //hoặc p = &a[0];
18 19 1A 1B 1C 1D 1E 1F
… 0B 00 00 00 …
p
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
a
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 106
Con trỏ và mảng một chiều
Phép toán số học trên con trỏ
Phép cộng (tăng)
◦ + n + n * sizeof(<kiểu dữ liệu>)
◦ Có thể sử dụng toán tử gộp += hoặc ++
p=a
+2
+1
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
int a[3];
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 107
Con trỏ và mảng một chiều
Tính khoảng cách giữa 2 con trỏ
◦ <kiểu dữ liệu> *p1, *p2;
◦ p1 – p2 cho ta khoảng cách (theo số phần tử) giữa hai con trỏ (cùng kiểu)
p1 = a p2 = &a [2]
p1 – p2= (0B – 13)/sizeof(int) = –2
0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17
… …
int a[3];
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 108
Con trỏ và mảng một chiều
Ví dụ: void main() {
int a[10], n = 4, *pa;
p = a; // hoặc pa = &a[0];
void main() {
int a[10], n = 5, *pa;
pa = a; // hoặc pa = &a[0];
…
for (int i = 0; i<n; i++) {
printf(“%d”, a[i]); // printf(“%d”, p[i]);
printf(“%d”, *(a + i));// printf(“%d”, *(p + i));
printf(“%d”, *(a++)); // printf(“%d”, *(p++));
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 111
Con trỏ và cấu trúc
Truy xuất bằng 2 cách:
<tên biến con trỏ cấu trúc>-><tên thành phần>
(*<tên biến con trỏ cấu trúc>).<tên thành phần>
Ví dụ:
struct PhanSo {
int tu, mau;
};
PhanSo ps1, *ps2 = &ps1; // ps2 là con trỏ
ps1.tu = 1; ps1.mau = 2;
ps2->tu = 1; ps2->mau = 2;
(*ps2).tu = 1; (*ps2).mau = 2;
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 112
Bài tập
1. Viết chương trình nhập 2 số thực bất kỳ a, b. Tạo 2 con trỏ
pa và pb trỏ đến a, b. Xuất giá trị các con trỏ trên.
2. Tạo mảng 1 chiều a chứa n phần tử.
a) Tìm max của a, xuất ra max và địa chỉ của max thông qua
con trỏ mảng.
b) Xuất địa chỉ của phần tử max và min
3. Viết chương trình nhập vào chuỗi st. Xuất giá trị từng ký tự
của st thông qua con trỏ trỏ đến chuỗi.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 113
KỸ THUẬT LẬP TRÌNH
(PROGRAMMING TECHNIQUES)
Chương 2
KỸ THUẬT XỬ LÝ MẢNG
NỘI DUNG
1. CÁC GIẢI THUẬT TÌM KIẾM VÀ SẮP XẾP
1.1. TÌM KIẾM TUYẾN TÍNH – LINEAR SEARCH
2. XỬ LÝ MẢNG 1 CHIỀU
3. XỬ LÝ MẢNG 2 CHIỀU
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 115
CÁC GIẢI THUẬT TÌM KIẾM
(Các thuật toán minh họa trên mảng 1 chiều chứa dữ liệu là các số nguyên)
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 116
BÀI TOÁN
Giả sử có một mảng A gồm n phần tử. Cần xác định
có hay không phần tử có giá trị bằng X trong mảng
A? Nếu có phần tử X thì phần tử bằng phần tử X là
phần tử thứ mấy trong mảng A?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 117
Các giải thuật tìm kiếm nội
Các giải thuật tìm kiếm nội đưa ra 2 cách tìm kiếm
◦ Tìm kiếm tuyến tính (Linear Search)
◦ Tìm kiếm nhị phân (Binary Search)
Tìm kiếm
Thuật toán tiến hành so sánh x lần lượt với các phần tử thứ 1, thứ 2,… của mảng a cho
đến khi gặp phần tử có khóa cần tìm, hoặc đã tìm hết mảng mà không thấy x.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 119
Minh họa ví dụ
Xét dãy số A có 7 phần tử:
5 3 6 8 9 1 2
Tìm x = 9?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 120
Minh họa ví dụ
Xét dãy số A có 7 phần tử:
5 3 6 8 9 1 2
Tìm x = 10?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 121
1.1. Tìm kiếm tuyến tính (Linear Search) (tt)
Cài đặt:
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 122
1.2. Tìm kiếm nhị phân (Binary Search)
Ý tưởng:
◦ Giả sử dãy số a đã có thứ tự tăng.
◦ Tại mỗi bước tiến hành so sánh x với phần tử nằm vị trí giữa của dãy tìm kiếm hiện hành, dựa
vào kết quả so sánh này để quyết định giới hạn dãy tìm ở bước kế tiếp là nửa trái hay nửa phải
của dãy tìm kiếm hiện hành.
Ví dụ:
◦ Cho dãy a có 7 phần tử: 3 4 6 8 9 10 13
◦ Tìm x = 10 và x = 2 ?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 123
Minh họa ví dụ
◦ Cho dãy số a: 3 4 6 8 9 10 13
Tìm x = 10?
Kết quả so
mid =
Bước left right a[mid] sánh
[(left+right)/2]
a[mid] và x?
1 0 6 [(0+6)/2] = 3 8 8 < 10
Tìm được x=10
tại vị trí 5.
2 left = mid+1 = 6 [(4+6)/2] = 5 10 10 == 10
3+1 = 4 Kết thúc quá
trình
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 124
Minh họa ví dụ
◦ Cho dãy số a: -2 0 6 8 9 10 13
Tìm x = 2?
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 127
Bài tập
Cho mảng 1 chiều a chứa n số nguyên. Viết chương trình thực hiện các yêu cầu sau:
1. Viết hàm nhập/xuất mảng a.
2. Tìm max/min của a.
3. Đếm số phần tử chẵn/lẻ trong a.
4. Tìm kiếm phần tử x trong a theo 2 dạng (trả về vị trí/xuất câu thông báo) với giải
thuật tìm kiếm tuyến tính/ tìm kiếm nhị phân.
5. Đếm trên a có bao nhiêu phần tử x (x nhập từ bàn phím).
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 128
Bài tập (tt)
6. Đếm trên a có bao nhiêu phần tử lớn hơn x.
7. Tính tổng các phần tử của a.
8. Xuất các số nguyên tố trong a.
9. Xuất các số hoàn thiện trong a.
10. Xuất các phần tử ở vị trí chẵn/ vị trí lẻ trong a.
11. Xuất giá trị max/min kèm theo vị trí của giá trị đó trong mảng a.
12. Cho 2 dãy số tăng dần: dãy b có m phần tử và dãy c có n phần tử. Ghép b và c thành
dãy a được xếp tăng dần.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 129
CÁC GIẢI THUẬT SẮP XẾP
(Các thuật toán minh họa sắp xếp dãy không tăng trên mảng 1 chiều chứa dữ liệu là các số nguyên)
Sắp xếp là quá trình xử lý một danh sách các phần tử để đặt chúng theo một thứ tự thỏa mãn một
tiêu chuẩn nào đó dựa trên nội dung thông tin lưu trữ tại mỗi phần tử.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 130
BÀI TOÁN
Giả sử có một mảng A gồm n phần tử. Cần sắp xếp
(bố trí lại) các phần tử trong mảng A theo một thứ tự
nhất định nhằm thỏa mãn một điều kiện (tiêu chuẩn)
nào đó dựa trên nội dung thông tin lưu trữ tại mỗi
phần tử.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 131
1.3. Sắp xếp đổi chỗ trực tiếp – interchange Sort
Khái niệm nghịch thế:
◦ Xét dãy a gồm các số: a1, a2, … an, với a là dãy chưa có thứ tự
(chưa được sắp xếp, chẳng hạn như a là dãy không giảm). Nếu i
< j và ai > aj thì ta gọi đó là 1 nghịch thế.
◦ Ví dụ: Cho dãy số a như sau:
14 5 7 8 3
Vậy dãy trên trên có các cặp nghịch thế sau: (14, 5); (7, 3); (8, 3); ….
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 132
1.3. Sắp xếp đổi chỗ trực tiếp – interchange Sort
Ý tưởng thuật toán:
Xuất phát từ đầu dãy, lần lượt xét từng phần tử cho đến cuối dãy. Tại mỗi phần tử tìm tất cả
nghịch thế chứa phần tử này, đổi chỗ phần tử này với các phần tử trong cặp nghịch thế.
Lặp lại công việc này với tất cả các phần tử còn lại cho đến khi dãy đã được sắp xếp xong.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 133
Minh họa ví dụ interchange sort
Cho dãy số a
10 2 1 14 5 9 30 7
10 2 1 14 5 9 30 7
i=0 j=1
2 10 1 14 5 9 30 7
i=0 j=2
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 134
Minh họa ví dụ interchange sort (tt)
Bước 2: xét nghịch thế của phần tử thứ 1 – a1
1 10 2 14 5 9 30 7
i=1 j=2
1 2 10 14 5 9 30 7
i=2 j=4
1 2 5 14 10 9 30 7
i=3 j=4
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 135
Minh họa ví dụ interchange sort (tt)
Bước 4: xét nghịch thế của phần tử thứ 3 – a3 tiếp theo
1 2 5 10 14 9 30 7
i=3 j=5
1 2 5 9 14 10 30 7
i=3 j=7
Bước 5: xét nghịch thế của phần tử thứ 4 – a4
1 2 5 7 14 10 30 9
1 2 5 7 10 14 30 9
i=4 j=7
Bước 5: xét nghịch thế của phần tử thứ 5 – a5 tiếp theo
1 2 5 7 9 14 30 10
i=5 j=7
Bước 5: xét nghịch thế của phần tử thứ 6 – a6
1 2 5 7 9 10 30 14
i=6 j=7
1 2 5 7 9 10 14 30
Dừng.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠOVậy dãy đã được sắp xếp. 137
Ví dụ
Minh họa thao tác sắp xếp dữ liệu theo phương pháp
interchange Sort cho các dãy dữ liệu sau:
◦ Sắp xếp tăng:
13 8 12 6 9 10 12 7
A H K R E C Z G
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 138
Cài đặt
void interchangeSort(int a[], int n)
{
for(int i = 0; i < n - 1; i++)
for(int j = i + 1; j < n; j++)
if(a[i] > a[j])
swap(a[i], a[j]); //ham hoan doi gia tri
2 so nguyen
}
void swap(int &x, int &y)
{
int t = x;
x = y;
y = t;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 139
Bài tập cài đặt
Viết bổ sung các hàm vào chương trình xử lý mảng 1 chiều các hàm thực hiện những
yêu cầu sau:
1. Viết hàm sắp xếp tăng theo PP interchange sort cho dữ liệu số nguyên/số thực/ký tự/
chuỗi ký tự.
2. Viết hàm sắp xếp giảm theo PP interchange sort cho dữ liệu số nguyên/số thực/ký tự/ chuỗi
ký tự.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 140
1.4. Sắp xếp chọn trực tiếp – Selection sort
Ý tưởng giải thuật
Chọn phần tử nhỏ nhất trong n phần tử ban đầu, đưa phần tử
này về vị trí thứ 0 của dãy hiện hành; sau đó không quan tâm
đến nó nữa. Xem dãy hiện hành chỉ còn (n – 1) phần tử, bắt
đầu từ vị trí thứ 1; lặp lại quá trình đó trên dãy hiện hành…
cho đến khi dãy hiện hành chỉ còn đúng 1 phần tử thì dừng.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 141
Minh họa ví dụ Selection sort
Cho dãy số a
10 2 1 14 5 9 30 7
Bước 1: Tìm min của dãy số từ a0 – an-1 . Sau đó hoán đổi min với a0
min
10 2 1 14 5 9 30 7
i=0
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 142
Minh họa ví dụ Selection sort (tt)
Bước 2: Tìm min của dãy số từ a1 – an-1 . Sau đó hoán đổi min với a1
min
1 2 10 14 5 9 30 7
i=1
Bước 3: Tìm min của dãy số từ a2 – an-1 . Sau đó hoán đổi min với a2
min
1 2 10 14 5 9 30 7
i=2
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 143
Minh họa ví dụ Selection sort (tt)
Bước 4: Tìm min của dãy số từ a3 – an-1 . Sau đó hoán đổi min với a3
min
1 2 5 14 10 9 30 7
i=3
Bước 5: Tìm min của dãy số từ a4 – an-1 . Sau đó hoán đổi min với a4
min
1 2 5 7 10 9 30 14
i=4
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 144
Minh họa ví dụ Selection sort (tt)
Bước 6: Tìm min của dãy số từ a5 – an-1 . Sau đó hoán đổi min với a5
min
1 2 5 7 9 10 30 14
i=5
Bước 7: Tìm min của dãy số từ a6 – an-1 . Sau đó hoán đổi min với a6 min
1 2 5 7 9 10 30 14
i=6
1 2 5 7 9 10 14 30
Dừng.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠOVậy dãy đã được sắp xếp. 145
Ví dụ
Minh họa thao tác sắp xếp dữ liệu theo phương pháp Selection Sort cho các dãy dữ liệu
sau:
◦ Sắp xếp tăng:
◦ 13 8 12 6 9 10 12 7
◦ A H K R E C Z G
◦ A H K R E C Z G
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 146
Cài đặt
void selectionSort(int a[], int n)
{
for(int i = 0; i < n - 1; i++)
{
int imin = i;
//tìm min của dãy số từ ai an-1
for(int j = i + 1; j < n; j++)
if(a[imin] > a[j])
imin = j;
swap(a[imin], a[i]); //Đổi chỗ phần tử nhỏ nhất
với phần tử đầu dãy hiện hành
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 147
1.5. Sắp xếp nhanh - Quick Sort
Ý tưởng: Giải thuật QuickSort dựa trên việc phân hoạch dãy ban đầu thành 3 phần:
Dãy con 1: gồm các phần tử a0…ai-1 có giá trị nhỏ hơn x.
Dãy con 2: gồm các phần tử ai…aj có giá trị bằng x.
Dãy con 3: gồm các phần tử aj+1…an-1 giá trị lớn hơn x.
Lặp lại quá trình trên với các dãy con.
với x là giá trị của một phần tử tùy ý trong dãy ban đầu.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 148
Quick Sort (tt)
Ý tưởng: Giải thuật QuickSort dựa trên việc phân hoạch dãy ban đầu thành 3 phần:
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 149
Quick Sort (tt)
Giải thuật:
Bước 1: Nếu left ≥ right //dãy có ít hơn 2 phần tử
Kết thúc; //dãy đã được sắp xếp
Bước 2: Phân hoạch dãy aleft … aright thành 3 đoạn: aleft.. aj, aj+1.. ai-1, ai.. aright
Đoạn 1: aleft.. aj có giá trị nhỏ hơn x
Đoạn 2: aj+1.. ai-1 có giá trị bằng x
Đoạn 3: ai.. aright có giá trị lớn hơn x
Bước 3: Sắp xếp đoạn 1: aleft.. aj
Bước 4: Sắp xếp đoạn 3: ai.. aright
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 150
Quick Sort (tt)
Giải thuật: Giai đoạn Phân hoạch đoạn left .. right (Bước 2)
Bước 1: Chọn một phần tử a[k] trong dãy là giá trị mốc (left ≤ k ≤ right):
Gán x = a[k]; i = left; j = right;
(thông thường chọn k = (left + right)/2).
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai vị trí:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 151
Quick Sort (tt)
Minh họa:
Sắp xếp dãy số a: 12 2 8 5 1 6 4 15
Lần 1 12 2 8 5 1 6 4 15
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 152
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 0, right = 7
12 2 8 5 1 6 4 15
Lần 1
i=0 k=
x3 j=6 j=7
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 153
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 0, right = 7
4 2 8 5 1 6 12 15
Lần 1
i=0 i=1 i=2 k=
x3 j=4 j=5 j=6
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 154
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 0, right = 7
Lần 1 4 2 1 5 8 6 12 15
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 155
Quick Sort (tt)
Minh họa:
Phân hoạch đoạn left = 0, right = 7
Lần 1 4 2 1 5 8 6 12 15
Giải thuật:
Bước 1: Nếu left ≥ right //dãy có ít hơn 2 phần tử
Kết thúc; //dãy đã được sắp xếp
Bước 2: Phân hoạch dãy aleft … aright thành 3 đoạn: aleft.. aj, aj+1.. ai-1, ai.. aright
Đoạn 1: aleft.. aj có giá trị nhỏ hơn x
Đoạn 2: aj+1.. ai-1 có giá trị bằng x
Đoạn 3: ai.. aright có giá trị lớn hơn x
Bước 3: Sắp xếp đoạn 1: aleft.. aj
Bước 4: Sắp xếp đoạn 3: ai.. aright
156
4 2 1 5 8 6 12 15
Lần 2
Tương tự i=0 x j=2
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 157
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 0, right = 2
1 2 4 5 8 6 12 15
Lần 2
Tương tự i=0 x j=2 Dừng
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 158
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 0, right = 2
Lần 2
1 2 4 5 8 6 12 15
Tương tự
Giải thuật:
Bước 1: Nếu left ≥ right //dãy có ít hơn 2 phần tử
Kết thúc; //dãy đã được sắp xếp
Bước 2: Phân hoạch dãy aleft … aright thành 3 đoạn: aleft.. aj, aj+1.. ai-1, ai.. aright
Đoạn 1: aleft.. aj có giá trị nhỏ hơn x
Đoạn 2: aj+1.. ai-1 có giá trị bằng x
Đoạn 3: ai.. aright có giá trị lớn hơn x
Bước 3: Sắp xếp đoạn 1: aleft.. aj
Bước 4: Sắp xếp đoạn 3: ai.. aright
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 159
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 4, right = 7, x = 6
1 2 4 5 8 6 12 15
Lần 3
Tương tự i=4 j =x 5 j=6 j=7
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 160
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 4, right = 7, x = 6
1 2 4 5 6 8 12 15
Lần 3
Tương tự Dừng ij = 4 ij = 5
Bước 2: Phát hiện và hiệu chỉnh cặp phần tử a[i], a[j] nằm sai chỗ:
Bước 2.1: Trong khi (a[i] < x) i++;
Bước 2.2: Trong khi (a[j] > x) j--;
Bước 2.3: Nếu (i j) { { Nếu (i < j) swap(a[i], a[j]); } i++; j--; }
Bước 3: Nếu i j: Lặp lại Bước 2. Ngược lại: Dừng phân hoạch.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 161
Quick Sort (tt)
Minh họa: Phân hoạch đoạn left = 6, right = 7, x = 12
1 2 4 5 6 8 12 15
Lần 3
Tương tự i=6 j=7
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 162
Quick Sort (tt)
Minh họa:
1 2 4 5 6 8 12 15
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 163
Quick Sort (tt)
void quickSort(int a[], int Left, int Right)
Cài đặt: {
if (Left >= Right) return;
int i = Left, j = Right, Mid = (Left+Right)/2;
int x = a[Mid];
do {
while(a[i] < x) i++; // lặp cho đến khi a[i] >= x
while(a[j] > x) j--; // lặp cho đến khi a[j] <= x
if(i <= j) {
if(i < j)
swap(a[i], a[j]); Phân hoạch bên trái
i++;
j--;
} Phân hoạch bên phải
} while(i <= j);
quickSort(a, Left, j);
quickSort(a, i, Right);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 164
Quick Sort (tt)
Bài tập: Áp dụng giải thuật Quick Sort, hãy sắp xếp dãy số sau:
10 3 7 6 2 5 16 4
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 165
2. Kỹ thuật xử lý mảng 1 chiều
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 166
Khái niệm
Mảng 1 chiều là một dãy các phần tử có cùng tên, cùng kiểu dữ liệu được đặt
liên tiếp nhau trong bộ nhớ và các phần tử được truy xuất thông qua chỉ số của
mảng.
Mỗi phần tử được xác định bởi một chỉ số biểu thị vị trí của phần tử đó trong
mảng.
Số lượng phần tử trong mảng được gọi là kích thước của mảng. Kích thước
của mảng là cố định và phải được xác định trước.
Ví dụ: Kích thước của mảng: 10
Vị trí 0 1 2 3 4 5 6 7 8 9
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 167
Khai báo mảng một chiều
Cú pháp:
<Kiểu dữ liệu><Tên biến kiểu mảng>[<Số phần tử tối đa trong mảng>] ;
Ví dụ:
int a[100]; //Khai báo mảng số nguyên a gồm 100 phần tử
float b[50]; // Khai báo mảng số thực a gồm 50 phần tử
char str[30]; //Khai báo mảng ký tự str gồm 30 ký tự
Truy cập vào các phần tử của mảng
Ví dụ: mảng A có 10 phần tử: A[0] 34; A[1] 56;. . . ; A[9] 34
34 56 47 67 12 32 34 23 123 34
0 1 2 3 4 5 6 7 8 9
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 168
2.1. Duyệt mảng có điều kiện
Cú pháp: duyệt tất cả các phần tử thỏa điều kiện trong mảng có n phần tử.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 169
Ví dụ: Tính tổng các số chẵn trong mảng
3. Tìm vị trí số lớn nhất (cuối cùng) của mảng, nghĩa là nếu trong mảng có nhiều phần
tử là số lớn nhất thì xuất vị trí số cuối cùng.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 171
2.2 Kiểm tra mảng thỏa điều kiện cho trước
Trường hợp 1: Kiểm tra tồn tại một phần tử trong mảng thỏa điều kiện
nào đó cho trước.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 174
3. Kỹ thuật xử lý mảng cấu trúc
Cú pháp: Ví dụ
struct <tên kiểu cấu trúc> struct Point2D
{ {
<kiểu dữ liệu> <tên thành phần 1>; int x;
… int y;
<kiểu dữ liệu> <tên thành phần n>; };
};
struct SinhVien
Bài tập: Khai báo các kiểu dữ liệu sau:
{
1. Phân số char MSSV[11];
2. Hỗn số char hoVaTen[31];
3. Điểm không gian 3 chiều float dTieuLuan,dKetThucMon,dTongKet;
};
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 175
Cấu trúc lồng
Các cấu trúc có thể được khai báo lồng nhau
struct DiemThi { Khai báo trực tiếp các trường dữ liệu
float dToan, dLy, dHoa; của một cấu trúc bên trong một cấu
trúc khác:
}
struct ThiSinh { struct ThiSinh {
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 176
Truy cập các trường dữ liệu
Cú pháp:
struct Point2D {
int x, y;
} p = {2912, 1706};
printf("x = %d, y = %d\n", p.x, p.y);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 177
Bài tập
Khai báo cấu trúc phân số cần thiết lưu trữ thông tin phân số, sau đó thực hiện
các chức năng:
5. Nhập vào 2 phân số. Tính tổng, hiệu, tích và thương của hai phân số.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 178
Gán dữ liệu giữa các biến cấu trúc
Có 2 cách:
<biến cấu trúc đích> = biến cấu trúc nguồn
<biến cấu trúc đích>.<tên thành phần> = <giá trị>
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 179
3.2. Mảng cấu trúc
Là tập hợp các phần tử có cùng kiểu dữ liệu là kiểu cấu trúc
Mục đích:
Lưu trữ một tập hợp các phần tử có cùng kiểu.
Mỗi phần tử là một tập hợp các thành phần có thể khác nhau: thông tin
các sinh viên trong lớp, đội bóng…
Khai báo:
struct <tên cấu trúc> <tên mảng cấu trúc>[số phần tử];
struct Student {
int id;
char name[10];
};
Ví dụ: struct Student st[5];
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 180
Ví dụ
struct Student{
int id;
char name[10];
};
int i;
Student st[5];
printf("Nhap thong tin cho 5 sinh vien: \n");
for (i = 0; i < 5; i++)
{
printf("Nhap id: "); scanf("%d", &st[i].id);
printf("Nhap name: "); scanf("%s", &st[i].name);
}
printf("Danh sach sinh vien: \n");
for (i = 0; i < 5; i++)
printf("Id: %d, Name: %s\n", st[i].id, st[i].name);
6. Sắp xếp mảng tăng/giảm dần theo các giải thuật đã học.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 182
Bài tập
Bài 2: Thiết kế một chương trình lưu trữ thông tin sinh viên được mô tả chi tiết như sau:
Danh sách sinh viên gồm n sinh viên, với n được nhập từ bàn phím. Thông tin một sinh
viên bao gồm: Số thứ tự, Mã số sinh viên, Họ tên sinh viên, Điểm tiểu luận, Điểm thi kết
thúc môn. Thực hiện các yêu cầu sau:
1. Hãy xây dựng hàm nhập/xuất danh sách sinh viên.
2. Hãy tính điểm tổng kết = 30%Điểm tiểu luận + 70% Điểm thi kết thúc môn.
3. In ra sinh viên có điểm tổng kết cao nhất và thấp nhất.
4. Cho biết có bao nhiêu sinh viên đạt và không đạt.
5. Hãy qui đổi điểm từ hệ 10 sang hệ 4 theo cơ chế tín chỉ.
6. Hãy sắp xếp danh sách sinh viên tăng/giảm dần theo điểm tổng kết.
7. Hãy tính điểm trung bình của tất cả các sinh viên.
8. Hãy thực hiện việc nhập xuất dữ liệu bằng file txt.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 183
Bài tập
Bài 3: Cho một danh sách sinh viên gồm n sinh viên, với n được nhập bất kỳ. Thông tin một sinh
viên: Mã số sinh viên, Họ tên sinh viên, Điểm các môn: Nhập môn lập trình, Môn toán A1, Môn
toán A2, Môn Vật lý kỹ thuật, Môn anh văn, Điểm trung bình tích lũy. Thông tin môn học bao gồm:
Mã môn, Tên môn, Số tín chỉ, Điểm. Thực hiện các yêu cầu sau:
a) Hãy thiết kế và xây dựng các cấu trúc (struct) phù hợp cho bài toán.
b) Xây dựng một chương trình hoàn chỉnh cho một lớp học đảm bảo các thao tác sau:
1. Hàm nhập/xuất danh sách sinh viên
2. Tìm một sinh viên thông qua mã số sinh viên.
3. Tìm sinh viên có điểm trung bình cao nhất.
4. Thêm, xóa một sinh viên ra khỏi danh sách.
5. Sắp xếp danh sách sinh viên tăng/giảm theo điểm trung bình.
6. Xếp loại học tập của sinh viên theo kết quả học tập.
7. Thống kê số môn đậu rớt của sinh viên.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 184
2. Kỹ thuật xử lý mảng hai chiều
1 Khái niệm
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 185
2.1. Khái niệm
Mảng 2 chiều thực chất là mảng 1 chiều mà mỗi phần tử của nó là một
mảng khác.
Mảng có từ 2 chiều trở lên được gọi chung là mảng nhiều chiều.
Mảng nhiều chiều được dùng để lưu các dữ liệu dạng bảng, ma trận
trong chương trình.
Ma trận An,m
Ma trận An
Ma trận có số hàng
và cột bằng nhau
là vuông
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 186
2.2. Khai báo mảng hai chiều
Cú pháp:
<Kiểu dữ liệu> <Tên mảng> [<Số dòng tối đa>][<Số cột tối đa>]
Ví dụ:
Khai báo mảng 2 chiều kiểu số nguyên gồm 10 dòng, 10 cột
int A[10][10];
Khai báo mảng 2 chiều kiểu số thực gồm 10 dòng, 10 cột
float A[10][10];
Truy xuất các phần tử của ma trận: dựa vào chỉ số dòng, chỉ số cột của ma trận
printf(“%d”, a[2][1]); //xuất ra màn hình giá trị dòng thứ 2, cột thứ 1
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 187
2.3. Các thuật toán xử lý mảng hai chiều
1 Duyệt ma trận
2 Tính tổng/tích
3 Tìm kiếm
4 Sắp xếp
5 Ma trận vuông
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 188
2.3.1. Duyệt ma trận
Duyệt từng dòng từ trên xuống dưới
for(i = 0; i < so_dong; i++) void nhapMang2C_SoNguyen(int a[][MAXCOL], int &m, int &n)
{
for(j = 0; j < so_cot; j++) do {
{ printf("Cho biet so dong cua ma tran: ");
scanf("%d",&m);
//Xử lý a[i][j]; } while (m <= 0);
} do {
printf("Cho biet so cot cua ma tran: ");
scanf("%d",&n);
} while (n <= 0);
Ví dụ: Viết chương trình for(int i = 0; i < m; i++)
nhập/xuất ma trận các phần for(int j = 0; j < n; j++){
printf("Gia tri phan tu a[%d,%d]=", i, j);
tử số nguyên scanf("%d", &a[i][j]);
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 189
2.3.1. Duyệt ma trận
Duyệt từng dòng từ trên xuống dưới
for(i = 0; i < so_dong; i++) void xuatMang2C_SoNguyen(int a[][MAXCOL], int m, int n)
{
for(j = 0; j < so_cot; j++) for(int i = 0; i < m; i++)
{ {
for(int j = 0; j < n; j++)
//Xử lý a[i][j]; {
} }
printf("%4d", a[i][j]);
printf("\n");
}
Ví dụ: Viết chương trình }
nhập/xuất ma trận các phần
tử số nguyên
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 190
Bài tập
Cho mảng hai chiều các số nguyên a có m hàng và n cột. Hãy thực hiện các yêu
cầu sau:
Bài 1: Tính tổng các phần tử có chữ số đầu là chữ số lẻ
Bài 2: Liệt kê các số hoàn thiện trong ma trận
Bài 3: Tính tổng các phần tử lớn hơn trị tuyệt đối của phần tử liền sau nó
Bài 4: Tính tổng giá trị trên dòng k của ma trận
Bài 5: Tính tổng các giá trị nằm trên biên của ma trận
Bài 6: Đếm tần suất xuất hiện của 1 giá trị x trong ma trận
Bài 7: Đếm số lượng các phần tử là số chẵn, số lẻ, số âm, số dương, số nguyên tố.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 191
2.3.3. Tìm kiếm (tt)
Tìm phần tử lớn nhất t rong mảng
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 192
2.3.3. Tìm kiếm (tt)
Tìm phần tử nhỏ nhất trên từng dòng của ma trận
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 193
2.3.3. Tìm kiếm (tt)
Tìm phần tử lớn nhất trên từng cột của ma trận
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 194
Bài tập
Cho mảng hai chiều các số nguyên a có m hàng và n cột. Hãy thực hiện
các yêu cầu sau:
Bài 1: Liệt kê các cột có tổng nhỏ nhất trong ma trận
Bài 2: Liệt kê các dòng có nhiều số hoàn thiện nhất trong ma trận
Bài 3: Liệt kê chỉ số các dòng chứa toàn giá trị chẵn
Bài 4: Tìm giá trị xuất hiện nhiều nhất trong ma trận
Bài 5: Tìm số nguyên tố nhỏ nhất trong ma trận
Bài 6: Tìm phần tử lớn (nhỏ) nhất trong dòng thứ k.
Bài 7: Tìm phần tử lớn (nhỏ) nhất trong cột thứ k
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 195
2.3.4. Sắp xếp
Sắp xếp các phần tử tăng dần trên từng dòng
Cho k chạy từ dòng 0 đến hết số dòng trong ma trận
Cho i chạy từ cột 0 đến số cột -1 trong ma trận
Cho j chạy từ cột i+1 đến hết số cột trong ma trận
So sánh nếu A[k][i] > A[k][j] thì
Đổi chỗ A[k][i] và A[k][j]
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 196
2.3.4. Sắp xếp
Sắp xếp các phần tử tăng dần trên từng dòng
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 197
2.3.4. Sắp xếp
Tương tự: Sắp xếp các phần tử tăng dần trên từng cột
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 198
Bài tập
Cho mảng hai chiều các số nguyên a có n hàng và m cột. Hãy thực hiện các yêu cầu
sau:
Bài 1: Sắp xếp các phần tử trong ma trận sao cho dòng chẵn tăng dòng lẻ giảm
Bài 2: Sắp xếp các phần tử trong ma trận sao cho cột chẵn tăng cột lẻ giảm
Bài 3: Sắp xếp ma trận tăng dần/giảm dần theo zic-zac.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 199
2.3.5. Ma trận vuông
Các phần tử trên đường chéo chính: dòng = cột
Các phần tử nằm bên dưới đường chéo chính: dòng > cột
Các phần tử nằm bên trên đường chéo chính: dòng < cột
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 200
Ma trận vuông (tt)
Các phần tử trên đường chéo phụ: dòng + cột = n-1
Các phần tử nằm bên dưới đường chéo phụ: dòng + cột > n-1
Các phần tử nằm bên trên đường chéo phụ: dòng + cột < n-1
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 201
Bài tập
Cho ma trận vuông n. Viết các hàm sau:
Bài 1: Tính tổng các phần tử nằm trên đường chéo chính.
Bài 2: Tính tổng các phần tử nằm trên đường chéo phụ.
Bài 3: Tính tổng các phần tử nằm phía trên đường chéo chính.
Bài 4: Tính tổng các phần tử nằm phía trên đường chéo phụ.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 202
KỸ THUẬT LẬP TRÌNH
(PROGRAMMING TECHNIQUES)
Chương 3
CHUỖI KÝ TỰ
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 203
Nội dung
1. Khái niệm
2. Khai báo và khởi tạo
3. Các thao tác trên chuỗi ký tự
4. Một số thuật toán xử lý chuỗi
a. Một số thuật ngữ
b. Tìm chuỗi con
c. So khớp chuỗi
5. Bài tập
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 204
1. Khái niệm
Chuỗi ký tự là một dãy các phần tử, mỗi phần tử là một ký tự.
Lưu ý: Chuỗi ký tự được kết thúc bằng ký tự ‘\0’. Do đó khi khai báo độ
dài của chuỗi luôn luôn khai báo dư 1 phần tử để chứa ký tự ‘\0’.
Độ dài chuỗi s = 4.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 205
2. Khai báo và khởi tạo
Để khai báo một chuỗi, ta có 2 cách khai báo sau:
Cách 1:
char <Tên_chuỗi>[<Số ký tự tối đa của chuỗi>];
Ví dụ: char hoten[50];
Cách 2:
char *<Tên_chuỗi>;
Ví dụ: char *hoten;
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 206
2. Khai báo và khởi tạo
Vừa khai báo vừa khởi tạo:
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 207
3. Các thao tác trên chuỗi ký tự
3.1. Xuất chuỗi
Sử dụng hàm printf với đặc tả “%s”
char monhoc[50] = “Tin hoc co so A”;
printf(“%s”, monhoc); //Không xuống dòng
Sử dụng hàm puts
char monhoc[50] = “Tin hoc co so A”;
puts(monhoc); //Tự động xuống dòng
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 208
3. Các thao tác trên chuỗi ký tự
3.2. Nhập chuỗi
Sử dụng hàm scanf với đặc tả “%s”
• Chỉ nhận các ký tự từ bàn phím đến khi gặp ký tự khoảng
trắng hoặc ký tự xuống dòng.
• Chuỗi nhận được không bao gồm ký tự khoảng trắng và
xuống dòng.
Ví dụ:
char monhoc[50];
Moi nhap chuoi: Tin hoc co so A
printf(“Moi nhap chuoi: ”); Chuoi vua nhap: Tin_
scanf(“%s”, monhoc);
printf(“Chuoi vua nhap: %s”, monhoc);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 209
3. Các thao tác trên chuỗi ký tự
3.2. Nhập chuỗi
Sử dụng hàm gets
• Nhận các ký tự từ bàn phím đến khi gặp ký tự xuống dòng.
• Chuỗi nhận được là những gì người dùng nhập (trừ ký tự
xuống dòng).
Ví dụ:
char monhoc[50]; Moi nhap chuoi: Tin hoc co so A
printf(“Moi nhap chuoi: ”); Chuoi vua nhap: Tin hoc co so A_
gets(monhoc);
printf(“Chuoi vua nhap: %s”, monhoc);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 210
3. Các thao tác trên chuỗi ký tự
Một số lưu ý khi làm việc với mảng ký tự theo quy ước chuỗi:
Các hàm xử lý chuỗi ký tự trong C được khai báo trong
<string.h>.
Người lập trình phải quản lý và cấp phát đủ vùng nhớ cho các
mảng ký tự dùng để lưu trữ chuỗi. Ngoài ra, phải đảm bảo chuỗi
ký tự luôn chứa ký tự NULL để đánh dấu kết thúc nội dung chuỗi.
Không sử dụng phép toán =, +, ==, >, < và các phép toán so sánh
khác đối với chuỗi được lưu ở dạng mảng ký tự mà phải sử dụng
các hàm thư viện trong <string.h>.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 211
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
Thuộc thư viện <string.h>
• strlen
• strcpy
• strcmp/ strcmpi/ stricmp
• strlwr/ strupr
• strcat
• strstr
• …
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 212
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
Độ dài chuỗi s
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 213
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
Sao chép chuỗi src sang chuỗi dest, dừng khi ký tự kết
thúc chuỗi ‘\0’ vừa được chép.
! dest phải đủ lớn để chứa src.
char s[100];
s = “Tin hoc co so A”;//=> SAI
strcpy(s, “Tin hoc co so A”);//=> ĐÚNG
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 214
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 215
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 216
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
STT Tên hàm Chức năng Ví dụ
char *s1 = “Khoa ”;
char *s2 = "CNTT";
strcat(char *s1, Nối chuỗi s2 vào
1 strcat(s1, s2);
char *s2) chuỗi s1
printf("%s\n", s1);
Kết quả: Khoa CNTT
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 217
3. Các thao tác trên chuỗi ký tự
3.3. Một số hàm thao tác trên chuỗi
STT Tên hàm Chức năng Ví dụ
char s[]=“Tin hoc”;
Chuyển chuỗi s thành strupr(s);
3 char* strupr(char *s)
chuỗi chữ hoa puts(s);
Kết quả: TIN HOC
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 218
Bài tập
1. Cho biết kết quả của đọan chương trình sau:
char s1[20] = “Truong DH CNTP”, s2[10] = “Tp. HCM”,
input[30], s3[10];
strcpy(input, s1);
strcpy(s3, “aeiou”);
strcat(input, s2);
int n = strlen(input), k = 0;
printf(“Chuoi: %s”, input);
for(int i = 0; i < n; i++)
if(strchr(s3, input[i])) k++;
printf(“\nKet qua: %d”, k);
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 219
4. Một số thuật toán xử lý chuỗi
4.1. Một số thuật ngữ
Một chuỗi X là chuỗi con (substring) của một chuỗi Y nếu X là một chuỗi
chứa các ký tự liên tiếp của Y.
Ví dụ: ab và bc là 2 chuỗi con của abcd. Nhưng ac thì không phải là chuỗi con
của abcd
Một chuỗi X là tiền tố (prefix) của một chuỗi Y nếu X là chuỗi con của Y và X
xuất hiện ở đầu của chuỗi Y.
Ví dụ: ab là tiền tố của abcd, nhưng bc không phải là tiền tố của abcd.
Một chuỗi X là hậu tố (postfix) của một chuỗi Y nếu X là chuỗi con của Y và
X xuất hiện ở cuối của chuỗi Y.
Ví dụ: cd là hậu tố của abcd, nhưng bc không phải là hậu tố của abcd.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 220
4.2. Thuật toán tìm xâu con của chuỗi - Brute Force
Thuật toán Brute Force có ý nghĩa là kiểm tra một chuỗi P có nằm
trong một văn bản T hay không, nếu có thì nằm ở vị trí nào, và xuất
hiện bao nhiêu lần.
Ý tưởng:
Kiểm tra tất cả các vị trí trên văn bản từ 0 cho đến kí tự cuối cùng
trong văn bản. Sau mỗi lần thử, thuật toán Brute Force dịch mẫu
sang phải một ký tự cho đến khi kiểm tra hết văn bản.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 221
4.2. Thuật toán tìm xâu con của chuỗi - Brute Force (tt)
void Brute Force (chuỗi P, chuỗi T) // tìm P trong T
{
if ((len (P) = 0) or (len (T) = 0) or (len (P) > len (T))
P không xuất hiện trong T
else
{
Tính vị trí dừng STOP = len(T) – len(P)
Vị trí bắt đầu so sánh START = 0
// ta cần vị trí dừng vì ra khỏi STOP
//chiều dài P lớn hơn chiều dài đoạn T còn lại thì dĩ nhiên P không thuộc T
While(START <= STOP)
{
So sánh các ký tự giữa P và T bắt đầu từ START.
IF ( các ký tự đều giống nhau )
P có xuất hiện trong T tại vị trí start
Tăng START lên 1 // quay lại so sánh từ đầu của P với các ký tự của T từ vị trí START
}Dừng khi P xuất hiện trong T hoặc START > STOP
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 222
Ví dụ
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 223
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 224
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 225
Bài tập
2. Viết hàm đếm có bao nhiêu khoảng trắng trong chuỗi.
3. Nhập vào một chuỗi, xóa những khoảng trắng thừa trong chuỗi.
4. Viết hàm đổi những kí tự đầu tiên của mỗi từ thành chữ in hoa và
những từ không phải đầu câu sang chữ thường
5. Viết chương trình tìm kiếm tên trong chuỗi họ tên. Nếu có thì xuất ra là
tên này đã nhập đúng, ngược lại thông báo là đã nhập sai.
6. Viết hàm cắt chuỗi họ tên thành chuỗi họ lót và chuỗi tên.
Ví dụ: chuỗi họ tên là: “Nguyễn Văn A” cắt ra 2 chuỗi là chuỗi họ lót:
“NguyễnVăn” ,chuỗi tên là: “A”.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 226
Bài tập
7. Viết chương trình chèn 1 từ ở bất cứ vị trí nào người dùng yêu
cầu.
8. Viết 1 chương trình xoá một từ nào đó trong chuỗi.
Ví dụ: Chuỗi ban đầu: “KHOA CNTT”
Nhập: “CNTT”, và kết quả xuất ra: “KHOA”.
9. Nhập 1 chuỗi bất kì, liệt kê xem mỗi ký tự xuất hiện mấy lần.
10. Viết chương trình tìm kiếm xem ký tự nào xuất nhiện nhiều
nhất trong chuỗi.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 227
KỸ THUẬT LẬP TRÌNH
(PROGRAMMING TECHNIQUES)
Chương 4
KỸ THUẬT ĐỆ QUY
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 228
Nội dung
1. Giới thiệu
2. Khái niệm
3. Hàm đệ quy
a. đệ quy tuyến tính
b. đệ quy nhị phân
c. đệ quy phi tuyến
d. đệ quy lồng
e. đệ quy hỗ tương
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 229
1. Mở đầu
Đệ quy là kỹ thuật đưa bài toán hiện tại về một bài toán cùng loại, cùng tính chất
(đồng dạng) nhưng ở cấp độ thấp hơn. Quá trình này tiếp tục cho đến khi bài
toán được đưa về một cấp độ mà tại đó có thể giải được. Từ cấp độ này ta
lần ngược lại để giải các bài toán ở cấp độ cao hơn cho đến khi giải xong bài
toán ban đầu.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 230
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 231
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 232
2. Khái niệm
Kỹ thuật đệ quy: là kỹ thuật định nghĩa một khái niệm có sử dụng chính khái niệm
đang cần định nghĩa.
Hàm đệ quy: là hàm mà trong thân của nó có lệnh gọi lại chính nó dành cho đối tượng
ở cấp thấp hơn.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 233
Hàm đệ quy (tt)
Mọi hàm đệ quy đều có 2 phần
◦ Phần cơ sở (phần neo – trường hợp dừng)
◦ Là trường hợp khi thực hiện không cần kỹ thuật gọi đệ quy.
◦ Nếu hàm đệ quy không có phần này thì hàm sẽ bị lặp vô hạn và sinh lỗi
khi thực hiện.
◦ Phần đệ quy
◦ Là phần trong khi thực hiện có dùng kỹ thuật gọi đệ quy nhưng dành cho
cấp độ thấp hơn.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 234
Ví dụ
Xét hàm đệ quy tính giai thừa của số nguyên n
n! = 1*2*3*4*5*... * n (định nghĩa tự nhiên)
tương đương: cách định nghĩa đệ quy
1, n 1 phần neo
n! =
n* (n-1)! phần đệ quy int tinhGiaiThua (int n)
{
int gt = 1; //khởi tạo ban đầu cho biến gt
if(n<=1)
gt = 1; //phần neo
else
gt = n * tinhGiaiThua(n-1); // phần
return gt; // đệ quy
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 235
Cách viết hàm đệ quy
Định nghĩa tác vụ đệ quy thế nào thì viết hàm đệ quy như vậy. Xét 2 trường hợp neo
(thực hiện không đệ quy) và trường hợp đệ quy.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 236
Bài toán dãy số Fibonacci
Fibonacci (1175 - 1250) được biết đến nhiều nhất với dãy số mang tên
ông - dãy số Fibonacci.
Dãy số này xuất hiện trong bài toán dưới đây viết trong cuốn Liber
Abaci: "Trong một năm, bắt đầu chỉ từ một đôi thỏ, bao nhiêu đôi thỏ
sẽ được sinh ra nếu mỗi tháng một đôi thỏ sinh được một đôi thỏ
con và cặp thỏ này lại đẻ được từ tháng thứ hai trở đi?"
Dãy số Fibonacci có nguồn gốc từ bài toán trên là một dãy sao cho mỗi
số hạng, kể từ sau số hạng thứ nhất, bằng tổng của hai số đứng ngay
trước nó. Dãy số đó là:
1,1,2,3,5,8,13,21,34,55,89,144....
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 237
Bài toán dãy số Fibonacci (tt)
Dãy số Fibonacci:
◦ F(n)= F(n-1)+F(n-2) (phần đệ quy)
◦ F(n)=1 với n<=2 (phần neo).
Hàm đệ quy tính số thứ n trong dãy số Finbonacci
int tinhSoFibonacci (int n)
{
if(n == 1) return 1;
if(n == 2) return 1;
return tinhSoFibonacci(n-1) + tinhSoFibonacci(n-2);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 238
Bài toán dãy số Fibonacci (tt)
Dãy số Fibonacci:
◦ F(n)= F(n-1)+F(n-2) (phần đệ quy)
◦ F(n)=1 với n<=2 (phần neo).
Hàm đệ quy tính số thứ n trong dãy số Finbonacci
int tinhSoFibonacci (int n)
{
int kq;
if(n<=2)
kq = 1;
else
kq = tinhSoFibonacci(n-1) + tinhSoFibonacci(n-2);
return kq;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 239
Bài tập Viết hàm đệ quy cho các bài toán sau:
Bài 1: Tính S(n)
◦ S(n) = 1+2 +3+… + n
Bài 3. Tìm trị phần tử thứ n của 1 cấp số nhân có số hạng đầu là a, công bội là q:
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 240
3. Các loại đệ quy
a. Đệ quy tuyến tính
b. Đệ quy nhị phân
c. Đệ quy phi tuyến
d. Đệ quy lồng
e. Đệ quy hỗ tương
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 241
3.1. Đệ quy tuyến tính
Đệ quy tuyến tính: bên trong thân hàm chỉ gọi hàm đệ quy 1 lần
Cấu trúc hàm dạng đệ quy tuyến tính
KieuDuLieu TenHam(Thamso)
{
if(Dieu Kieu Dung)
{
...
return Gia tri tra ve;
}
TenHam(Thamso)
...
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 242
Ví dụ: tính giai thừa
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 243
3.2. đệ quy nhị phân
Đệ quy nhị phân: bên trong thân hàm gọi hàm đệ quy 2 lần
Cấu trúc hàm dạng đệ quy nhị phân
KieuDuLieu TenHam(Thamso)
{
if(Dieu Kieu Dung)
{
...
return Gia tri tra ve;
}
...
TenHam(Thamso);
TenHam(Thamso);
...
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 244
Ví dụ: tính số trong dãy Fibonacci
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 245
3.3. Đệ quy phi tuyến
Đệ quy phi tuyến: nếu bên trong thân hàm có lời gọi lại chính nó được đặt bên trong
thân của vòng lặp.
Cấu trúc hàm dạng đệ quy phi tuyến
KieuDuLieu TenHam(Thamso) {
if(Dieu Kieu Dung) {
...
return Gia tri tra ve;
}
...
vòng lặp (điều kiện lặp) {
...
TenHam(Thamso);
...
}
return giá trị trả về;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 246
Ví dụ:bài tập 3 ở slide 13
long Un ( int n)
{
if (n < 6) return n;
long S = 0;
for (int i = 5; i > 0; i--)
S = S + Un(n-i);
return S;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 247
3.4. Đệ quy lồng
Hàm được gọi là đệ quy lồng nếu tham số trong lời gọi hàm là một lời gọi đệ quy.
Cấu trúc hàm dạng đệ qui lồng
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 249
3.5. Đệ quy tương hỗ
Trong đệ quy tương hỗ thì thường có 2 hàm, và trong thân của hàm này có lời gọi của hàm kia ,
điều kiện dừng và giá tri trả về của cả hai hàm có thể giống nhau hoặc khác nhau.
Cấu trúc hàm dạng đệ quy
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 250
Ví dụ
2 hàm đệ quy gọi nhau long Un( int n) {
Un = n , n < 5 if (n < 5) return n;
Un = Un-1 + Gn-2, n >= 5 return Un(n-1) + Gn(n-2);
}
Gn = n-3 , n < 8
long Gn(int n) {
Gn = Un-1 + Gn-2, n > 8
if (n < 8) return n-3;
return Un(n-1) + Gn(n-2);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 251
4.1 Ưu điểm của kỹ thuật đệ quy
Viết chương trình đơn giản dễ hiểu.
Có thể thực hiện một số lượng lớn các thao tác tính toán thông qua một đoạn chương trình
ngắn gọn.
Định nghĩa một tập vô hạn các đối tượng thông qua một số hữu hạn lời phát biểu.
Hầu hết các ngôn ngữ lập trình đều hỗ trợ kỹ thuật đệ quy.
Đưa ra phương án tối ưu, giải quyết được một số vấn đề mà vòng lặp thông thường không
giải quyết được.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 252
4.2 Nhược điểm của kỹ thuật đệ quy
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 253
BÀI TẬP
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 254
BÀI TẬP
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 255
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 256
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 257
5. Các bước tìm giải thuật đệ quy cho bài toán
Bước 2: Tìm các trường hợp cơ bản (phần neo) cùng giải
Bước 3: Tìm giải thuật giải trong trường hợp tổng quát (phần
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 258
5.1. Thông số hóa bài toán
Chuyển bài toán cần giải thành bài toán tổng quát.
Tìm ra các thông số cho bài toán tổng quát, đặc biệt là nhóm các thông số biểu thị kích thước
của bài toán.
𝐴𝑐𝑘𝑒𝑟 𝑚, 𝑛
𝑛 + 1, 𝑛ế𝑢 𝑚 = 0
Ví dụ:
=൞ 𝐴𝑐𝑘𝑒𝑟 𝑚 − 1,1 , 𝑛ế𝑢 𝑛 = 0 𝑣à 𝑚 > 0
𝐴𝑐𝑘𝑒𝑟(𝑚 − 1, 𝐴𝑐𝑘𝑒𝑟 𝑚, 𝑛 − 1 , 𝑛ế𝑢 𝑚 > 0 𝑣à 𝑛 > 0
-Bài toán tính n!: n là tham số tổng quát, biểu thị kích thước bài toán.
-Bài toán tính giá trị Ackermann’s là n và m là tham số tổng quát, biểu thị kích thước bài toán.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 259
5.2. Xác định thành phần cơ bản của bài toán
Xác định trường hợp neo của bài toán tổng quát.
Đây là các trường hợp tương ứng với các giá trị biên của các biến điều khiển mà giải
thuật giải không đệ quy. Thông thường trong trường hợp này giải thuật rất đơn giản.
Ví dụ:
• Giá trị giai thừa của n=0 hoặc n=1 là 1 (ví dụ 3.1)
• Giá trị hàm U(n)= n, với n<6 (ví dụ 3.5)
• Giá trị hàm Ackerman = n+1, với m=0 và n>0 (ví dụ 3.8)
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 260
5.3. Phân rã bài toán theo phương thức đệ quy
Tìm giải thuật giải bài toán trong trường hợp tổng quát bằng cách phân chia nó thành
các thành phần:
Mục đích của bước thực hiện này là tìm giải thuật để giải bài toán theo hướng đệ quy với
giá trị tham số tổng quát.
Ví dụ
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 261
5.4. Xây dựng giải thuật đệ quy cho một số bài toán
thông dụng
Bài toán 1. Tìm phần tử lớn nhất trong mảng 1 chiều a có n phần tử là số nguyên.
a: a[0], a[1], a[2]… a[n-2], a[n-1]
Phân tích bài toán:
◦ B1: Thông số hóa bài toán
◦ n chính là kích thước dữ liệu và là thông số tổng quát cho bài toán.
◦ B2: Xác định phần cơ bản (neo)
◦ Nếu n=1 thì mảng a có 1 phần tử a[0], và nó cũng chính là phần tử lớn nhất của mảng.
◦ B3: Xác định phần đệ quy (n>=2)
◦ n=2: max của mảng a chính là max(max(a[0]), a[1])
◦ n=3: max của mảng a chính là max (max(max(a[0]), a[1]),a[2])
◦ …
◦ n=n: max của mảng a chính là max (max(a[0]…a[n-2]),a[n-1])
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 262
Cài đặt
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 263
5.4. Xây dựng giải thuật đệ quy cho một số bài toán
thông dụng (tt)
Bài toán 2. Đếm số chữ số của số nguyên dương n.
Ví dụ: n=29084952106 kq = 11
Phân tích bài toán
Cài đặt
int DemSLChuSoCuaN (int n)
{
n = abs(n);
if(n < 10)
return 1;
return 1 + DemSLChuSoCuaN(n/10);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 264
5.4. Xây dựng giải thuật đệ quy cho một số bài toán thông dụng (tt)
a b c;
b a c
a c b
c a b
b c a
c b a
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 265
Các bước giải bài toán bằng đệ quy
Gọi hàm HoanVi(A, m) là hàm xuất tất cả các dạng hoán vị khác nhau của A có được
bằng cách hoán vị m thành phần đầu của dãy A.
Phần cơ sở bài toán:
m=1, hàm HoanVi (A,1) chỉ có 1 cách xuất A.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 266
Code mã hóa
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 267
Code mã hóa
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 268
Code mã hóa
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 269
6.2. Bài toán chia thưởng
Tìm số cách chia m vật (phần thưởng) cho n đối tượng (học sinh) có thứ tự.
Gọi Distribute là hàm tính số cách chia, khi đó Distribute là hàm có 2 tham số nguyên m và n
(Distribute(m, n)).
Gọi n đối tượng theo thứ tự xếp hạng 1, 2, 3, …, n ; Si là số vật mà đối tượng thứ i nhận
được.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 270
Ví dụ
Chia 5 phần thưởng cho 3 em học sinh theo yêu cầu của bài
toán chia thưởng, ta có các cách chia như sau:
5 0 0
4 1 0
3 2 0
3 1 1
2 2 1
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 271
Bước 1: Tìm cơ sở và cách giải
Nếu m=0 (số vật bằng 0) thì sẽ có 1 cách chia : mọi đối tượng đều
nhận 0 vật. Vậy Distribute(0, n) = 1 với mọi giá trị n.
Nếu n=0, m≠ 0 thì không có cách nào để thực hiện việc phân
chia. Vậy Distribute(m,0)=0 với mọi giá trị m≠ 0.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 272
Bước 2: Phân rã bài toán trong trường hợp tổng quát
Nếu số vật nhỏ hơn số đối tượng (m<n): n-m đối tượng xếp sau cuối sẽ không nhận
được gì trong mọi cách chia. Vậy khi m<n thì Distribute(m,n)=Distribute(m,m).
Nếu số vật lớn hơn hoặc bằng số đối tượng (m>=n) ta phân các cách chia thành 2
nhóm :
+ Nhóm 1 : không dành cho đối tượng thứ n (đối tượng xếp cuối) số vật nào cả (Sn
= 0). Vậy số cách = Distribute (m,n-1).
+ Nhóm 2 : có phân chia vật cho người cuối cùng (Sn>0). Ta thấy rằng số cách chia
của nhóm này bằng số cách chia (m-n) vật cho n đối tượng. Vì cách chia mà tất cả
đối tượng đều nhận được phần thưởng có thể thực hiện bằng cách cho mỗi đối
tượng nhận trước 1 vật, sau đó chia phần vật còn lại.
Distribute(m,n) = Distribute(m,n-1) + Distribute (m-n, n)
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 273
Mã hóa bài toán
int distribute(int m, int n)
{
if(n == 0) return 0;
if(n == 1) return 1;
if(m == 0) return 1;
if(m < n) return distribute(m, m);
return distribute(m, n-1) + distribute(m-n, n);
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 274
6.3. Bài toán tháp Hà Nội
Mô tả bài toán: chuyển n đĩa từ cột A sang cột C có cột B làm
trung gian, với yêu cầu sau:
◦ Mỗi lần di chuyển 1 đĩa.
◦ Trong quá trình chuyển các chồng đĩa ở các cột luôn được xếp đúng
(đĩa có kích thước bé đặt trên đĩa có kích thước lớn).
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 275
Bước 1. Tìm phần cơ sở và cách giải
n=1: HaNoiTower(1, A, B, C) : tìm dãy thao tác để chuyển
chồng 1 đĩa từ cột A sang cột C, lấy cột B làm cột trung gian.
𝑯𝒂𝑵𝒐𝒊𝑻𝒐𝒘𝒆𝒓 𝟏, 𝑨, 𝑩, 𝑪 ≡ 𝑴𝒐𝒗𝒆 𝑨, 𝑪
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 276
Bước 2. Phân rã bài toán
HaNoiTower(n, A, B, C): chuyển n đĩa từ cột A sang cột C, lấy cột B làm trung gian thành
dãy tuần tự 3 công việc sau:
◦ Chuyển (n-1) đĩa từ cột A sang cột B, lấy cột C làm trung gian : HaNoiTower (n-
1, A, C, B).
Move(1, A, C).
◦ Chuyển (n-1) đĩa từ cột B sang cột C, lấy A làm trung gian :
HaNoiTower (n-1, B, A, C)
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 277
Tổng quát giải thuật
𝐻𝑎𝑁𝑜𝑖𝑇𝑜𝑤𝑒𝑟 𝐴, 𝐵, 𝐶 ≡
{
𝑇ℎ𝑎𝑝_𝐻𝑁(𝑛 − 1, 𝐴, 𝐶, 𝐵) ;
Move (1, A, C) ;
𝑇ℎ𝑎𝑝_𝐻𝑁(𝑛 − 1, 𝐵, 𝐴, 𝐶) ;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 278
7. Khử đệ quy
Là quá trình chuyển đổi 1 giải thuật đệ quy thành giải thuật không đệ quy.
Chưa có giải pháp cho việc chuyển đổi này một cách tổng quát. Ta thường khử đệ
quy cho đệ quy tuyến tính.
◦ Dùng quan điểm đệ quy để tìm giải thuật cho bài toán.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 279
7.1. Khử đệ quy bằng vòng lặp
Ý tương: Lưu lại các trị của các lần tính toán trước làm dữ liệu cho việc tính toán của
lần sau.
return n * tinhGiaiThua(n-1);
}
Điều kiện biên
long tinhGiaiThua(int n)
{
long K=1; K chính là kết qủa của trị giai thừa
for (int i = 2; i <= n; i++) trước đó
K = K*i;
return K;
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 280
7.2. Khử đệ quy dùng Stack
Để thực hiện 1 hàm đệ quy, hệ thống phải tổ chức vùng nhớ
lưu trữ theo quy tắc LIFO (last in first out), còn gọi là Stack
(ngăn xếp). Như vậy thường rất tốn bộ nhớ, vì cách tổ chức
này mặc định cho mọi trường hợp.
Dựa vào ý tưởng trên, chúng ta chủ động tạo CTDL stack
riêng cho từng hàm cụ thể.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 281
Cấu trúc chuyển đệ quy thành khử đệ quy
Giả sử thủ tục đệ quy tuyến tính F(X) có cấu trúc như sau :
F(X)
{
if(dk(X)) //phần neo
A(X);
B(X);
F(X): hàm đệ quy có danh sách tham số là X.
F(f(X));
dk(X): biểu thức điều kiện theo X. C(X);
A(X), B(X), C(X): nhóm lệnh không đệ quy theo X. }
f(X): hàm thay đổi X để gọi đệ quy cấp thấp hơn.
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 282
Giải thuật khử đệ quy bằng Stack
F(X) {
taoStack(S);
//đưa các giá trị đã tính vào Stack
while(dk(X) == false) {
B(X);
//Xgt là giá trị cần lưu trữ sau mỗi
bước tính với X.
Push(S, Xgt);
X = f(X);
}
A(X);
//Lần lượt lấy các giá trị trong Stack ra.
while( Empty(S) == false) {
Pop(S, Xgt);
C(X);
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 283
VD: Xét hàm đệ quy chuyển số từ hệ 10 sang hệ 2
void convert10To2(int n)
{
if(n > 0)
{
convert10To2(n/2);
printf(“%d”, n%2);
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 284
Hàm khử đệ quy của Convert10To2
void convert10To2(int n) void convert10To2_NonRecursive (int n)
{ {
if(n > 0) int temp;
{ taoStack (S);
convert10To2(n/2);
while(n>0)
printf(“%d”, n%2);
} {
} temp = n%2;
push(S, temp);
n/=2;
}
while(Empty(S)==false)
{
pop(S, temp);
printf(“%d”, temp);
}
}
BỘ MÔN KHOA HỌC DỮ LIỆU & TRÍ TUỆ NHÂN TẠO 285
BỘ MÔN CÔNG NGHỆ PHẦN MỀM 286