Provádění iterátor nad binárního vyhledávacího stromu

hlasů
30

Byl jsem kódování vytvořit spoustu různých implementacích binární vyhledávací strom v poslední době (AVL, rozevírá, treap) a jsem zvědavý, jestli je to zejména „dobrý“ způsob, jak psát iterátor přetínat tyto struktury. Řešení Použil jsem právě teď je, aby každý uzel v BST ukládání ukazatele na další a předchozí prvků ve stromové struktuře, což snižuje iteraci do standardního připojený seznam iteraci. Nicméně, já nejsem spokojen s touto odpovědí. To zvyšuje využití prostoru každého uzlu pomocí dvou ukazatelů (další a předchozí), a v jistém smyslu je to jen podvod.

Znám způsob, jak budovat binární vyhledávací strom iterátor, který používá O (H) pomocného úložného prostoru (kde h je výška stromu) pomocí stoh sledovat hraničních uzlů, aby prozkoumala později, ale já jsem bránil toto kódování, protože k využití paměti. Doufal jsem, že tam je nějaký způsob, jak vybudovat iterátor, který používá pouze konstantní prostor.

Moje otázka zní - existuje způsob, jak navrhnout iterátor nad binárního vyhledávacího stromu s těmito vlastnostmi?

  1. Prvky jsou navštěvovány ve vzestupném pořadí (tj nezbytného průchod)
  2. next()a hasNext()spuštění dotazů v O (1) času.
  3. Využití paměti je O (1)

Aby to bylo jednodušší, je to v pořádku, pokud předpokládáme, že ve stromové struktuře se nemění tvar během iteraci (tj žádné inzerce, delece nebo otáčky), ale to by bylo opravdu skvělé, kdyby tam byl řešení, které by skutečně mohla zvládnout.

Položena 03/01/2011 v 02:54
zdroj uživatelem
V jiných jazycích...                            


8 odpovědí

hlasů
1

Strom traversal z Wikipedie:

Všechny implementace ukázkových bude vyžadovat zásobníku volání prostor úměrný výšce stromu. Ve špatně vyvážený strom, to může být docela značná.

Můžeme odstranit požadavek na zásobník o udržení nadřazené ukazatelů v každém uzlu nebo závitů strom. V případě použití nití, bude to umožňují výrazně zlepšila nezbytného průchod, když načítání na rodičovský uzel potřebný pro předobjednání a postorder průchod bude pomalejší, než algoritmus jednoduchý zásobník na bázi.

V článku je nějaký pseudokód pro iteraci s O (1), stav, který může být snadno uzpůsoben pro iterátor.

Odpovězeno 03/01/2011 v 03:09
zdroj uživatelem

hlasů
30

Nejjednodušší možné iterátor ukládá naposledy klíč, a poté na další iteraci, prohledává strom pro nejmenší horní mez pro tento klíč. Iterace je O (log n). To má tu výhodu, že jsou velmi jednoduché. Jestliže nějaké klávesy jsou malé pak iterátory jsou také malé. Samozřejmě to má tu nevýhodu, že je relativně pomalá cesta iterace stromu. To také nebude pracovat pro non-jedinečných sekvencí.

Některé stromy používat přesně realizaci již používáte, protože je to důležité pro jejich specifické použití, který skenování je velmi rychlý. V případě, že počet klíčů v každém uzlu je velká, pak trest ukládání sourozenec ukazatelů není příliš obtížné. Většina B-Stromy použití této metody.

mnoho implementací vyhledávacího stromu udržet mateřský ukazatel na každém uzlu pro zjednodušení dalších operací. Máte-li to, pak můžete využít jednoduchý ukazatel naposledy viděn uzlu jako stav vašeho iterátor je. při každé iteraci se podíváte na další dítě v nadřazeného naposledy viděn uzlu. pokud nejsou k dispozici žádné další sourozence, pak si jít nahoru ještě jednu úroveň.

Pokud žádný z těchto technik vám bude vyhovovat, můžete použít balík uzlů, uložený v iterátoru. To slouží stejnou funkci jako funkce zásobník volání při iterace vyhledávacím stromem jako obvykle, ale místo průchozí sourozenců a rekurzi na děti, budete tlačit děti do zásobníku a vrátit každého dalšího sourozence.

