0Pricing
Coding Interview Prep · درس

فئة TreeNode واجتياز BFS حسب المستويات

أنشئ أشجارًا ثنائية من مصفوفات، ونفّذ BFS باستخدام deque للطباعة مستوىً تلو الآخر، وحل maximum-depth باستخدام BFS

فئة TreeNode واجتياز BFS حسب المستويات درس مجاني في Coding Interview Prep على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Coding Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

أساس الفئة TreeNode

الشجرة الثنائية هي بنية بيانات هرمية، لا يملك كل عنصر فيها أكثر من ابنين، يُسمَّيان الابن الأيسر والابن الأيمن. في Python، نمثّل العقدة بفئة بسيطة: class TreeNode: def __init__(self, val=0, left=None, right=None). تبدأ كل مسألة عن الأشجار في المقابلات بهذا التعريف؛ وستراه في الشيفرة التمهيدية لمعظم مسائل الأشجار في LeetCode.

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

# Build a small tree manually:
#       1
#      / \
#     2   3
#    / \
#   4   5
root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)
print(root.val, root.left.val, root.right.val)

بناء الأشجار من المصفوفات

غالبًا ما تمنحك مسائل المقابلات شجرة ممثلة في صورة مصفوفة بترتيب المستويات، حيث تشير None إلى العقد المفقودة. عند إعطائك الفهرس i، يوجد الابن الأيسر عند الفهرس 2i+1، والابن الأيمن عند الفهرس 2i+2. تُعد كتابة دالة مساعدة لإلغاء تسلسل هذه المصفوفة وتحويلها إلى عقد TreeNode مترابطة أداة مفيدة توفر الوقت أثناء جلسات التدريب.

from collections import deque

def build_tree(arr):
    if not arr or arr[0] is None:
        return None
    root = TreeNode(arr[0])
    q = deque([root])
    i = 1
    while q and i < len(arr):
        node = q.popleft()
        if i < len(arr) and arr[i] is not None:
            node.left = TreeNode(arr[i])
            q.append(node.left)
        i += 1
        if i < len(arr) and arr[i] is not None:
            node.right = TreeNode(arr[i])
            q.append(node.right)
        i += 1
    return root

root = build_tree([1, 2, 3, 4, 5, None, 6])
print(root.val, root.left.val, root.right.val)

ما المقصود بـ BFS ولماذا نستخدم طابورًا؟

يزور البحث بعرض الشجرة (BFS) جميع العقد الموجودة عند العمق d قبل زيارة أي عقدة عند العمق d+1. وهذا الاجتياز مستوىً بعد مستوى هو بالضبط ما يوفره الطابور (FIFO): نضع الجذر في الطابور، ثم نعالج العقد واحدةً تلو الأخرى، ونضع أبناء كل عقدة في الطابور أثناء ذلك. توفر collections.deque في Python العمليتين appendleft وpopleft بزمن O(1)، مما يجعلها الخيار الصحيح بدلًا من قائمة عادية.

from collections import deque

def bfs_print(root):
    if not root:
        return
    q = deque([root])
    while q:
        node = q.popleft()
        print(node.val, end=' ')
        if node.left:
            q.append(node.left)
        if node.right:
            q.append(node.right)

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
bfs_print(root)  # 1 2 3 4

BFS بترتيب المستويات: التجميع حسب المستوى

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

from collections import deque

def level_order(root):
    if not root:
        return []
    result = []
    q = deque([root])
    while q:
        level_size = len(q)
        level = []
        for _ in range(level_size):
            node = q.popleft()
            level.append(node.val)
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        result.append(level)
    return result

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
print(level_order(root))  # [[1], [2, 3], [4]]

العمق الأقصى باستخدام BFS

يساوي العمق الأقصى لشجرة ثنائية عدد مستويات اجتيازها باستخدام BFS. ما عليك سوى عدّ عدد مرات إكمال حلقة مستوى. ينتج عن ذلك حل بزمن O(n) ومساحة O(w)، حيث تمثل w أقصى عرض للشجرة. في الشجرة المتوازنة، يكون w مساويًا لـ O(n/2)، ولذلك تكون المساحة في أسوأ الحالات O(n).

