بناء الـ Queue عن طريق الـ LinkedList

السلام عليكم ورحمة الله وبركاته

وقت القراءة: ≈ 15 دقيقة

المقدمة

الآن شرحنا الـ LinkedList وشرحنا الـ Stack وكيفية بنائها عن طريق الـ LinkedList
الآن سنقوم بشرح الـ Queue وهو مشابه لشقيقه للـ Stack مع فارق بسيط

وكما قلت لك في الـ Stack أنك إذا فهمت الـ LinkedList وكيفية بنائها
فإنك ستكون قادرًا على بناء الـ Stack بسهولة وبساطة
وهنا الأمر نفسه مع الـ Queue فإذا فهمت الـ LinkedList وكيفية بنائها
فإنك ستكون قادرًا على بناء الـ Queue بسهولة وبساطة

لكن هناك فارق بسيط بين الـ Stack والـ Queue
فالـ Stack يعمل بطريقة LIFO أي Last In First Out
أما الـ Queue فهو يعمل بطريقة FIFO أي First In First Out أي ما يدخل أولًا يخرج أولًا مثل فكرة طابور العيش

بالطبع كما في الـ Stack ستجد طبعًا ستجد بعض الأشخاص يقومون ببناء الـ Queue عن طريق الـ Array أو الـ Dynamic Array بطرق معينة
وهي طرق صحيحة ولكن نحن هنا سنقوم ببناء الـ Queue عن طريق الـ LinkedList

لنبدأ بالشرح

ما هو الـ Queue ؟

الـ Queue هو كما يوحي اسمه بأنه مثل طابور الانتظار في الحياة الواقعية
له مدخل واحد ومخرج واحد فقط بمعنى أنه يتم إضافة العناصر من البداية ويتم إزالتها من النهاية
وهذا يعني أن العنصر الذي يتم إضافته أولًا هو العنصر الذي يتم إزالته أولًا

فأبسط مثال هو كما قلنا هو الطابور فمثلًا لنتخيل أن هناك طابور للحصول على الطعام في مطعم ما
والأشخاص يدخلون الى الطابور وينتظرون حتى يصل دورهم للحصول على الطعام
والشخص الذي دخل الى الطابور أولًا هو الشخص الذي سيحصل على الطعام أولًا

وهذا هو مفهوم الـ Queue ببساطة لا أظن أنك ستحتاج لشرح أكثر من هذا
في الحقيقة طالما أنك فهمت الـ LinkedList والـ Stack فإنك تستطيع بناء الـ Queue الآن ولا تحتاج لإكمال القراءة

لكنني بالطبع سأواصل الشرح وسأقوم ببناء الـ Queue عن طريق الـ LinkedList لكي تتعلم كيفية بنائه في حالة واجهت مشكلة ما
وأيضًا سنقوم بعمل تطبيق عملي بسيط للـ Queue ببناء خدمة Ordering System لمطعم ما
وسنقوم بإضافة الطلبات الى الـ Queue ومن ثم تنفيذها وإزالتها من الـ Queue
وهذا سنقوم بتطبيقه في نهاية المقالة


سنرى الآن مفهوم الـ Queue بشكل توضيحي لكي تفهمه بشكل أفضل

تخيل أن هناك خدمة توصيل الطلبات وتقوم بتوصيل الطلبات الى العملاء
وهذه الخدمة تقوم بتوصيل الطلبات بالترتيب الذي تم إضافتها الى الـ Queue

فمثلًا لنفترض أن هناك 3 طلبات وهي Order 1 و Order 2 و Order 3
وتم إضافتها الى الـ Queue بالترتيب التالي Order 1 ثم Order 2 ثم Order 3
الطلب الذي تم إضافته أولًا وهو Order 1 هو الطلب الذي سيتم توصيله أولًا
ثم بعده Order 2 ثم Order 3 وهكذا

فتخيل الـ Queue كما في الشكل التالي
حاليًا هو فارغ ولا يحتوي على شيء

        +-------------------------------------+

        +-------------------------------------+

