قائمة الانتظار (نوع بيانات مجرد)

في علم الحاسوب ، الطابور هو نوع بيانات مجرد يُستخدم كمجموعة مرتبة من الكيانات. اصطلاحًا، يُطلق على نهاية الطابور حيث تُضاف العناصر اسم "مؤخرة الطابور". أما نهاية الطابور حيث تُزال العناصر فتُسمى "رأس الطابور". ويُشابه مصطلح "الطابور" المصطلحات المستخدمة لوصف الأشخاص الذين يقفون في طابور انتظار للحصول على سلع أو خدمات. يدعم الطابور عمليتين رئيسيتين.

  • إضافة عنصر واحد إلى نهاية قائمة الانتظار
  • Dequeue، الذي يزيل عنصرًا واحدًا من مقدمة قائمة الانتظار.

قد يُسمح أيضاً بعمليات أخرى، وغالباً ما تتضمن عملية إلقاء نظرة سريعة أو عملية أمامية تُرجع قيمة العنصر التالي المراد إزالته من قائمة الانتظار دون إزالته فعلياً.

تُصنّف عمليات الطابور كبنية بيانات من نوع "الأول في الأول خارج" (FIFO)، حيث يُزال أول عنصر يُضاف إلى الطابور أولًا. وهذا يُعادل شرط إزالة جميع العناصر التي أُضيفت قبل إضافة عنصر جديد. يُعدّ الطابور مثالًا على بنية بيانات خطية ، أو بشكل أكثر تجريدًا، مجموعة متسلسلة. تُستخدم الطوابير بكثرة في برامج الحاسوب، حيث تُنفّذ كبنى بيانات مُقترنة بإجراءات الوصول، أو كبنية بيانات مجردة ، أو في لغات البرمجة كائنية التوجه كفئات. يُمكن تنفيذ الطابور باستخدام مخازن مؤقتة دائرية وقوائم مرتبطة ، أو باستخدام كلٍ من مؤشر المكدس ومؤشر القاعدة.

تُستخدم قوائم الانتظار في علوم الحاسوب والنقل وبحوث العمليات، حيث تُخزَّن فيها كيانات متنوعة، مثل البيانات والأشياء والأشخاص والأحداث، ليتم معالجتها لاحقًا. في هذه السياقات، تؤدي قائمة الانتظار وظيفة المخزن المؤقت . كما تُستخدم قوائم الانتظار أيضًا في تطبيق خوارزمية البحث بالعرض أولًا .

تنفيذ قائمة الانتظار

من الناحية النظرية، تتميز قائمة الانتظار بأنها لا تملك سعة محددة. فبغض النظر عن عدد العناصر الموجودة فيها، يمكن دائمًا إضافة عنصر جديد. كما يمكن أن تكون فارغة، وعندها يستحيل إزالة أي عنصر منها حتى تتم إضافة عنصر جديد.

المصفوفات ذات الطول الثابت محدودة السعة، ولكن ليس بالضرورة نسخ العناصر باتجاه رأس الطابور. تكمن الحيلة البسيطة في تحويل المصفوفة إلى دائرة مغلقة، وترك رأسها وذيلها يدوران فيها بلا نهاية، مما يُغني عن نقل العناصر المخزنة فيها. إذا كان n هو حجم المصفوفة، فإن حساب باقي قسمة الفهارس على n سيحول المصفوفة إلى دائرة. لا تزال هذه الطريقة الأبسط من الناحية المفاهيمية لإنشاء طابور في لغة برمجة عالية المستوى ، ولكنها تُبطئ العملية قليلاً، لأن فهارس المصفوفة يجب مقارنتها بالصفر وحجم المصفوفة، وهو ما يُقارب الوقت اللازم للتحقق مما إذا كان فهرس المصفوفة خارج النطاق، وهو ما تفعله بعض اللغات، ولكن هذه الطريقة ستكون الخيار الأمثل للتنفيذ السريع والبسيط، أو لأي لغة برمجة عالية المستوى لا تدعم المؤشرات. يجب تحديد حجم المصفوفة مسبقًا، ولكن بعض التطبيقات تُضاعف حجم المصفوفة المُعلن عنه عند حدوث تجاوز. معظم اللغات الحديثة التي تدعم الكائنات أو المؤشرات تدعم القوائم الديناميكية أو تأتي مزودة بمكتبات خاصة بها. قد لا تحدد هياكل البيانات هذه حدًا ثابتًا للسعة باستثناء قيود الذاكرة. يحدث تجاوز سعة الطابور عند محاولة إضافة عنصر إلى طابور ممتلئ، بينما يحدث نقص سعة الطابور عند محاولة إزالة عنصر من طابور فارغ.

