Bir ağaç özyineleme, yığın veya kuyruk olmadan ve sadece bir avuç işaretçi olmadan geçilebilir mi?


15

Yarım on yıl önce, profesörün özyineleme, bir yığın, kuyruk, vb. (Veya benzer herhangi bir veri yapısı) ve sadece birkaç işaretçi kullanmadan bir ağaçtan geçebilmesi durumunda profesörün ekstra kredi sunduğu bir veri yapıları sınıfında oturuyordum. Sonunda profesör tarafından kabul edilen bu soruya açık bir cevap olduğunu düşündüğüm şeyi buldum. Aynı bölümde başka bir profesörle ayrı bir matematik dersinde oturuyordum ve özyineleme, yığın, kuyruk vb. Olmadan bir ağacı geçmenin imkansız olduğunu ve çözümümün geçersiz olduğunu iddia etti.

Peki, mümkün mü yoksa imkansız mı? Neden ya da neden olmasın?

Düzenleme: Biraz açıklama eklemek için, bu üç unsuru olan bir ikili ağaç - her bir düğüm ve iki çocuk için işaretçiler depolanan veri uygulandı. Çözümüm sadece birkaç değişiklikle n-ary ağaçlarına kadar genişletilebilir.

Veri yapıları öğretmenim, ağacın mutasyona uğramasına karşı herhangi bir kısıtlama koymadı ve aslında daha sonra kendi çözümünün, ağacı aşağıya doğru işaret etmek için çocuk işaretçileri kullanmak olduğunu öğrendim. Ayrık matematik profesörüm, bir ağacın herhangi bir mutasyonunun artık bir ağacın matematiksel tanımına göre bir ağaç olmadığı anlamına geldiğini, tanımının ebeveynlere herhangi bir işaretçiyi de engelleyeceğini söyledi.


3
Kısıtlamaları belirtmeniz gerekir. Ağacı değiştirmem mümkün mü? Ağaç nasıl temsil edilir? (Örneğin, her düğümün üst öğesine bir üst işaretçisi var mı?) Yanıt, belirli kısıtlamalara bağlı olacaktır; bu kısıtlamaları belirtmeden, bu iyi bir sorun değildir.
DW

2
Profesörlerin gerçekten ifade etmek istedikleri çelişki " ek alanlı" idi. Ama çözüm neydi? O(1)
Raphael

Yanıtlar:


17

Bu alanda bir çok araştırma kubbede, çöp toplama bağlamında "ucuz" çapraz ağaçlar ve genel liste yapıları yöntemi ile motive edilmiştir.

Bir dişli ikili ağaç, ağaçtaki halef düğümlerine bağlanmak için bazı nil işaretçilerin kullanıldığı ikili ağaçların uyarlanmış bir temsilidir. Bu ek bilgiler, bir ağacı yığın olmadan hareket ettirmek için kullanılabilir. Ancak, iş parçacıklarını alt işaretçilerden ayırmak için düğüm başına fazladan bir bit gerekir. Vikipedi: Tree_traversal

Bildiğim kadarıyla, normal şekilde işaretçiler kullanılarak uygulanan ikili ağaçlar (düğüm başına sol ve sağ işaretçi), Morris'e atfedilen bir yöntemde, iş parçacığı yöntemi kullanılarak geçilebilir . NIL-işaretçileri bir yolu kök dizinine bağlamak için geçici olarak yeniden kullanılır. Zeki kısım, çaprazlama sırasında, orijinal kenarları ağaçta döngü oluşturma biçimini kullanarak geçici iplik bağlantılarından ayırt edebilmesidir).

İyi kısım: ekstra veri yapısı yok. Kötü parçası: hafifçe hile, yığını içindeki zeki bir yolla ağacın. Çok zeki.

Morris ağacı Geçiş algoritması Reconsidered DOI: gizli yığının bir kanıtı, P. Mateti ve R. Manghirmalani gösterilmiştir 10.1016 / 0167-6423 (88) 90063-9

JM Morris: İkili ağaçları basit ve ucuz bir şekilde gezmek. IPL 9 (1979) 197-200 DOI: 10.1016 / 0020-0190 (79) 90068-1

Bir de Lindstrom taraması var. Bu yöntem, her bir düğüme (ebeveyn ve iki çocuk) dahil olan üç göstergeyi "döndürür". İyi bir ön sipariş veya sipariş sonrası algoritmaları gerçekleştirmek istiyorsanız, düğüm başına ekstra bitlere ihtiyacınız vardır. Sadece tüm düğümleri ziyaret etmek istiyorsanız (üç kez, ancak hangi ziyareti gerçekleştirdiğinizi asla bilemezsiniz), o zaman bitler olmadan yapılabilir.

G. Lindstrom: Yığın veya etiket biti olmayan liste yapılarının taranması. IPL 2 (1973) 47-51. DOI: 10.1016 / 0020-0190 (73) 90012-4

Belki de en basit yol Robson'un bir yöntemidir . Burada klasik algoritma için gereken yığın yapraklar boyunca işlenir.