الآن سنقوم بوضع الطلب رقم 1 في الـ Queue

        +-------------------------------------+
            | Order 1 |
        +-------------------------------------+

الآن سنقوم بوضع الطلب رقم 2 في الـ Queue

        +-------------------------------------+
            | Order 1 | Order 2 |
        +-------------------------------------+

الآن سنقوم بوضع الطلب رقم 3 في الـ Queue

        +-------------------------------------+
            | Order 1 | Order 2 | Order 3 |
        +-------------------------------------+

الآن بعد أن قمنا بوضع الطلبات في الـ Queue سنقوم بتوصيلها
والطلب الذي تم إضافته أولًا هو الطلب الذي سيتم توصيله أولًا

لنتخيل أننا انتهينا من توصيل الطلب رقم 1 ونحتاج لإزالته من الـ Queue

        +-------------------------------------+
            | Order 2 | Order 3 |
        +-------------------------------------+

الآن سنقوم بتوصيل الطلب رقم 2 والآن نحتاج لإزالته من الـ Queue

        +-------------------------------------+
            | Order 3 |
        +-------------------------------------+

الآن سنقوم بتوصيل الطلب رقم 3 والآن نحتاج لإزالته من الـ Queue

        +-------------------------------------+

        +-------------------------------------+

هكذا يعمل الـ Queue وهذا معنى ما يدخل أولًا يخرج أولًا وهو ما نسميه بـ FIFO أو First In First Out

المكونات الأساسية للـ Queue

الآن بعد أن فهمنا مفهوم الـ Queue

سنقوم بتحديد المكونات الأساسية للـ Queue والتي سنقوم بإنشائها وهي كالتالي

وعليك أن تعلم أن الـ Queue له جهتين، جهة للإضافة فقط وجهة أخرى للإزالة فقط وهذا ما يميزه عن الـ Stack
فالـ Stack له جهة واحدة فقط وهي للإضافة والإزالة من نفس الجهة

فستنتج أن الـ Queue له head و tail لأنك تحتاج للـ head ليقف عند العنصر الأول و tail ليقف عند العنصر الأخير
حسنًا في الـ LinkedList تعلمنا أن الـ push تأخذ سرعة O(1) لأن إضافة عنصر جديد ما ناحية الـ tail تأخذ سرعة O(1)
وهي الدالة التي كانت تدعى push في الـ LinkedList
وإزالة عنصر من ناحية الـ head تأخذ سرعة O(1) وهي الدالة التي كانت تدعى shift في الـ LinkedList

لذا دالة push و دالة pop الذي سنقوم بإنشائها في الـ Queue سيكونان نفسها دالة push و shift في الـ LinkedList
لأننا نحتاج لدالة push لإضافة عنصر من جهة الـ tail ودالة shift لإزالة عنصر من جهة الـ head وهذه هي العمليات الأساسية للـ Queue

قد يتسائل سائل هل نستطيع استخدام دالة unshift و pop في الـ LinkedList لبناء الـ Queue ؟ أي أننا عكسنا فقط الجهتين الجواب هو نعم يمكنك استخدامها ولكن لن تكون العمليات سريعة كما في الـ push و shift
لأنه برغم بأن unshift تأخذ سرعة O(1) فالمشكلة تكمن في الـ pop لأنها تأخذ سرعة O(n) وهذا ليس مقبول في الـ Queue لأننا نحتاج لسرعة في الإضافة والإزالة
قد تستخدم Doubly LinkedList لكن لما نعقد ونزيد مساحة الـ Node بدون داعي ونحن نستطيع بناء الـ Queue بسهولة عن طريق الـ LinkedList بأقل تعقيد باستخدام الـ push و shift

حسنًا الآن بعد أن فهمنا المكونات الأساسية للـ Queue سنقوم ببناء الـ Queue عن طريق الـ LinkedList

بناء الـ Queue عن طريق الـ LinkedList

قبل أن نبدأ ببناء الـ Queue عن طريق الـ LinkedList
فمثل ما تحديتك في الـ Stack أتحداك أن تقوم ببناء الـ Queue بنفسك