الطابور المحدود هو طابور يقتصر على عدد ثابت من العناصر. [ 1 ]

توجد عدة تطبيقات فعالة لقوائم الانتظار FIFO. التطبيق الفعال هو الذي يمكنه تنفيذ عمليات الإضافة والإزالة من قائمة الانتظار فييا(1){\displaystyle O(1)}وقت .

  • قائمة مرتبطة
    • تحتوي القائمة المرتبطة ثنائياً علىيا(1){\displaystyle O(1)}الإضافة والحذف من كلا الطرفين، لذا فهو خيار طبيعي للطوابير.
    • تتميز القائمة المرتبطة أحادية الاتجاه العادية بإمكانية الإضافة والحذف بكفاءة من طرف واحد فقط. مع ذلك، فإن تعديلًا بسيطًا - يتمثل في الاحتفاظ بمؤشر إلى العقدة الأخيرة بالإضافة إلى الأولى - سيمكنها من تنفيذ طابور فعال.
  • قائمة انتظار مزدوجة (deque) مُنفذة باستخدام مصفوفة ديناميكية مُعدلة

قوائم الانتظار ولغات البرمجة

يمكن تنفيذ قوائم الانتظار كنوع بيانات منفصل، أو اعتبارها حالة خاصة من قائمة انتظار ثنائية الطرف (deque) دون تنفيذها بشكل منفصل. على سبيل المثال، تسمح لغتا بيرل وروبي بإضافة عناصر إلى مصفوفة وحذفها منها من كلا الطرفين، لذا يمكن استخدام دالتي `add` و` deque` لإضافة عناصر إلى قائمة وحذفها منها (أو العكس )، [ 2 ] على الرغم من أن هذه العمليات قد لا تكون فعالة في بعض الحالات.push()shift()unshift()pop()

توفر مكتبة القوالب القياسية في لغة C++ std::queue<T>فئةً مُنمّطة تقتصر على عمليات الإضافة/الحذف فقط. ومنذ إصدار J2SE 5.0، تحتوي مكتبة Java على Queueواجهة تُحدد عمليات قائمة الانتظار؛ وتشمل الفئات المُنفذة لها LinkedList(منذ J2SE 1.6) ArrayDeque. أما PHP، فتضم فئة SplQueue ومكتبات خارجية مثل beanstalk'd و Gearman .

فئة قائمة انتظار UML.svg

مثال

قائمة انتظار بسيطة مُنفذة بلغة TypeScript :

class Queue <T> { private items : T [] = [ ] ;enqueue ( element : T ) : void { this.items.push ( element ) ; }dequeue ( ) : T | undefined { return this.items.shift ( ) ; }isEmpty ( ) : boolean { return this.items.length === 0 ; } }

التنفيذ الوظيفي البحت

يمكن أيضًا تنفيذ قوائم الانتظار كهيكل بيانات وظيفي بحت . [ 3 ] هناك تنفيذان. الأول يحقق فقطيا(1){\displaystyle O(1)}لكل عملية في المتوسط . أي أن الوقت المستهلك هويا(1){\displaystyle O(1)}لكن العمليات الفردية قد تستغرقيا(ن){\displaystyle O(n)}أينن{\displaystyle n}يمثل عدد العناصر في الطابور. يُطلق على التنفيذ الثاني اسم طابور الوقت الحقيقي [ 4 ] ، وهو يسمح للطابور بالاستمرارية مع تنفيذ العمليات في زمن O(1) في أسوأ الحالات. وهو تنفيذ أكثر تعقيدًا ويتطلب قوائم كسولة مع التخزين المؤقت .

قائمة الانتظار المستهلكة

