Luận bàn về Binary Search
Trước hết, cần biết rằng ở các nnlt ta hay có 1 built-in library cho binary search rồi, và ta hoàn toàn có thể sử dụng nó để giải quyết các câu hỏi về binary search. Chẳng hạn trong C++ ta có
// Return true if x is in arr
binary_search(arr.begin(), arr.end(), x)
// Return a pointer to the first array element whose value is at least x
lower_bound(arr.begin(), arr.end(), x)
// Return a pointer to the first array element whose value is larger than x
upper_bound(arr.begin(), arr.end(), x)
Nhưng 1 số vấn đề sẽ yêu cầu ta phải implement hẳn cái binary search ra nên ở post này tôi sẽ điểm lại cụ thể cơ chế của nó ra thôi, còn sau đó anh em có thể lựa chọn giữa built-in library với tự implement nó ra tùy theo yêu cầu đề bài.
Về chi tiết, đầu tiên binary search được dùng để giải quyết bài toán tìm 1 element trong 1 list đã được sắp xếp (nhớ nhé, sorted, sorted, sorted, đừng quên để đến lúc áp dụng vô mà chưa sorted lại ko biết sai vì đâu 💀). Nó tuân thủ 2 nguyên tắc sau:
- Lấy ra 1 element ở giữa trong 1 khoảng, nếu nó có giá trị bé hơn giá trị cần tìm (x), có nghĩa là x nằm trong khoảng bên phải của element đó (nghĩa là trong khoảng toàn giá trị lớn hơn).
- Ngược lại, nếu nó có giá trị lớn hơn x, thì nghĩa là x nằm trong khoảng bên trái của element đó
Áp dụng vào pseudocode, ta sẽ được như sau:
mid -> start + (end - start)/2
If arr[mid] equals to x, return found
Else if arr[mid] is less than x, start -> mid + 1
Else end -> mid - 1
Có 2 cách code cho binary search này (C++) , cho trước:
vector<int> arr { ... }; // Some sorted numbers
int x;
- Dùng đệ quy
bool binary_search(int start, int end) {
int mid = start + (end - start) / 2;
if (arr[mid] == x) return true;
else if (arr[mid] < x) return binary_search(mid+1, end);
else return binary_search(start, mid-1);
// If x is not in arr
return false;
}
- Dùng vòng lặp (hiện giờ tôi hay dùng cách này vì ta đều đc recommend ưu tiên khử đệ quy)
bool binary_search(int start, int end) {
while (start <= end) {
int mid = start + (end - start) / 2;
if (arr[mid] == x) return true;
else if (arr[mid] < x) start = mid + 1;
else end = mid - 1;
}
// If x is not in arr
return false;
}
Follow up
- Chú ý để tính mid tôi hay dùng start + (end - start) / 2 mà ko dùng (start + end) / 2 cho C++. Đó là vì ta cần tránh interger overflow, ví dụ end đề bài cho là
INTERGER_MAX, thì như vậy start + end nó sinh ra lỗi rồi. - Nhiều bài sẽ khó họ sẽ ko show hẳn cái list ra, cốt lõi ta cần nhận ra cái khoảng giá trị mà ta cần search là gì.
- Độ phức tạp O(logn), đây là độ phức tạp gần như là tốt nhất cho bài toán tìm giá trị trong 1 list đc sorted rồi, nếu muốn tốt hơn thì ta chỉ có cách dùng hash table thoy :v