Tìm kiếm trong mảng: Linear Search, Binary Search & lỗi hay gặp
Bài học giúp học sinh hiểu bản chất tìm kiếm tuyến tính và tìm kiếm nhị phân, biết chọn đúng thuật toán, tránh nhầm điều kiện “mảng đã sắp xếp”, đồng thời luyện các dạng bài thường gặp trong thi lập trình.
Mục tiêu bài học
- Hiểu cách tìm kiếm phần tử trong mảng bằng Linear Search và Binary Search.
- Phân biệt khi nào dùng tìm kiếm tuyến tính, khi nào dùng tìm kiếm nhị phân.
- Biết viết code C++ và Python cho các bài: tìm có/không, tìm vị trí, đếm số lần xuất hiện.
- Tránh các lỗi hay gặp: dùng binary search khi mảng chưa sắp xếp, sai chỉ số, vòng lặp vô hạn.
1. Hai kiểu tìm kiếm cơ bản
Linear Search
Duyệt lần lượt từng phần tử từ trái sang phải. Gặp phần tử cần tìm thì kết luận tìm thấy.
Dùng được với mảng chưa sắp xếp O(n)Binary Search
Mỗi bước lấy phần tử giữa, so sánh với giá trị cần tìm rồi bỏ đi một nửa khoảng không cần xét.
Rất nhanh O(log n) Chỉ dùng khi mảng đã sắp xếp| Tiêu chí | Linear Search | Binary Search |
|---|---|---|
| Điều kiện dùng | Mảng bất kỳ | Bắt buộc mảng đã sắp xếp tăng/giảm rõ ràng |
| Độ phức tạp | O(n) | O(log n) |
| Dễ code? | Dễ, ít lỗi | Nhanh nhưng dễ sai biên l, r, mid |
| Dạng bài phù hợp | Tìm trong dữ liệu nhỏ hoặc chưa sắp xếp | Tìm kiếm trong mảng đã sort, tìm first/last, lower_bound |
2. Linear Search - tìm kiếm tuyến tính
Ý tưởng: xét từng vị trí i. Nếu a[i] == x thì tìm thấy. Nếu duyệt hết mảng mà không thấy thì kết quả là -1.
Mô phỏng Linear Search
Mảng ví dụ: [4, 7, 2, 9, 7, 1], tìm x = 7.
Nhật ký
#include
using namespace std;
int main() {
int n, x;
cin >> n >> x;
vector a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int pos = -1;
for (int i = 0; i < n; i++) {
if (a[i] == x) {
pos = i; // vị trí đầu tiên tìm thấy
break; // dừng lại vì đã tìm thấy
}
}
if (pos == -1) cout << "Khong tim thay";
else cout << "Tim thay tai vi tri " << pos;
return 0;
}
n, x = map(int, input().split())
a = list(map(int, input().split()))
pos = -1
for i in range(n):
if a[i] == x:
pos = i
break
if pos == -1:
print("Khong tim thay")
else:
print("Tim thay tai vi tri", pos)
pos + 1.
Nếu đề yêu cầu chỉ số trong mảng C++/Python thì in pos.
3. Binary Search - tìm kiếm nhị phân
Binary Search chỉ đúng khi mảng đã được sắp xếp. Với mảng tăng dần:
nếu a[mid] < x thì tìm bên phải; nếu a[mid] > x thì tìm bên trái.
[4, 1, 9, 2] không thể binary search trực tiếp.
Mô phỏng Binary Search
Mảng ví dụ: [1, 3, 5, 7, 9, 11, 13, 15], tìm x = 11.
Nhật ký
#include
using namespace std;
int binary_search_pos(const vector& a, int x) {
int l = 0, r = (int)a.size() - 1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (a[mid] == x) return mid;
if (a[mid] < x) l = mid + 1;
else r = mid - 1;
}
return -1;
}
int main() {
int n, x;
cin >> n >> x;
vector a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int pos = binary_search_pos(a, x);
cout << pos;
return 0;
}
def binary_search_pos(a, x):
l, r = 0, len(a) - 1
while l <= r:
mid = (l + r) // 2
if a[mid] == x:
return mid
if a[mid] < x:
l = mid + 1
else:
r = mid - 1
return -1
n, x = map(int, input().split())
a = list(map(int, input().split()))
print(binary_search_pos(a, x))
mid = l + (r - l) / 2 thay vì (l + r) / 2.
Với dữ liệu rất lớn, cách này tránh nguy cơ tràn số.
4. Các dạng bài dễ nhầm
| Dạng bài | Cách xử lý đúng | Lỗi học sinh hay mắc |
|---|---|---|
Tìm xem x có xuất hiện không |
Linear Search hoặc Binary Search nếu mảng đã sort | Không dừng vòng lặp hoặc in sai vị trí |
Đếm số lần xuất hiện của x |
Duyệt toàn mảng hoặc dùng lower_bound/upper_bound nếu đã sort |
Dùng binary search thường chỉ tìm được 1 vị trí, không đếm đủ |
Tìm vị trí đầu tiên / cuối cùng của x |
Dùng biến lưu đáp án rồi tiếp tục thu hẹp trái/phải | Thấy a[mid] == x là trả về ngay, nên không chắc là đầu/cuối |
Tìm số nhỏ nhất lớn hơn hoặc bằng x |
Dùng lower_bound |
Nhầm với upper_bound |
Tìm số đầu tiên lớn hơn x |
Dùng upper_bound |
Nhầm với lower_bound |
#include
using namespace std;
int main() {
int n, x;
cin >> n >> x;
vector a(n);
for (int i = 0; i < n; i++) cin >> a[i];
int cnt = 0;
for (int value : a) {
if (value == x) cnt++;
}
cout << cnt;
return 0;
}
n, x = map(int, input().split())
a = list(map(int, input().split()))
cnt = 0
for value in a:
if value == x:
cnt += 1
print(cnt)
#include
using namespace std;
int main() {
int n, x;
cin >> n >> x;
vector a(n);
for (int i = 0; i < n; i++) cin >> a[i];
sort(a.begin(), a.end());
auto it1 = lower_bound(a.begin(), a.end(), x); // phần tử đầu tiên >= x
auto it2 = upper_bound(a.begin(), a.end(), x); // phần tử đầu tiên > x
int count_x = it2 - it1;
cout << count_x;
return 0;
}
from bisect import bisect_left, bisect_right
n, x = map(int, input().split())
a = list(map(int, input().split()))
a.sort()
left = bisect_left(a, x) # vị trí đầu tiên có giá trị >= x
right = bisect_right(a, x) # vị trí đầu tiên có giá trị > x
print(right - left)
5. Thực hành tương tác
Nhập một mảng và giá trị cần tìm. Hệ thống sẽ phân tích: mảng đã sắp xếp chưa, tìm thấy ở đâu, xuất hiện bao nhiêu lần, có thể dùng Binary Search không.
6. Lỗi thường gặp và cách tránh
Cách sửa: kiểm tra đề bài có nói “mảng tăng dần”, “đã sắp xếp” không. Nếu không có, phải
sort hoặc dùng Linear Search tùy yêu cầu.
Cách sửa: với mẫu cơ bản dùng
while (l <= r). Khi cập nhật phải là l = mid + 1 hoặc r = mid - 1.
C++/Python dùng chỉ số từ
0. Nhưng đề thi đôi khi yêu cầu “vị trí thứ mấy”, khi đó thường cần in i + 1.
Binary Search thường chỉ trả về một vị trí bất kỳ có
x. Nếu cần vị trí đầu/cuối, phải viết biến ans hoặc dùng lower_bound.
7. Bài tập luyện tập phân loại
Các bài tập được chia theo mức độ để học sinh không nhầm dạng.
Mức 1 - Cơ bản
- Nhập mảng gồm
nphần tử và giá trịx. InYESnếu x xuất hiện, ngược lại inNO. - In vị trí đầu tiên của
xtrong mảng. Nếu không có, in-1. - Đếm số lần xuất hiện của
xtrong mảng.
Mức 2 - Trung bình
- Mảng tăng dần. Dùng Binary Search để tìm vị trí của
x. - Mảng tăng dần có phần tử trùng nhau. Tìm vị trí đầu tiên của
x. - Mảng tăng dần có phần tử trùng nhau. Tìm vị trí cuối cùng của
x.
Mức 3 - Khá
- Cho mảng chưa sắp xếp. Hãy đếm số lần xuất hiện của
xsau khi sắp xếp bằnglower_boundvàupper_bound. - Cho mảng tăng dần. Tìm số nhỏ nhất lớn hơn hoặc bằng
x. - Cho mảng tăng dần. Tìm số đầu tiên lớn hơn
x.
Mức 4 - Nâng cao
- Cho
qtruy vấn, mỗi truy vấn hỏixcó trong mảng không. Mảng ban đầu có thể được sắp xếp trước. - Đếm số cặp
(i, j)sao choa[i] + a[j] = x. - Ứng dụng Binary Search trên đáp án: tìm giá trị nhỏ nhất thỏa mãn điều kiện.
💡 Gợi ý bài “tìm vị trí đầu tiên của x” bằng Binary Search
int first_pos(const vector& a, int x) {
int l = 0, r = (int)a.size() - 1;
int ans = -1;
while (l <= r) {
int mid = l + (r - l) / 2;
if (a[mid] >= x) {
if (a[mid] == x) ans = mid;
r = mid - 1; // tiếp tục tìm bên trái
} else {
l = mid + 1;
}
}
return ans;
}
8. Quiz kiểm tra nhanh
Câu 1. Binary Search dùng được trong trường hợp nào?
Câu 2. Độ phức tạp của Linear Search là gì?
Câu 3. Trong C++, hàm nào tìm phần tử đầu tiên lớn hơn hoặc bằng x trong vector đã sort?
Tổng kết
Linear Search đơn giản và dùng được với mọi mảng. Binary Search nhanh hơn rất nhiều,
nhưng chỉ đúng khi dữ liệu đã sắp xếp. Khi bài có nhiều truy vấn tìm kiếm,
hãy nghĩ đến việc sắp xếp trước và dùng binary_search, lower_bound, upper_bound.
➡ Tiếp theo: Sắp xếp và ứng dụng tìm kiếm trong các bài toán thực tế.
💳 Quét mã ủng hộ tuỳ tâm nhé!