مقدمة
لو بدك تدخل عالم هياكل البيانات والخوارزميات من الباب الصح—فلازم تبدأ من المكدسات والطوابير.
هدول الهيكلين بيظهروا بكل مكان بدون ما تحس:
- محركات الألعاب
- أنظمة التشغيل
- إدارة الذاكرة
- خوارزميات الرسوميات
- الـ DFS / BFS
- الذكاء الاصطناعي
- تحرير النصوص (Undo / Redo)
- المعالجات (Registers, IPC)
- السيرفرات وأنظمة الرسائل
- المترجمات Compilers
وبصراحة… مهما تطورت لغات البرمجة، Stack & Queue هم حجر الأساس لهياكل البيانات الحديثة.
بهذا الدرس رح نشرح:
- مفهوم الـ Stack
- مفهوم الـ Queue
- التنفيذ باستخدام المصفوفات
- التنفيذ باستخدام القوائم المتصلة
- مقارنة كاملة بين الهيكلين
- أهم الاستخدامات الواقعية
- الأخطاء الشائعة التي تدمر البرامج
- أمثلة عملية قوية
المقال طويل جدًا وموسع بالشكل اللي بتحبه… خليك مستعد.
أولًا: المكدس (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
| الخاصية | Stack | Queue |
|---|---|---|
| المبدأ | LIFO | FIFO |
| أكثر استخدام | Undo, Recursion, DFS | Scheduling, 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
- الحوسبة المتوازية
فهم هذا الدرس ليس “معلومة”…
بل بوابة لعالم كامل ينتظرك.
اكتشاف المزيد من كود التطور
اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.