أظنك بعد ما بنيت الـ LinkedList و Stack في المقالات السابقة
فستكون قادرًا على بناء الـ Queue بنفسك بسهولة فهمت مفهوم الـ Queue

أريدك أن تتوقف عن القراءة الآن وتحاول بناء الـ Queue بنفسك
ثم عندما تنتهي تعود لتكملة القراءة

حسنًا الآن بعد أن قمت بمحاولة بناء الـ Queue بنفسك (أتمنى أن تكون قد فعلت ...)

حسنًا الآن سنقوم ببناء الـ Queue وأولًا سنقوم بإنشاء الـ Node بالطبع

class Node {
  public data: number;
  public next: Node | null;

  constructor(data: number, next: Node | null = null) {
    this.data = data;
    this.next = next;
  }

  public setNext(next: Node | null) {
    this.next = next;
  }
}

الآن لدينا الـ Node وهو يحتوي على data و next
وهو الشكل البسيط الاعتيادي للـ Node في الـ LinkedList

الآن لنرى مثال الطلبات السابقة Order 1 و Order 2 و Order 3 في الـ Queue

        +-------------------------------------+
            | Order 1 | Order 2 | Order 3 |
        +-------------------------------------+

الآن كيف سيكون شكلها في الـ Queue مبنية عن طريق الـ LinkedList

  +---------+         +---------+         +---------+
  | Order 1 | ------> | Order 2 | ------> | Order 3 |
  +---------+         +---------+         +---------+
      ٨                                       ٨
      |                                       |
     head                                    tail

لاحظ أن كل Node يشير الى الـ Node الذي بعده لأننا لدينا next في الـ Node بالطبع وبالطبع لدينا head و tail لأننا نحتاج لهم لإضافة العناصر وإزالتها من الـ Queue


الآن بعد أن قمنا بإنشاء الـ Node سنقوم بإنشاء الـ Queue

class Queue {
  private head: Node | null;
  private tail: Node | null;
  private length: number;

  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  public isEmpty() {
    return this.length === 0;
  }
}

الآن لدينا الـ Queue وهو يحتوي على head و tail
والطول length ودالة isEmpty التي تقوم بإرجاع true إذا كان الـ Queue فارغًا و false إذا كان يحتوي على عناصر

إضافة Node الى الـ Queue

أول شيء سنقوم به بالطبع هو إضافة العناصر الى الـ Queue بشرح توضيحي

حاليًا لدينا الـ Queue فارغًا ولا يحتوي على شيء

        head = null
        tail = null

الآن سنقوم بإضافة Node بقيمة 10 الى الـ Queue
وبالطبع بسبب أن الـ Queue فارغًا فسيكون head و tail يشيران الى نفس الـ Node لأنها العنصر الوحيد

  +------------+
  | 10 |  NULL | ----> NULL
  +------------+
      0x100
       ٨
       |
      head
      tail

الآن سنقوم بإضافة Node بقيمة 20 الى الـ Queue

                                          new Node(20)
  +------------+                        +------------+
  | 10 |  NULL | ----> NULL             | 20 |  NULL | ----> NULL
  +------------+                        +------------+
      0x100                                 0x200
       ٨
       |
      head
      tail

هنا الـ Node الجديدة ليست مرتبطة بالـ Node السابقة بعد
أي أنها ليست ضمن الـ Queue حتى الآن

لكي نضمها نحتاج لتغيير لفعل خطوتين وهما

إذا أولًا سنقوم بجعل الـ next للـ Node السابقة يشير الى الـ Node الجديدة

                           new Node(20)
  +------------+         +------------+
  | 10 | 0x200 | ---+    | 20 |  NULL | ----> NULL
  +------------+    |    +------------+
      0x100         +------> 0x200
       ٨
       |
      head
      tail

الآن سنقوم بجعل الـ tail يشير الى الـ Node الجديدة

                           new Node(20)
  +------------+         +------------+
  | 10 | 0x200 | ---+    | 20 |  NULL | ----> NULL
  +------------+    |    +------------+
      0x100         +------> 0x200
       ٨                      ٨
       |                      |
      head                   tail

