Quicksort
التقسيم والغزو
Quicksort درس مجاني في C Academy على CoddyKit. هذا هو الدرس 2 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في C Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة C Academy 4 دروس في المجموع.
التقسيم والسيطرة
Quicksort هي خوارزمية ترتيب تعتمد على التقسيم والسيطرة. تختار محورًا، وتقسم المصفوفة بحيث تذهب العناصر الأصغر إلى اليسار والأكبر إلى اليمين، ثم ترتب كل جانب استدعائيًا.
متوسط الزمن هو O(n log n).
خطوة التقسيم
الفكرة الأساسية هي التقسيم: إعادة ترتيب المصفوفة حول محور بحيث يكون كل ما على يساره أصغر وكل ما على يمينه أكبر. ثم يستقر المحور في موضعه النهائي المرتب.
مخطط تقسيم Lomuto
يستخدم مخطط Lomuto العنصر الأخير محورًا. ويحتفظ بفهرس i يمثل حد العناصر الأصغر، ثم يجري التبديلات أثناء الاجتياز.
#include <stdio.h>
int partition(int a[], int lo, int hi) {
int pivot = a[hi], i = lo - 1;
for (int j = lo; j < hi; j++)
if (a[j] < pivot) {
i++;
int t = a[i]; a[i] = a[j]; a[j] = t;
}
int t = a[i+1]; a[i+1] = a[hi]; a[hi] = t;
return i + 1;
}
int main(void) {
int a[] = {5, 2, 9, 1, 3};
int p = partition(a, 0, 4);
printf("pivot index = %d\n", p);
for (int i = 0; i < 5; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}الترتيب الاستدعائي
تستدعي Quicksort عملية التقسيم، ثم تستدعي نفسها على المصفوفتين الفرعيتين المحيطتين بالمحور. والحالة الأساسية هي مصفوفة فرعية حجمها 0 أو 1.
#include <stdio.h>
int partition(int a[], int lo, int hi) {
int pivot = a[hi], i = lo - 1;
for (int j = lo; j < hi; j++)
if (a[j] < pivot) { i++; int t=a[i];a[i]=a[j];a[j]=t; }
int t = a[i+1]; a[i+1] = a[hi]; a[hi] = t;
return i + 1;
}
void quicksort(int a[], int lo, int hi) {
if (lo < hi) {
int p = partition(a, lo, hi);
quicksort(a, lo, p - 1);
quicksort(a, p + 1, hi);
}
}
int main(void) {
int a[] = {9, 3, 7, 1, 8, 2, 5};
quicksort(a, 0, 6);
for (int i = 0; i < 7; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}اختيار محور جيد
يؤدي المحور السيئ (مثل اختيار العنصر الأخير دائمًا عند إدخال مرتب) إلى أداء O(n تربيع). أما الاختيارات الأفضل فتوزع أقسام المصفوفة بالتساوي.
- وسيط ثلاثة عناصر
- محور عشوائي
وسيط ثلاثة عناصر
يختار أسلوب وسيط ثلاثة عناصر وسيط العناصر الأول والأوسط والأخير محورًا، متجنبًا أداء أسوأ الحالات على البيانات المرتبة مسبقًا.
#include <stdio.h>
int median_of_three(int a[], int lo, int hi) {
int mid = lo + (hi - lo) / 2;
if (a[mid] < a[lo]) { int t=a[mid];a[mid]=a[lo];a[lo]=t; }
if (a[hi] < a[lo]) { int t=a[hi];a[hi]=a[lo];a[lo]=t; }
if (a[hi] < a[mid]) { int t=a[hi];a[hi]=a[mid];a[mid]=t; }
return mid;
}
int main(void) {
int a[] = {7, 1, 5, 3, 9};
int m = median_of_three(a, 0, 4);
printf("median value = %d\n", a[m]);
return 0;
}تحليل أسوأ حالة
إذا فصل كل تقسيم عنصرًا واحدًا فقط، يصبح عمق الاستدعاء n وتكون التكلفة O(n تربيع). يحدث ذلك عند استخدام محور ثابت مع إدخال مرتب أو مرتب عكسيًا.
تجعل العشوائية حدوث أسوأ حالة غير محتمل للغاية.
المحور العشوائي
يؤدي تبديل عنصر عشوائي إلى موضع المحور قبل التقسيم إلى الحماية من المدخلات المصممة للتسبب في أسوأ أداء.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int a[] = {1, 2, 3, 4, 5};
int lo = 0, hi = 4;
srand(42);
int r = lo + rand() % (hi - lo + 1);
int t = a[r]; a[r] = a[hi]; a[hi] = t; /* move random to pivot slot */
printf("chosen pivot = %d\n", a[hi]);
return 0;
}داخل المكان وغير مستقر
ترتب Quicksort داخل المكان باستخدام مساحة مكدس O(log n) فقط في المتوسط. لكنها غير مستقرة: فقد يعاد ترتيب العناصر المتساوية بسبب تبديلات التقسيم.
تحسين الاستدعاء الذاتي الطرفي
يؤدي الاستدعاء على النصف الأصغر أولًا والتكرار على النصف الأكبر إلى تقييد عمق المكدس عند O(log n)، مما يمنع فيضان المكدس مع المصفوفات الكبيرة.
ترتيب السلاسل النصية
يمكن للبنية نفسها ترتيب أي نوع قابل للمقارنة. ترتب Quicksort هنا مصفوفة من الأعداد الصحيحة، لكن تغيير المقارنة يتيح التعامل مع أنواع أخرى.
#include <stdio.h>
int partition(int a[], int lo, int hi) {
int pivot = a[hi], i = lo - 1;
for (int j = lo; j < hi; j++)
if (a[j] < pivot) { i++; int t=a[i];a[i]=a[j];a[j]=t; }
int t = a[i+1]; a[i+1] = a[hi]; a[hi] = t; return i + 1;
}
void quicksort(int a[], int lo, int hi) {
if (lo < hi) { int p = partition(a, lo, hi); quicksort(a, lo, p-1); quicksort(a, p+1, hi); }
}
int main(void) {
int a[] = {42, -7, 0, 100, 13, 13};
quicksort(a, 0, 5);
for (int i = 0; i < 6; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}تحقق سريع
اختبروا فهمكم لـ Quicksort.
مراجعة
تعلّمتم Quicksort.
- قسّموا حول محور، ثم استدعوا الخوارزمية على كل جانب
- متوسط الأداء O(n log n)، وأسوأ حالة O(n تربيع)
- يتجنب وسيط ثلاثة عناصر أو المحاور العشوائية أسوأ حالة
- تعمل داخل المكان لكنها غير مستقرة
الأسئلة الشائعة
هل درس «Quicksort» مجاني؟
نعم — نص درس «Quicksort» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة C Academy، انتقل إلى CoddyKit PRO. تتضمن دورة C Academy 4 دروس في المجموع.
ماذا ستتعلم في «Quicksort»؟
التقسيم والغزو تتمرن على C Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ C Academy؟
لا تُشترط خبرة سابقة. C Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 2 من أصل 4.
كم من الوقت يستغرق درس «Quicksort»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس C Academy هذا؟
نعم. كل درس في C Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.