Top View of Binary Tree
لديك شجرة ثنائية مخزنة في المصفوفة root بترتيب الكومة: الجذر عند الفهرس 0، وأبناء الفهرس i عند 2*i+1 (يسارًا) و2*i+2 (يمينًا)، و-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, 5, 9, 6]
- الشرح
- من اليسار إلى اليمين، تحتوي الأعمدة على
4(العمود -2)، و2(-1)، والجذر5(0)، و9(1)، و6(2). يقع7و1أيضًا في العمود 0، لكن الجذر فوقهما.
- المدخلات
- root = [1, 2, -1, -1, 3, -1, -1, -1, -1, -1, 4]
- المخرجات
- [2, 1, 4]
- الشرح
- يوجد
2في العمود -1. ابنه الأيمن3عند الفهرس 4 عاد إلى العمود 0، تحت الجذر، لذا فهو مخفي. أما4عند الفهرس 10، وهو الابن الأيمن لـ3، فيوجد منفردًا في العمود 1.
- المدخلات
- root = [6, -1, -1]
- المخرجات
- [6]
- الشرح
- يحتوي الفهرسان 1 و2 على
-1، لذا تبقى6وحدها في العمود 0 وتشكل العرض بالكامل.
سؤال إضافي
مع كائنات العقد بدلًا من مصفوفة، لا يوجد فهرس لحسم التعادل. كيف ستصوغ قاعدة كسر التعادل، وهل ستغيّر شيفرة البحث بالعرض؟
تلميحات
افتحها واحدًا تلو الآخر. كل تلميح يكشف أكثر قليلًا.
رقّم الأعمدة: الجذر هو 0، والخطوة إلى اليسار تطرح 1، والخطوة إلى اليمين تضيف 1. أيُّ عقدة في أحد الأعمدة يمكنك رؤيتها من الأعلى، وكيف تميّزها عن العقد الأخرى في ذلك العمود؟
إذا زرت العُقد مستوىً تلو الآخر، ومن اليسار إلى اليمين داخل كل مستوى، فأي عُقدة في أحد الأعمدة ستقابلها أولًا؟
نفّذ بحثًا بعرض الشجرة على أزواج (index, column)، مع إضافة الابن الأيسر قبل الأيمن. خزّن قيمةً في المرة الأولى فقط التي يظهر فيها عمود، وتتبّع أصغر عمود وأكبره، ثم اقرأ الأعمدة بالترتيب في النهاية.
الحل
يعرض كل عمود العقدة الأقل عمقًا فيه، وعند التعادل يعرض العقدة التي تسبق غيرها في ترتيب المستويات. وهذا هو بالضبط الترتيب الذي تزور به عملية البحث بعرض الشجرة العقد، لذا تكون أول عقدة يصادفها البحث في العمود هي الإجابة. وينجح اجتياز الشجرة بعمق الشجرة أيضًا، لكنه يتطلب تتبّع الأعماق وكسر حالات التعادل بعناية.
البحث بالعمق أولًا، والعقدة الأقل عمقًا في كل عمود
الفكرة
اجتز الشجرة بترتيب العمق أولًا، مع تتبّع عمود كل عقدة وعمقها، واحتفظ لكل عمود بعمق وقيمة أقل عقدة عمقًا عُثر عليها حتى الآن.
استبدل القيمة فقط إذا كان العمق أصغر بصرامة. عند تعادل عقدتين، تُزار العقدة التي تسبق الأخرى في ترتيب المستويات أولًا في اجتياز يستكشف كل شجرة فرعية يسارية قبل نظيرتها اليمنى، لأن المسار يتجه يسارًا عند موضع تفرّع المسارين. الاستبدال عند تساوي العمق يجعل العقدة اللاحقة هي الفائزة: إذا كان الفهرس 3 فارغًا وكانت هناك عقدتان عند الفهرسين 19 و23، فكلتاهما تقعان في العمود -2 عند العمق 4، وتكون الإجابة هي العقدة عند الفهرس 19.
تشكل الأعمدة المشغولة نطاقًا متصلًا، لأن العقدة في العمود -3 لها أسلاف في العمودين -1 و-2، لذا تتبّع أصغر عمود وأكبره. تُزار كل عقدة مرة واحدة، بزمن O(n)، ويبلغ عمق الاستدعاء التكراري نحو h استدعاءات.
الخوارزمية
- زُر الجذر مع العمود 0 والعمق 0. عُد فورًا إذا كان الفهرس خارج المصفوفة أو كانت قيمته
-1. - إذا لم تكن هناك عقدة في العمود بعد، أو كان هذا العمق أصغر تمامًا من العمق المخزّن، فخزّن هذا العمق والقيمة.
- زُر
2i+1مع العمودcol-1، ثم2i+2مع العمودcol+1، على مستوى أعمق واحد. - أعِد القيم المخزّنة من أصغر عمود إلى أكبر عمود.
def topView(root):
n = len(root)
best = {} # column -> (depth, value) of the shallowest node found so far
def visit(i, col, depth):
if i >= n or root[i] == -1:
return
# Replace only on a strictly smaller depth: on a tie, the node
# met first is the one further left, which comes first 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، وكل مستوى من اليسار إلى اليمين. هذه هي الأولوية المطلوبة في المسألة، لذا تكون أول عقدة يتلقاها العمود هي العقدة المرئية من الأعلى، ويُتخطّى كل ما يأتي بعدها.
تكلّف كل عقدة عملية بحث واحدة في الخريطة، أي O(n) إجمالًا. ويمكن أن يحتوي الطابور على مستوى كامل، أي ما يصل إلى نحو نصف العقد، لذا فإن المساحة هي O(n).
الخوارزمية
- ضع الزوج (الفهرس 0، العمود 0) في قائمة انتظار.
- أخرج زوجًا (
i،col). إذا لم يكن للخريطة إدخال للعمودcol، فخزّنroot[i]فيه. - أضف (
2i+1،col-1)، ثم (2i+2،col+1)، لكل ابن موجود. - عندما تصبح قائمة الانتظار فارغة، أعد قيم الخريطة من أصغر عمود إلى أكبر عمود.
from collections import deque
def topView(root):
n = len(root)
top = {} # column -> value of the first 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 shallower nodes first, and left before right
# on one level, so the first node of a column is the one seen from above.
if col not in top:
top[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 [top[col] for col in range(min(top), max(top) + 1)]
أخطاء شائعة وحالات حدّية
- الاحتفاظ بأول عقدة يعثر عليها اجتيازٌ بالعمق أولًا. إذا كان للعقدة اليسرى للجذر عقدة يمنى لها عقدة يمنى (العمود 1، العمق 3)، فسيصل إليها الاجتياز قبل وصوله إلى العقدة اليمنى للجذر (العمود 1، العمق 1).
- إضافة العقدة اليمنى إلى قائمة الانتظار أولًا: عند التعادل، تُختار العقدة الخطأ.
- ترتيب الأعمدة كنص: يُرتَّب
"-1"قبل"-2". - افتراض أن الشجرة الفرعية اليسرى تشغل الأعمدة السالبة. في المثال 2، تقع العقدة
4في الشجرة الفرعية اليسرى للجذر وفي العمود 1.
أسئلة شائعة3
ما هو المنظر العلوي لشجرة ثنائية؟
رقّم الأعمدة بدءًا من الجذر عند 0، مع إنقاص الرقم بمقدار واحد للابن الأيسر وزيادته بمقدار واحد للابن الأيمن. يعرض المنظر العلوي، لكل عمود من اليسار إلى اليمين، العقدة الأقرب إلى الجذر.
ما هو التعقيد الزمني للعرض العلوي للشجرة الثنائية؟
يزور البحث بالعرض كلًّا من العقد n مرة واحدة، مع عملية بحث في الخريطة بتعقيد O(1) لكل عقدة، لذا يعمل بزمن O(n) إذا تتبعت العمود الأصغر والأكبر. أما خريطة الأعمدة المرتبة فتجعل التعقيد O(n log w) لعدد w من الأعمدة.
كيف يختلف المنظر العلوي عن المنظر الأيسر والمنظر السفلي؟
يعرض المنظور الأيسر أول عقدة في كل مستوى. ويعرض المنظور العلوي أقل عقدة عمقًا في كل عمود. أما المنظور السفلي فيعرض أعمق عقدة في كل عمود، لذا فإن البحث بالعرض أولًا عنه يستبدل العقدة في كل عمود بدلًا من الاحتفاظ بأول عقدة.
مسائل مشابهة
مسائل تعتمد على الأفكار نفسها. حلّ اثنتين أو ثلاث منها يثبّت النمط.
def topView(root):
# اكتب الكود هنااختبار #1
اختبار #2
اختبار #3
وسيط
root = [5, 2, 9, 4, 7, 1, 6]
الإخراج المتوقع
[4, 2, 5, 9, 6]