Muốn mọi vị trí thì không dừng ở lần đầu
Với `[14, 8, 21, 8, 5]`, yêu cầu tìm **mọi** số 8 cần trả `[1, 3]`. Hàm tìm một vị trí ở L11 dừng ngay tại chỉ số 1, nên phải đổi đầu ra thành danh sách và tiếp tục duyệt:
```python
def tim_tat_ca(a, k):
vi_tri = []
for i in range(len(a)):
if a[i] == k:
vi_tri.append(i)
return vi_tri
print(tim_tat_ca([14, 8, 21, 8, 5], 8))
print(tim_tat_ca([14, 8, 21, 8, 5], 99))
```
Kết quả là `[1, 3]` rồi `[]`. Dãy rỗng cũng cho `[]`. Thuật toán phải xét toàn dãy để chắc đã lấy đủ vị trí, dù phần tử đầu đã khớp.
Dãy giảm thì đổi hướng bỏ nửa
Với dãy **đã sắp xếp giảm**, nếu phần tử giữa **lớn hơn** giá trị cần tìm, các giá trị ở bên trái còn lớn hơn hoặc bằng phần tử giữa; cần đi sang **phải**. Nếu phần tử giữa nhỏ hơn giá trị cần tìm thì đi sang trái.
```python
def tim_nhi_phan_giam(a, k):
left = 0
right = len(a) - 1
while left <= right:
mid = (left + right) // 2
if a[mid] == k:
return mid
if a[mid] > k:
left = mid + 1
else:
right = mid - 1
return -1
print(tim_nhi_phan_giam([30, 25, 20, 15, 10], 15))
```
Kết quả là `3`. Lần đầu mid=2 cho 20>15 nên sang phải, lần sau thấy 15 ở chỉ số 3. Nếu dãy có phần tử trùng, hàm nhị phân trên dãy đã sắp xếp chỉ trả **một** chỉ số khớp, không hứa luôn là vị trí đầu; cần thuật toán khác nếu bài toán yêu cầu mọi vị trí. Hàm cũng không tự kiểm dãy đã giảm.
Bài luyện
Với dãy `[14,8,21,8,5]`, hàm `tim_tat_ca(a,8)` trả gì?
Chỉ một vị trí, chưa đủ yêu cầu.
Hai lần gặp 8 ở chỉ số 1 và 3.
Đây là số thứ tự từ 1.
Dãy có hai số 8.
Lời giải
Hàm duyệt hết dãy và thêm từng chỉ số có a[i]==8; hai chỉ số đó là 1 và 3.
Bài luyện
Dãy giảm `[30,25,20,15,10]`, tìm 15. Sau khi xét số giữa 20, cần tìm tiếp ở đâu?
Bên trái chứa số lớn hơn hoặc bằng 20.
15 nhỏ hơn 20 nên nằm về phía phải của dãy giảm.
15 còn có thể nằm ở phía phải.
Dãy hiện đã có trật tự giảm thích hợp cho hàm giảm.
Lời giải
Dãy giảm nên các phần tử sau 20 nhỏ hơn 20. Đặt left=mid+1 để tiếp tục ở bên phải, rồi gặp 15.
Bài luyện
Vì sao hàm tìm nhị phân trong bài không bảo đảm trả mọi vị trí của một giá trị trùng?
Một lần khớp đã kết thúc hàm.
Dãy đã sắp xếp vẫn có thể có giá trị trùng.
Danh sách Python cho phép trùng.
Chỉ số đầu là 0.
Lời giải
Khi a[mid]==k, hàm trả mid ngay, không tiếp tục dò các phần tử bằng k ở hai phía.
Đang học