Bottom View of Binary Tree
تحتوي المصفوفة root على شجرة ثنائية: الجذر عند الفهرس 0، وأبناء الفهرس i عند 2i+1 (يسارًا) و2i+2 (يمينًا)، وتُستخدم -1 للدلالة على موضع فارغ، وقد توجد إدخالات إضافية من -1 في النهاية.
يقع الجذر في العمود 0؛ فالانتقال خطوة إلى اليسار ينقل عقدةً عمودًا واحدًا إلى اليسار، والانتقال خطوة إلى اليمين ينقلها عمودًا واحدًا إلى اليمين. عند النظر من الأسفل، يُظهر كل عمود أعمق عقدة فيه، وعند التعادل تُختار العقدة الأخيرة في ذلك المستوى عند القراءة من اليسار إلى اليمين (ذات الفهرس الأكبر). أعد القيم التي تراها، بدءًا من العمود الأيسر.
الدالة
- rootinteger-array
- الشجرة بترتيب الكومة، مع -1 للدلالة على موضع فارغ
- تُرجعinteger-array
- القيمة التي تُرى من الأسفل في كل عمود، بدءًا بالعمود الأيسر
القيود
1 ≤ root.length ≤ 32767- كل
root[i]يساوي-1أو قيمة تحقق0 ≤ root[i] ≤ 104، ولا تتكرر أي قيمة. root[0]لا تساوي أبدًا-1، لذا تحتوي الشجرة على عقدة واحدة على الأقل.- قد تنتهي المصفوفة بعناصر
-1إضافية بعد العقدة الأخيرة. - كلا ابني الموضع الفارغ فارغان أيضًا، والشجرة لها
14مستوى على الأكثر.
أمثلة
- المدخلات
- root = [5, 2, 9, 4, 7, 1, 6]
- المخرجات
- [4, 2, 1, 9, 6]
- الشرح
- يحتوي العمود 0 على
5، وعلى مستوى أدنى بمستويين على7(الفهرس 4) و1(الفهرس 5). يظهر1لأنه يأتي لاحقًا على المستوى نفسه. تحتوي الأعمدة -2 و-1 و1 و2 على4و2و9و6.
- المدخلات
- root = [1, 2, 3, -1, 4, -1, -1, -1, -1, 5]
- المخرجات
- [5, 4, 3]
- الشرح
- يحتوي العمود -1 على
2، وأسفله بمستويين5(الفهرس 9)، لذا ترى5. في العمود 0، يقع4أسفل الجذر. يحتوي العمود 1 على3فقط.
- المدخلات
- root = [8, -1, -1, -1]
- المخرجات
- [8]
- الشرح
- كل موضع بعد الجذر هو
-1، لذا فإن8هي العقدة الوحيدة والعمود 0 هو العمود الوحيد.
سؤال إضافي
إذا كان ينبغي أن يُرجع التعادل في أعمق مستوى لعمودٍ ما جميعَ العقد المتعادلة، من اليسار إلى اليمين، فكيف ستتغير النسخة ذات البحث بعرض الشجرة أولًا؟ ماذا ستُرجع القيمة في العمود 0 من المثال 1؟
تلميحات
افتحها واحدًا تلو الآخر. كل تلميح يكشف أكثر قليلًا.
رقّم الأعمدة: الجذر هو 0، والخطوة إلى اليسار تطرح 1، والخطوة إلى اليمين تضيف 1. عند النظر من الأسفل، أيُّ عقدة من عمودٍ ما تراها، وماذا يحدث إذا جلست عقدتان من ذلك العمود على المستوى نفسه؟
زُر العقد مستوىً تلو الآخر، من اليسار إلى اليمين داخل كل مستوى، واكتب قيمة كل عقدة في عمودها، مستبدلًا ما كان موجودًا فيه. ما الذي يبقى في كل عمود في النهاية؟
استخدم طابورًا من أزواج (الفهرس، العمود)، وأضف الابن الأيسر إلى الطابور قبل الابن الأيمن. استبدل قيمة العمود عند كل عقدة، وتتبّع أصغر عمود وأكبره، ثم اقرأ الأعمدة بالترتيب في النهاية.
الحل
يعرض كل عمود أعمق عقدة فيه، وعند التعادل تكون العقدة اللاحقة في ترتيب المستويات هي الفائزة. يزور البحث بالعرض العقد بترتيب المستويات، لذا تكون آخر عقدة يزورها في العمود هي الإجابة: دع كل عقدة تستبدل القيمة الموجودة في خانة عمودها.
البحث بالعمق أولًا، أعمق عقدة في كل عمود
الفكرة
اجتز الشجرة بترتيب العمق أولًا، مع تمرير العمود والعمق إلى كل عقدة، واحتفظ، لكل عمود، بعمق وقيمة أعمق عقدة تم الوصول إليها حتى الآن.
إن زيارة الشجرة الفرعية اليسرى قبل اليمنى تجعلنا نلتقي بعقد المستوى من اليسار إلى اليمين، لذا فالعقدة التي تريدها هي آخر عقدة نلتقي بها: استبدل القيمة عندما يكون العمق أكبر أو مساويًا. في المثال 1، يلتقي الاجتياز بالعقدتين 7 ثم 1، وكلتاهما بعمق 2 في العمود 0، فتكون 1 هي قيمة العمود.
تشكل الأعمدة نطاقًا متصلًا بلا انقطاع، لذا تتبّع أصغرها وأكبرها واقرأ كل عمود بينهما. تمنحك زيارة واحدة لكل عقدة زمنًا قدره O(n)، ويكون عمق الاستدعاء التكراري h استدعاءات لشجرة ذات h مستويات.
الخوارزمية
- زُر الجذر مع العمود 0 والعمق 0.
- تجاوز الفهرس
iعندما يتجاوز نهاية المصفوفة أو تكون قيمةroot[i]هي-1. - إذا لم تكن هناك عقدة للعمود بعد، أو كان هذا العمق أكبر من العمق المخزَّن أو مساويًا له، فخزّن هذا العمق والقيمة.
- زُر
2i+1مع العمودcol-1، ثم2i+2مع العمودcol+1، وكلاهما بمستوى أعمق واحد. - أعِد القيم المخزَّنة، بدءًا من أصغر عمود.
def bottomView(root):
n = len(root)
best = {} # column -> (depth, value) of the deepest node found so far
def visit(i, col, depth):
if i >= n or root[i] == -1:
return
# Replace on a greater or equal depth: on a tie, the node
# met last is the one further right, which comes later in level order.
if col not in best or depth >= best[col][0]:
best[col] = (depth, root[i])
visit(2 * i + 1, col - 1, depth + 1)
visit(2 * i + 2, col + 1, depth + 1)
visit(0, 0, 0)
# The columns that hold a node form one unbroken range.
return [best[col][1] for col in range(min(best), max(best) + 1)]البحث بالعرض أولًا، العقدة الأخيرة لكل عمود
الفكرة
يعرض الطابور العقد بترتيب المستويات، إذ تضيف كل عقدة تغادره ابنها الأيسر، ثم ابنها الأيمن: كل عقدة بعمق d قبل أي عقدة بعمق d+1، وكل مستوى من اليسار إلى اليمين. من بين عقد العمود الواحد، تخرج العقدة الأعمق أخيرًا، وعند التعادل تخرج العقدة الأبعد إلى اليمين. لذا، عندما تستبدل كل عقدة قيمة خانة عمودها، تحتوي كل خانة في النهاية على العقدة التي تُرى من الأسفل، دون الحاجة إلى مقارنة الأعماق.
في المثال 1، تكتب 7 في العمود 0، ثم تستبدلها 1.
تكلّف كل عقدة زيارة واحدة للطابور وكتابة واحدة في الخريطة: زمن O(n). يمكن أن يحتوي الطابور على مستوى كامل، أي نحو نصف العقد، لذا فإن المساحة هي O(n).
الخوارزمية
- ضع الزوج (الفهرس 0، العمود 0) في طابور.
- أخرج زوجًا (
i،col) وخزّنroot[i]تحتcol، مع استبدال أي قيمة سابقة. - أضف (
2i+1،col-1)، ثم (2i+2،col+1)، لكل ابن موجود داخل المصفوفة وليس-1. - كرّر حتى يصبح الطابور فارغًا، مع تتبّع أصغر عمود وأكبره.
- أعِد قيم الخريطة، بدءًا من أصغر عمود.
from collections import deque
def bottomView(root):
n = len(root)
bottom = {} # column -> value of the last node seen in that column
queue = deque([(0, 0)]) # (node index, column); the root is never empty
while queue:
i, col = queue.popleft()
# The queue hands out deeper nodes later, and right after left
# on one level, so the last node to reach a column is the one seen from below.
bottom[col] = root[i]
left, right = 2 * i + 1, 2 * i + 2
if left < n and root[left] != -1:
queue.append((left, col - 1))
if right < n and root[right] != -1:
queue.append((right, col + 1))
# The columns that hold a node form one unbroken range.
return [bottom[col] for col in range(min(bottom), max(bottom) + 1)]
أخطاء شائعة وحالات حدّية
- استبدال العقدة فقط عند عمق أكبر strictly في نسخة البحث بالعمق أولًا. لذلك يُبقي المثال 1 على
7بدلًا من1. - الاحتفاظ بأول عقدة يصل إليها البحث بالعرض أولًا لكل عمود. وهذا هو المنظر العلوي.
- إضافة الابن الأيمن إلى قائمة الانتظار قبل الأيسر. عندئذٍ يظهر كل مستوى من اليمين إلى اليسار، وتُحسم حالات التعادل لصالح العقدة الخاطئة.
- اعتبار الأوراق هي الإجابة. في المثال 1، تُحجب الورقة
7بواسطة1، وفي المثال 2، للعقدة4ابن، لكنها مع ذلك تقع في أسفل العمود 0. - ترتيب الأعمدة كنصوص. كسلاسل نصية، يقع
"-10"بين"-1"و"-2".
أسئلة شائعة3
ما هو المنظر السفلي لشجرة ثنائية؟
أعطِ العمود الجذري الرقم 0، واجعل كل خطوة إلى اليسار أقل بعمود واحد، وكل خطوة إلى اليمين أكثر بعمود واحد. يسرد المنظور السفلي أعمق عقدة في كل عمود، من اليسار إلى اليمين. تحسم هذه المسألة التعادل في العمق حسب ترتيب المستويات، مع الاحتفاظ بالعقدة اللاحقة.
ما هو التعقيد الزمني لإيجاد المنظر السفلي لشجرة ثنائية؟
O(n) لـ n من العقد: يتعامل البحث بعرض المستوى مع كل عقدة مرة واحدة، مع عملية كتابة واحدة في خريطة تجزئة. وتضيف خريطة مرتبة مثل خريطة الشجرة عامل log w لـ w من الأعمدة.
ما الفرق بين العرض العلوي والعرض السفلي؟
يختار كلاهما عقدة واحدة لكل عمود. يختار العرض العلوي العقدة الأقل عمقًا، بينما يختار العرض السفلي العقدة الأعمق. في نسخة البحث بالعرض أولًا، يحتفظ العرض العلوي بأول قيمة يتلقاها العمود؛ أما العرض السفلي فيستبدلها في كل مرة.
مسائل مشابهة
مسائل تعتمد على الأفكار نفسها. حلّ اثنتين أو ثلاث منها يثبّت النمط.
def bottomView(root):
# اكتب الكود هنااختبار #1
اختبار #2
اختبار #3
وسيط
root = [5, 2, 9, 4, 7, 1, 6]
الإخراج المتوقع
[4, 2, 1, 9, 6]