هكذا تمت عملية إضافة الـ Node الجديدة الى الـ Queue
الآن سنقوم بكتابة الدالة push التي تقوم بإضافة الـ Node الى الـ Queue

class Queue {
  // ...
  public push(data: number) {
    const node = new Node(data);

    if (this.isEmpty()) {
      this.head = node;
      this.tail = node;
    } else {
      this.tail.setNext(node);
      this.tail = node;
    }

    this.length++;
  }
  // ...
}

الآن لدينا دالة push التي تقوم بإضافة الـ Node الى الـ Queue
وهي نفسها دالة push في الـ LinkedList دون أي تعديل

وتطبق ما شرحناه سابقًا وهو إذا كانت الـ Queue فارغة فسيتم إضافة الـ Node جديدة
وجعل الـ head والـ tail يشيران إلى هذه الـ Node مباشرةً
وإذا كانت الـ Queue بها عناصر فسيتم إضافة الـ Node الجديدة
ثم جعل الـ Node الأخيرة تشير إلى الـ Node الجديدة عن طريق الـ tail
ثم تحديث الـ tail ليشير إلى الـ Node الجديدة

ثم بالطبع تحديث الـ length ونزيد قيمته بـ 1

الآن نستطيع إضافة العناصر الى الـ Queue

const queue = new Queue();

queue.push(10);
// 10
queue.push(20);
// 10 -> 20
queue.push(30);
// 10 -> 20 -> 30

الآن لدينا الـ Queue وهي تحتوي على العناصر 10 و 20 و 30

إزالة Node من الـ Queue

الآن بعد أن قمنا بإضافة العناصر الى الـ Queue سنقوم بإزالتها
لكن تذكر أن الـ Queue يعمل بطريقة FIFO أي أن العنصر الذي تم إضافته أولًا هو العنصر الذي يتم إزالته أولًا
بمعنى أننا لو أضفنا العنصر 10 ثم 20 ثم 30 سنقوم بإزالة العنصر 10 أولًا ثم 20 ثم 30

وهذا يعني أننا سنقوم بإزالة العنصر من الـ head لأنه العنصر الذي تم إضافته أولًا
وكما قلنا فسوف نستخدم دالة shift التي كانت تقوم بإزالة العنصر من الـ LinkedList من ناحية الـ head

سنشرها بشكل توضيحي ثم نرى الكود

حاليًا لدينا الـ Queue تحتوي على العناصر 10 و 20 و 30

  +------------+         +------------+         +------------+
  | 10 | 0x200 | ---+    | 20 | 0x300 | ---+    | 30 | 0x400 | ----> NULL
  +------------+    |    +------------+    |    +------------+
      0x100         +------> 0x200         +------> 0x300
       ٨                                             ٨
       |                                             |
      head                                          tail

الآن نريد إزالة العنصر 10 من الـ Queue لذا سنبدأ بوضع متغير مؤقت يشير إلى الـ head

  +------------+         +------------+         +------------+
  | 10 | 0x200 | ---+    | 20 | 0x300 | ---+    | 30 | 0x400 | ----> NULL
  +------------+    |    +------------+    |    +------------+
      0x100         +------> 0x200         +------> 0x300
       ٨                                             ٨
       |                                             |
      head                                          tail
      temp

ثم نقوم بتحريك الـ head لتشير إلى الـ Node التالية لها

  +------------+         +------------+         +------------+
  | 10 | 0x200 | ---+    | 20 | 0x300 | ---+    | 30 | 0x400 | ----> NULL
  +------------+    |    +------------+    |    +------------+
      0x100         +------> 0x200         +------> 0x300
       ٨                      ٨                      ٨
       |                      |                      |
      temp                   head                   tail

الآن نستطيع حذف والتخلص من الـ Node القديمة التي كانت تشير إليها الـ head القديمة
والتي تشير إليها الـ temp وهذا ما كنا نفعله في الـ LinkedList

    temp.next = null
    delete temp
  +------------+         +------------+         +------------+
  | 10 | Null  |         | 20 | 0x300 | ---+    | 30 | 0x400 | ----> NULL
  +------------+         +------------+    |    +------------+
      0x100                  0x200         +------> 0x300
                              ٨                      ٨
                              |                      |
                             head                   tail