JM Robson: Yardımcı yığın IPL 1 (1973) 149-152 olmadan ikili ağaçların geçişi için geliştirilmiş bir algoritma. 10,1016 / 0020-0190 (73) 90018-5

IPL = Bilgi İşleme Mektupları


Bu çözümü de seviyorum, ancak bilgisayar bilimi derslerinin ilk yılında ortaya çıkacak bir şey değil. Evet, muhtemelen profesörün kurallarına göre aldatıyor.
NL - Monica'dan özür

2
Stratejiler için bağlantılar / referanslar verebilir misiniz?
Raphael

1
Bu yöntemin asıl kötü yanı, herhangi bir zamanda birden fazla geçişin yapılamayacağıdır.
Gilles 'SO- kötü olmayı kes'

6

v


Bu, sorunu çözmek için kullanılan sorunu öneren veri yapıları profesörünün çözümüne benzer. Ayrık matematik profesörü, ebeveynlere geri işaretçiler varsa, "bunun bir ağaçtan ziyade bir grafik haline geldiğine" itiraz etti.
NL - Monica'dan

@NathanLiddle: Bu, kullanılan ağaç tanımına (vermediğiniz) bağlı olacaktır. "Gerçek dünyada", grafik teorisi, tanımladığı şeylerin elbette ağaç olmadığını söylese bile, Yuval'ın ağaç temsili makul olur.
Raphael

@Raphael Evet, orijinal profesörün gereksinimlerini karşılıyor, bu yüzden benim için kabul edilebilir bir cevap.
NL - Monica'dan

0

Benim çözümüm, ağaca kaba kuvvet uygulamak için iç içe geçmiş döngüler kullanan bir ilk yetiştirilen traversaldi. Bu hiçbir şekilde etkili değildir ve aslında ağaç gibi özyinelemeli bir veri yapısı, özyinelemeli bir geçiş için yalvarmaktadır, ancak soru, bir ağacın verimli bir şekilde geçilip geçirilemeyeceği değil, hatta mümkün olup olmadığıydı.

Pseudocode:
root = pointer root 
depth = integer 0
finished = bool false
//If we n-ary tree also track how many children have been found 
//on the node with the most children for the purposes of this psuedocode 
//we'll assume a binary tree and insert a magic number of 2 so that we 
//can use bitwise operators instead of integer division 
while(!finished)
    ++depth
    treePosition = pointer root
    finished = true;
    for i := 0..2**depth
        for j := 0..depth
            if (i & j) //bitwise operator explained below
                // if right child doesn't exist break the loop
                treePosition = treePosition.rightChild
            else
                // if left child doesn't exist break the loop
                treePosition = treePosition.leftChild
        if j has any children
            finished = false
            do anything else you want when visiting the node

İlk birkaç seviye için, gördüğünüz gibi, sözde koddaki bitsel operatör, bir ikili ağaçta sola veya sağa dönüşe karar verir:

2**1       0               1
2**2   00      01      10      11
2**3 000 001 010 011 100 101 110 111

N-ary için, 0 ile maxChildren arasında hangi yolu kullanacağınızı belirlemek için i% (maxChildren ** j) / j alırsınız.

N-ary üzerindeki her düğümde, çocuk sayısının maxChildren'den daha yüksek olup olmadığını kontrol etmeniz ve uygun şekilde güncellemeniz gerekir.


İkiliden daha fazlasını kullanmak isterseniz, sihirli 2 sayısını, gördüğü maksimum çocuklarla eşleşecek şekilde artırılacak bir değişkenle değiştirmeniz gerekir ve bitsel operatörler yerine, yükseltilen aynı değişkene bölmeniz gerekir. bulunduğunuz ağacın derinliğinin gücü.
NL - Monica'dan özür dileyin

O(1)O(lgn)O(1)O(n)O(1)Örneğin "depthint
DW

Sorunu ortaya koyan profesör DW, problem üzerinde bu kısıtlamayı koymadı ve ayrık matematik profesörü ile tartışmam hakkında beni çok rahatsız eden şey, ASLA bir ağacı özyineleme yapmadan, hatta istiflemenin bile mümkün olduğunu kabul etmiyor veya kuyruk, maliyeti ne olursa olsun. Çözümümün gösterdiği tek şey, yığın, kuyruk vb. İçin seçenekleri kaldırsanız bile yinelemeli olarak yapılabilecek her şeyi yinelemeli olarak yapabilmenizdir.
NL - Monica'dan

O (1) ek alan olmadan çözülemez olduğunu söylemek bir şeydir, sorunu özyineleme, yığın veya kuyruk olmadan çözülemez olarak bildirmek başka bir şeydir. Ve aslında, kodumu gördükten sonra, ayrık matematik profesörü hala bu noktayı kabul etmeyecekti çünkü ilk döngüde "i" bir kuyruğun yerini aldı. Sert kafalılar için bu nasıl?
NL - Monica'dan özür

1
idepthdepthΘ(n)Θ(n)iO(1)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.