1. Tìm Ước số chung lớn nhất (UCLN) của hai số ngun
Tìm ƯCLN của 2 số a, b. Băi tơn có thể được định nghĩa dưới dạng đệ qui như sau:
- nếu a = b thì UCLN = a
- nếu a > b thì UCLN(a, b) = ƯCLN(a-b, b) - nếu a < b thì UCLN(a, b) = UCLN(a, b-a)
Từ đó ta có chương trình đệ qui để tỉnh UCLN của a vă b như sau: int UCLN(int a, int b) // qui uoc a. b > 0
if (a < b) ƯCLN(a, b-a); if (a == b) return a;
if (a > b) UCLN(a-b, b); . }
2. Tính sơ hạng thứ n trong dêy Fibonaci
Tính sổ hạng thứ n của dêy Fibonaci lă dêy f(n) được định nghĩa: - f(0)-f(l)=l
- f(n) = f(n-l) + f(n-2) với n >= 2. /
* Tinh so fibonacci thu n */ ^include <iostream> long fibonacci(long n) { if (n = 0 II n “ 1) return n; else
return fibonacci(n - 1) + fibonacci(n - 2); }
3. Băi toân cổ Thâp Hă Nội
Chuyển thâp lă băi toân cổ nổi tiếng, nội dung như sau: Cho một thâp n tầng, đang xếp tại vị trí 1. Yíu cầu băi toân lă hêy chuyển toăn bộ thâp, sang vị trí 2 (cho phĩp sử dụng vị trí trung gian 3) theo câc điíu kiện sau đđy:,
+ Mỗi lần chỉ được chuyển một tầng trín cùng của thâp
+ Tại bất kỳ thời điểm tại cả 3 vị trí câc tầng thâp lớn hon phải năm dưới câc tầng thâp nhỏ hon.
Băi tôn chuyển thâp được minh hoạ bởi hình vẽ dưới đđy:
trước khi chuyển sau khi chuyển
Băi tơn có thể được đặt ra tổng quât hơn như sau: chuyển thâp từ vị trí source đến vị trí dest, trong đó source, dest lă câc tham số có thể lấy giâ trị lă 1, 2, 3 thể hiện cho 3 vị trí. Đối với 2 vị trí đi vă đến, dễ thấy vị trí trung gian tmp cịn lại sẽ lă vị trí (6 - source - dest) (vi source+dest+temp = 1+2+3 = 6). Từ đó để chuyển thâp từ vị trí source đen vị trí dest, ta có thể xđy dựng một câch chuyển đệ qui như sau:
+ chuyển 1 tầng từ source sang tmp,
+ chuyển n-1 tầng còn lại từ source sang dest, + chuyển trả tầng tại vị trí temp về lại vị trí dest.
Hiển nhiín nếu số tầng lă một thì ta chỉ phải thực hiện một phĩp chuyển từ
source sang dest.
Mỗi lần chuyển 1 tầng từ vị trí i đến j ta kí hiệu i j. Chương trình sẽ nhập văo input lă số tầng vă in ra câc bước chuyển theo kí hiệu trín.
Từ đó ta có thể xđy dựng chương trình chuyển thâp Hă Nội sử dụng hăm đệ qui sau đđy:
void move(int n, int source, int dest)
// n: sổ tầng ; di,den:vị trí đi, đến
if (n=l) cout« source « "==>" « dest « endl; else {
// 1 tầng từ source qua trung gian tmp
cout« source « "==>" « 6- source - dest « endl; // n-1 tầng từ source qua dest
move (n-1, source, dest);
// 1 tầng từ tmp về lại dest
cout« 6- source - dest « "==>" « dest « endl; }
Ví dụ nếu số tầng bằng 3 thì chương trình in ra kểt quả lă dêy câc phĩp chuyển sau đđy:
l->2,1^3,2->3,l->2,3-^1,3->2,1-^2. * có thể tính được số lần chuyển lă 2n - 1 với n lă số tầng.
❖ Tóm tắt chưong
V Đệ quy lă kỹ thuật tìm lời giải của một băi tơn bằng câch tìm lời giải của băi tôn nhỏ hơn.
J Hai điều kiện quan trọng để có thể giải băi tơn bằng đệ quy lă băi toân
tồn tại bước đệ quy vă phải có điều kiện dừng
J Một giải thuật đệ quy phải có ít nhất một điều kiện dừng. ■
J Ỉai bước giải một băi toân đệ quy lă Bước phđn tích vă Bước thí
ngược.
J Chỉ nín sử dụng đệ quy khi'không thể giải quyết băi tôn bằng vịng
lặp.
❖ Cđu hỏi củng cố
1. Câc phât biểu sau đđy lă đúng hay sai:
a. Điều kiện để có thể giải băi tơn bằng đệ quy lă phải có bước đệ quy
b. Câc băi tơn giải bằng đệ quy ln ln có thể chuyển sang băi tôn dạng lặp.
c. Trong đệ quy chỉ có duy nhất một điều kiện dừng.
d. Chương trình sử dụng giải thuật đệ quy thì tốn bộ nhớ mây tính vă thời gian thực thi lđu.
2. Cho hăm đệ quy sau: int mystery(int number) { ’
if(number==O)
return number; else
retum(number+mystery(number-1)); a. Hêy xâc định bước phđn tích.
b. Hêy xâc định bước thế.
c. Nếu lời gọi hăm mystery(O) lă đúng thì kết quả lă bao nhiíu, nếu sai thì giải thích vi sao?
d. Nếu lời gọi hăm mystery(5) lă đúng thì kết quả lă bao nhiíu, nếu sai thì giải thích vì sao?
e. Nếu lời gọi hăm mystery(-3) lă đúng thì kết quả lă bao nhiíu, nếu sai thì giải thích vì sao?
❖ Băi tập
1. Tìm ƯSCLN của hai số ngun
/* Tinh so ước số chung lớn nhất của hai số a,b */ #include <iostream>
using namespace std; int ƯCLN(int a, int b); int mainQ
int a,b;
cout«"Nhap vao hai so a, b: cin»a»b;
cout«"UCLN(" « a « "," «b«") = "« UCLN(a,b); system("pause");
return 0;
int UCLN(int a, int b) ' // qui uoc a, b > 0
if (a < b) UCLN(a, b-a); if (a = b) return a; if (a > b) UCLN(a-b, b);
1_______________________________________________________ 2. Viết chương trình nhập văo một số ngun n. Xuất ra măn hình số
ngun thứ n trong dêy số Fibonacci. /* Tinh so Fibonacci thu n */
#include <iostream> using namespace std; long fíbonacci(long n); int main() I long n; long fibonacci(long); cout«"Enter value of n: cin»n; cout«"Fibonacci(" « n « ") = " « íĩbonacci(n); system("pause"); return 0; long fíbonacci(long n)
if(n = O||n = 1) return n; else
return fibonacci(n - 1) + fibonacci(n - 2); 1
3. Viết chương trình hiện thực băi toân Thâp hă Nội. Biết nhập văo số đĩa vă in ra măn hình câc bước chuyển đĩa giữa câc cọc.
/
* Bai toan thap Ha Noi */ ^include <iostream>
t
using namespace std;
void chuyen(int n, int di, int den); int main()
{
int sotang;
cout« "So tang = "; cin » sotang; chuyen(sotang, 1, 2);
system("pause"); return 0;
}
void chuyen(int n, int di, int den) {
// n: số tầng ; di.demvị trí đi, đến
if (n== 1) cout« di ơ 'ã==> ô den « endl; else {
//1 tầng từ di qua trung gian
coutô di ô "==>'ã' ô 6-di-den ô endl; // n-1 tầng tìr di qua den
chuyen(n-l, di, den); // 1 tầng từ tg về lại den
cout« 6-di-den « "==>" « den « endl;
}
}
4. Sử dụng giải thuật đệ quy để viết chương trình Tính tổng câc sổ tử 1 đến n với n lă số nguyín dương do người dùng nhập văo.
S(n) = 1 +2+3 + . . .+n
5. Sử dụng giải thuật đệ quy để viết chương trình hiển thị dòng thứ n của tam giâc Pascal.
Dòng 0: 1 Dòng 1:11 Dòng 2:121 Dòng 3:1331 Dòng 4: 1 4 6 4 1 - a[i][0] = a[i][i] = l - a[i][k]=a[i-l][k-l] + a[i-l][k]
CHƯƠNG 4: CHUÔI KÝ Tự
Mục tiíu:
Sau khi học xong chương năy Sình viín có khả năng:
Khai bâo vă khởi tạo câc biến kiểu chuỗi
- Thực hiện câc thao tâc xử lý trín chuỗi như: Truy xuất từng kí tự, sao chĩp, nối chuỗi, chuyển đổi chữ hoa chữ thường, xử lý câc kí tự trắng - Sử dụng câc hăm thư viện có sẵn
I. Khâi niệm chuỗi
Ngơn ngữ C++ khơng có kiểu dữ liệu riíng dănh riíng cho chuỗi. Chuỗi lă một mảng một chiều mă mỗi phần tử trong mảng lă một kỹ tự.
1. Hằng chuỗi
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 chưỡi dượ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 Due” được lưu trữ trong bộ nhớ như sau:
Lưu ý “A” vă ‘A’ có ý nghĩa khâc nhau như sau:
T H u D u c \0
“A” lă một hằng chuỗi chiếm 2 byte 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 tôn ‘A’+l nhưng khơng thể lăm được như vậy đối với “A”.
2. Biến chuỗi
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ự.
II. Khai bâo biến chuỗiCú phâp: 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 hotenl[30];
char hoten2[30] = “Nguyen Van A”; char hoten3[] = “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 hotenl chiếm 30 byte chứa tối đa 29 ký tự.
Mảng hoten2 có độ dăi tối đa 29 ký tự vă được gân bằng chuỗi “Nguyen Van A”
Mang hoten3 có độ dăi bằng 13 do 12 phần tử của họ tín vă một ký tự ‘\0’ ở cuối mảng.
III. Nhập, xuất chuỗi■1. Nhập chuỗi ■1. Nhập chuỗi
1.1. Nhập chuỗi bẵng cin»
Cú phâp:
cin »tínchuỗi
Theo cđu lệnh năy mây sẽ thực hiện câc bước sau:
+ Bước 1: Kiểm tra bộ đệm băn phím, nếu bộ đệm rỗng thì chuyển qua bước
2. Ngược lại thì chuyển qua bước 3.
+ Bước 2: Cho phĩp người sử dụng nhập từ băn phím văo bộ đệm một chuỗi.
Q trình nhập sẽ kết thúc khi người sử dụng gõ phím Enter
+ Bước 3: Bỏ câc ký tự trắng đầu tiín của chuỗi hiện có trong bộ đệm.
»
+ Bước 4: Đọc từng ký tự trong bộ đệm vă gân cho từng phần tử của mảng.
Quâ trình gân sẽ kết thúc khi gặp dấu câch hoặc ký tự Enter. Câc ký tự chưa đọc - gân vẫn cón lưu trong bộ đệm
*
+ Bước 5: Thím ký tự null ‘\0’ văo byte tiếp theo của chuỗi.
+ Bước 6: Nếu dêy ký tự nhập có kích thước lớn hơn kích thước mảng -1 thì
chuỗi ký tự sẽ được gân cho biến có tín trong lệnh. Ngược lại thì kết quả khơng xâc định được vă thường khơng lường trước được kết quả.
Ví dụ:
/♦Chương trình minh họa nhập chuỗi băng cin »♦/ #inchide <iostream>
using namespace std; int main()
char hoten[20];
cout« "Nhap vao ho ten" «'\n'; cin » hoten;
cout« "Ho va ten = " « hoten «endl; system("pause");
return 0;
____________________________
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ẽ bị bỏ đi
1.2. Nhập chuỗi bằng cin.geto
Câch nhập chuỗi bằng cin vă ‘»’ có hạn chế lă khơng nhập được dấu câch vă dễ gđy ra lỗi treo mây. Có thể dùng hăm cin.geto để nhập từ băn phím chuỗi có chứa dấu câch
Cú phâp:
cin.getịtín biến chuỗi, số_kỷ tự_tổi_da_củamả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.
Ví dụ: ,
/*
Chương trình minh họa nhập'chuỗi bằng cin .get()*/ #include <iostream>
#include <conio.h> using namespace std; int main()
char hoten[20];
cout « "Nhap vao ho ten" «'\n'; cin.get(hoten,20);
cout« "Ho va ten = " « hoten «endl; system("pause");
return 0; }
Kít quả ký tự trăng có thí được lưu văo mảng::
■ C:\Users\AnhThu\DesHop\PracticeQDeQu
Hhap vao ho ten A
SMguyen Uan A
nõ ũa ten = Nguyen Uan A
Press any key to continue . . . _
Ví dụ sau minh họa trường, hợp đoạn chưong 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
^include <iostream> #include <conio.h> using namespace std; int main()
char hotenl[20], hoten2[20];
cout« "Nhap vao ho ten nguoi thu 1: " «'\n'; cin.get(hotenl,20);
cout« "Nhap vao ho ten nguoi thu 2: " «Ạn’; cin.get(hoten2,20);
cout« "Hai ten vua nhap la = " « hotenl «" va " «hoten2 « endl; system("pause");
return 0;
Kít quả cđu lệnh cin.get(hoten2, 20) bị trơi qua vă kít quả khơng chính xâc:
□3 C:\Windows\system32\cmd.exe '
Mhap uao ho ten nguoi thui: T
Nguyen Van ft
Nhap vao ho ten nguoi thu 2: .
Hai ten vua nhap la = Nguyen Uan A va (Press any key to continue . . . w
Để 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(l) để bỏ qua ký tự Enter hoặc dùng cin.get(c) để thu lấy ký tự enter.
1.3. Phương thức cin.ignore(l)
Thím lệnh cin.ignore(l)sau mỗi lệnh cin.getQ ^include <iostream>
#include <conio.h> using namespace std;
int main()
char hotenl[20], hoten2[20];
cout« "Nhap vao ho ten ngi thu 1: " «'\n'; cin.get(hotenl,20);
cin.ignore(l);//BỎ qua ký tự Enter
cout« "Nhap vao ho ten nguoi thu 2: " «'\n'; cin.get(hoten2,20);
cout« "Hai ten vua nhap la = " « hotenl «" va " «hotem system("pause");
return 0; }
ĩ« endl;
1.4. Phương thức cin.get(c)
Thím lệnh cin.get(c)sau mỗi lệnh cin.get()
#include <iostream> ^include <conio.h> using namespace std;
int main() {
char hotenl[20], hoten2[20], c;
cout« "Nhap vao ho ten nguoi thu 1: " «'\n’; cin.get(hotenl,20);
cin.get(c); //BỎ qua ký tự Enter
cout« "Nhap vao ho ten nguoi thu 2: " «'\n'; cin.get(hoten2,20);
cout« "Hai ten vua nhap la = " « hotenl «" va " «hoten2 « endl; system("pause"); return 0; Kít quả: 1.5. Nhập chuỗi bằng cin.getlineO Cú phâp: cin.getline(tín_biến, chiều_dăi_mảng)
Cđu lệnh năy cho phĩp đọc cả ký tự ENTER văo phần tử của mảng.
1.6. Xuất chuỗi:
Có thể xuất chuỗi bằng lệnh cơut« hoặc puts(). Hăm puts
IV.MỘt số hăm xử lý chuỗi:
Câc hăm xử lý chuỗi nằm trong thư viện “string.h”. Câc ký tự s viít tắt trong hăm lă chuỗi
1. strcpy(s,t);
Gân nội dung của xđu t cho xđu s (thay cho phĩp gân = không được dùng). Hăm sẽ sao chĩp toăn bộ nội dung của xđu t (kể cả kí tự kết thúc xđu) văo cho
xđu s. Để sử dụng hăm năy cần đảm bảo độ dăi của mảng s ít nhất cũng bằng độ dăi của mảng t. Trong trường hợp ngược lại kí tự kết thúc xđu sẽ khơng được ghi văo s vă điều năy có thể gđy treo mây khi chạy chương trình.
Ví dụ:
char s[10], t[10] ;
t = "Face" ; // không được dùng
s = t; // không được dùng
strcpy(t, "Face"); // được, gân "Face" cho t
strcpy(s, t); // được, sao chĩp t sang s
cout« s « " to " «t; // in ra: Face to Face
2. strcat(s, t);
Nối một bản sao của t văo sau s (thay cho phĩp +). Hiển nhiín hăm sẽ loại bỏ kí tự kết thúc xđu s trước khi nối thím t. Việc nối sẽ đảm bảo lấy cả kí tự kết thúc của xđu t văo cho s (nếu s đủ chỗ) vì vậy ta khơng cần thím kí tự năy văo cuối xđu. Tuy nhiín, hăm khơng kiểm tra xem liệu độ dăi của s có đủ chỗ đế nối thím nội dung, việc kiểm tra năy phải do người dùng đảm nhiệm. Ví dụ: char a[ 100] = "Man", b[4] = "toi";
strcat(a, “ va ”); strcat(a, b);
cout« a // Man vă toi
char s[ 100], t[ 100] = "Steve" ;
stmcpy(s, t, 3); s[3] = '\0'; // s = "Ste" strcat(s, "p"); // s = "Step"
cout«t« " goes "« s « " by " «s // Steve goes Step by Step
3. strcmp(s, t);
Hăm so sânh 2 xđu s vă t (thay cho câc phĩp toân so sânh). Giâ trị trả lại lă hiệu 2 kí tự khâc nhau đầu tiín của s vă t:
nếu sl < s2 : hăm trả lại giâ trị đm, nếu sl=s2 :hăm trả về giâ trọ 0
t
nếu sl > s2:hăm trả về giâ trị dương.
Trong trường hợp chỉ quan tđm đến so sânh bằng, nếu hăm trả lại giâ trị 0 lă 2 xđu bằng nhau vă nếu giâ trị trả lại khâc 0 lă 2 xđu khâc nhau.
Ví dụ:
if (strcmp(s,t)) cout « "s khâc t"; else cout« "s bằng t" ;
4. strcmpi(s, t) ;
Như strcmp(s, t) nhưng không phđn biệt chữ hoa, thường. Ví dụ:
char s[] = "Hă Nội", t[] = "hă nội";
cout « strcmpi(s, t); //0 (vì s = t)
5. strupr(s);
Hăm đổi xđu s thănh in hoa, vă cũng trả lại xđu in hoa đó. Ví dụ:
char s[10] = "Ha noi" ; cout« strupr(s); cout« s;
// HA NOI // HA NOI
6. strlwr(s);
Hăm đổi xđu s thănh in thuờng, kết quả trả lại lă xđu s. Ví dụ:
char s[10] = "Ha Nói" ; cout« strlwr(s); cout« s ;
// ha noi