المكدسات والطوابير (Stacks & Queues)

مقدمة

لو بدك تدخل عالم هياكل البيانات والخوارزميات من الباب الصح—فلازم تبدأ من المكدسات والطوابير.
هدول الهيكلين بيظهروا بكل مكان بدون ما تحس:

  • محركات الألعاب
  • أنظمة التشغيل
  • إدارة الذاكرة
  • خوارزميات الرسوميات
  • الـ DFS / BFS
  • الذكاء الاصطناعي
  • تحرير النصوص (Undo / Redo)
  • المعالجات (Registers, IPC)
  • السيرفرات وأنظمة الرسائل
  • المترجمات Compilers

وبصراحة… مهما تطورت لغات البرمجة، Stack & Queue هم حجر الأساس لهياكل البيانات الحديثة.

بهذا الدرس رح نشرح:

  1. مفهوم الـ Stack
  2. مفهوم الـ Queue
  3. التنفيذ باستخدام المصفوفات
  4. التنفيذ باستخدام القوائم المتصلة
  5. مقارنة كاملة بين الهيكلين
  6. أهم الاستخدامات الواقعية
  7. الأخطاء الشائعة التي تدمر البرامج
  8. أمثلة عملية قوية

المقال طويل جدًا وموسع بالشكل اللي بتحبه… خليك مستعد.

أولًا: المكدس (Stack)

ما هو المكدس؟

المكدس هو هيكل بيانات يعمل بنظام:

LIFO – Last In, First Out
آخر شيء تدخله هو أول شيء يطلع منه.

تمامًا مثل كومة الصحون:
كل ما تحط صحن، بتحطه “فوق”.
ولما بدك تاخذ صحن، بتاخذ اللي فوق.

العمليات الأساسية في Stack:

العمليةالوصف
push(x)إضافة عنصر فوق المكدس
pop()إزالة العنصر العلوي
peek() / top()قراءة العنصر العلوي بدون حذفه
isEmpty()هل المكدس فارغ؟
size()حجم المكدس

تمثيل رسومي بسيط:

    ↑
   top
  [50]
  [30]
  [20]
  [10]

مميزات المكدس:

  • بسيط جدًا
  • سريع
  • مناسب للتراجع Undo
  • مناسب للتنقل Backtracking
  • يعتمد على الذاكرة بطريقة منظمة

عيوبه:

  • ما تقدر توصل لأي عنصر إلا من الأعلى فقط
  • غير مناسب للبحث
  • لا يمكنك إضافة في منتصفه

ثانيًا: تنفيذ Stack باستخدام Array

هذا أسهل وأكثر تنفيذ منتشر.

تعريف المكدس بالمصفوفة:

struct Stack {
    int arr[1000];
    int top;

    Stack() {
        top = -1;
    }
};

push:

void push(int x) {
    arr[++top] = x;
}

pop:

void pop() {
    if (top == -1) return;
    top--;
}

peek:

int peek() {
    return arr[top];
}

isEmpty:

bool isEmpty() {
    return top == -1;
}

مثال استخدام:

Stack s;
s.push(5);
s.push(10);
s.push(20);
cout << s.peek(); // 20
s.pop();
cout << s.peek(); // 10

ثالثًا: تنفيذ Stack باستخدام Linked List

تنفيذ ديناميكي، مافي حد لحجم المكدس.

العقدة:

struct Node {
    int data;
    Node* next;
};

رأس المكدس:

Node* top = nullptr;

push:

void push(int x) {
    Node* n = new Node();
    n->data = x;
    n->next = top;
    top = n;
}

pop:

void pop() {
    if (!top) return;

    Node* temp = top;
    top = top->next;
    delete temp;
}

peek:

int peek() {
    return top->data;
}

مميزات هذا التنفيذ:

  • لا يحتاج حجم ثابت
  • سريع
  • ديناميكي
  • يستخدم الذاكرة بكفاءة

رابعًا: الطابور (Queue)

ما هو الطابور؟

الطابور يعمل بمبدأ:

FIFO – First In, First Out
أول شخص يدخل → أول شخص يطلع.

مثل طابور بنك، طابور باص، طابور طلبات سيرفر.

العمليات الأساسية:

العمليةالوصف
enqueue(x)إضافة عنصر في نهاية الطابور
dequeue()حذف أول عنصر
front()جلب أول عنصر بدون حذفه
isEmpty()هل الطابور فارغ؟
size()عدد العناصر

تمثيل رسومي:

