بحث أول العمق – depth-first search

البحث أولاً بالعمق (Depth-First Search – DFS)

المجال (المجالات) التخصصية الرئيسية: علم الحاسوب، نظرية المخططات، الخوارزميات.

1. التعريف الأساسي للخوارزمية

يمثل البحث أولاً بالعمق (DFS) خوارزمية أساسية ومنهجية منظمة تستخدم لاستكشاف هياكل البيانات المتمثلة في الرسوم البيانية (Graphs) أو الأشجار. يرتكز المبدأ الجوهري لهذه الخوارزمية على الاستكشاف العميق قدر الإمكان على طول كل فرع قبل الشروع في عملية التراجع (Backtracking). بمعنى آخر، تبدأ الخوارزمية عند عقدة جذرية (أو عقدة بداية عشوائية)، وتنتقل إلى أقصى حد ممكن في مسار معين، وتستمر في زيارة العقد المجاورة غير المزارة، حتى تصل إلى عقدة لا تحتوي على أي جيران غير مزورين. عند هذه النقطة، تبدأ الخوارزمية في التراجع للوراء (Backtracking) لاستكشاف الفروع الأخرى التي لم تزر بعد.

تعتمد كفاءة ونجاح خوارزمية البحث أولاً بالعمق بشكل كبير على استخدام هيكل بيانات المكدس (Stack)، الذي يعمل بمبدأ “الداخل أخيراً، يخرج أولاً” (LIFO). يتم استخدام المكدس لتخزين العقد التي يجب زيارتها لاحقاً. عندما تكتشف الخوارزمية عقدة جديدة، يتم دفعها إلى المكدس، وعندما تصل إلى طريق مسدود، يتم سحب العقدة الأخيرة التي تم إدخالها من المكدس لاستئناف عملية البحث من نقطة سابقة. هذا التركيز على الاستكشاف المتعمق يجعلها مختلفة جذرياً عن خوارزمية البحث أولاً بالعرض (BFS) التي تستخدم قائمة الانتظار (Queue) وتستكشف المستويات تدريجياً.

إن الطريقة التي تتعامل بها DFS مع الرسم البياني تجعلها مفيدة بشكل خاص في تحديد خصائص هيكلية معينة للرسم البياني، مثل تحديد المكونات المتصلة، أو الكشف عن وجود الدورات (Cycles). يتم تصنيف الحواف في الرسم البياني أثناء تنفيذ DFS إلى أربعة أنواع رئيسية: حواف الشجرة (Tree Edges)، حواف خلفية (Back Edges)، حواف أمامية (Forward Edges)، وحواف متقاطعة (Cross Edges)، وهذا التصنيف يعد حجر الزاوية في العديد من تحليلات المخططات المتقدمة.

2. التاريخ والتطور

على الرغم من أن المبادئ الأساسية لاستكشاف المسارات في المتاهات والشبكات قد تكون قديمة قدم الألغاز نفسها، إلا أن خوارزمية البحث أولاً بالعمق في شكلها الرسمي الحاسوبي لم يتم تحديدها وتوثيقها بشكل كامل إلا في سياق تطور علوم الحاسوب ونظرية الرسوم البيانية في منتصف القرن العشرين. تعود الأصول التاريخية لمفهوم البحث العميق إلى أساليب حل المتاهات التي طورها علماء مثل المهندس الفرنسي تشارلز بيير ترايمي (Charles Pierre Trémaux) في القرن التاسع عشر، والذي قدم طريقة منهجية لضمان زيارة كل ممر مرة واحدة على الأقل.

ومع ظهور الحوسبة الرقمية وتطور نظرية الخوارزميات، تم تكييف هذه المبادئ الهيكلية لتطبيقها على هياكل البيانات الرسمية. تم تناول خوارزميات مسح الرسوم البيانية، بما في ذلك DFS، على نطاق واسع في الخمسينيات والستينيات من القرن الماضي كجزء من تطوير الذكاء الاصطناعي المبكر (AI) وأنظمة البحث. كان الهدف الأولي هو إيجاد مسارات في مساحات الحالة الكبيرة. وقد ساهمت أعمال باحثين مثل روبرت تارجان (Robert Tarjan) في السبعينيات في ترسيخ الأهمية النظرية والعملية لـ DFS، حيث استخدمها في تطوير خوارزميات فعالة للغاية لحل مشكلات معقدة مثل إيجاد المكونات المتصلة بقوة (Strongly Connected Components).

