Özyineleme kullanmadan bir ağaçta nasıl gezinebilirim?


19

Bellek düğüm ağacında çok büyük var ve ağacı geçmem gerekiyor. Her alt düğümün döndürülen değerlerini üst düğümüne geçirme. Bu, tüm düğümlerin verileri kök düğüme kadar kabarcıklı hale gelene kadar yapılmalıdır.

Geçiş böyle çalışır.

private Data Execute(Node pNode)
{
    Data[] values = new Data[pNode.Children.Count];
    for(int i=0; i < pNode.Children.Count; i++)
    {
        values[i] = Execute(pNode.Children[i]);  // recursive
    }
    return pNode.Process(values);
}

public void Start(Node pRoot)
{
    Data result = Execute(pRoot);
}

Bu iyi çalışıyor, ancak çağrı yığınının düğüm ağacının boyutunu sınırlandırmasından endişe ediyorum.

Yinelemeli çağrı yapılmaması için kod nasıl yeniden yazılabilir Execute?


8
Düğümleri takip etmek için kendi yığınınızı korumanız veya ağacın şeklini değiştirmeniz gerekir. Bkz. Stackoverflow.com/q/5496464 ve stackoverflow.com/q/4581576
Robert Harvey

1
Bu Google Arama'da , özellikle Morris Traversal'da da çok yardım buldum .
Robert Harvey

@RobertHarvey teşekkürler Rob, bunun hangi şartlar altında olacağını bilmiyordum.
Reactgular

2
Matematiği yaptıysanız hafıza gereksinimlerine şaşırabilirsiniz. Örneğin, mükemmel dengelenmiş bir teranode ikili ağaç sadece 40 giriş derinliğinde bir desteğe ihtiyaç duyar.
Karl Bielefeldt

@KarlBielefeldt Bu, ağacın mükemmel bir şekilde dengelendiğini varsayar. Bazen ağaçlar modelleme gereken değildir dengeli ve bu durumda öyle çok öfkelenmek kolay.
14'te

Yanıtlar:


27

Özyineleme kullanmayan genel amaçlı bir ağaç geçiş uygulaması:

public static IEnumerable<T> Traverse<T>(T item, Func<T, IEnumerable<T>> childSelector)
{
    var stack = new Stack<T>();
    stack.Push(item);
    while (stack.Any())
    {
        var next = stack.Pop();
        yield return next;
        foreach (var child in childSelector(next))
            stack.Push(child);
    }
}

Sizin durumunuzda daha sonra şu şekilde arayabilirsiniz:

IEnumerable<Node> allNodes = Traverse(pRoot, node => node.Children);

Önce derinlik yerine nefes almak için a Queueyerine bir Stackarama yapın. PriorityQueueEn iyi ilk arama için a tuşunu kullanın .


Bunun sadece ağacı bir koleksiyon haline getireceğini düşünerek haklı mıyım?
Reactgular

1
@ MathewFoscarini Evet, amacı bu. Tabii ki, mutlaka gerçek bir koleksiyon haline getirilmesi gerekmez. Sadece bir sekans. Tüm veri setini belleğe çekmeye gerek kalmadan veri akışı için tekrarlayabilirsiniz.
14'te

Bunun sorunu çözdüğünü sanmıyorum.
Reactgular

4
Sadece arama gibi bağımsız işlemler gerçekleştiren grafiğin üzerinden geçmiyor, alt düğümlerdeki verileri topluyor. Ağacın düzleştirilmesi, kümelenmeyi gerçekleştirmek için ihtiyaç duyduğu yapı bilgisini yok eder.
Karl Bielefeldt

1
FYI Bence bu soruda googling yapan çoğu insanın aradığı doğru cevap bu. +1
Anders Arpi

4

Önceden ağacınızın derinliği için bir tahmininiz varsa, belki de kasanızın yığın boyutunu uyarlaması yeterlidir? Sürüm 2.0'dan beri C # 'da, yeni bir iş parçacığı başlattığınızda bu mümkündür, buraya bakın:

http://www.atalasoft.com/cs/blogs/rickm/archive/2008/04/22/increasing-the-size-of-your-stack-net-memory-management-part-3.aspx

Bu şekilde, daha karmaşık bir şey uygulamak zorunda kalmadan, yinelemeli kodunuzu koruyabilirsiniz. Tabii ki, kendi yığını ile özyinelemeli olmayan bir çözüm oluşturmak daha fazla zaman ve bellek tasarruflu olabilir, ancak kodun şimdilik olduğu kadar basit olmayacağından eminim.


Kısa bir test yaptım. Makinemde stackoverflow'a ulaşmadan önce 14000 özyinelemeli arama yapabilirim. Ağaç dengelenirse, 4 milyar düğümü saklamak için sadece 32 çağrı gerekir. Her düğüm 1 bayt ise (olmayacak) 32 yükseklikte dengeli bir ağaç saklamak 4 GB koç alacaktı.
Esben Skov Pedersen

Yığında 14000 çağrının hepsini kullanacaktık. Her bir düğüm bir baytsa (ki olmayacak) ağaç 2.6x10 ^ 4214 bayt alacaktı
Esben Skov Pedersen

-3

Özyineleme kullanmadan veri biçiminde bir ağaç yapısında gezinebilirsiniz - dilinizin sağladığı yığın çerçevelerini ve işlev çağrılarını kullanmazsanız, temel olarak kendi yığın ve işlev çağrılarınızı programlamanız gerekir ve olası bunu yapmak için yönetmek içinde derleyici yazarlar program üzerinde aday olacağını makinede mi daha daha verimli bir şekilde bir şekilde dile.

Bu nedenle, kaynak sınırlarına girme korkusu nedeniyle özyinelemeden kaçınmak genellikle yanlış yönlendirilir. Emin olmak için, erken kaynak optimizasyonu her zaman yanlış yönlendirilir, ancak bu durumda, bellek kullanımının darboğaz olduğunu ölçüp onaylasanız bile, muhtemelen seviyesine düşmeden onu geliştiremezsiniz. derleyici yazarı.


2
Bu sadece yanlış. Bir ağacı özyineleme kullanmadan geçmek kesinlikle mümkündür. Hatta zor değil . Ayrıca, sadece belirli geçişiniz için ihtiyacınız olduğundan emin olduğunuz kadar açık bilgi yığınına ekleyebileceğiniz için, daha verimli, oldukça önemsiz bir şekilde de yapabilirsiniz. vakalar.
14'te

2
Bu tartışma burada arada bir ortaya çıkıyor. Bazı posterler kendi yığınınızı özyinelememek için yuvarlamayı düşünürken, diğerleri sadece aynı şeyi açıkça çalışma zamanının örtük olarak yapacağına işaret ediyorlar. Bunun gibi tanımları tartışmanın bir anlamı yok.
Kilian Foth

O zaman özyinelemeyi nasıl tanımlarsınız? Bunu kendi tanımı içinde çağıran bir işlev olarak tanımlardım. Cevabımda göstermiş olduğum gibi, kesinlikle bunu yapmadan bir ağacı gezebilirsiniz.
14'te

2
Bu kadar yüksek bir rep skoru olan biri için aşağı oyu tıklama eylemi zevk için kötü mü? Bu web sitesinde çok nadir bir zevk.
Reactgular

2
Hadi @Mat, bu çocuk işi. Eğer çok derin bir ağaca bombalamaktan korkuyorsanız, bu makul bir endişe olabilir. Sadece söyleyebilirsin.
Mike Dunlavey
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.