Front → [10] → [20] → [30] → [40] ← Rear

خامسًا: تنفيذ Queue باستخدام Array

التعريف:

struct Queue {
    int arr[1000];
    int front, rear;

    Queue() {
        front = 0;
        rear = -1;
    }
};

enqueue:

void enqueue(int x) {
    arr[++rear] = x;
}

dequeue:

void dequeue() {
    if (front > rear) return;
    front++;
}

front():

int frontElement() {
    return arr[front];
}

مشكلة التنفيذ:

مع كل dequeue، front يتحرك → مساحة تُهدر.
الحل الحقيقي: Circular Queue
لكن هذا الدرس يمهّد للفكرة فقط.

سادسًا: تنفيذ Queue باستخدام Linked List

تعريف Node:

struct Node {
    int data;
    Node* next;
};

مؤشرين مهمين:

Node* front = nullptr;
Node* rear = nullptr;

enqueue:

void enqueue(int x) {
    Node* n = new Node();
    n->data = x;
    n->next = nullptr;

    if (!rear) {
        front = rear = n;
        return;
    }

    rear->next = n;
    rear = n;
}

dequeue:

void dequeue() {
    if (!front) return;

    Node* temp = front;
    front = front->next;

    if (!front) rear = nullptr;

    delete temp;
}

front():

int frontElement() {
    return front->data;
}

مميزات هذا التنفيذ:

  • ديناميكي
  • قوي
  • ما فيه أي إهدار مساحة
  • مناسب للطوابير الكبيرة

سابعًا: مقارنة شاملة بين Stack و Queue

الخاصيةStackQueue
المبدأLIFOFIFO
أكثر استخدامUndo, Recursion, DFSScheduling, BFS
الوصول العشوائي
الإضافةأعلى المكدس فقطنهاية الطابور فقط
الحذفأعلى المكدس فقطبداية الطابور فقط
مناسب لـمعالجات الاستدعاءاتأنظمة الطوابير

ثامنًا: استخدامات واقعية قوية

استخدامات Stack:

✔ تنفيذ الاستدعاء الذاتي (Recursion)
✔ عمليات Undo/Redo
✔ معالجة الأقواس الرياضية (Parsing)
✔ تنفيذ DFS
✔ تقييم التعبيرات postfix
✔ Call Stack في المعالج
✔ إدارة الذاكرة (Stack Frame)

استخدامات Queue:

✔ تنفيذ BFS
✔ إدارة طوابير السيرفر
✔ جدولـة عمليات CPU
✔ معالجة الرسائل Message Queue
✔ الطوابير في أنظمة الألعاب
✔ الحوسبة المتوازية
✔ طوابير الطباعة

تاسعًا: الأخطاء القاتلة عند المبتدئين

❌ نسيان delete في Linked List
❌ تخطي حدود المصفوفة
❌ إجراء dequeue على طابور فارغ
❌ إجراء pop على مكدس فارغ
❌ عدم تحديث front و rear
❌ استخدام Queue في مكان Stack والعكس

عاشرًا: مشروع مصغّر يجمع كل المفاهيم

تنفيذ Stack لحساب تعبير postfix:

Input: 
5 3 + 2 *

Stack operation:
push(5)
push(3)
pop → 3  
pop → 5  
push(8)
push(2)
pop → 2  
pop → 8  
push(16)

النتيجة: 16

تنفيذ Queue لمحاكاة طابور بنك:

enqueue(1001)
enqueue(1002)
dequeue()
enqueue(1003)
dequeue()

الطابور يشتغل بكفاءة تامة.

الخلاصة النهائية

اليوم تعلّمت واحد من أهم دروس هياكل البيانات:

  • المكدسات Stack
  • الطوابير Queue
  • تنفيذهم باستخدام Array
  • تنفيذهم باستخدام Linked List
  • الاستخدامات الواقعية
  • مقارنة قوية بينهم
  • الأخطاء الشائعة
  • أمثلة عملية
  • فهم عميق للبنية الداخلية

Stack & Queue هم الأب والأم لهياكل البيانات المتقدمة:

  • Deque
  • Priority Queue
  • Heap
  • BFS / DFS
  • Graph Traversal
  • Scheduling
  • Parsing
  • Memory Frames
  • الحوسبة المتوازية

فهم هذا الدرس ليس “معلومة”…
بل بوابة لعالم كامل ينتظرك.


اكتشاف المزيد من كود التطور

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

اترك رد

Scroll to Top