Odpovězeno 03/01/2011 v 03:25
zdroj uživatelem

hlasů
3

Ok, vím, že to je starý, ale já jsem byl požádán to v rozhovoru s Microsoftem, zatímco zadní a rozhodl jsem se pracovat na tom trochu. Testoval jsem to a funguje to docela dobře.

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;
    }

};
Odpovězeno 20/10/2012 v 05:53
zdroj uživatelem

hlasů
15

Jak TokenMacGuy bylo zmíněno, můžete použít balík uložený v iterátoru. Zde je rychlý testovat provádění tohoto v Javě:

/**
 * 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);
    }
}

Ostatní variace bude procházet strom v době výstavby a uložit Traversal do seznamu. Můžete použít seznam iterátor později.

Odpovězeno 31/07/2013 v 00:21
zdroj uživatelem

hlasů
0

Co používáte prohledávání do hloubky techniku. Objekt Iterátor prostě musí mít stoh již navštívených uzlů.

Odpovězeno 21/05/2014 v 22:02
zdroj uživatelem

hlasů
0

Používáte-li zásobník, pouze dosáhnout „Extra využití paměti O (h), h je výška stromu“. Nicméně, pokud chcete použít pouze O (1) další paměť, je třeba zaznamenat Zde jsou analýza: - Je-li aktuální uzel má právo dítě: najít min pravého dílčího stromu - to aktuální uzel nemá právo dítěte, budete potřebovat podívat se na to z kořene, a zachovat aktualizaci je to nejnižší předchůdce, což je nejnižší úrovni další uzel

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;
           }
       }
Odpovězeno 24/04/2015 v 23:41
zdroj uživatelem

hlasů
0

Použití O (1) prostor, což znamená, že se nepoužívá O (h) zásobníku.

Začít:

  1. hasNext ()? current.val <= endNode.val zkontrolovat, zda je strom plně provázán.

  2. Najít min přes nejvíce vlevo: Můžeme alwasy hledat nejvíce vlevo najít další minimální hodnotu.

  3. Poté, co nejvíce vlevo min je kontrolována (pojmenovat current). Příští min bude 2 případy: Pokud current.right = null, můžeme dál hledat nejvíce vlevo dítě current.right, jak je další minutu!. Nebo, musíme se podívat zpět na rodiče. Použijte strom binárního vyhledávání najít aktuální je nadřazený uzel.

Poznámka : Při provádění binární vyhledávání pro rodiče, ujistěte se, že splňuje parent.left = proud.

Protože: Pokud parent.right == proudu, který musí rodič již navštívili. V binárním vyhledávacím stromu, víme, že parent.val <parent.right.val. Musíme přeskočit tento zvláštní případ, protože to vede k ifinite smyčku.

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;
    }
}
Odpovězeno 27/01/2016 v 16:42
zdroj uživatelem

hlasů
0

Podle definice, že není možné, aby další () a hasNext () pro spuštění v O (1) času. Když se díváte na konkrétním uzlu v BST, nemáte tušení, výška a struktury ostatních uzlů, takže je můžete nejen „skok“ do správného dalšího uzlu.

Nicméně složitost prostor může být snížena na O (1), (s výjimkou paměti pro BST samotné). Zde je způsob, jak bych to udělat v C:

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;
}

Trik je v tom mají oba mateřský odkaz a navštívil příznak pro každý uzel. Podle mého názoru, můžeme tvrdit, že to není další využití místa, to je prostě součástí struktury uzlu. A samozřejmě, iter_next () musí být volána, aniž by stát stromové struktury měnící se (samozřejmě), ale také to, že „navštívil“ flags nemění hodnoty.

Zde je funkce testeru, který iter_next () volání a Vytiskne hodnotu pokaždé na této větvi:

                  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;
}

Která se bude tisknout hodnoty v seřazeném pořadí:

15 16 20 21 25 27 40 62 71 
Odpovězeno 13/02/2016 v 06:56
zdroj uživatelem

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