from collections import deque

def max_depth_bfs(root):
    if not root:
        return 0
    depth = 0
    q = deque([root])
    while q:
        depth += 1
        for _ in range(len(q)):
            node = q.popleft()
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
    return depth

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
print(max_depth_bfs(root))  # 3

الرؤية من الجانب الأيمن لشجرة ثنائية

تعيد الرؤية من الجانب الأيمن آخر عقدة يمكن رؤيتها عند النظر إلى الشجرة من اليمين، أي العنصر الأخير في كل مستوى من مستويات اجتياز BFS. وهذا تطبيق مباشر لـ BFS بترتيب المستويات: اجمع العقدة الأخيرة في حلقة كل مستوى. التعقيد الزمني هو O(n)، أما المساحة فهي O(w) للطابور.

from collections import deque

def right_side_view(root):
    if not root:
        return []
    result = []
    q = deque([root])
    while q:
        level_size = len(q)
        for i in range(level_size):
            node = q.popleft()
            if i == level_size - 1:
                result.append(node.val)
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
    return result

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.right = TreeNode(5)
print(right_side_view(root))  # [1, 3, 5]

اجتياز المستويات بالترتيب المتعرج

في الاجتياز المتعرج، تُجمع المستويات الفردية من اليسار إلى اليمين، والمستويات الزوجية من اليمين إلى اليسار. ويحافظ التنفيذ الأبسط على قائمة انتظار BFS كما هي، ويكتفي بعكس قوائم المستويات بالتناوب قبل إلحاقها بالنتيجة. تتبّعوا الاتجاه باستخدام علامة منطقية تتبدّل عند كل مستوى. وبذلك تتجنبون تعقيد قائمة الانتظار مزدوجة الطرفين داخل الحلقة الداخلية.

from collections import deque

def zigzag_level_order(root):
    if not root:
        return []
    result = []
    q = deque([root])
    left_to_right = True
    while q:
        level = []
        for _ in range(len(q)):
            node = q.popleft()
            level.append(node.val)
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        result.append(level if left_to_right else level[::-1])
        left_to_right = not left_to_right
    return result

root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)
print(zigzag_level_order(root))

تحليل التعقيد المكاني لـ BFS

يستخدم BFS مساحة قدرها O(w)، حيث تمثل w أقصى عرض للشجرة. في شجرة ثنائية مثالية تحتوي على n عقدة، يضم المستوى الأخير (n+1)/2 عقدة، ولذلك قد يحتفظ BFS بما يصل إلى n/2 عقدة في قائمة الانتظار في الوقت نفسه. وهذا يجعل BFS أسوأ من DFS من حيث المساحة (O(h)) في الأشجار العريضة والمتوازنة، لكنه أفضل في الأشجار العميقة المنحرفة، حيث يساوي عمق مكدس استدعاءات DFS قيمة n.

# Space comparison: BFS vs DFS on a complete binary tree
# n=15 nodes, height=4
# BFS max queue size = 8 (last level)
# DFS max call stack = 4 (height)

# For a skewed tree (like a linked list):
# n=1000 nodes
# BFS max queue size = 1 (always 1 node per level)
# DFS max call stack = 1000 (recursion depth -> stack overflow!)

from collections import deque

def skewed_tree(n):
    root = TreeNode(1)
    cur = root
    for i in range(2, n+1):
        cur.right = TreeNode(i)
        cur = cur.right
    return root

root = skewed_tree(10)
print('BFS on skewed tree is safe')

متوسطات المستويات في الشجرة الثنائية

يُعد حساب متوسط القيمة في كل مستوى تطبيقًا مباشرًا آخر لـ BFS. اجمعوا جميع القيم في المستوى، واقسموها على عددها، ثم أضيفوها إلى قائمة النتائج. تختبر هذه المسألة قدرتكم على إجراء الحسابات داخل حلقة المستوى. استخدموا دائمًا القسمة على float في Python 3، أي العامل /، وتعاملوا مع حالة الشجرة الفارغة في البداية.

