ترتيب الفقاعات
ترتيب الفقاعات (bubble sort) خوارزمية ترتّب مصفوفة بمقارنة العناصر المتجاورة وتبديلها باستمرار. تشبه هذه العملية فقاعات تصعد من الأسفل إلى الأعلى، ومن هنا جاء اسم ترتيب الفقاعات.
وكما يوضح الشكل أدناه، يمكن محاكاة عملية التفقيع بتبديل العناصر: بدءاً من الطرف الأيسر للمصفوفة والاجتياز نحو اليمين، قارن كل زوج من العناصر المتجاورة، وإذا كان "العنصر الأيسر > العنصر الأيمن" فبدّلهما. وبعد اكتمال الاجتياز، يكون أكبر عنصر قد انتقل إلى الطرف الأيمن للمصفوفة.
سير الخوارزمية
لنفترض أن طول المصفوفة . وتُعرض خطوات ترتيب الفقاعات في الشكل أدناه.
- أولاً، نفّذ "التفقيع" على عنصراً، فتبدّل أكبر عنصر في المصفوفة إلى موضعه الصحيح.
- ثم نفّذ "التفقيع" على العناصر المتبقية، فتبدّل ثاني أكبر عنصر إلى موضعه الصحيح.
- وهكذا. بعد جولة من "التفقيع"، تكون أكبر عنصراً قد بدّلت جميعها إلى مواضعها الصحيحة.
- العنصر الوحيد المتبقي لا بد أن يكون أصغر عنصر، ولا يحتاج إلى ترتيب، وبذلك يكتمل ترتيب المصفوفة.

مثال الشيفرة كما يلي:
Go
/* ترتيب الفقاعات */
func bubbleSort(nums []int) {
// الحلقة الخارجية: المجال غير المرتب هو [0, i]
for i := len(nums) - 1; i > 0; i-- {
// الحلقة الداخلية: بدّل أكبر عنصر في المجال غير المرتب [0, i] إلى الطرف الأيمن من ذلك المجال
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// بدّل nums[j] و nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
}
}
}
}
TypeScript
/* ترتيب الفقاعات */
function bubbleSort(nums: number[]): void {
// الحلقة الخارجية: المجال غير المرتب هو [0, i]
for (let i = nums.length - 1; i > 0; i--) {
// الحلقة الداخلية: بدّل أكبر عنصر في المجال غير المرتب [0, i] إلى الطرف الأيمن من ذلك المجال
for (let j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// بدّل nums[j] و nums[j + 1]
let tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
}
}
}
}
تحسين الكفاءة
يمكننا أن نلاحظ أنه إذا لم يحدث أي تبديل خلال جولة "تفقيع"، فالمصفوفة مرتبة بالفعل ويمكن للخوارزمية أن تعود فوراً. لذلك يمكننا إضافة علامة flag لكشف هذا الوضع والإنهاء بمجرد حدوثه.
بعد هذا التحسين، يبقى التعقيد الزمني لترتيب الفقاعات في أسوأ الحالات ومتوسطها ؛ غير أنه عندما تكون مصفوفة الإدخال مرتبة بالفعل، يصبح التعقيد الزمني في أفضل الحالات .
Go
/* ترتيب الفقاعات (تحسين العلامة) */
func bubbleSortWithFlag(nums []int) {
// الحلقة الخارجية: المجال غير المرتب هو [0, i]
for i := len(nums) - 1; i > 0; i-- {
flag := false // هيّئ العلامة
// الحلقة الداخلية: بدّل أكبر عنصر في المجال غير المرتب [0, i] إلى الطرف الأيمن من ذلك المجال
for j := 0; j < i; j++ {
if nums[j] > nums[j+1] {
// بدّل nums[j] و nums[j + 1]
nums[j], nums[j+1] = nums[j+1], nums[j]
flag = true // سجّل تبديل العنصر
}
}
if flag == false { // لم تُبدَّل أي عناصر في هذه الجولة من "التفقيع"، اخرج مباشرة
break
}
}
}
TypeScript
/* ترتيب الفقاعات (تحسين العلامة) */
function bubbleSortWithFlag(nums: number[]): void {
// الحلقة الخارجية: المجال غير المرتب هو [0, i]
for (let i = nums.length - 1; i > 0; i--) {
let flag = false; // هيّئ العلامة
// الحلقة الداخلية: بدّل أكبر عنصر في المجال غير المرتب [0, i] إلى الطرف الأيمن من ذلك المجال
for (let j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
// بدّل nums[j] و nums[j + 1]
let tmp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = tmp;
flag = true; // سجّل تبديل العنصر
}
}
if (!flag) break; // لم تُبدَّل أي عناصر في هذه الجولة من "التفقيع"، اخرج مباشرة
}
}
خصائص الخوارزمية
- التعقيد الزمني ؛ ترتيب تكيّفي (adaptive): في الجولات المتتالية من "التفقيع"، تبلغ أطوال الجزء المجتاز من المصفوفة و و و و، أي مجموع . وبعد إدخال تحسين
flag، يمكن أن يصل التعقيد الزمني في أفضل الحالات إلى . - التعقيد المكاني ، ترتيب في المكان (in-place): يستخدم المؤشران و مقداراً ثابتاً من المساحة الإضافية.
- ترتيب مستقر: لا تُبدَّل العناصر المتساوية أثناء "التفقيع".