أصبحت DFS منذ ذلك الحين أداة معيارية في أي مجموعة أدوات لأي مبرمج أو عالم حاسوب، نظراً لبساطتها النسبية عند التنفيذ، سواء باستخدام الأسلوب التكراري (Recursive) أو التكراري الصريح (Iterative). إن قدرتها على التعامل مع الرسوم البيانية الكبيرة بكفاءة زمنية جيدة جعلتها الأساس للعديد من الخوارزميات المتخصصة الأخرى في مجالات تتراوح من تحليل الشبكات إلى تجميع المترجمات (Compilers).

3. مبدأ العمل: التتبع العميق والتراجع

يتمحور مبدأ العمل لخوارزمية البحث أولاً بالعمق حول مفهومين أساسيين: الاستكشاف المتعمق (Deep Exploration) والتراجع المنهجي (Systematic Backtracking). تبدأ الخوارزمية من عقدة البداية S وتختار مساراً واحداً للاستمرار فيه. تنتقل الخوارزمية بعمق من عقدة إلى جارتها غير المزارة، وتستمر في هذا الاتجاه حتى تصل إلى عقدة طرفية (Leaf Node) أو عقدة تكون جميع جيرانها قد تمت زيارتهم بالفعل. في هذه المرحلة، تكون الخوارزمية قد وصلت إلى طريق مسدود مؤقت، مما يستدعي عملية التراجع.

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

لإدارة عملية البحث، تحتفظ الخوارزمية بحالة لكل عقدة. عادةً ما تكون هذه الحالات: غير مزورة (Unvisited)، قيد الزيارة (Visiting – وهي العقدة الحالية الموجودة في المكدس)، ومزورة بالكامل (Visited – بعد الانتهاء من استكشاف جميع فروعها). هذا النظام ثلاثي الحالات ضروري ليس فقط لضمان عدم تكرار الزيارة، ولكن أيضاً لتحديد الدورات داخل الرسم البياني، حيث يمكن اكتشاف الدورة عندما تحاول الخوارزمية الانتقال من عقدة قيد الزيارة إلى عقدة أخرى قيد الزيارة بالفعل (باستثناء العقدة الأم المباشرة).

4. آليات التنفيذ: التكرار والمكدس الصريح

يمكن تنفيذ البحث أولاً بالعمق باستخدام طريقتين رئيسيتين، وكلاهما يحقق نفس الكفاءة النظرية، لكنهما يختلفان في أسلوب البرمجة وإدارة الذاكرة: التنفيذ التكراري (Recursive Implementation) والتنفيذ باستخدام المكدس الصريح (Explicit Stack Implementation).

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

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

5. التعقيد الزمني والمكاني

يُعد تحليل التعقيد (Complexity Analysis) أمراً حيوياً لفهم كفاءة خوارزمية البحث أولاً بالعمق. يتميز DFS بكفاءته العالية في استكشاف جميع العقد والحواف في الرسم البياني مرة واحدة فقط. إذا كان الرسم البياني يحتوي على V من العقد (Vertices) و E من الحواف (Edges):

  • التعقيد الزمني (Time Complexity): يعتمد التعقيد الزمني لـ DFS على طريقة تمثيل الرسم البياني. إذا تم تمثيل الرسم البياني باستخدام قائمة التجاور (Adjacency List)، فإن التعقيد الزمني هو O(V + E). هذا يعني أن الوقت المستغرق يتناسب خطياً مع عدد العقد بالإضافة إلى عدد الحواف. هذا هو السيناريو الأكثر كفاءة، حيث يتم فحص كل عقدة وكل حافة مرة واحدة. أما إذا تم استخدام مصفوفة التجاور (Adjacency Matrix)، فإن التعقيد الزمني يرتفع إلى O(V²)، لأن فحص جيران كل عقدة يتطلب المرور على جميع العقد V الأخرى.
  • التعقيد المكاني (Space Complexity): يرتبط التعقيد المكاني لـ DFS بشكل أساسي بالمساحة اللازمة لتخزين المكدس، بالإضافة إلى المساحة المطلوبة لتخزين حالة الزيارة لكل عقدة (مزورة/غير مزورة). في أسوأ الحالات، يمكن أن يصل عمق المكدس إلى عدد العقد في الرسم البياني، خاصة في حالة المخططات الشبيهة بالقائمة. وبالتالي، فإن التعقيد المكاني هو O(V). هذه الميزة تجعل DFS أكثر كفاءة من حيث الذاكرة في بعض الحالات مقارنة بـ BFS، التي قد تحتاج إلى O(E) أو حتى O(V²) لتخزين قائمة الانتظار في الرسوم البيانية الكثيفة جداً.

