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

المشروع الختامي

المشروع ودراسة الحالة والمحاكاة كما تطلبها الخطة الرسمية. ⏱ نحو 60 دقيقة.

بعد هذا الدرس ستستطيع أن:
  • تنفذ قائمة مترابطة كاملة برأس وذيل وتختبرها.
  • تحلل تعقيد كل عملية وتقيسه تجريبيًّا مقارنة بـ list و deque.
  • تستخدم المحاكاة لتحليل خوارزمية ترتيب في حالاتها المختلفة.
  • تكتب تقريرًا يحقق معايير التقييم.

١ثلاثة أنشطة من الخطة الرسمية

الخطة التدريبية للمقرر تحدد ثلاثة أنشطة عملية، تنفذها في المعمل وتسلّمها لمعلمك:

النشاطالمطلوبالناتج
1. المشروع: هيكل بياناتتنفيذ قائمة مترابطة (أو مصفوفة ديناميكية) بلغة بايثون: إنشاء، وإضافة، وحذف، وبحث.كود يعمل + تقرير يحلل تعقيد كل عملية.
2. دراسة حالة: تحليل خوارزميةتحليل الترتيب السريع أو البحث الثنائي: كيف يعمل، وتعقيده، ومقارنته بغيره.تقرير: شرح وتحليل وتوصيات استخدام.
3. المحاكاةتنفيذ خوارزمية بحث أو ترتيب خطوة بخطوة بأداة محاكاة (المحاكي في هذا الموقع، أو Visualgo).تقرير بالخطوات والتحليل الزمني والتحسينات.

٢النشاط 1: القائمة المترابطة الكاملة

هذا هيكل المشروع. أكمل الدوال، واختبر كل واحدة بـ assert، ثم قِس زمن العمليات وقارنه بـ list:

main.py
class Node:    def __init__(self, data):        self.data = data        self.next = None class LinkedList:    """قائمة مترابطة: رأس وذيل وحجم"""    def __init__(self):        self.head = self.tail = None        self.size = 0     def push_front(self, x):          # O(1)        n = Node(x)        n.next = self.head        self.head = n        if self.tail is None:            self.tail = n        self.size += 1     def push_back(self, x):           # O(1) بفضل الذيل        n = Node(x)        if self.tail is None:            self.head = self.tail = n        else:            self.tail.next = n            self.tail = n        self.size += 1     def pop_front(self):              # O(1)        if self.head is None:            raise IndexError("القائمة فارغة")        x = self.head.data        self.head = self.head.next        if self.head is None:            self.tail = None        self.size -= 1        return x     def find(self, x):                # O(n)        cur, i = self.head, 0        while cur:            if cur.data == x:                return i            cur, i = cur.next, i + 1        return -1     def __len__(self):        return self.size     def __iter__(self):        cur = self.head        while cur:            yield cur.data            cur = cur.next # اختباراتL = LinkedList()assert len(L) == 0 and L.find(5) == -1L.push_back(2); L.push_back(3); L.push_front(1)assert list(L) == [1, 2, 3] and len(L) == 3assert L.find(3) == 2assert L.pop_front() == 1 and list(L) == [2, 3]L.pop_front(); L.pop_front()assert L.head is None and L.tail is Noneprint("نجحت كل الاختبارات")
Shell×
>>> %Run main.py
نجحت كل الاختبارات
>>>

٣قياس الأداء للتقرير

main.py
import timefrom collections import deque class Node:    __slots__ = ("data", "next")    def __init__(self, d): self.data, self.next = d, None class LinkedList:    def __init__(self): self.head = None    def push_front(self, x):        n = Node(x); n.next = self.head; self.head = n def bench(n):    t = time.perf_counter(); a = []    for i in range(n): a.insert(0, i)    t_list = time.perf_counter() - t     t = time.perf_counter(); L = LinkedList()    for i in range(n): L.push_front(i)    t_ll = time.perf_counter() - t     t = time.perf_counter(); d = deque()    for i in range(n): d.appendleft(i)    t_dq = time.perf_counter() - t    return t_list, t_ll, t_dq for n in [10_000, 40_000]:    a, b, c = bench(n)    print(f"n={n:>6} | list.insert(0): {a*1000:7.1f} ms | LinkedList: {b*1000:6.1f} ms | deque: {c*1000:5.1f} ms")
Shell×
>>> %Run main.py
n= 10000 | list.insert(0): 10.2 ms | LinkedList: 1.9 ms | deque: 0.4 ms
n= 40000 | list.insert(0): 165.0 ms | LinkedList: 10.2 ms | deque: 1.8 ms
>>>

