Binary Search (חיפוש בינארי)
עודכן לאחרונה
חיפוש בינארי מוצא ערך מטרה במערך **ממוין** על ידי חציית חלון החיפוש שוב ושוב. הוא משווה את האיבר האמצעי למטרה: התאמה מסיימת את החיפוש; אחרת, החצי שלא יכול להכיל את המטרה נזרק והחלון מצטמצם לחצי השני. כל השוואה מוציאה מהמשחק חצי מהאיברים שנותרו, ולכן הוא רץ ב-O(log n): חיפוש במיליון ערכים ממוינים דורש לכל היותר כ-20 השוואות.
האנימציה למעלה מציגה את המצביעים lo, mid ו-hi ומעמעמת את החצי שנפסל אחרי כל השוואה. תנאי מוקדם אחד שאין עליו ויכוח: המערך חייב להיות ממוין מראש. על נתונים לא ממוינים צריך חיפוש ליניארי או מיון קודם (ראו מיון מיזוג). אותו רעיון של חציה מניע גם את עץ החיפוש הבינארי.
סיבוכיות זמן וזיכרון
| מקרה | סיבוכיות | הערות |
|---|---|---|
| המקרה הטוב | O(1) | האיבר האמצעי הוא המטרה כבר בהשוואה הראשונה. |
| המקרה הממוצע | O(log n) | כל השוואה חוצה את החלון שנותר. |
| המקרה הגרוע | O(log n) | החלון מצטמצם לאיבר יחיד לפני התאמה או החטאה. |
| זיכרון | O(1) | הגרסה האיטרטיבית שומרת רק את האינדקסים lo, hi ו-mid. |
צעד אחר צעד
| צעד | מה קורה |
|---|---|
| 1 | קובעים את lo לאינדקס הראשון ואת hi לאינדקס האחרון של המערך הממוין. |
| 2 | מחשבים את האינדקס האמצעי: mid = (lo + hi) // 2. |
| 3 | אם a[mid] שווה למטרה, מחזירים את mid (נמצא). |
| 4 | אם a[mid] **קטן** מהמטרה, המטרה יכולה להיות רק בחצי הימני: lo = mid + 1. |
| 5 | אם a[mid] **גדול** מהמטרה, מחפשים בחצי השמאלי: hi = mid - 1. |
| 6 | חוזרים לצעד 2 כל עוד lo <= hi; אם החלון מתרוקן, המטרה לא נמצאת במערך. |
דוגמה מפורטת
חיפוש של 5 ב-[1, 2, 3, 5, 7, 8, 9]:
| סבב | חלון (lo..hi) | mid | a[mid] | פעולה |
|---|---|---|---|---|
| 1 | [1, 2, 3, 5, 7, 8, 9] (0..6) | 3 | 5 | a[3] = 5: המטרה נמצאה באינדקס 3. |
החטאה, צעד אחר צעד
חיפוש של 4 באותו מערך מראה איך החלון מתרוקן:
| סבב | חלון (lo..hi) | mid | a[mid] | פעולה |
|---|---|---|---|---|
| 1 | 0..6 | 3 | 5 | 5 > 4: מחפשים בחצי השמאלי, hi = 2. |
| 2 | 0..2 | 1 | 2 | 2 < 4: מחפשים בחצי הימני, lo = 2. |
| 3 | 2..2 | 2 | 3 | 3 < 4, ולכן lo הופך ל-3 והחלון מתרוקן: לא נמצא. |
מתי להשתמש בחיפוש בינארי
| השתמשו בו כאשר | הימנעו ממנו כאשר |
|---|---|
| הנתונים כבר ממוינים (או שמחפשים בהם פעמים רבות) | הנתונים לא ממוינים ומחפשים בהם רק פעם אחת (מיון קודם עולה O(n log n)) |
| האוסף תומך בגישה אקראית מהירה (מערכים) | יש רק גישה סדרתית (רשימות מקושרות) |
כמות הנתונים גדולה (O(log n) בולט בקנה מידה גדול) | כמות הנתונים זעירה (סריקה פשוטה מהירה באותה מידה ופשוטה יותר) |
קוד Binary Search
מימוש נקי של Binary Search שאפשר להריץ, ב-Python, JavaScript, Java, C++, C, Pseudocode. בחרו שפה, העתיקו את הקוד, או פתחו אותו טעון מראש בעורך האונליין של Coddy.
קוד Binary Search ב-Python
1def binary_search(a, target):2 lo, hi = 0, len(a) - 13 while lo <= hi:4 mid = (lo + hi) // 25 if a[mid] == target:6 return mid7 if a[mid] < target:8 lo = mid + 1 # search the right half9 else:10 hi = mid - 1 # search the left half11 return -112
13
14nums = [1, 2, 3, 5, 7, 8, 9] # must be sorted15print("Index of 5:", binary_search(nums, 5))16print("Index of 4:", binary_search(nums, 4))קוד Binary Search ב-JavaScript
1function binarySearch(a, target) {2 let lo = 0;3 let hi = a.length - 1;4 while (lo <= hi) {5 const mid = Math.floor((lo + hi) / 2);6 if (a[mid] === target) return mid;7 if (a[mid] < target) {8 lo = mid + 1; // search the right half9 } else {10 hi = mid - 1; // search the left half11 }12 }13 return -1;14}15
16const nums = [1, 2, 3, 5, 7, 8, 9]; // must be sorted17console.log("Index of 5:", binarySearch(nums, 5));18console.log("Index of 4:", binarySearch(nums, 4));קוד Binary Search ב-Java
1public class Main {2 static int binarySearch(int[] a, int target) {3 int lo = 0;4 int hi = a.length - 1;5 while (lo <= hi) {6 int mid = (lo + hi) / 2;7 if (a[mid] == target) return mid;8 if (a[mid] < target) {9 lo = mid + 1; // search the right half10 } else {11 hi = mid - 1; // search the left half12 }13 }14 return -1;15 }16
17 public static void main(String[] args) {18 int[] nums = {1, 2, 3, 5, 7, 8, 9}; // must be sorted19 System.out.println("Index of 5: " + binarySearch(nums, 5));20 System.out.println("Index of 4: " + binarySearch(nums, 4));21 }22}קוד Binary Search ב-C++
1#include <iostream>2#include <vector>3
4int binarySearch(const std::vector<int>& a, int target) {5 int lo = 0;6 int hi = static_cast<int>(a.size()) - 1;7 while (lo <= hi) {8 int mid = lo + (hi - lo) / 2;9 if (a[mid] == target) return mid;10 if (a[mid] < target) {11 lo = mid + 1; // search the right half12 } else {13 hi = mid - 1; // search the left half14 }15 }16 return -1;17}18
19int main() {20 std::vector<int> nums = {1, 2, 3, 5, 7, 8, 9}; // must be sorted21 std::cout << "Index of 5: " << binarySearch(nums, 5) << "\n";22 std::cout << "Index of 4: " << binarySearch(nums, 4) << "\n";23 return 0;24}קוד Binary Search ב-C
1#include <stdio.h>2
3int binary_search(const int a[], int n, int target) {4 int lo = 0;5 int hi = n - 1;6 while (lo <= hi) {7 int mid = lo + (hi - lo) / 2;8 if (a[mid] == target) return mid;9 if (a[mid] < target) {10 lo = mid + 1; /* search the right half */11 } else {12 hi = mid - 1; /* search the left half */13 }14 }15 return -1;16}17
18int main(void) {19 int nums[] = {1, 2, 3, 5, 7, 8, 9}; /* must be sorted */20 int n = sizeof(nums) / sizeof(nums[0]);21 printf("Index of 5: %d\n", binary_search(nums, n, 5));22 printf("Index of 4: %d\n", binary_search(nums, n, 4));23 return 0;24}קוד Binary Search ב-Pseudocode
1DECLARE nums : ARRAY[1:7] OF INTEGER2DECLARE n : INTEGER3n ← 74// The array must be sorted for binary search5nums[1] ← 16nums[2] ← 27nums[3] ← 38nums[4] ← 59nums[5] ← 710nums[6] ← 811nums[7] ← 912
13FUNCTION binarySearch(target : INTEGER) RETURNS INTEGER14 DECLARE lo : INTEGER15 DECLARE hi : INTEGER16 DECLARE mid : INTEGER17 lo ← 118 hi ← n19 WHILE lo <= hi DO20 mid ← (lo + hi) DIV 221 IF nums[mid] = target THEN22 RETURN mid23 ENDIF24 IF nums[mid] < target THEN25 // Target is larger, search the right half26 lo ← mid + 127 ELSE28 // Target is smaller, search the left half29 hi ← mid - 130 ENDIF31 ENDWHILE32 RETURN -133ENDFUNCTION34
35OUTPUT "Index of 5 is ", binarySearch(5)36OUTPUT "Index of 4 is ", binarySearch(4)שאלות נפוצות על חיפוש בינארי
מהי סיבוכיות הזמן של חיפוש בינארי?
O(log n) במקרה הממוצע ובמקרה הגרוע, כי כל השוואה חוצה את חלון החיפוש שנותר, ו-O(1) במקרה הטוב, כשהאיבר האמצעי הראשון הוא המטרה. הגרסה האיטרטיבית משתמשת ב-O(1) זיכרון נוסף.למה חיפוש בינארי דורש מערך ממוין?
מה ההבדל בין חיפוש בינארי לחיפוש ליניארי?
O(n)) ועובד על כל מערך; חיפוש בינארי חוצה את חלון החיפוש של מערך ממוין (O(log n)) אבל דורש קלט ממוין. עבור קומץ פריטים ההבדל זניח; בקנה מידה גדול חיפוש בינארי מנצח בגדול.כמה השוואות צריך חיפוש בינארי?
log2(n) + 1: עשר השוואות מכסות 1,000 איברים, ועשרים השוואות מכסות 1,000,000. הגדילה הלוגריתמית הזו היא מה שהופך אותו לדרך החיפוש המוגדרת כברירת מחדל על נתונים ממוינים.מהו באג הגלישה הקלאסי בחיפוש בינארי?
(lo + hi) / 2 יכול לגרום לגלישה במספרים שלמים בגודל קבוע כש-lo + hi חורג מהערך המקסימלי של הטיפוס. הצורה הבטוחה היא mid = lo + (hi - lo) / 2. ב-Python זה לא משנה (מספרים שלמים בדיוק שרירותי), אבל ב-Java, C ו-C++ זה באג אמיתי ומפורסם.