تجدر الإشارة إلى أن التعقيد الزمني O(V + E) هو الأفضل لأنه يضمن أن الخوارزمية “سريعة خطياً”، أي أنها تستغل كل الوقت اللازم لقراءة الرسم البياني بالكامل مرة واحدة فقط دون تكرار.

6. التطبيقات الرئيسية والمفاهيم المشتقة

تُعد خوارزمية البحث أولاً بالعمق ذات أهمية بالغة في علم الحاسوب ولها تطبيقات واسعة النطاق في العديد من المجالات التي تتطلب تحليل العلاقات الهيكلية. أحد أهم تطبيقاتها هو الفرز الطوبولوجي (Topological Sorting)، وهو ترتيب خطي للعقد في رسم بياني موجه غير حلقي (DAG)، حيث يتم تطبيق DFS لضمان ظهور كل عقدة قبل العقد التي تعتمد عليها.

بالإضافة إلى ذلك، تُستخدم DFS بشكل أساسي في:

  • إيجاد المكونات المتصلة (Finding Connected Components): سواء كانت مكونات متصلة عادية في الرسوم البيانية غير الموجهة أو المكونات المتصلة بقوة (SCCs) في الرسوم البيانية الموجهة (باستخدام خوارزميات مشتقة مثل خوارزمية تارجان أو خوارزمية كوزاراجو)، حيث يتم استخدام DFS لتحديد مجموعات العقد التي يمكن الوصول إليها بشكل متبادل.
  • كشف الدورات (Cycle Detection): في الرسوم البيانية الموجهة وغير الموجهة، يمكن لخوارزمية DFS أن تحدد بسهولة وجود دورة عندما تحاول الانتقال إلى عقدة تم وضع علامة “قيد الزيارة” عليها بالفعل.
  • حل المتاهات وألغاز المسار (Maze Solving): نظراً لطبيعتها في الاستكشاف العميق، تُعد DFS مثالية لحل المتاهات، حيث تستمر في مسار واحد حتى الوصول إلى الهدف أو طريق مسدود.
  • خوارزميات التراجع (Backtracking Algorithms): تُستخدم DFS كأساس عام لخوارزميات التراجع التي تهدف إلى إيجاد حلول لمشاكل تتطلب تجربة جميع التركيبات الممكنة، مثل مشكلة الباحث عن الألغام أو مشكلة الألغاز الرقمية المعقدة.

7. المقارنة مع البحث أولاً بالعرض (BFS)

غالباً ما تتم مقارنة البحث أولاً بالعمق (DFS) بخوارزمية البحث أولاً بالعرض (BFS)، وكلاهما خوارزميتان شاملتان لمسح الرسم البياني، ولكنهما تختلفان بشكل جوهري في كيفية استكشاف مساحة البحث. تستخدم BFS هيكل قائمة الانتظار (Queue) لضمان زيارة جميع العقد في مستوى معين قبل الانتقال إلى المستوى التالي، مما يجعلها مثالية لإيجاد أقصر مسار في الرسوم البيانية غير الموزونة.

في المقابل، تتميز DFS بأنها:

  • غير مثالية لإيجاد أقصر مسار: نظراً لأنها تتعمق عشوائياً، قد تجد DFS مساراً طويلاً جداً للوصول إلى الهدف حتى لو كان هناك مسار أقصر متاح.
  • أكثر كفاءة في الذاكرة: في الرسوم البيانية ذات العرض الكبير والعمق الضحل، تكون DFS أفضل من حيث المساحة المطلوبة (O(V)) مقارنة بـ BFS التي قد تحتاج إلى تخزين جميع العقد في مستوى واحد (ربما O(V) أيضاً، ولكن قد يكون المكدس أكبر بكثير في الممارسة).
  • ضرورية للتطبيقات الهيكلية: تستخدم DFS لإجراء تحليلات هيكلية مثل الفرز الطوبولوجي وكشف الدورات، وهي مهام لا يمكن لـ BFS إنجازها بنفس السهولة والكفاءة.

بشكل عام، يتم اختيار DFS عندما يكون الهدف هو استكشاف الرسم البياني بأكمله، أو عندما يكون من المحتمل أن يكون الهدف موجوداً في عمق كبير ومبكر في مساحة البحث، أو عندما تكون قيود الذاكرة صارمة.

8. قراءات إضافية