حين يتضاعف n أربع مرات، يتضاعف زمن list.insert(0) نحو 16 مرة (O(n²) للكل)، ويتضاعف زمن القائمة المترابطة و deque نحو 4 مرات فقط (O(n) للكل). الأرقام تختلف من جهاز لآخر، والنمط ثابت.

٤النشاط 2 و 3: تحليل ومحاكاة

استخدم المحاكي لتتبع الترتيب السريع على قائمتك، وسجّل عدد المقارنات والتبديلات في تقريرك. ثم جرّب قائمة مرتبة لترى الحالة الأسوأ:

🎬 محاكاة: الترتيب السريع

مقارنةتبديل أو نقلالمحور أو الوسطفي مكانه النهائي أو وُجد
🎬 الترتيب السريع على قائمة مرتبة: الحالة الأسوأ

مقارنةتبديل أو نقلالمحور أو الوسطفي مكانه النهائي أو وُجد

٥معايير التقييم

المعيارالوزنممتاز حين…
صحة الكود30%كل العمليات تعمل، ومعها اختبارات assert للحالات الحدّية.
تحليل التعقيد25%تعقيد زمني ومساحي صحيح لكل عملية، مع السبب.
القياس التجريبي20%قياس على حجمين أو أكثر، ومقارنة بـ list أو deque، وتفسير النمط.
دراسة الحالة والمحاكاة15%شرح خطوة بخطوة، وحالات أفضل وأسوأ، وتوصيات.
جودة الكود والتقرير10%أسماء واضحة، وتعليقات، وتقرير منظم.

توقّع وتحقّق

بوجود مؤشر الذيل، الإضافة في آخر القائمة المترابطة:

لا مرور.

إضافة n عنصر في أول list بـ insert(0) كلفتها الكلية:

إزاحات.

عند حذف آخر عقدة في القائمة يجب جعل:

القائمة فارغة.

أداة المحاكاة المذكورة في الخطة:

أو المحاكي في الموقع.

الخلاصة

تحدٍّ برمجي: طابور بقائمة مترابطة

متقدم

استخدم القائمة المترابطة لبناء طابور: enqueue بالإضافة في الذيل، و dequeue بالحذف من الرأس، ثم حاكِ طابور 5 عملاء يُخدم منهم 3.

المطلوب:
  • enqueue و dequeue كلاهما O(1).
  • أضف: أحمد، سارة، خالد، منى، فهد.
  • اخدم ثلاثة: اطبع «خدمة: …» لكل منهم.
  • اطبع الباقين: الباقون: ['منى', 'فهد']

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

main.py
class Node:    def __init__(self, data):        self.data = data        self.next = None class Queue:    def __init__(self):        self.head = self.tail = None     def enqueue(self, x):        pass     def dequeue(self):        pass
المطلوب×
خدمة: أحمد
خدمة: سارة
خدمة: خالد
الباقون: ['منى', 'فهد']
💡 تلميح 1
enqueue مثل push_back، و dequeue مثل pop_front.
💡 تلميح 2
لا تنسَ: إن فرغت القائمة اجعل tail = None.

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

main.py
class Node:    def __init__(self, data):        self.data = data        self.next = None class Queue:    def __init__(self):        self.head = self.tail = None     def enqueue(self, x):        n = Node(x)        if self.tail is None:            self.head = self.tail = n        else:            self.tail.next = n            self.tail = n     def dequeue(self):        x = self.head.data        self.head = self.head.next        if self.head is None:            self.tail = None        return x     def items(self):        out, cur = [], self.head        while cur:            out.append(cur.data)            cur = cur.next        return out q = Queue()for name in ["أحمد", "سارة", "خالد", "منى", "فهد"]:    q.enqueue(name)for _ in range(3):    print("خدمة:", q.dequeue())print("الباقون:", q.items())
انتقل إلى تقييم الدرساختيار من متعدد، وصح وخطأ، وأكمل الفراغ، وتتبّع الناتج.ابدأ التقييم ←

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