وأظن لا داعي لتكرار الكلام عن الـ Garbage Collection وكيف تعمل لأننا تحدثنا عنها كثيرًا في الـ LinkedList


الآن بعد أن تعرفنا على كيفية إزالة Node من بداية الـ Queue بشكل توضيحي
لنرى كيف يمكننا تنفيذ ذلك في الكود

class Queue {
  // ...
  public pop(): number | null {
    if (this.isEmpty()) {
      throw new Error('The Queue is empty');
    }

    if (this.length === 1) {
      this.head = null;
      this.tail = null;
    } else {
      let temp = this.head;

      this.head = this.head.next;

      temp.setNext(null);
    }

    this.length--;
    return this;
  }
}

الدالة pop تقوم بإزالة الـ Node من بداية الـ Queue
أولًا تحقق إذا كانت الـ Queue فارغة فسيتم رمي Exception
ثم تحقق إذا كانت الـ Queue تحتوي على عنصر واحد فقط فسيتم جعل الـ head والـ tail يشيران إلى null بكل بساطة

ثم إذا كانت الـ Queue تحتوي على أكثر من عنصر فسيتم حفظ الـ Node القديمة التي تشير إليها الـ head في متغير مؤقت temp
ثم سيتم تحريك الـ head لتشير إلى الـ Node التالية لها
ثم يتم التخلص من الـ Node القديمة التي كانت تشير إليها الـ head القديمة
وهذا ما كنا نفعله في الـ LinkedList والآن نفعله في الـ Queue تمامًا دون أي تعديل
لأن دالة pop هذه هي تمامًا دالة shift في الـ LinkedList

الآن كالعادة يمكنك استخدام هذه الدالة كالتالي:

const queue = new Queue();
queue.push(10).push(20).push(30);
// 10 -> 20 -> 30

queue.pop();
// 20 -> 30

queue.pop();
// 30

أهم شيء أريدك أن تلاحظه أننا بكل نجاح قمنا ببناء الـ Queue عن طريق الـ LinkedList
مع مراعاة أن الـ Queue يعمل بطريقة FIFO أي أن العنصر الذي تم إضافته أولًا هو العنصر الذي يتم إزالته أولًا
فلاحظ هنا أننا قمنا بإضافة العناصر 10 ثم 20 ثم 30 وقمنا بإزالتها بنفس الترتيب
وهذا هو مفهوم الـ Queue ببساطة

عرض أول وآخر عنصر في الـ Queue

بصراحة هذه الفقرة هي مجرد تحلية لأننا انتهينا من الطبق الرئيسي للتو مع الـ push والـ pop
الآن نحن فقط لنقوم بإنشاء دالتين لعرض العنصر الأول والعنصر الأخير في الـ Queue دون إزالتهم بالطبع

class Queue {
  // ...
  public first(): number | null {
    if (this.isEmpty()) {
      throw new Error('The Queue is empty');
    }

    return this.head.data;
  }

  public last(): number | null {
    if (this.isEmpty()) {
      throw new Error('The Queue is empty');
    }

    return this.tail.data;
  }
}

الدالة first تقوم بإرجاع العنصر الأول في الـ Queue
والدالة last تقوم بإرجاع العنصر الأخير في الـ Queue

بكل بساطة وبدون أي تعقيد
هل تريدني حقًا أن أشرح لك كيف تعمل هذه الدوال ؟ لا أظن ذلك
لذا سأتركك لتجربتها بنفسك

والآن لديك الـ Queue بكل مكوناتها الأساسية

سنقوم الآن بعمل تطبيق عملي للـ Queue وهو خدمة Ordering System لمطعم ما

بناء خدمة Ordering System لمطعم

هذه الفقرة مميزة جدًا لأننا سنقوم ببناء تطبيق عملي للـ Queue
الفكرة هي بناء خدمة Ordering System لمطعم ما
وهي خدمة حجز الطلبات وتوصيلها بترتيب الحجز

