Kĩ thuật lập trình và thuật toán (định hướng CS)
Tìm kiếm tuần tự, nhị phân; sắp xếp chọn, nổi bọt, chèn bằng Python và C++ kèm vết chạy; độ phức tạp thời gian, kiểm thử, lập trình có cấu trúc (định hướng CS).
Đây là trọng tâm của định hướng Khoa học máy tính (CS): phần II thường có một câu đúng/sai cho một chương trình sắp xếp hoặc tìm kiếm in song song Python và C++, hỏi trạng thái dãy sau từng lượt, số lần so sánh, đổi chỗ và độ phức tạp. Phần I cũng có thể hỏi độ phức tạp của một đoạn chương trình. Kĩ năng cốt lõi: chạy tay thuật toán chính xác.
Kiến thức trọng tâm
Bảng tổng hợp thuật toán
| Thuật toán | Ý tưởng | Điều kiện | Tốt nhất | Xấu nhất |
|---|---|---|---|---|
| Tìm kiếm tuần tự | Duyệt lần lượt từ đầu tới khi gặp | Không cần sắp xếp | ||
| Tìm kiếm nhị phân | So với phần tử giữa, bỏ một nửa | Dãy đã sắp xếp | ||
| Sắp xếp chọn | Mỗi lượt chọn phần tử nhỏ nhất đưa lên đầu đoạn chưa xếp | |||
| Sắp xếp nổi bọt | Đổi chỗ cặp kề nhau sai thứ tự, phần tử lớn "nổi" về cuối | nếu có dừng sớm | ||
| Sắp xếp chèn | Chèn từng phần tử vào đúng chỗ trong đoạn đầu đã xếp | khi dãy đã tăng |
Tìm kiếm nhị phân
def nhi_phan(a, x):
trai, phai = 0, len(a) - 1
while trai <= phai:
giua = (trai + phai) // 2
if a[giua] == x:
return giua
if a[giua] < x:
trai = giua + 1
else:
phai = giua - 1
return -1
a = [2, 5, 9, 14, 20, 27, 33, 41, 48]
print(nhi_phan(a, 33), nhi_phan(a, 10))#include <iostream>
#include <vector>
using namespace std;
int nhi_phan(vector<int>& a, int x) {
int trai = 0, phai = a.size() - 1;
while (trai <= phai) {
int giua = (trai + phai) / 2;
if (a[giua] == x)
return giua;
if (a[giua] < x)
trai = giua + 1;
else
phai = giua - 1;
}
return -1;
}
int main() {
vector<int> a = {2, 5, 9, 14, 20, 27, 33, 41, 48};
cout << nhi_phan(a, 33) << " " << nhi_phan(a, 10);
return 0;
}Chương trình in 6 -1. Vết khi tìm x = 10:
| Bước | trai | phai | giua | a[giua] | So sánh với 10 |
|---|---|---|---|---|---|
| 1 | 0 | 8 | 4 | 20 | lớn hơn → phai = 3 |
| 2 | 0 | 3 | 1 | 5 | nhỏ hơn → trai = 2 |
| 3 | 2 | 3 | 2 | 9 | nhỏ hơn → trai = 3 |
| 4 | 3 | 3 | 3 | 14 | lớn hơn → phai = 2 |
| 3 | 2 | trai > phai → dừng, trả về −1 |
Tìm x = 33 chỉ cần 2 bước (gặp 20 rồi 33). Với n phần tử, số bước tối đa khoảng : 1000 phần tử chỉ cần tối đa
10 bước, trong khi tìm tuần tự có thể phải so sánh cả 1000 phần tử.
Ba thuật toán sắp xếp đơn giản
Cả ba chương trình in dãy sau mỗi lượt của vòng lặp ngoài, chạy với dãy 6 3 8 2 5.
Sắp xếp chọn
def sap_xep_chon(a):
n = len(a)
for i in range(n - 1):
vt = i
for j in range(i + 1, n):
if a[j] < a[vt]:
vt = j
a[i], a[vt] = a[vt], a[i]
for x in a:
print(x, end=" ")
print()
sap_xep_chon([6, 3, 8, 2, 5])#include <iostream>
#include <vector>
using namespace std;
void sap_xep_chon(vector<int> a) {
int n = a.size();
for (int i = 0; i < n - 1; i++) {
int vt = i;
for (int j = i + 1; j < n; j++)
if (a[j] < a[vt])
vt = j;
swap(a[i], a[vt]);
for (int x : a) cout << x << " ";
cout << endl;
}
}
int main() {
sap_xep_chon({6, 3, 8, 2, 5});
return 0;
}Sắp xếp chèn
def sap_xep_chen(a):
for i in range(1, len(a)):
k = a[i]
j = i - 1
while j >= 0 and a[j] > k:
a[j + 1] = a[j]
j = j - 1
a[j + 1] = k
for x in a:
print(x, end=" ")
print()
sap_xep_chen([6, 3, 8, 2, 5])#include <iostream>
#include <vector>
using namespace std;
void sap_xep_chen(vector<int> a) {
for (int i = 1; i < (int)a.size(); i++) {
int k = a[i];
int j = i - 1;
while (j >= 0 && a[j] > k) {
a[j + 1] = a[j];
j = j - 1;
}
a[j + 1] = k;
for (int x : a) cout << x << " ";
cout << endl;
}
}
int main() {
sap_xep_chen({6, 3, 8, 2, 5});
return 0;
}Sắp xếp nổi bọt xem ở Dạng 2. Vết của cả ba thuật toán trên dãy 6 3 8 2 5:
| Sau lượt | Chọn | Nổi bọt | Chèn |
|---|---|---|---|
| 1 | 2 3 8 6 5 (đổi 6 và 2) |
3 6 2 5 8 (8 về cuối) |
3 6 8 2 5 (chèn 3) |
| 2 | 2 3 8 6 5 (3 đã đúng chỗ) |
3 2 5 6 8 |
3 6 8 2 5 (8 giữ nguyên) |
| 3 | 2 3 5 6 8 (đổi 8 và 5) |
2 3 5 6 8 |
2 3 6 8 5 (chèn 2 lên đầu) |
| 4 | 2 3 5 6 8 |
2 3 5 6 8 |
2 3 5 6 8 (chèn 5) |
Độ phức tạp thời gian
Độ phức tạp đánh giá số phép toán cơ bản theo kích thước dữ liệu n, viết dạng : bỏ hằng số, giữ số hạng bậc cao nhất. Hai vòng lặp lồng nhau → nhân; hai đoạn nối tiếp → lấy đoạn lớn hơn.
n = 8
s = 0
for i in range(n):
for j in range(i, n):
s = s + 1
d = 0
k = 1
while k < n:
k = k * 2
d = d + 1
print(s, d)In 36 3. Vòng lồng chạy lần → . Vòng while nhân đôi k nên chạy khoảng
lần → . Cả đoạn: .
| Độ phức tạp | Tên gọi | Ví dụ |
|---|---|---|
| Hằng số | Truy cập a[i], hoán đổi hai biến |
|
| Lôgarit | Tìm kiếm nhị phân | |
| Tuyến tính | Tìm tuần tự, tính tổng dãy | |
| Bình phương | Sắp xếp chọn, nổi bọt, chèn |
Kiểm thử và lập trình có cấu trúc
- Kiểm thử bằng nhiều bộ dữ liệu: trường hợp thông thường, biên (phần tử ở đầu, ở cuối, dãy một phần tử, dãy rỗng), đặc biệt (số âm, các giá trị bằng nhau, dãy đã sắp xếp). Kiểm thử phát hiện lỗi nhưng không chứng minh chương trình hết lỗi.
- Làm mịn dần: chia bài toán lớn thành các bước nhỏ, chi tiết hóa từng bước cho tới khi viết được thành lệnh.
- Mô đun hóa: tách mỗi nhiệm vụ thành một hàm (nhập, xử lí, in kết quả) → dễ đọc, dễ kiểm thử, dùng lại được; gom các hàm hay dùng thành thư viện.
Dạng bài thường gặp trong đề thi
Dạng 1: Chọn bộ dữ liệu kiểm thử phát hiện lỗi
Xem lời giải
Chương trình in 9 0. Với [3, 9, 4] kết quả đúng, nhưng với dãy toàn số âm [-5, -2, -9] hàm trả về 0 (giá trị khởi tạo) thay vì
−2. Lỗi: khởi tạo m = 0. Sửa: m = a[0]. Bộ dữ liệu đặc biệt (toàn số âm) mới lộ ra lỗi này.
Dạng 2: Câu đúng/sai về sắp xếp nổi bọt có dừng sớm
Xem lời giải
- a) Đúng. Lượt 1 (j = 0…3): đổi (2, 1) →
1 2 3 5 4; (2, 3), (3, 5) giữ nguyên; đổi (5, 4) →1 2 3 4 5. - b) Sai. Lượt 2 (j = 0…2) không đổi chỗ lần nào nên
doivẫn làFalse; chương trình in dòng thứ hai rồi dừng → 2 dòng. - c) Đúng. Lượt 1 có 4 phép so sánh, lượt 2 có 3 → .
- d) Sai. Không dừng sớm thì chạy đủ 4 lượt (in 4 dòng), nhưng dãy đã tăng dần nên dòng cuối vẫn là
1 2 3 4 5. Dừng sớm chỉ giúp chạy nhanh hơn: trường hợp tốt nhất còn .
Dạng 3: Xác định độ phức tạp
Xem lời giải
Phần 1: . Phần 2: m lần, mỗi lần → . Hai phần nối tiếp nên tổng là ; nếu m không vượt quá n thì , độ phức tạp là .
Lỗi thường gặp
Mẹo làm bài
Tự kiểm tra nhanh
- Với dãy đã sắp xếp gồm 9 phần tử ở trên, tìm
x = 48bằng hàmnhi_phancần so sánh với những phần tử nào?
Xem đáp án
20 (giua = 4) → 33 (giua = 6) → 41 (giua = 7) → 48 (giua = 8): 4 lần, trả về vị trí 8.
- Sắp xếp chọn dãy
6 3 8 2 5(tăng dần) thực hiện bao nhiêu lần đổi chỗ thực sự (hai phần tử khác vị trí)?
Xem đáp án
2 lần: lượt 1 đổi 6 và 2, lượt 3 đổi 8 và 5. Lượt 2 và lượt 4 phần tử nhỏ nhất đã ở đúng chỗ.
- Dãy đã tăng dần gồm n phần tử. Sắp xếp chèn thực hiện bao nhiêu phép so sánh
a[j] > k?
Xem đáp án
phép: mỗi lượt chỉ so sánh một lần với phần tử ngay trước rồi dừng → .
- Đoạn
for i in range(n): for j in range(n): ...lồng nhau có độ phức tạp gì? Nếu thay vòng trong bằngfor j in range(5)thì sao?
Xem đáp án
. Vòng trong chạy 5 lần cố định → tổng lần → .
- Vì sao "chạy thử 10 bộ dữ liệu đều đúng" chưa chứng minh được chương trình không có lỗi?
Xem đáp án
Kiểm thử chỉ kiểm tra các trường hợp đã chọn; có thể còn trường hợp chưa thử (biên, đặc biệt) làm chương trình sai.
Làm 10 câu luyện tập về kĩ thuật lập trình và thuật toán (định hướng cs) và xem lời giải ngay sau mỗi câu.
Đề có câu hỏi về chủ đề này
Đề bám sát cấu trúc 2026: trí tuệ nhân tạo, thiết bị và giao thức mạng, HTML/CSS, đạo đức số, SQL và đọc chương trình Python/C++; phần đúng/sai gồm mạng gia đình, trang web lớp học, sắp xếp nổi bọt, học máy, phần mềm tạo trang web và CSDL thư viện.
Đề cơ bản củng cố kiến thức nền: khái niệm và ứng dụng AI, thiết bị và dịch vụ mạng, thẻ HTML và cách dùng CSS, an toàn tài khoản, hệ quản trị CSDL, rẽ nhánh và vòng lặp; phần đúng/sai gồm thư lừa đảo, AI trong trường học, tìm kiếm nhị phân, làm sạch dữ liệu, trang web làng nghề và CSDL câu lạc bộ thể thao.
Đề nâng cao, nhiều câu vận dụng: hệ chuyên gia, trình tự DNS – TCP – HTTP, quyền hình ảnh và giấy phép sử dụng, bộ chọn và tính kế thừa CSS, biểu mẫu, bảng gộp ô, phạm vi biến; phần đúng/sai gồm sự cố mã độc trong mạng trường, truy vấn nối ba bảng, kiểm thử sắp xếp chèn, phân loại k láng giềng gần nhất, trang web hội chợ khoa học và CSDL cửa hàng.