İkili arama ağacı üzerinden bir yineleyici uygulama

oy
30

Geçenlerde (AVL, yayvan, treap) farklı ikili arama ağacı uygulamaları bir demet kadar kodlama ve bu yapıları çapraz için bir yineleyici yazmak için özellikle iyi yolu varsa merak ediyorum oldum. Şu an kullandığım çözüm standart bağlantılı liste yineleme için iterasyon azaltır ağacın sonraki ve önceki öğelerine BST mağaza işaretçiler her düğümü sahip olmaktır. Ancak, ben bu cevap gerçekten tatmin olmadım. İki işaretçiler (sonraki ve önceki) tarafından her düğümün alanı kullanımını arttırır ve bir bakıma sadece aldatıyor.

Ben daha sonra keşfetmek için sınır düğümler takip etmek için bir yığın kullanarak O (h ağacının yüksekliğidir) (h) Yardımcı depolama alanı kullanan bir ikili arama ağacı yineleyici kurmasının bir yol biliyor ama ben çünkü bellek kullanımı bu kadar kodlama direnmiş ettik. Sadece sabit alan kullanan bir yineleyici oluşturmak için birkaç yol var umuyordum.

aşağıdaki özelliklere sahip bir ikili arama ağacına üzerinde bir yineleyici tasarlamak için bir yol yoktur - Benim sorum şudur?

  1. Elemanlar (yani bir inorder geçişi) küçükten büyüğe ziyaret edilmektedir
  2. next()ve hasNext()sorgular O (1) zaman içinde çalışır.
  3. Hafıza kullanımı O olduğu (1)

kolaylaştırmak için, ağaç yapısı yineleme (yani hiçbir ekleme, silme veya dönmeler) sırasında şekil değiştirme olmadığını varsayarsak gayet ama aslında bu ele verebilir bir çözüm olsaydı gerçekten harika olurdu.

Oluştur 03/01/2011 saat 02:54
kaynak kullanıcı
Diğer dillerde...                            


8 cevaplar

oy
1

Ağaç dolaşma Wikipedia'dan:

Tüm örnek uygulamaları ağacın yüksekliği ile orantılı çağrı yığını alanı gerektirecektir. Bir kötü dengeli ağacında, bu oldukça önemli olabilir.

Biz, ya da ağaç geçirilerek her düğümünde ebeveyn işaretçileri sürdürerek yığın şartı kaldırabilirsiniz. Basit bir yığın göre algoritma daha yavaş olacaktır Ön sipariş ve postorder geçişi için gerekli olan ana düğüm alınırken, ancak iplikler kullanılarak durumunda, bu, büyük ölçüde geçişi olgusudur geliştirilmiş sağlayacaktır.

makalede kolayca bir yineleyici uyarlanabilir O (1) durum ile yineleme için bir yalancı kod bulunmaktadır.

Cevap 03/01/2011 saat 03:09
kaynak kullanıcı

oy
30

mümkün olan en basit yineleyici depolar son görülme anahtar ve sonra bir sonraki denemede, bu anahtarın en küçük alt sınır için ağaç arar. Yineleme O (log n) 'dir. Bu çok basit olma avantajına sahiptir. tuşları küçükse o zaman yineleyiciler da küçüktür. Tabi ki ağacın yineleme nispeten yavaş bir yol olması bakımından avantajlı. Aynı zamanda benzersiz olmayan diziler için çalışmaz.

o tarama çok hızlı kendi özel kullanımı için önemli çünkü bazı ağaçlar, zaten kullanmakta tam uygulanması kullanın. her düğüm anahtarların sayısı büyükse, o zaman kardeş işaretçileri depolamak ceza çok külfetli değildir. En çok B-Ağaçları bu yöntemi kullanır.

Birçok arama ağacı uygulamaları diğer işlemleri basitleştirmek için her düğümde bir ana işaretçisi tutun. Bunu varsa, o zaman yineleyici devlet olarak son görülme düğüme basit işaretçi kullanabilirsiniz. her tekrarda, son görüldüğü düğümün ebeveyni sonraki çocuk için arayın. Artık kardeşleri varsa, o zaman bir daha üst seviyeye gidin.

