- vừa được xem lúc

Tìm kiếm nhị phân(binary search)

0 0 24

Người đăng: BeautyOnCode

Theo Viblo Asia

Tìm kiếm nhị phân(binary search) là một thuật toán tìm kiếm xác định vị trí của một giá trị cần tìm trong một mảng đã được sắp xếp.

Thuật toán sẽ tiến hành so sánh giá trị cần tìm với phần tử đứng giữa của mảng. Nếu hai giá trị không bằng nhau, thì một nửa mảng không chứa giá trị cần tìm sẽ được bỏ qua, và tiếp tục tìm kiếm trên nửa còn lại. Một lần nữa, lấy phần tử ở giữa so sánh với giá trị cần tìm, cứ thế lặp lại cho đến khi tìm thấy giá trị đó. Nếu phép tìm kiếm kết thúc mà vẫn chưa có giá trị cần tìm tức là nó không có trong mảng.

Binary search chạy theo giời gian logarit trong trường hợp tệ nhất, thực hiện O(logn) bước so sánh, với n là số phần tử của mảng. Thuật toán này sẽ nhanh hơn thuật toán tìm kiếm tuyến tính thông thường(lặp qua toàn mảng) là O(n)


Một số vấn đề thường gặp/cần suy nghĩ khi giải bài toán binary search:

– Khi nào thì sẽ thoát ra khỏi vòng lặp?

Nên dùng left < right hay left ≤ right làm điều kiện lặp

– Làm sao để khởi tạo và cập nhật biên bên trái(left), biên bên phải(right)?

Nên chọn left = mid hay left = mid + 1 hay right = mid, right = mid – 1?

Vì thế, một bản mẫu tổng quát về cách giải binary search sẽ giúp bạn hình dung về cách giải bài toán này dễ dàng hơn.

Cách giải tổng quát

Đề bài: Giả sử có một không gian tìm kiếm, có thể là một mảng, một phạm vi, … Thông thường, nó được sắp xếp theo thứ tự tăng dần.

Tìm k thỏa điều kiện với condition sao cho tối ưu nhất:

Minimize k, s.t. condition(k) is True

Cách giải tổng quát:

def binary_search(array) -> int: def condition(value) -> bool: pass left, right = 0, len(array) while left < right: mid = left + (right + left) // 2 if condition(mid): right = mid else: left = mid + 1 return left

Đối với từng đề bài, bạn chỉ cẩn sửa đổi 3 phần sau:

  1. Khởi tạo biên left, right sao cho bao gồm tất cả các phần tử có thể

  2. Quyết định giá trị trả về

  3. Thiết kế điều kiện kiểm tra(condition) – đây chính là phần thú vị nhất và cũng là khó nhất.

Vì sao mid = left + (right - left) // 2 mà không là (left + right) // 2

Lúc xem template này mình đã có câu hỏi như vậy. Chỉ là lấy số ở giữa, thì cộng hai bên chia đôi lấy nguyên tức là mid = (left + right) // 2 chứ sao lại có công thức mid = left + (right – left) // 2 ?

Khi mid = (left + right) // 2, với left, right là các số rất lớn, gần bằng 2^31 – 1 chẳng hạn, thì tổng của left + right sẽ lớn hơn 2^31 – 1(số nguyên lớn nhất) và gây tràn số.

Có nhiều cách giải quyết vấn đề này, một trong số đó là left + (right – left) // 2

Bạn có thể đọc thêm ở đây

Bài toán: First Bad Version

LeetCode link First Bad Version.

You are a product manager and currently leading a team to develop a new product. Since each version is developed based on the previous version, all the versions after a bad version are also bad. Suppose you have n versions [1, 2, …, n] and you want to find out the first bad one, which causes all the following ones to be bad. You are given an API bool isBadVersion(version) which will return whether version is bad.

Example:

