فئة TreeNode واجتياز BFS حسب المستويات
أنشئ أشجارًا ثنائية من مصفوفات، ونفّذ BFS باستخدام deque للطباعة مستوىً تلو الآخر، وحل maximum-depth باستخدام BFS
فئة TreeNode واجتياز BFS حسب المستويات درس مجاني في DSA Interview Prep على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في DSA Interview Prep، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة DSA 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 4BFS بترتيب المستويات: التجميع حسب المستوى
يجمع الإصدار القياسي من 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) وفتح باقي دورة DSA Interview Prep، انتقل إلى CoddyKit PRO. تتضمن دورة DSA Interview Prep 4 دروس في المجموع.
ماذا ستتعلم في «فئة TreeNode واجتياز BFS حسب المستويات»؟
أنشئ أشجارًا ثنائية من مصفوفات، ونفّذ BFS باستخدام deque للطباعة مستوىً تلو الآخر، وحل maximum-depth باستخدام BFS تتمرن على DSA Interview Prep مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ DSA Interview Prep؟
لا تُشترط خبرة سابقة. DSA Interview Prep على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «فئة TreeNode واجتياز BFS حسب المستويات»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس DSA Interview Prep هذا؟
نعم. كل درس في DSA Interview Prep يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- فئة TreeNode واجتياز BFS حسب المستويات
- DFS بالترتيب الوسطي والقبلي والبعدي
- قطر الأشجار وارتفاعها وتوازنها
- مجموع المسار والسلف المشترك الأدنى