طَبِّقْهاتعلّم المبدأ، وطبّقه بيدكمقرر 101 · تركيب البيانات والخوارزميات
الدرس 23 · الوحدة 11: اختيار وتطبيق الحلول المناسبة

طابور الأولوية heapq

طابور لا يحترم الأقدمية: يخرج منه الأهم دائمًا. ⏱ نحو 30 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تشرح طابور الأولوية وتقارن طرق تنفيذه.
  • تشرح فكرة الكومة الصغرى وتمثيلها في قائمة.
  • تستخدم heappush و heappop و heapify و nlargest.
  • تطبق الكومة على الجدولة واختيار أفضل k عنصر.

١طابور يخرج منه الأهم لا الأقدم

في الطوارئ لا يُخدم المريض حسب وقت وصوله، بل حسب خطورة حالته. هذا طابور الأولوية Priority Queue: كل عنصر له أولوية، ونُخرج دائمًا الأعلى أولوية.

التنفيذإضافةإخراج الأصغر
قائمة عاديةO(1)O(n): نبحث عن الأصغر
قائمة مرتبةO(n): نُدرج في مكانهO(1)
الكومة HeapO(log n)O(log n)

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

٢الكومة: شجرة في قائمة

3851210973081521231049576
الكومة الصغرى: كل أب أصغر من ابنيه، فالأصغر دائمًا في الجذر (الموقع 0). وتُخزَّن الشجرة في قائمة عادية: ابنا الموقع i في 2i+1 و 2i+2.

عند الإضافة نضع العنصر في آخر القائمة ثم «يطفو» بالتبديل مع أبيه ما دام أصغر منه. وعند الإخراج نأخذ الجذر، ونضع آخر عنصر مكانه ثم «يغطس». كل منهما بعدد مستويات الشجرة: O(log n).

٣مكتبة heapq

main.py
import heapq er = []                               # (الخطورة، الاسم): الأصغر أخطرheapq.heappush(er, (3, "التواء كاحل"))heapq.heappush(er, (1, "أزمة قلبية"))heapq.heappush(er, (2, "كسر"))heapq.heappush(er, (5, "زكام")) print("القائمة الداخلية:", er)while er:    level, case = heapq.heappop(er)    print("الآن:", case, "| الخطورة", level)
Shell×
>>> %Run main.py
القائمة الداخلية: [(1, 'أزمة قلبية'), (3, 'التواء كاحل'), (2, 'كسر'), (5, 'زكام')]
الآن: أزمة قلبية | الخطورة 1
الآن: كسر | الخطورة 2
الآن: التواء كاحل | الخطورة 3
الآن: زكام | الخطورة 5
>>>

العناصر tuple: يقارن بايثون العنصر الأول (الأولوية) أولًا. و heapq كومة صغرى: يخرج الأصغر. ولكومة كبرى نخزن سالب الأولوية.

٤أشهر الاستخدامات

main.py
import heapq scores = [72, 95, 64, 88, 99, 51, 83, 90, 77, 60] # 1) أكبر 3 وأصغر 3: O(n log k)print(heapq.nlargest(3, scores))print(heapq.nsmallest(3, scores)) # 2) تحويل قائمة إلى كومة في المكان: O(n)h = list(scores)heapq.heapify(h)print("الأصغر:", h[0]) # 3) دمج قوائم مرتبة كثيرةprint(list(heapq.merge([1, 4, 9], [2, 3, 10], [5, 6])))
Shell×
>>> %Run main.py
[99, 95, 90]
[51, 60, 64]
الأصغر: 51
[1, 2, 3, 4, 5, 6, 9, 10]
>>>

٥مثال: جدولة مهام الخادم

خادم ينفّذ المهام بالأولوية، وعند التساوي بترتيب الوصول. نضيف رقم وصول متسلسلًا في الـ tuple ليفصل بين المتساويين:

main.py
import heapq tasks = []arrival = 0def submit(priority, name):    global arrival    heapq.heappush(tasks, (priority, arrival, name))    arrival += 1 submit(2, "إرسال تقرير")submit(1, "دفع فاتورة")submit(2, "نسخ احتياطي")submit(3, "تنظيف الملفات المؤقتة")submit(1, "تسجيل دخول") while tasks:    p, _, name = heapq.heappop(tasks)    print(p, name)
Shell×
>>> %Run main.py
1 دفع فاتورة
1 تسجيل دخول
2 إرسال تقرير
2 نسخ احتياطي
3 تنظيف الملفات المؤقتة
>>>

توقّع وتحقّق

heapq.heappop يُخرج:

كومة صغرى.

تعقيد heappush:

يطفو بعدد المستويات.

ابنا العقدة في الموقع i:

شجرة في قائمة.

لكومة كبرى بـ heapq نخزن:

-x.

الخلاصة

تحدٍّ برمجي: أسرع 3 متسابقين أثناء السباق

متوسط

نتائج سباق تصل واحدة واحدة (الاسم، الزمن بالثواني). احتفظ بكومة كبرى حجمها 3 لأسرع ثلاثة دون ترتيب كل النتائج، ثم اطبعهم من الأسرع.

المطلوب:
  • خزّن (-الزمن، الاسم) لتصبح الكومة كبرى.
  • إن زاد حجمها عن 3 أخرج الأبطأ بـ heappop.
  • اطبع في الآخر الثلاثة من الأسرع: الاسم ثم الزمن.

ابدأ من هذا الكود، واضغط ▶ شغّل هنا لتكتب حلّك وتجرّبه مباشرة:

main.py
import heapqresults = [("أحمد", 52.4), ("سالم", 49.8), ("فهد", 55.1), ("عمر", 48.9), ("ماجد", 50.2), ("زياد", 51.0)]
المطلوب×
عمر 48.9
سالم 49.8
ماجد 50.2
💡 تلميح 1
الكومة الكبرى بالسالب: الأبطأ (أكبر زمن) هو أصغر سالب فيخرج أولًا.
💡 تلميح 2
sorted(top, reverse=True) يرتب من الأسرع.

حاول بنفسك أولًا؛ للمسألة أكثر من حلّ صحيح.

main.py
import heapqresults = [("أحمد", 52.4), ("سالم", 49.8), ("فهد", 55.1), ("عمر", 48.9), ("ماجد", 50.2), ("زياد", 51.0)] top = []for name, t in results:    heapq.heappush(top, (-t, name))    if len(top) > 3:        heapq.heappop(top) for neg_t, name in sorted(top, reverse=True):    print(name, -neg_t)
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

📝 معلم؟ أرسل ملاحظة للمؤلف عن هذا الدرس