Given n = 5, and version = 4 is the first bad version. call isBadVersion(3) -> false
call isBadVersion(5) -> true
call isBadVersion(4) -> true Then 4 is the first bad version.

Giải:

Không gian tìm kiểm ở đây là từ 1 đến n, sắp xếp theo thứ tự version này release rồi mới đến version khác.

Vì nếu version nào fail sẽ dẫn đến tất cả những version tiếp theo fail, nên biên của right sẽ thu hẹp dần về left cho đến khi nào tìm được version fail đầu tiên thì thôi.

Áp dụng công thức ở trên vào bài toán này:

  1. left bắt đầu từ 0, right bắt đầu từ n

  2. giá trị trả về sẽ là left

  3. điều kiện được cho sẵn là hàm isBadVersion() nên sẽ không cần định nghĩa điều kiện.

def firstBadVersion(self, n): left, right = 1, n while left < right: mid = left + (right - left) // 2 if isBadVersion(mid): right = mid else: left = mid + 1 return left

Bài toán: sqrt(x)

LeetCodeLinkSqrtX

Cho một số nguyên x, tìm căn bậc hai của x.

Example 1: Input: x = 4. Output: 2

Giải:

Gọi căn bậc hai của x là k, ta có: x = k * k. Bài toán đưa về tìm số k sao cho k bình phương là x.

Phạm vi tìm kiếm sẽ là từ 0 tới x, điều kiện là k * k bằng hoặc gần bằng x nhất

def mySqrt(self, x): if x == 0: return 0 if x == 1: return 1 left, right = 0, x while left < right: mid = left + (right - left) // 2 if round(mid * mid, 2) <= x: left = mid + 1 else: right = mid return left - 1

Thêm các bài toán khó hơn nữa mời bạn ghé đọc bài viết của tác giả đóng góp trên LeetCode ở đây nhé

Bài viết gốc nằm ở blog cá nhân của mình, mời các bạn ghé chơi nhé.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Thuật toán quay lui (Backtracking)

Quay lui là một kĩ thuật thiết kế giải thuật dựa trên đệ quy. Ý tưởng của quay lui là tìm lời giải từng bước, mỗi bước chọn một trong số các lựa chọn khả dĩ và đệ quy.

0 0 51

- vừa được xem lúc

Các thuật toán cơ bản trong AI - Phân biệt Best First Search và Uniform Cost Search (UCS)

Nếu bạn từng đọc các thuật toán trong AI (Artificial Intelligence - Trí tuệ nhân tạo), rất có thể bạn từng nghe qua về các thuật toán tìm kiếm cơ bản: UCS (thuộc chiến lược tìm kiếm mù) và Best First Search (thuộc chiến lược tìm kiếm kinh nghiệm). Khác nhau rõ từ khâu phân loại rồi, thế nhưng hai th

0 0 169

- vừa được xem lúc

Sử dụng vector trong lập trình C++ - giải bài toán lập trình muôn thủa

Chào buổi tối mọi người, hôm nay lang thang trên mạng bắt gặp bài toán quen thuộc một thời của quãng đường sinh viên IT. Đấy chính là câu số 1 trong đề thi dưới đây:.

0 0 54

- vừa được xem lúc

MÔ PHỎNG THUẬT TOÁN VƯƠNG HẠO TRONG PROLOG

. 1. Các luật suy diễn trong thuật toán Vương Hạo. Luật 1: Chuyển vế các giả thuyết và kết luận ở dạng phủ định. Ví dụ: p v q, !(r ^ s), !q, p v r -> s, !p <=> p v q, p v r, p -> s, r ^ s, q.

0 0 89

- vừa được xem lúc

A* Search Algorithm

What is A* Search Algorithm. How it works. . Explanation.

0 0 58

- vừa được xem lúc

Python: Jump Search

Search là một từ khóa khá là quen thuộc đối với chúng ta. Hiểu theo đúng nghĩa đen của nó chính là "Tìm kiếm".

0 0 50