ما هو الترتيب الفقاعي (Bubble Sort)؟ شرح مبسّط مع مثال وأكواد

شروحات تقنية

الترتيب الفقاعي (Bubble Sort) هو أبسط خوارزميات الترتيب على الإطلاق: يمرّ على القائمة عنصرًا عنصرًا، يقارن كل رقمين متجاورين، ويبدّل مكانهما إذا كانا في الترتيب الخطأ. يكرّر هذا المرور مرات عديدة حتى تصبح القائمة مرتبة بالكامل. سُمّي «فقاعيًا» لأن القيم الكبيرة «تطفو» تدريجيًا نحو نهاية القائمة كما تطفو الفقاعة إلى سطح الماء. هو خوارزمية تعليمية بامتياز يبدأ بها معظم من يدرس علوم الحاسوب، لكنه بطيء عمليًا ونادرًا ما يُستخدم في البرامج الحقيقية — وسنوضّح السبب بدقة.

كيف تعمل خطوة بخطوة

الفكرة كلها تقوم على تكرار عمليتين: المقارنة ثم التبديل.

  1. ابدأ من أول عنصر في القائمة.
  2. قارن العنصر الحالي بالعنصر الذي يليه مباشرة.
  3. إذا كان العنصر الأول أكبر من الثاني (في الترتيب التصاعدي)، بدّل مكانهما.
  4. انتقل إلى الزوج التالي وكرّر حتى نهاية القائمة — هذا يُسمّى «تمريرة» واحدة (pass).
  5. أعد التمريرات من البداية. بعد كل تمريرة يستقر أكبر عنصر متبقٍّ في مكانه الصحيح آخر القائمة.
  6. توقّف عندما تمرّ تمريرة كاملة دون أي تبديل، فهذا يعني أن القائمة أصبحت مرتبة.

مثال عملي على القائمة ‎[5, 3, 8, 4, 2]

لنرتّب هذه الأرقام تصاعديًا. أُبرز في كل تمريرة الجزء الذي «استقر» في نهايته.

التمريرة الأولى: نقارن 5 و3 فنبدّل ← ‎[3, 5, 8, 4, 2]، ثم 5 و8 (لا تبديل)، ثم 8 و4 فنبدّل ← ‎[3, 5, 4, 8, 2]، ثم 8 و2 فنبدّل ← ‎[3, 5, 4, 2, 8]. الرقم 8 استقر في مكانه.

التمريرة الثانية: ‎[3, 5, 4, 2, 8] ← 3 و5 (لا تبديل)، 5 و4 نبدّل، 5 و2 نبدّل ← ‎[3, 4, 2, 5, 8].

التمريرة الثالثة: ‎[3, 4, 2, 5, 8] ← 4 و2 نبدّل ← ‎[3, 2, 4, 5, 8].

التمريرة الرابعة: ‎[3, 2, 4, 5, 8] ← 3 و2 نبدّل ← ‎[2, 3, 4, 5, 8].

التمريرة الخامسة: لا يحدث أي تبديل، إذن القائمة مرتبة ونتوقّف. النتيجة: ‎[2, 3, 4, 5, 8].

النسخة المحسّنة: لا تكمل بلا فائدة

الخطأ الأكثر شيوعًا عند كتابة الترتيب الفقاعي هو ترك الخوارزمية تُكمل كل التمريرات حتى لو صارت القائمة مرتبة مبكرًا. الحل هو راية تبديل (swapped flag): إذا انتهت تمريرة دون أي تبديل، اخرج فورًا. تحسين ثانٍ: بما أن آخر عنصر يستقر بعد كل تمريرة، قلّل مدى المقارنة في كل مرة.

def bubble_sort(arr):
    n = len(arr)
    for i in range(n - 1):
        swapped = False
        for j in range(n - 1 - i):        # نتجاهل الجزء المستقر
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swapped = True
        if not swapped:                    # القائمة مرتبة، نتوقف
            break
    return arr

ونفس المنطق بلغة جافاسكريبت:

function bubbleSort(arr) {
  for (let i = 0; i < arr.length - 1; i++) {
    let swapped = false;
    for (let j = 0; j < arr.length - 1 - i; j++) {
      if (arr[j] > arr[j + 1]) {
        [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
        swapped = true;
      }
    }
    if (!swapped) break;
  }
  return arr;
}

التعقيد الزمني ولماذا يُعدّ بطيئًا

في أسوأ الحالات ومتوسطها، يقوم الترتيب الفقاعي بعدد مقارنات يتناسب مع مربّع حجم القائمة، أي O(n²). مضاعفة عدد العناصر تعني تقريبًا أربعة أضعاف العمل. لذلك يصبح غير عملي مع القوائم الكبيرة: ترتيب مليون عنصر قد يتطلب تريليون عملية.

  • أفضل حالة: ‎O(n) إذا كانت القائمة مرتبة مسبقًا واستخدمت راية التبديل — تمريرة واحدة فقط.
  • الذاكرة: ‎O(1)، أي لا يحتاج مساحة إضافية تُذكر لأنه يرتّب داخل نفس المصفوفة (in-place).
  • الاستقرار: خوارزمية مستقرة (stable)، تحافظ على الترتيب النسبي للعناصر المتساوية.

الترتيب الفقاعي مقابل خوارزميات أخرى

المعيارالترتيب الفقاعيالترتيب بالإدراج (Insertion)الترتيب السريع (Quick Sort)
متوسط التعقيدO(n²)O(n²)O(n log n)
أفضل حالةO(n)O(n)O(n log n)
الذاكرة الإضافيةO(1)O(1)O(log n)
مستقر؟نعمنعملا (افتراضيًا)
الأنسب لـالتعليم فقطقوائم صغيرة أو شبه مرتبةقوائم كبيرة عامة

عمليًا، لغات البرمجة الحديثة تأتي بدوال ترتيب جاهزة ومحسّنة مثل sorted() في بايثون وArray.sort() في جافاسكريبت، وتعتمد داخليًا على خوارزميات هجينة أسرع بكثير. استخدمها في المشاريع الفعلية، واحفظ الترتيب الفقاعي لفهم كيف تعمل الأمور تحت الغطاء.

الأسئلة الشائعة

متى أستخدم الترتيب الفقاعي فعليًا؟ شبه أبدًا في الإنتاج. هو ممتاز للتعلّم، أو لقائمة صغيرة جدًا (بضعة عناصر) تكون شبه مرتبة أصلًا. لأي شيء جدّي استخدم الدالة الجاهزة في لغتك.

لماذا سُمّي «فقاعيًا»؟ لأن القيم الكبيرة تتحرك تدريجيًا نحو نهاية القائمة مع كل تمريرة، فتبدو كأنها فقاعات تصعد إلى السطح.

هل هو أسرع أم أبطأ من الترتيب بالإدراج؟ كلاهما O(n²)، لكن الترتيب بالإدراج أسرع عمليًا في معظم الحالات لأنه يقوم بعدد أقل من عمليات النقل، خصوصًا مع القوائم شبه المرتبة.

ما الفرق بين الترتيب التصاعدي والتنازلي هنا؟ مجرد عكس شرط المقارنة: في التصاعدي نبدّل إذا كان arr[j] > arr[j+1]، وفي التنازلي نبدّل إذا كان arr[j] < arr[j+1].