أولًا سنقوم بإنشاء كلاس يسمى OrderingSystem وسنقوم بإضافة الدوال التالية:

وسنقوم ببناء الـ OrderingSystem الآن

class OrderingSystem {
  private queue: Queue;

  constructor() {
    this.queue = new Queue();
  }
}

أولًا سنقوم بإنشاء الـ OrderingSystem ونجعلها تحتوي على الـ Queue التي قمنا ببنائها
لاحظ أننا هكذا نستخدم الـ Queue كأداة تساعدنا لحجز الطلبات وتوصيلها بترتيب الحجز

الآن سنقوم بإضافة الدالة addOrder التي تقوم بإضافة الطلبات الى الـ Queue

class OrderingSystem {
  // ...
  public addOrder(order: number) {
    this.queue.push(order);
    return this;
  }
}

الدالة بسيطة جدًا تقوم بإضافة الطلب الى الـ Queue بكل بساطة
وهي تستخدم الدالة push التي قمنا ببنائها في الـ Queue

الآن سنقوم بإضافة الدالة showNextOrder التي تقوم بعرض الطلب التالي في الـ Queue ولكن لا تقوم بإزالته

class OrderingSystem {
  // ...
  public showNextOrder() {
    return this.queue.first();
  }
}

الدالة تقوم بإرجاع الطلب التالي الذي سيتم توصيله
ستفيدنا في حالة أردنا أن نعرف الطلب التالي الذي عليه الدور الآن
وهي تستخدم الدالة first التي قمنا ببنائها في الـ Queue

الآن سنقوم بإضافة الدالة getNextOrder التي تقوم بإحضار الطلب التالي من الـ Queue وتزيله من الـ Queue

class OrderingSystem {
  // ...
  public getNextOrder() {
    return this.queue.pop();
  }
}

الدالة تقوم بإرجاع الطلب التالي الذي سيتم توصيله وتزيله من الـ Queue
نستطيع استخدامها لتوصيل الطلبات بالترتيب الصحيح
وهي تستخدم الدالة pop التي قمنا ببنائها في الـ Queue

الآن سنقوم بإضافة الدالة showOrders التي تقوم بعرض الطلبات الموجودة في الـ Queue بترتيب الحجز

class OrderingSystem {
  // ...
  public showOrders() {
    let temp = this.queue.head;

    while (temp !== null) {
      console.log(temp.data);
      temp = temp.next;
    }
  }
}

الدالة تقوم بعرض الطلبات الموجودة في الـ Queue بترتيب الحجز
لاحظ أننا لكي نفعل هذا نحتاج للتحرك من الـ head الى الـ tail
لذا قمنا بإنشاء متغير مؤقت temp يشير الى الـ head وهو الذي سنستخدمه للتحرك

ثم قمنا بعمل while تقوم بالتحرك من الـ head الى الـ tail وتقوم بعرض الطلبات طالما أن الـ temp ليس null
ومع كل لفة فيها ستقوم بطباعة البيانات data للـ Node الحالية ثم تنتقل الى الـ Node التالية عن طريق الـ next هكذا temp = temp.next
الـ while سيستمر حتى يصل الى الـ tail ويكون temp يشير الى null

ملاحظة: فكرة وجود أكواد متعلقة بعمل عمليات الـ Queue في الـ OrderingSystem ليست جيدة وهذا يخالف مبدأ الـ Single Responsibility
وهو يجب على الـ OrderingSystem أن يكون مسؤول عن عمليات الـ Ordering فقط وليس عن عمليات خالصة بالـ Queue
لذا علينا نقل الأكواد المتعلقة بالـ Queue فمثلا في دالة تدعى print ثم نقوم بإستدعاء هذه الدالة في الـ OrderingSystem

class Queue {
  // ...
  public print() {
    let temp = this.head;

    while (temp !== null) {
      console.log(temp.data);
      temp = temp.next;
    }
  }
}

