טכניקות אלגוריתמיותבינוני7 דק׳ קריאה

חיפוש בינארי - לא רק על מערך ממוין

חיפוש בינארי הוא יותר ממה שלימדו אתכם: Search on Answer, גבולות מדויקים, ושגיאות נפוצות שכולם עושים.

חיפוש בינארי (Binary Search) ידוע כאלגוריתם ל-O(log n) על מערך ממוין - אבל הוא חזק הרבה יותר מזה. כל בעיה שיש לה 'תנאי מונוטוני' (אמת עד נקודה מסוימת, שקר אחרי) ניתנת לפתרון בחיפוש בינארי.

המימוש הקלאסי - ושגיאות נפוצות

function binarySearch(arr, target) {
  let left = 0, right = arr.length - 1;
  while (left <= right) {
    const mid = left + Math.floor((right - left) / 2); // לא (left+right)/2 - גלישת מספרים!
    if (arr[mid] === target) return mid;
    else if (arr[mid] < target) left = mid + 1;
    else right = mid - 1;
  }
  return -1; // לא נמצא
}

שגיאה נפוצה: mid = (left + right) / 2 יכול לגרום לגלישת מספרים (integer overflow) ב-C++/Java כשהמערך גדול. תמיד: mid = left + (right - left) / 2.

מציאת גבול - Lower Bound ו-Upper Bound

לעיתים רוצים למצוא לא ערך מדויק אלא את הגבול: הופעה ראשונה של ערך, או הופעה אחרונה. שני הגרסאות שונות ברגע אחד קטן:

// Lower Bound: האינדקס הראשון שגדול-או-שווה ל-target
function lowerBound(arr, target) {
  let left = 0, right = arr.length;
  while (left < right) {
    const mid = left + Math.floor((right - left) / 2);
    if (arr[mid] < target) left = mid + 1;
    else right = mid;
  }
  return left; // אינדקס הופעה ראשונה, או arr.length אם לא קיים
}

Search on Answer - הכוח האמיתי

במקום לחפש ערך במערך, מחפשים את התשובה עצמה בטווח ערכים. אם ניתן לבדוק 'האם תשובה X אפשרית?' ב-O(n), ניתן למצוא את ה-X האופטימלי ב-O(n log n) סך הכל.

// בעיה: כמה ימים לחלק bananas כך שכל יום אוכלים לכל היותר k?
// (LeetCode: Koko Eating Bananas)
function minEatingSpeed(piles, h) {
  let left = 1, right = Math.max(...piles);
  while (left < right) {
    const mid = left + Math.floor((right - left) / 2);
    const days = piles.reduce((s, p) => s + Math.ceil(p / mid), 0);
    if (days <= h) right = mid; // mid אפשרי, נסה קטן יותר
    else left = mid + 1;        // mid קטן מדי
  }
  return left;
}

בעיות קלאסיות של Search on Answer

  • Koko Eating Bananas - מציאת מהירות אכילה מינימלית
  • Capacity to Ship Packages - קיבולת ספינה מינימלית
  • Split Array Largest Sum - חלוקת מערך ל-k חלקים עם סכום מקסימלי מינימלי
  • Find the Minimum in Rotated Sorted Array - חיפוש בינארי על מערך מסובב

שאלו את עצמכם: 'אם ה-X שאני מחפש הוא Y, האם אוכל לבדוק זאת ב-O(n)?' אם כן - כנראה שיש כאן Binary Search על התשובה.

סיבוכיות

גרסהזמןזיכרון
חיפוש בינארי קלאסיO(log n)O(1)
Search on AnswerO(n log(max-min))O(1)
Lower/Upper BoundO(log n)O(1)

שאלות נפוצות

מה התנאי ההכרחי לחיפוש בינארי?

מונוטוניות. בדרך כלל מציגים זאת כדרישה למערך ממוין, אבל התנאי הכללי הוא שקיימת תכונה שערכה נכון עד נקודה מסוימת ולא נכון אחריה. כל עוד אפשר לענות בכל נקודה על השאלה האם עברנו את הגבול, אפשר לחפש בינארית — גם אם אין מערך ממוין כלל.

מה זה חיפוש בינארי על התשובה?

טכניקה שבה מחפשים בינארית על טווח התשובות האפשריות ולא על מערך נתון. במקום לחשב את התשובה ישירות, מנחשים ערך ובודקים בפונקציית עזר אם הוא אפשרי, ולפי התוצאה מצמצמים את הטווח. זה עובד בכל בעיה שבה אפשריות התשובה מונוטונית — למשל מציאת המהירות המינימלית לסיום משימה בזמן נתון.

מהן השגיאות הנפוצות בחיפוש בינארי?

גלישה בחישוב האמצע — יש לכתוב low + (high-low)/2 ולא (low+high)/2; בלבול בין גבול סגור לפתוח שגורם ללולאה אינסופית או לדילוג על התשובה; ועדכון שגוי של הגבולות שאינו מצמצם את הטווח. הפתרון המעשי הוא לבחור תבנית גבולות אחת ולהיצמד אליה בעקביות.

מאמרים קשורים

ויזואליזציה של אלגוריתמי מיוןלכל המאמרים