Bài toán nhỏ hơn là một miền chỉ số
A đã được sắp xếp không giảm. Hàm tìm giá trị K trong miền từ left tới right, gồm cả hai đầu. Nếu tìm thấy, trả một chỉ số i có A[i]==K; nếu không có, trả −1. Nếu có nhiều giá trị bằng K, thuật toán dưới đây chỉ cần trả một vị trí, không hứa là vị trí đầu tiên.
Đầu tiên gọi với left=0, right=len(A)−1. Khi left>right, miền rỗng nên trả −1 trước khi đọc A[mid]. Khi còn phần tử, so sánh phần tử giữa với K để chỉ tìm tiếp một phía.
Loại phần tử giữa khi đã biết nó khác K
```python
def tim_nhi_phan(A, left, right, K):
if left > right:
return -1
mid = (left + right) // 2
if A[mid] == K:
return mid
if A[mid] < K:
return tim_nhi_phan(
A, mid + 1, right, K)
return tim_nhi_phan(
A, left, mid - 1, K)
```
Nếu A[mid]<K thì cả phần bên trái và phần tử giữa không thể là kết quả; tìm tiếp từ mid+1. Nếu A[mid]>K thì tìm bên trái tới mid−1. Hai cách đều thu hẹp miền. Return ở lời gọi tiếp chuyển kết quả tìm được về nơi gọi ban đầu.
Đếm cả lời gọi đầu và khoảng rỗng
Với A=[2,5,8,11,14] và K=17, tiến trình là:
| Miền [left,right] | mid | A[mid] | Tiếp theo |
|---|---|---|---|
| [0,4] | 2 | 8 | [3,4] |
| [3,4] | 3 | 11 | [4,4] |
| [4,4] | 4 | 14 | [5,4] |
| [5,4] | Không tính | Không đọc | Trả−1 |
Có **bốn lần vào hàm nếu tính cả lời gọi đầu và lời gọi trên miền rỗng**; có ba lời gọi tiếp từ trong thân hàm. Phải nêu rõ cách đếm trước khi so sánh số lần gọi.
Một phần tử khác với không còn phần tử
Với A rỗng, lời gọi đầu dùng left=0, right=−1. Chỉ kiểm left==right sẽ bỏ sót trường hợp này; cần kiểm miền rỗng trước khi truy cập A.
Cũng có thể thiết kế một biến thể dừng riêng khi left==right, rồi giữ mid trong nhánh trái. Với biến thể ấy vẫn cần nhánh left>right để xử lí A rỗng. Không đổi tùy ý mid−1 thành mid trong chương trình đang dùng nếu chưa kiểm lại cách thu hẹp miền và cơ sở.
Bài luyện
Theo bảng A=[2,5,8,11,14], K=17, có bao nhiêu lần vào hàm nếu tính cả lời gọi đầu và lời gọi trên miền rỗng?
Không có miền thứ năm sau khi trả −1.
Có bốn miền xuất hiện trong bảng.
3 chỉ đếm các lời gọi tiếp từ thân hàm.
Còn thiếu các miền tiếp theo.
Lời giải
Các lần vào hàm ứng với [0,4], [3,4], [4,4], [5,4]. Miền cuối rỗng và trả −1, nên tổng là4 theo cách đếm đã nêu.
Bài luyện
Trong hàm đang dùng, A[mid]<K và A đã xếp không giảm. Miền gọi tiếp nào phù hợp?
Chưa loại phần miền đã biết không phù hợp.
Không được đổi giá trị cần tìm để làm bài toán khác.
Các phần tử từ left tới mid đều không lớn hơn A[mid], nên nhỏ hơn K.
Miền ấy chứa các giá trị không thể bằng K trong trường hợp này.
Lời giải
Vì A đã xếp không giảm, mọi phần tử ở bên trái hoặc tại mid đều≤A[mid]<K. Do đó chỉ cần tìm tiếp ở [mid+1,right].
Bài luyện
Tìm trên A=[] bằng lời gọi left=0, right=-1. Việc nào cần xảy ra trước khi tính A[mid]?
A rỗng không có phần tử chỉ số 0.
0 và−1 không bằng nhau nên điều kiện ấy bỏ sót miền rỗng.
Tìm kiếm không cần sửa dữ liệu để có kết quả.
Miền rỗng không có phần tử để đọc.
Lời giải
A rỗng cho miền [0,−1]. Vì left>right, hàm trả −1 trước khi tính mid hay truy cập A; nhánh dừng này xử lí được cả trường hợp không có dữ liệu.
Bài luyện
A=[2,5,5,5,9] đã sắp xếp. Hàm trong bài tìm K=5 theo yêu cầu trả một chỉ số phù hợp. Kết luận nào đúng?
Chỉ số giữa2 có A[2]=5 và đáp ứng yêu cầu.
Yêu cầu chưa bắt tìm vị trí đầu tiên.
Hàm trả vị trí, không phải giá trị cần tìm.
Dãy không giảm vẫn có thể chứa các giá trị trùng.
Lời giải
Lời gọi đầu có mid=(0+4)//2=2. Vì A[2]=5, hàm trả2; đó là một chỉ số hợp lệ dù chưa phải số5 đầu tiên.
Đang học