اكتشف كيفية عمل Binary Search وتطبيقاته العملية في برمجة الخوارزميات

تخيل أنك تبحث عن كلمة في قاموس ورقي يحتوي على مليون صفحة، هل ستمر على الصفحات واحدة تلو الأخرى من البداية؟ بالطبع لا! ستفتح القاموس من المنتصف، فإذا وجدت أن الحرف الذي تبحث عنه يقع في النصف الثاني، ستتجاهل النصف الأول تماماً، وتكرر نفس العملية مع الصفحات المتبقية. هذا الذكاء البشري الفطري هو بالضبط ما تفعله خوارزمية البحث الثنائي (Binary Search). إنها واحدة من أقوى وأسرع الخوارزميات في علوم الحاسوب، وفي هذا الدليل من منصة نوفيل، سنشرح كيف تحول هذه الخوارزمية البحث المستحيل إلى مهمة تافهة، وما هي الفخاخ البرمجية التي يجب تجنبها عند كتابتها.
في البحث العادي (Linear Search)، إذا كان لديك مصفوفة تحتوي على مليار عنصر، وتبحث عن العنصر الأخير، ستقوم بالمرور على مليار سطر، وهو ما يستهلك وقتاً هائلاً. أما مع الـ Binary Search، بفضل استراتيجية "قسّم وتغلّب" (Divide and Conquer)، فإن أقصى عدد من الخطوات ستحتاجه للبحث في وسط المليار عنصر هو 30 خطوة فقط! تقنياً، نعبر عن هذه السرعة الخارقة في تحليل الخوارزميات بـ O(log n)
الشرط الصارم والوحيد: لا يمكنك تطبيق الـ Binary Search أبداً إلا إذا كانت المصفوفة مرتبة تصاعدياً أو تنازلياً (Sorted Array). بدون الترتيب، ستعم الفوضى وتفشل الخوارزمية تماماً.
— الشرط الصارم
لنرى كيف نكتب الخوارزمية بشكل عملي ونظيف في أشهر لغات البرمجة:
def binary_search(arr, target):
left, right = 0, len(arr) - 1
while left <= right:
mid = (left + right) // 2 # حساب العنصر الأوسط
if arr[mid] == target:
return mid # تم العثور على العنصر بنجاح
elif arr[mid] < target:
left = mid + 1 # تجاهل النصف الأيسر
else:
right = mid - 1 # تجاهل النصف الأيمن
return -1 # العنصر غير موجود في المصفوفةلاحظ كيف نتجنب خطأ فيضان الذاكرة (Integer Overflow) في جافا عند حساب mid بالطريقة الذكية:
public class BinarySearch {
public static int binarySearch(int[] array, int target) {
int left = 0;
int right = array.length - 1;
while (left <= right) {
// طريقة عبقرية لحساب المنتصف دون تخطي حدود الذاكرة القصوى لـ int
int mid = left + (right - left) / 2;
if (array[mid] == target) {
return mid;
} else if (array[mid] < target: {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
}رغم بساطة فكرة الخوارزمية، إلا أن الشيطان يكمن في التفاصيل. إليك أشهر المشاكل التي تسبب "Bug" في كودك:
تحدث عندما ينسى المبرمج كتابة + 1 أو - 1 عند تحديث مؤشرات البحث (left أو right). بدونها، إذا كان العنصر غير موجود، قد يعلق الكود في حلقة تكرار تستهلك موارد السيرفر بالكامل وتؤدي إلى توقف التطبيق.
الـ Binary Search التقليدية تعود بأي عنصر مطابق تجده في المنتصف. لكن ماذا لو كانت المصفوفة تحتوي على أرقام مكررة (مثل [1, 2, 2, 2, 3, 4]) وأنت تريد الوصول إلى أول ظهور للرقم 2؟ هنا ستحتاج إلى تعديل الخوارزمية (تعرف بـ Upper/Lower Bound Search) لتستمر في الضغط جهة اليسار حتى تضمن أنك في السجل الأول تماماً.
الـ Binary Search ليست مجرد سؤال لاجتياز مقابلات العمل، بل هي العمود الفقري لـ:
قبل أن تبدأ بكتابة خوارزمية Binary Search بنفسك من الصفر في مشاريعك البرمجية الفعالة، تأكد أولاً مما إذا كانت لغتك المفضلة توفرها بشكل مدمج ومحسن للغاية (مثل مكتبة bisect في بايثون أو دالة Arrays.binarySearch() في جافا) لتوفر على نفسك الوقت وتضمن كوداً خالياً من ثغرات حدود الذاكرة!