Bu tekniklerin hiçbiri size uygun değilse, yineleyici saklanan düğümlerin bir yığın kullanabilirsiniz. Normal olarak arama ağacının yineleme, ancak bunun yerine kardeşler arasında döngü ve çocuklar üzerindeki recursing yerine, yığının üstüne çocukları itmek ve her ardışık kardeş döndüğünüzde Bu işlev çağrısı yığını olarak aynı işlevi görür.

Cevap 03/01/2011 saat 03:25
kaynak kullanıcı

oy
3

Tamam, bu eski olduğunu biliyorum ama ben bir süre önce Microsoft ile bir röportajda bu istendi ve ben üzerinde biraz çalışmaya karar verdik. Bunu test ettik ve oldukça iyi çalışıyor.

template <typename E>
class BSTIterator
{  
  BSTNode<E> * m_curNode;
  std::stack<BSTNode<E>*> m_recurseIter;

public:
    BSTIterator( BSTNode<E> * binTree )
    {       
        BSTNode<E>* root = binTree;

        while(root != NULL)
        {
            m_recurseIter.push(root);
            root = root->GetLeft();
        }

        if(m_recurseIter.size() > 0)
        {
            m_curNode = m_recurseIter.top();
            m_recurseIter.pop();
        }
        else
            m_curNode = NULL;
    }

    BSTNode<E> & operator*() { return *m_curNode; }

    bool operator==(const BSTIterator<E>& other)
    {
        return m_curNode == other.m_curNode;
    }

    bool operator!=(const BSTIterator<E>& other)
    {
        return !(*this == other);
    }

    BSTIterator<E> & operator++() 
    { 
        if(m_curNode->GetRight())
        {
            m_recurseIter.push(m_curNode->GetRight());

            if(m_curNode->GetRight()->GetLeft())
                m_recurseIter.push(m_curNode->GetRight()->GetLeft());
        }

        if( m_recurseIter.size() == 0)
        {
            m_curNode = NULL;
            return *this;
        }       

        m_curNode = m_recurseIter.top();
        m_recurseIter.pop();

        return *this;       
    }

    BSTIterator<E> operator++ ( int )
    {
        BSTIterator<E> cpy = *this;     

        if(m_curNode->GetRight())
        {
            m_recurseIter.push(m_curNode->GetRight());

            if(m_curNode->GetRight()->GetLeft())
                m_recurseIter.push(m_curNode->GetRight()->GetLeft());
        }

        if( m_recurseIter.size() == 0)
        {
            m_curNode = NULL;
            return *this;
        }       

        m_curNode = m_recurseIter.top();
        m_recurseIter.pop();

        return cpy;
    }

};
Cevap 20/10/2012 saat 05:53
kaynak kullanıcı

oy
15

TokenMacGuy belirtildiği gibi yineleyici saklanan bir yığın kullanabilirsiniz. İşte Java bu hızlı test uygulama görebilirsiniz:

/**
 * An iterator that iterates through a tree using in-order tree traversal
 * allowing a sorted sequence.
 *
 */
public class Iterator {

    private Stack<Node> stack = new Stack<>();
    private Node current;

    private Iterator(Node argRoot) {
        current = argRoot;
    }

    public Node next() {
        while (current != null) {
            stack.push(current);
            current = current.left;
        }

        current = stack.pop();
        Node node = current;
        current = current.right;

        return node;
    }

    public boolean hasNext() {
        return (!stack.isEmpty() || current != null);
    }

    public static Iterator iterator(Node root) {
        return new Iterator(root);
    }
}

Diğer varyasyon inşaat sırasında ağaç travers ve bir liste halinde geçişi kaydetmek olacaktır. Sonradan liste yineleyici kullanabilirsiniz.

Cevap 31/07/2013 saat 00:21
kaynak kullanıcı

