1. نظرة عامة على دالة qsort
تُعتبر دالة qsort
، المقدمة في مكتبة C القياسية، أداة قوية لفرز العناصر داخل المصفوفات. تستخدم qsort
خوارزمية الترتيب السريع (Quick Sort) لفرز البيانات بسرعة وكفاءة عالية. في هذا القسم، سنشرح آلية عمل qsort
الأساسية ولماذا تعتبر هذه الدالة مهمة.
ما هي qsort؟
qsort
هي دالة عامة لفرز البيانات مثل المصفوفات أو القوائم بناءً على دالة مقارنة يحددها المستخدم. تُعد من الدوال القياسية في لغة C وتستخدم بشكل واسع في بيئات التطوير المختلفة. على سبيل المثال، يمكن استخدامها لفرز مصفوفات الأعداد الصحيحة أو مصفوفات السلاسل النصية، بل وحتى هياكل البيانات (Structs) بكفاءة عالية.
#include <stdio.h>
#include <stdlib.h>
int compare(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
int main() {
int values[] = {40, 10, 100, 90, 20, 25};
size_t values_len = sizeof(values)/sizeof(values[0]);
qsort(values, values_len, sizeof(int), compare);
for(int i = 0; i < values_len; i++) {
printf("%d ", values[i]);
}
return 0;
}
الكود أعلاه هو مثال أساسي لكيفية فرز مصفوفة أعداد صحيحة باستخدام qsort
. يتم تحديد ترتيب العناصر من خلال دالة المقارنة.
2. معاملات qsort وكيفية استخدامها
نموذج تعريف qsort
هو كالتالي:
void qsort(void *base, size_t num, size_t size, int (*compar)(const void *, const void *));
تفاصيل المعاملات
- base: مؤشر لأول عنصر في المصفوفة المراد فرزها.
- num: عدد العناصر في المصفوفة.
- size: حجم كل عنصر (بالبايت).
- compar: مؤشر إلى دالة المقارنة.
تقوم هذه الدالة بفرز العناصر استناداً إلى دالة المقارنة. compar
هي دالة تأخذ عنصرين كمعاملين وتُرجع أيهما أكبر. هذه المرونة تتيح فرز أنواع بيانات متعددة.
إنشاء دالة المقارنة
تعد دالة المقارنة محور عمل qsort
. يمكن تعريفها كما يلي:
int compare(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
ترجع هذه الدالة قيمة سالبة إذا كان a
أصغر، وصفر إذا كانا متساويين، وقيمة موجبة إذا كان a
أكبر. وبهذا يتم ترتيب المصفوفة تصاعدياً.
3. كيفية عمل qsort داخليًا: خوارزمية الترتيب السريع
تستخدم qsort
خوارزمية الترتيب السريع (Quick Sort) داخليًا، وهي خوارزمية تعتمد على مبدأ “التقسيم والسيطرة”. خطوات عملها كالتالي:
- اختيار المحور (Pivot): اختيار عنصر في منتصف أو نهاية المصفوفة كمحور للفرز.
- التقسيم: نقل العناصر الأصغر من المحور إلى اليسار، والأكبر إلى اليمين.
- الفرز التكراري: تطبيق نفس العملية تكرارياً على كل جزء من المصفوفة.
يتيح هذا الأسلوب فرزاً فعالاً للمصفوفات، مع متوسط تعقيد زمني O(n log n). ومع ذلك، في أسوأ الحالات (مثل المصفوفة المرتبة مسبقاً)، قد يصل التعقيد إلى O(n^2).

4. كيفية إنشاء دوال المقارنة
تتمثل قوة qsort
في إمكانية إنشاء دوال مقارنة مخصصة لمختلف أنواع البيانات. فيما يلي بعض الأمثلة لدوال مقارنة لأنواع مختلفة:
دالة مقارنة للأعداد الصحيحة
int compare_int(const void *a, const void *b) {
return (*(int*)a - *(int*)b);
}
دالة مقارنة للسلاسل النصية
int compare_str(const void *a, const void *b) {
return strcmp(*(const char**)a, *(const char**)b);
}
دالة مقارنة لهياكل البيانات (Structs)
struct Person {
int id;
const char* name;
};
int compare_person(const void *a, const void *b) {
return ((struct Person*)a)->id - ((struct Person*)b)->id;
}
بهذه الطريقة، يمكن تخصيص qsort
لتناسب محتويات المصفوفة باستخدام دالة مقارنة مخصصة.
5. فرز مصفوفة من الهياكل باستخدام qsort
يعد qsort
فعالًا جدًا عند فرز مصفوفة تحتوي على هياكل بيانات. على سبيل المثال، إذا أردنا فرز مصفوفة من هيكل Person
استناداً إلى حقل id
:
struct Person people[] = {
{1, "Alice"},
{3, "Charlie"},
{2, "Bob"}
};
qsort(people, 3, sizeof(struct Person), compare_person);
عند استخدام دالة compare_person
، سيتم ترتيب العناصر بناءً على id
بحيث يكون الترتيب: Alice
ثم Bob
ثم Charlie
.
6. تحسين الأداء ومقارنة مع الخوارزميات الأخرى
أداء الترتيب السريع (Quick Sort)
يعتبر الترتيب السريع فعالًا جدًا مع البيانات العشوائية، حيث أن متوسط التعقيد الزمني له هو O(n log n). ولكن، إذا كانت البيانات مرتبة مسبقًا، فقد يصل إلى O(n^2). لذلك يُوصى باستخدام خوارزميات أخرى في ظروف معينة.
مقارنة مع الترتيب بالهيب (Heap Sort)
يحافظ Heap Sort على تعقيد زمني O(n log n) حتى في أسوأ الحالات، مما يجعله أكثر استقرارًا من Quick Sort. ولكن عادةً ما يكون Quick Sort أسرع في المتوسط مع البيانات العشوائية.
مقارنة مع الترتيب بالدمج (Merge Sort)
يتمتع Merge Sort أيضًا بتعقيد O(n log n) ويوفر ترتيبًا مستقرًا (أي يحافظ على ترتيب العناصر المتساوية كما هي). إذا كان الاستقرار مطلوبًا، فإن Merge Sort هو الخيار الأفضل. أما Quick Sort فهو غير مستقر، لكن غالبًا لا يمثل ذلك مشكلة في الاستخدامات العادية.
7. الخلاصة
تُعتبر qsort
من أقوى دوال الفرز في لغة C بفضل مرونتها وسرعتها العالية. ومن خلال إنشاء دوال مقارنة مخصصة، يمكن فرز أنواع متعددة من البيانات، بما في ذلك المصفوفات وهياكل البيانات.
يُمكنك الاستفادة القصوى من qsort
عبر فهم خوارزمية الترتيب السريع جيدًا. وحسب نوع البيانات المراد فرزها، قد يكون من المفيد مقارنة أداء Quick Sort مع Heap Sort أو Merge Sort لاختيار الأفضل من حيث الأداء.
جرب استخدام qsort
في مشاريعك القادمة لتحقيق أداء أفضل وبرمجة أكثر كفاءة.