يتم تخزين بيانات هذا الطابور في قائمتين مرتبطتين بشكل فردي باسمو{\displaystyle f}ور{\displaystyle r}القائمةو{\displaystyle f}يحتل الجزء الأمامي من الطابور. القائمةر{\displaystyle r}يحتفظ هذا الجزء بالعناصر المتبقية (أي الجزء الخلفي من الطابور) بترتيب عكسي . من السهل إضافته إلى مقدمة الطابور عن طريق إضافة عقدة في بدايته.و{\displaystyle f}وإذار{\displaystyle r}إذا لم تكن فارغة، فمن السهل إزالتها من نهاية قائمة الانتظار عن طريق إزالة العقدة الموجودة في رأسها.ر{\displaystyle r}. متىر{\displaystyle r}القائمة فارغةو{\displaystyle f}يتم عكسها وتخصيصها لـر{\displaystyle r}ثم رئيسر{\displaystyle r}تمت إزالته.

تستغرق عملية الإدراج ("enqueue") دائمًايا(1){\displaystyle O(1)}يستغرق الأمر بعض الوقت. عملية الإزالة ("إزالة العنصر من قائمة الانتظار")يا(1){\displaystyle O(1)}عندما القائمةر{\displaystyle r}ليس فارغًا. عندمار{\displaystyle r}إذا كان فارغًا، فإن العكس هو الصحيحيا(ن){\displaystyle O(n)}أينن{\displaystyle n}هو عدد العناصر فيو{\displaystyle f}لكن يمكننا القول إنه كذلكيا(1){\displaystyle O(1)}الوقت المستهلك ، لأن كل عنصر فيو{\displaystyle f}كان لا بد من إدراجه ويمكننا تعيين تكلفة ثابتة لكل عنصر في الاتجاه المعاكس لوقت إدراجه.

قائمة انتظار في الوقت الفعلي

تحقق قائمة الانتظار في الوقت الفعلييا(1){\displaystyle O(1)}الوقت اللازم لجميع العمليات، دون احتساب الاستهلاك. ستكون هذه المناقشة فنية، لذا تذكر ذلك، بالنسبة لـل{\displaystyle l}قائمة،|ل|{\displaystyle |l|}يدل على طوله، ذلكباطل{\displaystyle {\texttt {null}}}يمثل قائمة فارغة وسلبيات(ح،ت){\displaystyle {\texttt {cons}}(h,t)}يمثل القائمة التي رأسهاح{\displaystyle h}والذي ذيلهت{\displaystyle t}.