oy
0

Ne bir derinlik ilk arama tekniği kullanılarak yaklaşık. yineleyici nesne sadece önceden ziyaret düğümler bir yığın olması gerekir.

Cevap 21/05/2014 saat 22:02
kaynak kullanıcı

oy
0

Eğer yığın kullanırsanız, sadece "Ekstra bellek kullanımı O (h) h ağacının yüksekliğidir" ulaşırlar. Yalnızca O (1) ekstra bellek kullanmak istiyorsanız Ancak, analiz İşte kaydetmek gerekir: - Geçerli düğümün sağ çocuğu varsa: sağ alt ağacın min bulmak - Mevcut düğüm hakkı çocuğu vardır, ihtiyacınız kökünden bunun için bakmak ve onun en düşük sonraki düğümdür düşük ata, var güncellenmesi tutmak

public class Solution {
           //@param root: The root of binary tree.

           TreeNode current;
           TreeNode root;
           TreeNode rightMost;
           public Solution(TreeNode root) {

               if(root==null) return;
                this.root = root;
                current = findMin(root);
                rightMost = findMax(root);
           }

           //@return: True if there has next node, or false
           public boolean hasNext() {

           if(current!=null && rightMost!=null && current.val<=rightMost.val)    return true; 
        else return false;
           }
           //O(1) memory.
           public TreeNode next() {
                //1. if current has right child: find min of right sub tree
                TreeNode tep = current;
                current = updateNext();
                return tep;
            }
            public TreeNode updateNext(){
                if(!hasNext()) return null;
                 if(current.right!=null) return findMin(current.right);
                //2. current has no right child
                //if cur < root , go left; otherwise, go right

                    int curVal = current.val;
                    TreeNode post = null;
                    TreeNode tepRoot = root;
                    while(tepRoot!=null){
                      if(curVal<tepRoot.val){
                          post = tepRoot;
                          tepRoot = tepRoot.left;
                      }else if(curVal>tepRoot.val){
                          tepRoot = tepRoot.right;
                      }else {
                          current = post;
                          break;
                      }
                    }
                    return post;

            }

           public TreeNode findMin(TreeNode node){
               while(node.left!=null){
                   node = node.left;
               }
               return node;
           }

            public TreeNode findMax(TreeNode node){
               while(node.right!=null){
                   node = node.right;
               }
               return node;
           }
       }
Cevap 24/04/2015 saat 23:41
kaynak kullanıcı

oy
0

Biz O, (H) yığın kullanmaz anlamına O (1) alanı, kullanın.

Başlamak:

  1. hasNext ()? Ağaç tamamen geçtiği takdirde current.val <= endNode.val kontrol etmek.

  2. Bul dk aracılığıyla en soldaki: Biz Geniune en soldaki bulmak için bir sonraki minimum değerden arayabilirsiniz.

  3. Bir kez en soldaki dk olduğu kontrol (Adını current). Sonraki dk 2 olgu olacaktır: current.right olursa = null, önümüzdeki dk olarak, current.right en en soldaki çocuk için aramaya devam edebilir!. Ya da, ebeveyn için geriye bakmak gerekir. Mevcut ana düğüm bulmak için ikili arama ağacı kullanın.

Not : ebeveyn için ikili arama yaparken, bu parent.left = akımını karşılayan emin olun.

Çünkü: parent.right == akım, o ana zorunluluk önce ziyaret edilmiştir. ikili arama ağacında, biz parent.val <parent.right.val biliyoruz. o döngü ifinite yol açar beri, bu özel durum atlamak gerekiyor.

public class BSTIterator {
    public TreeNode root;
    public TreeNode current;
    public TreeNode endNode;
    //@param root: The root of binary tree.
    public BSTIterator(TreeNode root) {
        if (root == null) {
            return;
        }
        this.root = root;
        this.current = root;
        this.endNode = root;

        while (endNode != null && endNode.right != null) {
            endNode = endNode.right;
        }
        while (current != null && current.left != null) {
            current = current.left;
        }
    }

