Tài liệu giảng dạy Trang i LỜI TÁC GIẢ Quyển giáo trình này được biên soạn dựa theo đề cương môn học “Kỹ thuật lập trình 2” của Khoa Công nghệ thông tin Trường Cao đẳng Công nghệ Thủ Đ
Trang 1TRƯỜNG CAO ĐẲNG CÔNG NGHỆ THỦ ĐỨC
TÀI LIỆU GIẢNG DẠY | BẬC CAO ĐẲNG
KỸ THUẬT LẬP TRÌNH 2
| 2016 – Lưu h{nh nội bộ |
Trang 3Tài liệu giảng dạy Trang i
LỜI TÁC GIẢ
Quyển giáo trình này được biên soạn dựa theo đề cương môn học “Kỹ thuật lập trình 2” của Khoa Công nghệ thông tin Trường Cao đẳng Công nghệ Thủ Đức Giáo trình không tránh khỏi những sai sót về nội dung lẫn hình thức, nhóm biên soạn rất mong nhận được sự g p chân thành từ qu thầy cô và các em
sinh viên để giáo trình hoàn thiện hơn
Trang 4Tài liệu giảng dạy Trang ii
GIỚI THIỆU
Đ}y là học phần cơ sở chuyên ngành tiếp theo học phần Kỹ thuật lập trình 1, giúp sinh viên ngành Công nghệ thông tin, ngành Truyền thông & Mạng máy tính nâng cao kiến thức, kỹ năng cơ bản về lập trình Từ đó sinh viên ph|t triển tư duy về lập trình và có khả năng giải một số bài toán nâng cao trên máy tính Thông qua các hoạt động học tập, sinh viên còn có thể hoàn thiện dần tính chủ động, tích cực, khả năng tự học, tư duy hệ thống, kỹ năng l{m việc nhóm và thói quen tuân thủ c|c quy định làm việc trong môi trường chuyên nghiệp
Quyển gi|o trình n{y được biên soạn dựa theo đề cương môn học “Kỹ thuật
lập trình 2” của Khoa Công nghệ thông tin Trường Cao đẳng Công nghệ Thủ
Đức
Trang 5Tài liệu giảng dạy Trang iii
MỤC LỤC
1 1 GIỚI THIỆU M NG HAI CHI U 21.1|
KHAI B O M NG 21.2|
KHỞI TẠO TRỊ B ĐẦU 31.3|
TRUY XUẤT M NG 41.4|
TRUY N M ĐẾN HÀM 71.5|
2 B Ế 9 KHÁI NIỆM CON TR , ĐỊA CHỈ TRONG BỘ NHỚ 102.1|
KHAI BÁO, KHỞI TẠO BIẾN CON TR 102.2|
CÁC PHÉP TOÁN TRÊN CON TR 122.3|
CẤP PHÁT BỘ NHỚ ĐỘNG 152.4|
Ệ VÀ M NG 172.5|
3 ĐỆ 21 KHÁI NIỆ ĐỆ QUI 223.1|
THIẾT KẾ À ĐỆ QUI 243.2|
Ư ƯỢ Đ ỂM CỦ À ĐỆ QUI 263.3|
4 CHUỖI KÍ TỰ 27 KHÁI NIỆM CHUỖI 284.1|
KHAI BÁO BIẾN CHUỖI 284.2|
NHẬP, XUẤT CHUỖI 294.3|
MỘT SỐ HÀM XỬ LÝ CHUỖI 324.4|
CON TR VÀ CHUỖI 364.5|
5 Ấ 38 KHÁI NIỆM CẤU TRÚC 395.1|
KHAI BÁO BIẾN CẤU TRÚC 395.2|
TRUY XUẤT CÁC PHẦN TỬ TRONG CẤU TRÚC 415.3|
CON TR KIỂU CẤU TRÚC 465.4|
6 Ậ 52 KHÁI NIỆM TẬP TIN 536.1|
TRUY XUẤT TẬP TIN TUẦN TỰ 546.2|
TRUY XUẤT TẬ 666.3|
TÀI LIỆU THAM KH O 71
Trang 7Tài liệu giảng dạy Trang 1
Chương n{y nhằm giới thiệu cho sinh viên các khái niệm vè mảng hai chièu, khai báo và khởi tạo giá trị, truy xuát các phàn tử tre n mảng hai chièu, sử dụng mảng hai chièu làm tham só cho hàm
Trang 8Tài liệu giảng dạy Trang 2
GIỚI THIỆU MẢNG HAI CHIỀU
1.1|
Mảng là biến lưu trữ một nhóm các phàn tử có cùng kie u dư lie ̣u
Mảng hai chiều (Ma trận) thực chất là mảng một chiều trong đó mỗi phần tử của mảng là một mảng một chiều v{ được truy xuất bởi hai chỉ số dòng và cột
Mảng hai chiều thường dùng để biểu diễn dữ liệu kiểu bảng, kiểu dữ liệu này rất thích hợp cho c|c b{i b{i liên quan đến đồ thị, biểu diễn ảnh, …
Mảng nhiều chiều là mảng có từ hai chiều trở lên
Ví dụ: Mảng 2 chiều các số nguyên có 2 dòng, 3 cột (int arrM[2][3]) biểu diễn trong bộ nhớ máy tính là một vùng nhớ liên tục như sau:
arrM[0][0] arrM[0][1] arrM[0][2] arrM[1][0] arrM[1][1] arrM[1][2]
Dạng biểu diễn trên là một mảng một chiều có hai phần tử, mỗi phần tử này
là một mảng một chiều có 3 phần tử Tất cả tạo thành sáu vùng nhớ liên tục nhau trong bộ nhớ máy tính
Tuy nhiên, ta thường biểu diễn theo dạng bảng để dễ quan sát chỉ số dòng và cột hơn như sau:
arrM[0][0] arrM[0][1] arrM[0][2]
arrM[1][0] arrM[1][1] arrM[1][2]
float arrB[3][3], arrC[2][3];
Cách 2: typedef <kiểu ữ ệ > <tên kiểu>[<số dòng>][<số cột>];
<tên_kiểu> <tên biến>;
Trang 9Tài liệu giảng dạy Trang 3
<tên_kiểu> <tên biến 1>, <tên biến 2>;
Ví dụ:
typedef int arrA10x20[10][20];
typedef float arrA5x10[5][10];
arrA10x20 a, b; //Ma trận a, b là ma trận số nguyên có 10 dòng, 20 cột
<Kiểu cơ s ><Tên biến mảng>[<Số dòng>][<Số cột>]
= {{tập giá trị dòng 0}, {tập giá trị dòng 1},…, {tập giá trị n-1}};
Ví dụ 3: Khai báo mảng ký tự có tên là arrChar gồm 3 dòng và 2 cột
char arrChar[3][2] = {{‘A’ , ‘B’} , {‘C’ , ‘D’} , {‘E’ , ‘F’}};
Ví dụ 4: Khai báo mảng số thực có tên là arrDouble gồm 6 dòng và 5 cột; phần tử đầu tiên arrDouble[0][0] được khởi tạo là 2.0; các phần tử còn lại có giá trị khởi tạo là 0.0
double arrDouble[6][5] = {2.0};
Trang 10Tài liệu giảng dạy Trang 4
Câu lệnh sẽ tạo ra mảng arrDouble có 30 phần tử kiểu số thực Phần tử đầu tiên arrDouble[0][0] có giá trị là 2.0, các phần tử còn lại có giá trị là 0
GÁN TRỰC TIẾP GIÁ TRỊ/BIỂU THỨC CHO TỪNG PHẦN TỬ
Trang 11Tài liệu giảng dạy Trang 5
Truy xuất trực tiếp đến bất kỳ phần tử trong mảng
Ví dụ: Câu lệnh cout << arrA[2][3]; cho phép ta truy xuất ngay đến phần tử ở dòng thứ 2, cột thứ 3 của mảng để lấy giá trị và in ra màn hình
TRUY XUẤT TUẦN TỰ
Truy xuất từng cột theo hướng từ trên xuống dưới và từ trái sang phải -
for (int j = 0; j < nCot; j++)
Ta dùng hai vòng lặp for lồng nhau v{ thay đổi các biến điều khiển vòng lặp
để có c|c hướng duyệt đúng theo yêu cầu bài toán
Trang 12Tài liệu giảng dạy Trang 6
while (nColumn < 2)
{
while (nRow < 3) {
cout << arrMatrix[nRow][nColumn] << “ “;
nRow++;
} //end while nColumn++;
nRow = 0;
cout << endl;
} //end while
Hiển thị giá trị các phần tử trong mảng arrMatrix ra màn hình theo 3 dòng
và 2 cột theo cách duyệt mảng theo từng cột
Ví dụ 2: Sử dụng vòng lặp for để duyệt mảng theo từng dòng
for (int nColumn = 0; nColumn < 5; nColumn += 1)
cout << arrMatrix [nRow][nColumn] << “\t”;
//end for nRow += 1;
cout << endl;
Trang 13Tài liệu giảng dạy Trang 7
Có thể sử dụng mảng hai chiều như l{ một tham số cho hàm
Tham số kiểu mảng trong khai báo hàm giống như khai b|o biến mảng -
Ví dụ: void nhapMaTran(int a[50][100]);
Tham số kiểu mảng truyền cho h{m chính l{ địa chỉ của phần tử đầu tiên
void nhapMaTran(int a[][100]);
Số lượng phần tử thực sự truyền qua biến khác
-Ví dụ:
void xuatMaTran(int a[50][100], int m, int n);
void xuatMaTran(int a[][100], int m, int n);
Với m, n lần lượt là số dòng và số cột của ma trận
Lời gọi hàm
void nhapMaTran(int a[][100], int &m, int &n);
void xuatMaTran(int a[][100], int m, int n);
Chương trình sau cho phép nhập và xuất mảng orders Thao tác nhập và
xuất mảng được viết thành hai hàm con với tham số truyền vào là mảng
orders
#include <iostream>
using namespace std;
#define MAXR 30 //số dòng lớn nhất của mảng là 30
#define MAXC 20 //số cột lớn nhất của mảng là 20
Trang 14Tài liệu giảng dạy Trang 8
//Khai bao prototype
void inputMatrix(int arrA[][MAXC], int nBranch, int nMonth);
void outputMatrix(int arrA[][MAXC], int nBranch, int nMonth);
int main()
{
//khai báo mảng có nbranch dòng và nmonth cột
int arrOrders[MAXR][MAXC];
int nBranch = 0, nMonth = 0;
cout << "Enter number of branches: ";
cin >> nBranch;
cout << "Enter number of months: ";
cin >> nMonth;
inputMatrix(arrOrders, nBranch, nMonth);
outputMatrix(arrOrders, nBranch, nMonth);
for (int i = 0; i < nBranch; i++)
for (int j = 0; j < nMonth; j++) {
cout << "Number of orders for Branch "
//hàm xuất mảng hai chiều
void outputMatrix(int arrA[][ MAXC], int nBranch, int nMonth)
}//end for
}
Trang 15Tài liệu giảng dạy Trang 9
Chương n{y nhằm giới thiệu cho sinh viên các khái niệm vè con trỏ, địa chỉ bộ nhớ, nội dung trong bộ nhớ; khai báo và sử dụng con trỏ để cấp phát bộ nhớ động; dùng con trỏ làm tham só cho hàm; điều khiển mảng một chiều và mảng hai chiều bằng con trỏ
Trang 16Tài liệu giảng dạy Trang 10
KHÁI NIỆM CON TRỎ, ĐỊA CHỈ TRONG BỘ NHỚ
2.1|
Con trỏ là một biến chứa địa chỉ của biến khác Nếu pa là con trỏ chứa địa chỉ của biến a ta gọi pa trỏ tới a và a được trỏ bởi pa Thông qua con trỏ ta có thể làm việc được với nội dung của những ô nhớ mà pa trỏ đến
Hình 1: Minh họa biến pa trỏ đến biến a
KHAI BÁO, KHỞI TẠO BIẾN CON TRỎ
2.2|
CÚ PHÁP KHAI BÁO
2.2.1|
<kiể được trỏ> <*tên biến>;
Như vậy khai báo biến con trỏ cũng giống như khai b|o một biến thường ngoại trừ cần thêm dấu * trước tên biến (hoặc sau tên kiểu)
2.2.2.1| Toán tử địa chỉ &
Địa chỉ của biến là số thứ tự của byte đầu tiên trong một dãy các byte liên tiếp mà máy dành cho biến
Trang 17Tài liệu giảng dạy Trang 11
Trong C/C++, toán tử & là toán tử một ngôi trả về địa chỉ của biến sau nó Ví
Trang 18Tài liệu giảng dạy Trang 12
Nếu x l{ mảng ta viết: p = x hoặc p = &x[0], nghĩa l{ p chứa gi| trị l{ địa chỉ phần tử đầu tiên của mảng x
Không gán p cho một hằng địa chỉ cụ thể Ví dụ viết p = 200 là sai
-Phép toán * cho phép lấy nội dung nơi p trỏ đến, ví dụ để gán nội dung nơi
-p trỏ đến cho biến f ta viết f = *-p
Nếu p = &x thì x = *p Từ đó nếu p trỏ đến x thì bất kỳ nơi n{o xuất hiện x
-đều có thể thay được bởi *p v{ ngược lại
Không nên sử dụng con trỏ khi chưa được khởi tạo vì kết quả sẽ không
-lường trước được
CÁC PHÉP TOÁN TRÊN CON TRỎ
2.3|
PHÉP TOÁN GÁN
2.3.1|
Gán địa chỉ một biến cho con trỏ: p = &x;
Phép gán giữa hai con trỏ: p = q; (sau phép toán gán này p, q chứa cùng một địa chỉ, cùng trỏ đến một vùng nhớ)
Ví dụ:
int i = 10; // khai b|o v{ khởi tạo biến i = 10
int *p, *q, *r; // khai b|o 3 con trỏ nguyên p, q, r
Trang 19Tài liệu giảng dạy Trang 13
p += 5;
cout << *p; // *p = a[5] = 6;
q = p - 4;
cout << *q; // q = a[1] = 2;
for (int i = 0; i < 100; i++)
cout << *(p + i);// in toàn bộ mảng a
2.3.2.2| Phép toán ++, :
p++, p , ++p, p: tương tự p+1 và p-1, có chú ý đến tăng (giảm) trước, sau
Ví dụ sau minh hoạ kết quả kết hợp phép tự tăng giảm với lấy giá trị nơi con trỏ trỏ đến a là một mảng gồm 2 số, p là con trỏ trỏ đến mảng a Các lệnh
dưới đ}y được q ước à độc lập với nhau (tức lệnh sau không bị ảnh
hưởng bởi lệnh trước, đối với mỗi lệnh p luôn luôn trỏ đến phần tử đầu (a[0]) của a
int a[2] = {3, 7};
int *p = a;
(*p)++; // tăng (sau) gi| trị nơi p trỏ: tăng a[0] th{nh 4
++(*p); // tăng (trước) giá trị nơi p trỏ: tăng a[0] thành 4
*(p++); // lấy giá trị nơi p trỏ (3) v{ tăng trỏ p
*(++p); // tăng trỏ p, p->a[1] và lấy giá trị nơi p trỏ (7)
Phân biệt *(p++) và *(++p):
o Các phép toán tự tăng giảm cũng l{ một ngôi, mức ưu tiên của chúng l{ cao hơn c|c phép to|n hai ngôi kh|c v{ cao hơn phép lấy giá trị (*) Cụ thể:
Ví dụ:
int i, j; // khai báo 2 biến nguyên i, j
int *p, *q; // khai báo 2 con trỏ nguyên p, q
p = &i; // cho p trỏ tới i
q = &j; // cho q trỏ tới j
Trang 20Tài liệu giảng dạy Trang 14
cout << &i; // hỏi địa chỉ biến i
cout << q; //hỏi địa chỉ biến j (thông qua q)
*q = 5; // gán j bằng 5 (thông qua q)
i++; cout << i; // tăng i v{ hỏi i, i = 3
(*q)++; cout << j; // tăng j (thông qua q) v{ hỏi j, j = 6
(*p) = (*q) * 2 + 1; // gán lại i (thông qua p)
cout << i; // 13
Qua ví dụ trên ta thấy mọi thao tác với i l{ tương đương với *p, với j l{ tương đương với *q và ngược lại
2.3.2.3| Hiệu của 2 con trỏ
Phép toán này chỉ thực hiện được khi p và q là hai con trỏ cùng trỏ đến các phần tử của một dãy dữ liệu n{o đó trong bộ nhớ (ví dụ cùng trỏ đến mo ̣t mảng dữ liệu) Khi đó hiệu p - q là số phần tử giữa p và q (chú ý p - q không phải là hiệu của hai địa chỉ mà là số phần tử giữa p và q)
Ví dụ: giả sử p và q là 2 con trỏ nguyên, p có địa chỉ 200 v{ q có địa chỉ 208 Khi đó p - q = -4 và q - p = 4 (4 là số thành phần nguyên từ địa chỉ 200 đến 208)
2.3.2.4| Phép toán so sánh
C|c phép to|n so s|nh cũng được áp dụng đối với con trỏ, thực chất là so sánh giữa địa chỉ của hai vùng nhớ được trỏ bởi các con trỏ này Thông thường các phép so sánh <, <=, >, >= chỉ áp dụng cho hai con trỏ trỏ đến phần tử của cùng một mảng dữ liệu n{o đó Thực chất của phép so sánh này chính là so sánh chỉ số của hai phần tử được trỏ bởi hai con trỏ đó
Trang 21Tài liệu giảng dạy Trang 15
CẤP PHÁT BỘ NHỚ ĐỘNG
2.4|
CẤP PHÁT ĐỘNG VÀ CẤP PHÁT TĨNH
2.4.1|
Cấp phát tĩnh (static memory allocation): khi khai báo biến, mảng, cấu
trúc bắt buộc phải khai b|o trước cần bao nhiêu dung lượng bộ nhớ để lưu trữ Trong suốt quá trình thực thi chương trình, khối lượng bộ nhớ này sẽ bị chương trình chiếm dụng và không thể thay đổi kích thước Điều này dẫn đến việc hao tốn bộ nhớ m|y tính khi chương trình sử dụng không hết hoặc chương trình không thể tiếp tục chạy trong trường hợp dung lượng bộ nhớ d{nh cho chương trình đ~ bị dùng hết
Cấp phát động (dynamic memory allocation): khắc phục được hạn chế
của việc cấp ph|t tĩnh, khi dùng kỹ thuật cấp ph|t động lập trình viên không cần phải khai b|o trước kích thước biến mảng, cấu trúc Khi chương trình cần bao nhiêu sẽ cấp phát bấy nhiêu và có thể giải phóng bộ nhớ khi không cần dùng đến nữa
Trang 22Tài liệu giảng dạy Trang 16
q = new float[100]; // cấp phát vùng nhớ chứa được 100 số thực
Khi gặp toán tử new, chương trình sẽ tìm trong bộ nhớ một lượng ô nhớ còn trống và liên tục với số lượng đủ theo yêu cầu và cho p trỏ đến địa chỉ (byte đầu tiên) của vùng nhớ này Nếu không có vùng nhớ với số lượng như vậy thì việc cấp phát là thất bại và p = NULL (NULL là một địa chỉ rỗng, không xác định) Do vậy ta có thể kiểm tra việc cấp phát có thành công hay không thông qua kiểm tra con trỏ p bằng hay khác NULL
2.4.1.2| Toán tử thu hồi bộ nhớ delete
Để giải phóng bộ nhớ đ~ cấp phát cho một biến (khi không cần sử dụng nữa)
ta sử dụng câu lệnh delete
delete p; // p là con trỏ được cấp phát bằng toán tử new
v{ để giải phóng toàn bộ mảng được cấp pháp thông qua con trỏ p ta dùng câu lệnh:
int *head, *p, *q, n, tam;// head trỏ đến (đ|nh dấu) đầu dãy
cout << "Cho biết số số hạng của dãy: ";
cin >> n;
head = new int[n]; // cấp phát bộ nhớ chứa n số nguyên
for (p = head; p < head + n; p++) // nhập dãy
Trang 23Tài liệu giảng dạy Trang 17
{
for (q = p + 1; q < head + n; q++)
if (*q < *p) {
2.5.1.1| Con trỏ trỏ đến mảng một chiều
int a[MAX]; //khai báo mảng một chiều a
int *p; //khai báo con trỏ p
p = a; // hoặc p = &a[0]: p đến phần tử a[0]
2.5.1.2| Con trỏ trỏ đến mảng hai chiều
int a[MAXR][MAXC]; //khai báo mảng hai chiều a
int *p; //khai báo con trỏ p
Tương tự, nếu p trỏ đến mảng a thì p+i l{ địa chỉ thành phần thứ i của mảng
a v{ do đó *(p+i) = a[i] = *(a+i)
Nếu i = 2 thì *(p + 2) = a[2] = *(a + 2) = giá trị phần tử thứ ba của mảng a
Trang 24Tài liệu giảng dạy Trang 18
Hình 3: Minh họa con trỏ trên mảng một chiều
Để in toàn bộ mảng thông qua con trỏ, ta viết như sau:
C|ch 2: thay đổi p
-for (p = a; p <= a + 4; p++) cout << *p;
Đối với cách 2 con trỏ sẽ dịch chuyển dọc theo mảng a bắt đầu từ địa chỉ a (phần tử đầu tiên) đến phần tử cuối cùng Tại bước thứ i, p sẽ trỏ vào phần
tử a[i], do đó ta chỉ cần in giá trị *p Để kiểm tra khi n{o p đạt đến phần tử cuối cùng, ta có thể so sánh p với địa chỉ cuối mảng chính l{ địa chỉ đầu mảng cộng thêm số phần tử trong a và trừ 1 (tức a+4 trong ví dụ trên)
SỬ DỤNG CON TRỎ ĐỂ TRUY XUẤT CÁC PHẦN TỬ TRÊN MẢNG
2.5.3|
HAI CHIỀU
Thực tế, bộ nhớ m|y tính lưu trữ các phần tử của mảng hai chiều thành một dãy các ô nhớ liên tiếp giống như mảng một chiều Vì vậy, việc sử dụng con trỏ để điều khiển mảng hai chiều có m dòng và n cột tương tự như với mảng một chiều có mxn phần tử
Cho p trỏ đến mảng hai chiều a[3][3].Như vậy:
Trang 25Tài liệu giảng dạy Trang 19
Như vậy để truy cập đến phần tử thứ a[i][j] ta dùng phép toán *(p+ i*m + j),
Ví dụ: ta viết lại ứng dụng của Công ty Mambo trong chương 1 mục 1.5 nhưng sử dụng con trỏ p để thao tác trên mảng mảng thay vì là thao tác trực tiếp trên mảng orders Như vậy giá trị phần tử orders[region][month] sẽ được thay bằng *( *(p+region*3+month)
for (int nRegion = 0; nRegion < 4; nRegion += 1)
for (int nMonth = 0; nMonth < 3; nMonth += 1)
Trang 26Tài liệu giảng dạy Trang 20
cout << "Region " << nRegion + 1
system("pause");
return 0;
}
Trang 27Tài liệu giảng dạy Trang 21
Chương n{y nhằm giúp sinh vie n có the pha n loại đe ̣ quy, nha ̣n biét các bài toán đe ̣ quy và sử dụng ky thua ̣t đe ̣ quy đe giải mo ̣t só bài toán đơn giản
Trang 28Tài liệu giảng dạy Trang 22
KHÁI NIỆM ĐỆ QUI
Ví dụ, bài toán tính tổng từ 1 đến n, với n là số nguyên không âm:
Tính S (n) = 1 + 2 + 3 + … + (n – 1) + n
Ta nhận thấy: 1 + 2 + 3 + … + (n – 1) = S (n – 1)
S (n) = S (n – 1) + n: Bước đệ qui
Hơn nữa, S (0) = 0: Điều kiện dừng
Vậy, bài toán tồn tại bước đệ qui v{ có điều kiện dừng
Một h{m được gọi l{ đệ qui nếu bên trong thân của h{m đó có lời gọi hàm lại chính nó một cách trực tiếp hay gián tiếp
Khi thực hiện một h{m đệ qui, hàm sẽ phải chạy rất nhiều lần, trong mỗi lần chạy chương trình sẽ tạo một tập biến cục bộ mới trên ngăn xếp (c|c đối số, các biến riêng khai b|o trong h{m) độc lập với lần chạy trước đó, do đó dễ g}y tr{n ngăn xếp Vì vậy đối với những bài toán có thể giải được bằng phương ph|p lặp thì không nên dùng đệ qui
PHÂN LOẠI ĐỆ QUI
… … Lời gọi Hàm2 …
… … }
ĐQ gián tiếp
… Hàm2(…) {
… … Lời gọi Hàmx …
… … }
Trang 29Tài liệu giảng dạy Trang 23
Trang 30Tài liệu giảng dạy Trang 24
Bước 1 - Phân tích: Ph}n tích b{i to|n th{nh b{i to|n đồng dạng nhưng đơn
giản hơn v{ dừng lại ở b{i to|n đồng dạng đơn giản nhất có thể x|c định ngay kết quả
Bước 2 - Thế ngược: Xác định kết quả b{i to|n đồng dạng từ đơn giản đến
phức tạp để có kết quả cuối cùng
CẤU TR C HÀM ĐỆ QUI
3.2.3|
Một h{m thông thường gồm 2 phần sau:
<Kiểu_trả_về> <Tên hàm>(<Tham số>)
Trang 31Tài liệu giảng dạy Trang 25
{
if (<Điều kiện dừng>)
{
… return <Giá trị>;
Phần đệ qui (recursion step): phần sử dụng thuật
to|n đang được định nghĩa
Để tính n! ta có thể dùng phương ph|p lặp như sau:
long tinhGiaiThua (int n)
if (n==0) return 1; //điều kiện dừng
return tinhGiaiThua(n-1)*n; // bước đệ qui
if (n==0) return 1; //điều kiện dừng
return tinhGiaiThua(n-1)*n; // bước đệ qui
}
int main()
{
Trang 32Tài liệu giảng dạy Trang 26
Tiết kiệm thời gian thực hiện m~ nguồn
Một số b{i to|n rất khó giải nếu không dùng đệ qui
Nhược điểm:
- Tốn nhiều bộ nhớ, thời gian thực thi l}u
Một số tính to|n có thể bị lặp lại nhiều lần
Trang 33Tài liệu giảng dạy Trang 27
Trang 34Tài liệu giảng dạy Trang 28
KHÁI NIỆM CHUỖI
4.1|
Chuỗi ký tự là một mảng một chiều mà mỗi phần tử trong mảng là một ký tự
HẰNG CHUỖI
4.1.1|
Hằng chuỗi là một hoặc một số các ký tự đặt trong cặp dấu nháy kép Trong
bộ nhớ hằng chuỗi được lưu th{nh một dãy byte liên tục và kết thúc bằng ký
tự \0 Ví dụ chuỗi “Thu Duc” được lưu trữ trong bộ nhớ như sau:
Lưu ý “A” v{ ‘A’ có ý nghĩa kh|c nhau như sau:
“A” l{ một hằng chuỗi chiếm 2 buyte trong bộ nhớ
‘A’ l{ một ký tự nên chỉ chiếm 1 byte trong bộ nhớ Vì vậy có thể thực hiện phép to|n ‘A’+1 nhưng không thể l{m được như vậy đối với “A”
BIẾN CHUỖI
4.1.2|
Biến chuỗi là biến có thể nhận những giá trị chuỗi khác nhau Muốn sử dụng biến chuỗi chúng ta phải khai báo nó Biến chuỗi được khai báo theo quy cách khai báo một mảng các ký tự
KHAI BÁO BIẾN CHUỖI
4.2|
Cú pháp:
char <tên x}u>[độ dài]; // không khởi tạo
char <tên x}u>[độ dài] = xâu kí tự; // có khởi tạo
char <tên xâu>[] = xâu kí tự; // có khởi tạo
Ví dụ:
char szHoTen1[30];
char szHoTen2[30] = “Nguyen Van A”;
char szHoTen3[] = “Nguyen Van A”;
Các câu lệnh khai báo trên tạo ra các mảng ký tự có độ dài khác nhau Cụ thể: Mảng szHoTen1 chiếm 30 byte chứa tối đa 29 ký tự
Trang 35Tài liệu giảng dạy Trang 29
Kết quả như sau:
Trường hợp có khoảng trắng thì các ký tự phía sau khoảng trắng đầu tiên sẽ
Trang 36Tài liệu giảng dạy Trang 30
cin.get(tên biến chuỗi , số_ký tự_tố _đa_của_mảng)
Theo câu lệnh này máy sẽ thực hiện c|c bước tương tự cin >> nhưng cho phép đọc ký tự khoảng trắng vào phần tử của chuỗi
Kết quả ký tự trắng có thể được lưu v{o mảng::
Ví dụ sau minh họa trường hợp đoạn chương trình sử dụng nhiều lệnh cin.get() liên tiếp nhau gây nên hiện tượng trôi câu lệnh đọc chuỗi
char szHoten1[20], szHoten2[20];
cout << "Nhap vao ho ten nguoi thu 1: " << "\n";
Trang 37Tài liệu giảng dạy Trang 31
Để khắc phục tình trạng này ta dùng một trong hai cách là dùng hàm cin.ignore(1) để bỏ qua ký tự Enter hoặc dùng cin.get(c) để thu lấy ký tự enter
char szHoten1[20], szHoten2[20];
cout << "Nhap vao ho ten nguoi thu 1: " << "\n";
cin.get(szHoten1,20);
cin.ignore(1); //Bỏ qua ký tự Enter
cout << "Nhap vao ho ten nguoi thu 2: " << "\n";
Trang 38Tài liệu giảng dạy Trang 32
int main()
{
char szHoten1[20], szHoten2[20], c;
cout << "Nhap vao ho ten nguoi thu 1: " << "\n";
cin.get(szHoten1,20);
cin.get(c); //Bỏ qua ký tự Enter
cout << "Nhap vao ho ten nguoi thu 2: " << "\n";
Ví dụ:
char s[10], t[10];
t = "Face"; // không được dùng
strcpy(t, "Face"); // được, gán "Face" cho t