from collections import deque

def average_of_levels(root):
    if not root:
        return []
    result = []
    q = deque([root])
    while q:
        size = len(q)
        total = 0
        for _ in range(size):
            node = q.popleft()
            total += node.val
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
        result.append(total / size)
    return result

root = TreeNode(3)
root.left = TreeNode(9)
root.right = TreeNode(20)
root.right.left = TreeNode(15)
root.right.right = TreeNode(7)
print(average_of_levels(root))  # [3.0, 14.5, 11.0]

إيجاد أقل عمق باستخدام BFS

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

from collections import deque

def min_depth(root):
    if not root:
        return 0
    q = deque([(root, 1)])
    while q:
        node, depth = q.popleft()
        # A leaf has no children
        if not node.left and not node.right:
            return depth
        if node.left:
            q.append((node.left, depth + 1))
        if node.right:
            q.append((node.right, depth + 1))
    return 0

root = TreeNode(2)
root.left = TreeNode(3)
root.left.left = TreeNode(4)
root.right = TreeNode(5)  # leaf at depth 2
print(min_depth(root))  # 2

ربط العقد المتجاورة في المستوى

تطلب مسألة ملء مؤشرات الجار الأيمن التالي ربط كل عقدة بجارتها اليمنى في المستوى نفسه. وباستخدام BFS، يصبح الأمر مباشرًا: داخل حلقة كل مستوى، عيّنوا node.next = q[0] لجميع العقد باستثناء العقدة الأخيرة. هذا مثال كلاسيكي على المسائل التي يجعل فيها BFS الحل واضحًا، بينما يتطلب DFS تتبّعًا دقيقًا للمؤشرات عبر الشجرات الفرعية.

from collections import deque

class Node:
    def __init__(self, val=0, left=None, right=None, next=None):
        self.val = val
        self.left = left
        self.right = right
        self.next = next

def connect(root):
    if not root:
        return root
    q = deque([root])
    while q:
        size = len(q)
        for i in range(size):
            node = q.popleft()
            if i < size - 1:
                node.next = q[0]
            if node.left:
                q.append(node.left)
            if node.right:
                q.append(node.right)
    return root

print('BFS connect: O(n) time, O(w) space')

تحقق سريع

اختبروا مدى فهمكم لمفاهيم Data Structures & Algorithms — Coding Interview Prep الواردة في هذا الدرس.

مراجعة الدرس

تعلمتم في هذا الدرس: تعريف الفئة TreeNode وكيفية بناء الأشجار من المصفوفات، واجتياز المستويات باستخدام BFS وقائمة انتظار مزدوجة الطرفين مع حيلة تحديد حجم المستوى لتجميع العقد، وتطبيقات تشمل أقصى عمق، وأقل عمق، ومنظور الجانب الأيمن، والاجتياز المتعرج، ومتوسطات المستويات. بعد ذلك سنستكشف ترتيب اجتياز DFS التعاودي.

الأسئلة الشائعة

هل درس «فئة TreeNode واجتياز BFS حسب المستويات» مجاني؟

نعم — نص درس «فئة TreeNode واجتياز BFS حسب المستويات» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Coding Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة Coding Interview Prep 4 دروس في المجموع.

ماذا ستتعلم في «فئة TreeNode واجتياز BFS حسب المستويات»؟

أنشئ أشجارًا ثنائية من مصفوفات، ونفّذ BFS باستخدام deque للطباعة مستوىً تلو الآخر، وحل maximum-depth باستخدام BFS تتمرن على Coding Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Coding Interview Prep؟

لا تُشترط خبرة سابقة. Coding Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.

كم من الوقت يستغرق درس «فئة TreeNode واجتياز BFS حسب المستويات»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Coding Interview Prep هذا؟

نعم. كل درس في Coding Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. فئة TreeNode واجتياز BFS حسب المستويات
  2. DFS بالترتيب الوسطي والقبلي والبعدي
  3. قطر الأشجار وارتفاعها وتوازنها
  4. مجموع المسار والسلف المشترك الأدنى
← العودة إلى Coding Interview Prep