    //@return: True if there has next node, or false
    public boolean hasNext() {
        return current != null && current.val <= endNode.val;
    }

    //@return: return next node
    public TreeNode next() {
        TreeNode rst = current;
        //current node has right child
        if (current.right != null) {
            current = current.right;
            while (current.left != null) {
                current = current.left;
            }
        } else {//Current node does not have right child.
            current = findParent();
        }
        return rst;
    }

    //Find current's parent, where parent.left == current.
    public TreeNode findParent(){
        TreeNode node = root;
        TreeNode parent = null;
        int val = current.val;
        if (val == endNode.val) {
            return null;
        }
        while (node != null) {
            if (val < node.val) {
                parent = node;
                node = node.left;
            } else if (val > node.val) {
                node = node.right;
            } else {//node.val == current.val
                break;
            }
        }
        return parent;
    }
}
Cevap 27/01/2016 saat 16:42
kaynak kullanıcı

oy
0

Tanım olarak, bu (1) zaman O'da çalıştırmak için () aşağıdaki () ve hasNext için mümkün değildir. Eğer bir BST belirli bir düğüm bakıyor, sen, diğer düğümlerin yüksekliği ve yapısı hakkında hiçbir fikrimiz yok bu nedenle doğru bir sonraki düğüme değil, sadece "atlama" can.

Bununla birlikte, boşluk karmaşıklığı (1) (BST kendisi için bellek hariç), O indirgenebilir. İşte C yapacağını yoludur:

struct node{
    int value;
    struct node *left, *right, *parent;
    int visited;
};

struct node* iter_next(struct node* node){
    struct node* rightResult = NULL;

    if(node==NULL)
        return NULL;

    while(node->left && !(node->left->visited))
        node = node->left;

    if(!(node->visited))
        return node;

    //move right
    rightResult = iter_next(node->right);

    if(rightResult)
        return rightResult;

    while(node && node->visited)
        node = node->parent;

    return node;
}

hüner bir üst bağlantısını ve her düğüm için bir ziyaret bayrağı hem sahip olmaktır. Bence, biz sadece düğüm yapının parçasıdır, bu ek alanı kullanımı olmadığını iddia edebilir. Ve tabii ki, iter_next () (elbette) değiştirerek ağaç yapısının devlet olmadan çağrılması gerekir, ama aynı zamanda "ziyaret" olduğunu bayraklar değerleri değişmez.

İşte iter_next () çağırır ve değerini bu ağaç için her zaman yazdırır tester fonksiyonudur:

                  27
               /      \
              20      62
             /  \    /  \
            15  25  40  71
             \  /
             16 21

int main(){

    //right root subtree
    struct node node40 = {40, NULL, NULL, NULL, 0};
    struct node node71 = {71, NULL, NULL, NULL, 0};
    struct node node62 = {62, &node40, &node71, NULL, 0};

    //left root subtree
    struct node node16 = {16, NULL, NULL, NULL, 0};
    struct node node21 = {21, NULL, NULL, NULL, 0};
    struct node node15 = {15, NULL, &node16, NULL, 0};
    struct node node25 = {25, &node21, NULL, NULL, 0};
    struct node node20 = {20, &node15, &node25, NULL, 0};

    //root
    struct node node27 = {27, &node20, &node62, NULL, 0};

    //set parents
    node16.parent = &node15;
    node21.parent = &node25;
    node15.parent = &node20;
    node25.parent = &node20;
    node20.parent = &node27;
    node40.parent = &node62;
    node71.parent = &node62;
    node62.parent = &node27;

    struct node *iter_node = &node27;

    while((iter_node = iter_next(iter_node)) != NULL){
        printf("%d ", iter_node->value);
        iter_node->visited = 1;
    }
    printf("\n");
    return 1;
}

sıralanmış sırayla değerleri yazdırır Hangi:

15 16 20 21 25 27 40 62 71 
Cevap 13/02/2016 saat 06:56
kaynak kullanıcı

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more