C++: خوارزميات STL

في الدرس 34 تعلّمنا عن حاويات STL — ماذا نخزّن البيانات فيه.

لكن الحاويات وحدها لا تكفي — تحتاج أيضًا إلى معالجة البيانات: البحث، والفرز، والعد، والتحويل...

كتابتها بنفسك قد تستغرق عشرات الأسطر؛ مع خوارزميات STL، سطر واحد يكفي.


1. نظرة عامة على خوارزميات STL

(1) 1.1 ما هي خوارزميات STL؟

خوارزميات STL هي مجموعة من قوالب الدوال العامة التي توفرها مكتبة C++ القياسية لمعالجة البيانات في الحاويات.

لماذا نستخدم خوارزميات STL؟

كتابتها بنفسك خوارزميات STL
تحتاج لكتابة حلقات سطر واحد من الكود
عرضة للأخطاء مختبرة بدقة
الأداء قد يتفاوت محسّنة بأعلى مستوى
كود مطوّل كود موجز

تشبيه من الحياة الواقعية:


(2) 1.2 ملفات رأس الخوارزميات

معظم خوارزميات STL في الرأس algorithm، والخوارزميات العددية في numeric.

▶ مثال 2: تطبيق خوارزميات STL (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <algorithm> // معظم الخوارزميات
#include <numeric> // الخوارزميات العددية (accumulate، إلخ)

المخرجات:

TEXT 📖 للعرض فقط
(مخرجات البرنامج)

(3) 1.3 فئات الخوارزميات

تُقسَّم خوارزميات STL إلى عدة فئات رئيسية حسب الوظيفة:

الفئة الخوارزميات الممثّلة الوصف
غير معدِّلة find، count، for_each لا تُعدّل محتوى الحاوية
معدِّلة copy، transform، replace تُعدّل محتوى الحاوية
الفرز sort، stable_sort، partial_sort متعلقة بالفرز
البحث الثنائي binary_search، lower_bound البحث في نطاقات مرتّبة
الدمج merge، inplace_merge دمج نطاقات مرتّبة
عددية accumulate، inner_product حسابات عددية
المجموعات set_union، set_intersection عمليات المجموعات


2. الخوارزميات غير المعدِّلة

(1) 2.1 find — البحث عن عناصر

الوظيفة: البحث عن عنصر محدد في حاوية، وإرجاع مكرّر.

النموذج الأولي:

CPP
InputIt find(InputIt first, InputIt last, const T& value);

مثال: البحث عن درجة (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 
 // البحث عن الدرجة 90
 auto it = std::find(scores.begin(), scores.end(), 90);
 
 if (it != scores.end()) {
 // حساب الموضع (الفهرس)
 int index = std::distance(scores.begin(), it);
 std::cout << "وُجدت الدرجة 90 في الموضع: " << index << std::endl;
 } else {
 std::cout << "الدرجة 90 غير موجودة" << std::endl;
 }
 
 return 0;
}

النتيجة:

TEXT 📖 للعرض فقط
وُجدت الدرجة 90 في الموضع: 3

💡 نصيحة:


(2) 2.2 count — العد

الوظيفة: عدّ عدد العناصر المساوية لقيمة محددة.

مثال: عدّ الدرجات الكاملة (الصعوبة ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {100, 85, 100, 92, 78, 100};
 
 // عدّ الدرجات الكاملة (100)
 int perfect = std::count(scores.begin(), scores.end(), 100);
 
 std::cout << "عدد الدرجات الكاملة: " << perfect << std::endl; // المخرج: 3
 
 return 0;
}

(3) 2.3 for_each — التكرار

الوظيفة: تنفيذ عملية محددة على كل عنصر في حاوية.

مثال: طباعة جميع الدرجات (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 
 // استخدام تعبير lambda لطباعة كل درجة
 std::for_each(scores.begin(), scores.end(), [](int s) {
 std::cout << s << " ";
 });
 std::cout << std::endl;
 
 return 0;
}

النتيجة:

TEXT 📖 للعرض فقط
85 92 78 90 88 

💡 نصيحة:



3. الخوارزميات المعدِّلة

(1) 3.1 copy — النسخ

الوظيفة: نسخ العناصر من نطاق إلى آخر.

مثال: نسخ مصفوفة (الصعوبة ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` src = {1, 2, 3, 4, 5};
 std::vector`<int>` dst(5); // الحاوية الوجهة، الحجم 5
 
 // النسخ
 std::copy(src.begin(), src.end(), dst.begin());
 
 // طباعة النتيجة
 for (int x : dst) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

(2) 3.2 transform — التحويل

الوظيفة: تحويل العناصر من نطاق ونسخها إلى آخر.

مثال: درجات مُوزونة (الصعوبة ⭐⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 std::vector`<int>` adjusted(scores.size()); // الدرجات المعدّلة
 
 // أعمال دورية 70%، امتحان 30%
 std::transform(scores.begin(), scores.end(), adjusted.begin(),
 [](int s) { return s * 0.7 + 90 * 0.3; });
 
 std::cout << "الدرجات المعدّلة: ";
 for (int x : adjusted) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

النتيجة:

TEXT 📖 للعرض فقط
الدرجات المعدّلة: 86.5 91.9 81.6 90 88.6 

(3) 3.3 replace — الاستبدال

الوظيفة: استبدال العناصر المساوية لقيمة معينة بقيمة أخرى.

مثال: معالجة درجات الامتحان التعويضي (الصعوبة ⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 
 // استبدال الدرجات الراسبة (<60) بـ 60 (حد النجاح في الامتحان التعويضي)
 std::replace_if(scores.begin(), scores.end(),
 [](int s) { return s < 60; },
 60);
 
 std::cout << "الدرجات المُعالجة: ";
 for (int x : scores) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}


4. خوارزميات الفرز

(1) 4.1 sort — الفرز

الوظيفة: فرز نطاق في حاوية (تصاعديًا افتراضيًا).

مثال: فرز الدرجات (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <algorithm>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 
 // فرز تصاعدي
 std::sort(scores.begin(), scores.end());
 
 std::cout << "تصاعدي: ";
 for (int x : scores) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 // فرز تنازلي
 std::sort(scores.begin(), scores.end(), std::greater`<int>`());
 
 std::cout << "تنازلي: ";
 for (int x : scores) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

النتيجة:

TEXT 📖 للعرض فقط
تصاعدي: 78 85 88 90 92 
تنازلي: 92 90 88 85 78 

(2) 4.2 قواعد فرز مخصصة

مثال: فرز الطلاب حسب الدرجة (الصعوبة ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>

struct Student {
 std::string name;
 int score;
};

int main() {
 std::vector`<Student>` students = {
 {"Zhang San", 85},
 {"Li Si", 92},
 {"Wang Wu", 78}
 };
 
 // فرز تنازلي حسب الدرجة
 std::sort(students.begin(), students.end(),
 [](const Student& a, const Student& b) {
 return a.score > b.score;
 });
 
 std::cout << "ترتيب الدرجات:" << std::endl;
 for (const auto& s : students) {
 std::cout << s.name << ": " << s.score << std::endl;
 }
 
 return 0;
}

النتيجة:

TEXT 📖 للعرض فقط
ترتيب الدرجات:
Li Si: 92
Zhang San: 85
Wang Wu: 78


5. الخوارزميات العددية

(1) 5.1 accumulate — الجمع

الوظيفة: حساب المجموع التراكمي للعناصر في نطاق.

مثال: حساب المجموع الكلي للدرجات (الصعوبة ⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <numeric>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88};
 
 // حساب المجموع الكلي للدرجات
 int total = std::accumulate(scores.begin(), scores.end(), 0);
 
 std::cout << "المجموع الكلي: " << total << std::endl; // المخرج: 433
 std::cout << "المتوسط: " << total / 5.0 << std::endl; // المخرج: 86.6
 
 return 0;
}

(2) 5.2 الجداء الداخلي

مثال: جداء متجه نقطي (الصعوبة ⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <numeric>

int main() {
 std::vector`<int>` v1 = {1, 2, 3};
 std::vector`<int>` v2 = {4, 5, 6};
 
 // حساب الجداء النقطي: 1*4 + 2*5 + 3*6 = 32
 int dot_product = std::inner_product(v1.begin(), v1.end(), v2.begin(), 0);
 
 std::cout << "الجداء النقطي للمتجه: " << dot_product << std::endl; // المخرج: 32
 
 return 0;
}


6. مثال شامل

▶ مثال 1: نظام تحليل الدرجات (الصعوبة ⭐⭐⭐)

TEXT 📖 للعرض فقط
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <string>

int main() {
 std::vector`<int>` scores = {85, 92, 78, 90, 88, 76, 95, 83, 89, 91};
 
 // 1. حساب العدد الإجمالي للطلاب
 int count = scores.size();
 std::cout << "إجمالي الطلاب: " << count << std::endl;
 
 // 2. حساب المجموع والمتوسط
 int total = std::accumulate(scores.begin(), scores.end(), 0);
 double average = static_cast`<double>`(total) / count;
 std::cout << "المجموع الكلي: " << total << "، المتوسط: " << average << std::endl;
 
 // 3. إيجاد أعلى وأدنى درجة
 int max_score = *std::max_element(scores.begin(), scores.end());
 int min_score = *std::min_element(scores.begin(), scores.end());
 std::cout << "الأعلى: " << max_score << "، الأدنى: " << min_score << std::endl;
 
 // 4. عدّ الطلاب الناجحين
 int passed = std::count_if(scores.begin(), scores.end(),
 [](int s) { return s >= 60; });
 std::cout << "الطلاب الناجحون: " << passed << std::endl;
 
 // 5. الفرز وإخراج أفضل 3
 std::vector`<int>` top3 = scores;
 std::sort(top3.begin(), top3.end(), std::greater`<int>`());
 std::cout << "أفضل 3: ";
 for (int i = 0; i < 3; i++) {
 std::cout << top3[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

المخرجات:

TEXT 📖 للعرض فقط
85 92 78 90 88 76 95 83 89 91

النتيجة:

TEXT 📖 للعرض فقط
إجمالي الطلاب: 10
المجموع الكلي: 867، المتوسط: 86.7
الأعلى: 95، الأدنى: 76
الطلاب الناجحون: 10
أفضل 3: 95 92 91 


7. نصائح استخدام الخوارزميات

(1) 7.1 دوال المساعدة للمكرّرات

الدالة الغرض
std::distance(first, last) حساب المسافة بين مكرّرين
std::advance(it, n) تقديم مكرّر بمقدار n خطوة
std::next(it) إرجاع المكرّر التالي
std::prev(it) إرجاع المكرّر السابق

(2) 7.2 تعبيرات Lambda المتقدمة

تعبيرات lambda هي رفيق رائع لخوارزميات STL:

CPP
// الشكل الأساسي
[capture](parameters) -> return_type { body }

// مثال: الفرز بمعايير متعددة
std::sort(students.begin(), students.end(),
 [](const Student& a, const Student& b) {
 if (a.score != b.score)
 return a.score > b.score; // أولًا حسب الدرجة
 return a.name < b.name; // ثم حسب الاسم
 });

❓ أسئلة شائعة

س هل خوارزميات STL أسرع من الحلقات؟
ج خوارزميات STL عادةً أسرع لأن: - محسّنة بأعلى مستوى - لديها نسخ متخصصة لحاويات مختلفة - المُصرِّفات يمكنها تحسينها بشكل أفضل

س هل يمكن لجميع الحاويات استخدام خوارزميات STL؟
ج نظريًا نعم، لكن الكفاءة تتفاوت: - الحاويات المتسلسلة (vector، deque): كفاءة عالية - الحاويات الترابطية (set، map): لديها دوال عضوية خاصة، وهي أسرع

س: ما هي تعبيرات Lambda؟ ج: تعبيرات lambda هي دوال مجهولة أُدخلت في C++11 يمكن تعريفها مباشرة أينما تكون هناك حاجة لدالة.

الصياغة الأساسية:

TEXT 📖 للعرض فقط
[capture](params) -> return_type { body }

مثال:

CPP
auto add = [](int a, int b) { return a + b; };
std::cout << add(3, 5) << std::endl; // المخرج: 8

س ماذا لو أبلغت خوارزمية عن خطأ؟
ج الأخطاء الشائعة: 1. استخدام البحث الثنائي على حاوية غير مرتّبة → رتّب أولًا 2. الحاوية الوجهة صغيرة جدًا → استخدم back_inserter 3. عدم تطابق نوع المكرّر → تحقق من نوع الحاوية

▶ مثال 3: sort (الصعوبة ⭐)

CPP
#include <iostream>
#include <algorithm>
#include <vector>

int main() {
    std::vector`<int>` v = {5, 2, 8, 1, 9};

    std::sort(v.begin(), v.end());

    for (int x : v) {
        std::cout << x << " ";
    }
    std::cout << std::endl;

    return 0;
}
▶ جرّب الكود

المخرجات:

TEXT 📖 للعرض فقط
1 2 3 4 5
💡 نصيحة: std::sort يفرز ترتيبًا تصاعديًا افتراضيًا، ويأخذ نطاق مكرّرات [begin, end).


📖 ملخص

توصيات الدراسة:


📝 تمارين

  1. أساسي (الصعوبة ⭐): أنشئ vector<int> يحتوي على 10 أرقام عشوائية، ورتّبه بـ sort وأخرجه، ثم اقلبه بـ reverse وأخرجه.

  2. متوسط (الصعوبة ⭐⭐): استخدم find للبحث عن سلسلة محددة في vector<string>، واستخدم count لعدّ مرات ظهور قيمة معينة.

  3. متقدم (الصعوبة ⭐⭐⭐): استخدم remove_if وlambda لتطبيق "إزالة جميع الأرقام الزوجية من vector". افهم أسلوب erase-remove.



الدرس التالي: تطبيق: شامل OOP (#36) — إعادة هيكلة نظام إدارة الطلاب باستخدام التفكير كائني التوجه

Web-Tutorial.com

فريق Web-Tutorial التقني

منصة دروس برمجية يديرها عدة مطورين. كل درس يتم كتابته ومراجعته بواسطة مطورين متخصصين في المجال. نعمل على ضمان دقة وموثوقية المحتوى — إذا لاحظت أي مشكلة، فيرجى إخبارنا.

100%