Vertical Order Traversal of a Binary Tree
İkili ağaç, yığın sırasına göre root dizisinde saklanır: kök 0 indeksindedir, i indeksinin çocukları 2*i+1 (sol) ve 2*i+2 (sağ) indekslerindedir, boş bir konumu -1 belirtir ve dizinin sonunda fazladan -1 girdileri bulunabilir.
Kök, 0 sütunundadır; sol çocuk, ebeveyninin bir sütun solunda, sağ çocuk ise bir sütun sağındadır. Soldan sağa her sütun için bir liste döndürün. Bir sütundaki düğümleri yukarıdan aşağıya sıralayın; aynı sütunu ve derinliği paylaşan düğümleri ise değerleri artan sırada listeleyin.
Fonksiyon
- rootinteger-array
- yığın düzenindeki ağaç; boş bir konum için -1 kullanılır
- Döndürürinteger-2d-array
- Her sütun için bir değer listesi; önce en soldaki sütun, her sütunda yukarıdan aşağıya
Kısıtlar
1 ≤ root.length ≤ 32767- Her
root[i],-1ya da0 ≤ root[i] ≤ 5000koşulunu sağlayan bir değerdir ve hiçbir değer iki kez geçmez. root[0]hiçbir zaman-1değildir, dolayısıyla ağaçta en az bir düğüm vardır.- Dizi, son düğümden sonra fazladan
-1girdileriyle bitebilir. - Boş bir konumun her iki çocuğu da boştur ve ağaç en fazla
14seviyeye sahiptir.
Örnekler
- Girdi
- root = [5, 2, 9, 4, 7, 1, 6]
- Çıktı
- [[4], [2], [5, 1, 7], [9], [6]]
- Açıklama
- 0. sütun kök
5'i, ardından iki seviye aşağıda7ve1'i içerir. Bu ikisi aynı sütunu ve derinliği paylaşır, bu nedenle7daha solda olmasına rağmen1,7'den önce gelir.
- Girdi
- root = [1, 2, 3, -1, 4, -1, -1, -1, -1, 5]
- Çıktı
- [[2, 5], [1, 4], [3]]
- Açıklama
- -1. sütunda derinlik 1'de
2ve derinlik 3'te (4öğesinin sol çocuğu)5bulunur. 0. sütunda kök1ve 4. indeksteki4bulunur. 1. sütunda yalnızca3bulunur.
- Girdi
- root = [6, -1, -1]
- Çıktı
- [[6]]
- Açıklama
6değerinin alt öğesi yoktur, bu nedenle yanıtın tek bir sütunu vardır:[6].
Ek soru
Her değer en fazla 5000. Karşılaştırmalı sıralamayı kullanmayı bırakıp O(n + 5000) zamanda bitirmek için bundan yararlanabilir misin?
İpuçları
Tek tek açın. Her biri biraz daha fazlasını gösterir.
Her düğümün bir sütunu, bir derinliği ve bir değeri vardır. Tüm bu üçlüler tek bir listede olsaydı, onları yanıttaki sıraya nasıl dizerdin?
Genişlik öncelikli arama düğümleri zaten yukarıdan aşağıya sıralar. Birden fazla düğüm aynı sütunu ve seviyeyi paylaştığında sıralamanın hangi kısmı hâlâ eksiktir?
Ağacı her seferinde bir düzey işleyin. Her düzeyi önce sütuna, ardından değere göre sıralayın, her değeri sütununun listesine ekleyin ve sonunda listeleri en küçük sütundan en büyük sütuna doğru okuyun.
Çözüm
Her düğümün üç anahtarı vardır: sütun, derinlik ve değer. Yanıt, bu anahtarlara göre sıralanmış düğümlerdir; her sütun için bir liste. Sütun ve derinlik eşit olduğunda, düğümler geçiş sırasında soldan sağa karşılaşıldıkları sıraya göre değil, değere göre sıralanır; bu nedenle sıralama yapmalısınız: tüm düğümleri bir kerede ya da genişlik öncelikli aramanın her seviyesini.
Düğümün her birini kaydedin, ardından sıralayın
Sezgi
Her düğümü bir kez, herhangi bir sırayla ziyaret edin ve (sütun, derinlik, değer) üçlüsünü kaydedin. Bir çocuğa geçmek derinliği 1 artırır ve sütunu 1 sola ya da sağa taşır. Ardından üçlüleri sütuna, derinliğe ve değere göre sıralayın. Artık her sütunun düğümleri gerekli sırayla bir arada bulunur. Sıralanmış listeyi tarayın ve sütun değiştiğinde yeni bir iç liste başlatın.
Örnek 1'de, 0. sütunun üçlüleri şu sırada sıralanır: (0, 0, 5), (0, 2, 1), (0, 2, 7). O(n log n) olan sıralama, O(n) olan taramadan daha baskın maliyete sahiptir ve üçlüler O(n) alan kullanır.
Algoritma
- Yığına (indeks 0, sütun 0, derinlik 0) ekleyin.
- Bir düğümü çıkarın, (sütun, derinlik, değer) kaydını tutun ve her gerçek çocuğu sütun
col-1veyacol+1ve derinlikdepth+1olacak şekilde yığına ekleyin. - Yığın boşaldığında, üçlüleri önce sütuna, sonra derinliğe, ardından değere göre sıralayın.
- Üzerlerinde ilerleyin ve sütun her değiştiğinde yeni bir liste başlatın.
def verticalTraversal(root):
n = len(root)
entries = [] # (column, depth, value) for every node
stack = [(0, 0, 0)] # (node index, column, depth); the root is never empty
while stack:
i, col, depth = stack.pop()
entries.append((col, depth, root[i]))
for child, child_col in ((2 * i + 1, col - 1), (2 * i + 2, col + 1)):
if child < n and root[child] != -1:
stack.append((child, child_col, depth + 1))
# Column, then depth, then value: exactly the order of the answer.
entries.sort()
result = []
for k, (col, depth, value) in enumerate(entries):
if k == 0 or col != entries[k - 1][0]:
result.append([]) # a new column starts
result[-1].append(value)
return resultHer seviyenin içinde seviyeye göre sırala
Sezgi
Genişlik öncelikli arama, her seferinde bir seviyeyi işler. Her düğüm işlendiğinde değerini sütununun listesine eklerse, her liste zaten yukarıdan aşağıya sıralı olur; dolayısıyla derinliğe göre sıralama gerekmez. Eşitlikler yalnızca aynı seviyede oluşur, bu yüzden işlemeden önce her seviyeyi sütuna, ardından değere göre sıralayın.
Örnek 1'de 2. seviye, -2, 0, 0, 2 sütunlarında 4, 7, 1, 6 olarak gelir ve 4, 1, 7, 6 şeklinde sıralanır; böylece 0. sütun [5, 1, 7] olur. Dolu sütunlar kesintisiz bir aralık oluşturur, bu yüzden en küçüğü ve en büyüğü izlemek yeterlidir.
m düğümlü bir seviyeyi sıralamanın maliyeti O(m log m) olur. En kötü durum yine de O(n log n) şeklindedir, çünkü tam bir ağacın son seviyesi düğümlerin yaklaşık yarısını içerir. Alan karmaşıklığı O(n) olur.
Algoritma
- (indeks 0, sütun 0) içeren bir düzeyle başla.
- Düzeyi önce sütuna, sonra değere göre sırala.
- Bu sırayla, her değeri kendi sütununun listesine ekle ve gerçek çocuklarını,
col-1vecol+1sütunlarıyla birlikte, sonraki düzeyde topla. - Bir düzey boş olana kadar, en küçük ve en büyük sütunu takip ederek tekrarla.
- Listeleri en küçük sütundan en büyük sütuna doğru döndür.
def verticalTraversal(root):
n = len(root)
columns = {} # column -> its values, top to bottom
level = [(0, 0)] # (node index, column) of every node on the current level
while level:
# Every node here has the same depth: order them by column, then value.
level.sort(key=lambda node: (node[1], root[node[0]]))
next_level = []
for i, col in level:
columns.setdefault(col, []).append(root[i])
for child, child_col in ((2 * i + 1, col - 1), (2 * i + 2, col + 1)):
if child < n and root[child] != -1:
next_level.append((child, child_col))
level = next_level
# The columns that hold a node form one unbroken range.
return [columns[col] for col in range(min(columns), max(columns) + 1)]
Tuzaklar ve uç durumlar
- Eşitliklerde soldan sağa sırayı korumak. Seviye başına sıralama yapmadan gerçekleştirilen genişlik öncelikli arama, örnek 1'deki 0. sütun için
[5, 7, 1]sonucunu verir;[5, 1, 7]sonucunu değil. - Sıralama yapmadan derinlik öncelikli dolaşım sırasında değerleri eklemek.
[1, 2, 3, -1, 4, -1, -1, -1, -1, -1, 5]dizisinde, önce sola giden bir dolaşım3(derinlik 1) değerinden önce5(derinlik 3) değerini 1. sütuna ekler. - Sütuna ve değere göre sıralayıp derinliği dikkate almamak. Bu durumda örnek 1'in 0. sütunu
[1, 5, 7]olur. - Sütunları dizgilerle anahtarlamak. Metin olarak sıralandığında
"-1","-2"değerinden önce gelir; dolayısıyla 0'ın solundaki sütunlar yanlış sırada çıkar.
Sıkça sorulan sorular3
İkili ağacın dikey sıralı dolaşımının zaman karmaşıklığı nedir?
Tüm düğümleri sütun, derinlik ve değere göre sıralamanın maliyeti O(n log n)’dir ve O(n) gezinme işlemine baskın gelir. Seviyeleri tek tek sıralamak da aynı en kötü duruma sahiptir; çünkü tam bir ağacın son seviyesi düğümlerin yaklaşık yarısını içerir. Alan karmaşıklığı O(n)’dir.
Dikey sıralı dolaşım, sıralama yapılmadan gerçekleştirilebilir mi?
Yalnızca eşitlikler soldan sağa sıralanıyorsa. Bu durumda, her değeri kendi sütununun listesine ekleyen bir genişlik öncelikli arama, yanıtı O(n) zamanda verir. Eşitlikler değere göre sıralanıyorsa bir miktar sıralama gerekir, ancak yalnızca aynı seviyedekiler arasında; çünkü yalnızca aynı seviyedeki düğümler eşit olabilir.
Dikey sıralı dolaşım, üst görünüm ve alt görünümle nasıl ilişkilidir?
Her üçü de düğümleri aynı sütun numaralarına göre gruplandırır. Üstten görünüm her sütunun en sığ düğümünü, alttan görünüm ise en derin düğümünü tutar. Dikey sıralı dolaşım her düğümü tutar ve aynı seviyeyi paylaşan birden fazla düğüm olduğunda bunları değerlerine göre sıralar; görünümler ise konuma göre seçim yapar.
Benzer problemler
Aynı fikirleri kullanan problemler. İki üçünü çözmek bir kalıbı kalıcı hale getirir.
def verticalTraversal(root):
# Kodu buraya yazınTest #1
Test #2
Test #3
Argüman
root = [5, 2, 9, 4, 7, 1, 6]
Beklenen Çıktı
[[4], [2], [5, 1, 7], [9], [6]]