تتكون بنية البيانات المستخدمة لتنفيذ قوائم الانتظار لدينا من ثلاث قوائم مرتبطة بشكل فردي.(و،ر،s){\displaystyle (f,r,s)}أينو{\displaystyle f}هو مقدمة الصف ور{\displaystyle r}يمثل الجزء الخلفي من الطابور بترتيب عكسي. الثابت في هذا الهيكل هو أنs{\displaystyle s}الجزء الخلفي منو{\displaystyle f}بدونها|ر|{\displaystyle |r|}العناصر الأولى، أي|s|=|و|-|ر|{\displaystyle |s|=|f|-|r|}ذيل الطابور(سلبيات(x،و)،ر،s){\displaystyle ({\texttt {cons}}(x,f),r,s)}ثم يصبح الأمر تقريبًا(و،ر،s){\displaystyle (f,r,s)}وإدراج عنصرx{\displaystyle x}ل(و،ر،s){\displaystyle (f,r,s)}يكاد يكون(و،سلبيات(x،ر)،s){\displaystyle (f,{\texttt {cons}}(x,r),s)}يقال ذلك تقريبًا، لأنه في كلتا النتيجتين،|s|=|و|-|ر|+1{\displaystyle |s|=|f|-|r|+1}دالة مساعدةمساعد{\displaystyle {\texttt {aux}}}يجب استدعاء الدالة بعد ذلك لكي يتحقق الشرط الثابت. يجب النظر في حالتين، اعتمادًا على ما إذا كانs{\displaystyle s}القائمة فارغة، وفي هذه الحالة|ر|=|و|+1{\displaystyle |r|=|f|+1}أو لا. التعريف الرسمي هومساعد(و،ر،سلبيات(_،s))=(و،ر،s){\displaystyle {\texttt {aux}}(f,r,{\texttt {cons}}(\_,s))=(f,r,s)}ومساعد(و،ر،باطل)=(و،باطل،و){\displaystyle {\texttt {aux}}(f,r,{\texttt {null}})=(f',{\texttt {null}},f')}أينو{\displaystyle f'}يكونو{\displaystyle f}ثم يتبع ذلكر{\displaystyle r}معكوسة.

دعونا نتصليعكس(و،ر){\displaystyle {\texttt {reverse}}(f,r)}الدالة التي تُرجعو{\displaystyle f}ثم يتبع ذلكر{\displaystyle r}معكوسة. لنفترض كذلك أن|ر|=|و|+1{\displaystyle |r|=|f|+1}، لأن هذا هو الحال عند استدعاء هذه الدالة. وبشكل أدق، نُعرّف دالة كسولة.تناوب(و،ر،أ){\displaystyle {\texttt {rotate}}(f,r,a)}والتي تأخذ كمدخل ثلاث قوائم بحيث|ر|=|و|+1{\displaystyle |r|=|f|+1}، وإرجاع سلسلة منو{\displaystyle f}، لر{\displaystyle r}معكوسة وأ{\displaystyle a}. ثميعكس(و،ر)=تناوب(و،ر،باطل){\displaystyle {\texttt {reverse}}(f,r)={\texttt {rotate}}(f,r,{\texttt {null}})}التعريف الاستقرائي للدوران هوتناوب(باطل،سلبيات(y،باطل)،أ)=سلبيات(y،أ){\displaystyle {\texttt {rotate}}({\texttt {null}},{\texttt {cons}}(y,{\texttt {null}}),a)={\texttt {cons}}(y,a)}وتناوب(سلبيات(x،و)،سلبيات(y،ر)،أ)=سلبيات(x،تناوب(و،ر،باطل(y،أ))){\displaystyle {\texttt {rotate}}({\texttt {CONS}}(x,f),{\texttt {cons}}(y,r),a)={\texttt {cons}}(x,{\texttt {rotate}}(f,r,{\texttt {null}}(y,a)))}مدة تشغيله هييا(ر){\displaystyle O(r)}ولكن، نظرًا لاستخدام التقييم الكسول، يتم تأخير الحساب حتى يتم فرض النتائج بواسطة الحساب.

القائمةs{\displaystyle s}لهذه القائمة في بنية البيانات غرضان. فهي بمثابة عداد لـ|و|-|ر|{\displaystyle |f|-|r|}، بالفعل،|و|=|ر|{\displaystyle |f|=|r|}إذا وفقط إذاs{\displaystyle s}هي القائمة الفارغة. يسمح لنا هذا العداد بالتأكد من أن القائمة الخلفية لا تتجاوز القائمة الأمامية أبدًا. علاوة على ذلك، باستخدامs{\displaystyle s}، وهو ذيل لـو{\displaystyle f}، يجبر على حساب جزء من القائمة (الكسولة)و{\displaystyle f}خلال كلتأأنال(){\displaystyle tail()}وأنانsهـرت(){\displaystyle insert()}العملية. لذلك، عندما|و|=|ر|{\displaystyle |f|=|r|}القائمةو{\displaystyle f}الأمر مفروض تمامًا. لو لم يكن كذلك، لكان التمثيل الداخلي لـو{\displaystyle f}قد يكون الأمر عبارة عن إضافة لإضافات لإضافات لإضافات أخرى، ولن تكون عملية الإجبار عملية ثابتة الوقت بعد الآن.

انظر أيضاً

مراجع

  1. "Queue (Java Platform SE 7)" . Docs.oracle.com. 2014-03-26 . تم الاطلاع عليه بتاريخ 2014-05-22 .
  2. "مصفوفة الفئات" .
  3. أوكاساكي، كريس. "هياكل البيانات الوظيفية البحتة" (ملف PDF) . مؤرشف من الأصل (ملف PDF) بتاريخ 7 ديسمبر 2023. تم الاطلاع عليه بتاريخ 19 أبريل 2022 .
  4. هود، روبرت؛ ميلفيل، روبرت (نوفمبر 1981). "عمليات قائمة الانتظار في الوقت الحقيقي بلغة ليسب خالصة". رسائل معالجة المعلومات . 13 (2): 50-54 . doi : 10.1016/0020-0190(81)90030-2 . hdl : 1813/6273 .

مراجع عامة

للمزيد من القراءة