لكن فكرة وجود أمر طباعة مثل console.log في الـ Queue ليست جيدة أيضًا لكن هذا مجرد توضيح للفكرة وليس للتطبيق العملي
إذا أردت التطبيق الصحيح فبدلًا من console.log يمكنك ارجاع البيانات التي داخل الـ Queue في أراي
ثم تقوم بطباعتها في الـ OrderingSystem بالطريقة التي تريدها
أو يمكنك تطبيق مفهوم الـ Iterator في الـ Queue وتجعل أي شخص يستطيع عمل loop على الـ Queue

هناك أفكار وتفاصيل كثير لكن لنتركها ونكمل ما نقوم به

الآن نحن انتهينا من عمل كلاس الـ Ordering System

OrderingSystem orderingSystem = new OrderingSystem();

orderingSystem.addOrder(10).addOrder(20).addOrder(30);

orderingSystem.showNextOrder(); // 10
const nextOrder = orderingSystem.getNextOrder(); // 10
console.log(nextOrder); // 10

// Process the order
// ...

orderingSystem.showOrders(); // 20 -> 30

الآن لديك خدمة Ordering System لمطعمك
بالطبع هذا مجرد مثال بسيط وتافه لكنه يوضح لك كيف يمكنك استخدام الـ Queue في الحياة العملية
فمثلا نظام مثل الـ Ordering System يجب أن يكون لديك كلاس خاص بالـ Order
وبدلًا من استخدام الأرقام أو الـ string لتخزينها داخل الـ Queue
يمكننا بناء الـ Queue لتخزن object من الـ Order وهكذا
بالتالي كل Node في الـ Queue سيحتوي على Order وليس number أو string

OrderingSystem orderingSystem = new OrderingSystem();

const orders = [
  new Order('Pizza'),
  new Order('Burger'),
  new Order('Pasta'),
];

// Loop through the orders and add them to the Queue
for (const order of orders) {
  orderingSystem.addOrder(order);
}

orderingSystem.showNextOrder(); // Pizza
const nextOrder = orderingSystem.getNextOrder(); // Pizza

nextOrder.process(); // Process the order

orderingSystem.showOrders(); // Burger -> Pasta

وهكذا يمكنك بناء تطبيقات كبيرة ومعقدة بسهولة باستخدام الـ Queue
وهذا ما يجعل الـ Queue من الهياكل البيانية المهمة جدًا في البرمجة

خاتمة

كما ترى بناء الـ Queue ليس بالأمر الصعب على الإطلاق وكما ترى أنها تعتمد على الـ LinkedList
وكما قلت لك سابقًا طالما أنك قمت ببناء الـ LinkedList وفهمتها جيدًا فأنت تستطيع بناء أي شيء

وأيضًا لا تنسى أن الـ Queue والـ Stack مجرد أفكار لتخزين البيانات وتنظيمها والتعامل معها بطريقة معينة
وهذه هي جوهر الـ Data Structures وهي تساعدك على تنظيم البيانات وتخزينها بشكل منظم وسهل الوصول
وكل Data Structure لها فكرتها واستخداماتها ومميزاتها وعيوبها ويجب عليك أن تعرفها جيدًا لكي تستطيع استخدامها بشكل جيد

بالنسبة للـ Queue و الـ Stack فهما أدوات نستخدمها للتعامل مع البيانات بطريقة معينة كما رأيت في مثال الـ Ordering System

وأيضًا عليك أن تعرف أن يوجد نوع آخر من الـ Queue يسمى Priority Queue وهو يعمل بنفس فكرة الـ Queue لكن يعمل بطريقة Priority
أي نظام يعتمد على الأولوية والأهمية وهذا يعني أن العنصر الذي يتم إضافته يتم ترتيبه بحسب أولويته وأهميته
وهذا يعني أن العنصر الذي يحمل أعلى أولوية سيتم توصيله أولًا وهكذا
يمكنك البحث عن Priority Queue وتعرف عليها وكيفية بنائها إن أردت

وأخيرًا أتمنى أن تكون قد استفدت من هذا الدرس وأن تكون قد فهمت الـ Queue جيدًا