Implementering av en iterator over et binært søketre

stemmer
30

Jeg har vært koding opp en haug med forskjellige binært søketre implementeringer nylig (AVL, spriker, treap) og er nysgjerrig på om det er en spesielt god måte å skrive en iterator å krysse disse strukturene. Den løsningen jeg har brukt akkurat nå er å få hver node i BST butikken pekere til neste og forrige elementer i treet, noe som reduserer iterasjon til en standard knyttet listen iterasjon. Men jeg er ikke veldig fornøyd med dette svaret. Det øker plassbruken til hver node med to pekere (neste og forrige), og i en viss forstand er det bare juks.

Jeg vet om en måte å bygge et binært søketre iterator som bruker O (h) hjelpe lagringsplass (der h er høyden av treet) ved hjelp av en stabel for å holde styr på grense noder for å utforske senere, men jeg er har motstått koding dette opp på grunn av minnebruk. Jeg håpet det er noen måte å bygge en iterator som bare bruker konstant plass.

Mitt spørsmål er dette - er det en måte å utforme en iterator over et binært søketre med følgende egenskaper?

  1. Elementene er besøkt i stigende rekkefølge (dvs. en inorder traversering)
  2. next()og hasNext()spørringer kjøres i O (1) tid.
  3. Hukommelse er O (1)

For å gjøre det lettere, er det fint hvis du antar at trestrukturen ikke endrer form under iterasjon (dvs. ingen innsett, slettinger eller rotasjoner), men det ville være veldig kult hvis det var en løsning som faktisk kunne håndtere dette.

Publisert på 03/01/2011 klokken 01:54
kilden bruker
På andre språk...                            


8 svar

stemmer
1

Tre traversering , fra Wikipedia:

Alle prøve implementeringer vil kreve kallstakken plass proporsjonal med høyden av treet. I en dårlig balansert treet, kan dette være ganske betydelig.

Vi kan fjerne stabelen kravet ved å opprettholde moder pekere i hver node, eller ved å tre treet. I tilfellet med anvendelse av tråder, vil dette muliggjøre sterkt forbedret inorder traversering, selv om å hente foreldrenoden som kreves for preorder og Postorder gjennomsøking skal være langsommere enn en enkel stabel basert algoritme.

I artikkelen er det noen pseudokode for iterasjon med O (1) tilstand, som lett kan tilpasses i en iteratorblokk.

Svarte 03/01/2011 kl. 02:09
kilden bruker

stemmer
30

De enkleste mulig iteratorknapper lagrer sist sett nøkkelen, og deretter på den neste iterasjon, søker treet for minste øvre grense for den tasten. Iterasjon er O (log n). Dette har fordelen av å være svært enkel. Hvis tastene er små så iteratorer er også små. Selvfølgelig har den den ulempe at den er en relativt langsom måte å iterere gjennom treet. Det vil heller ikke jobbe for ikke-unike sekvenser.

Noen trær bruker nøyaktig gjennomføring du allerede bruker, fordi det er viktig for deres spesifikke bruken at skanningen er svært rask. Hvis antall nøkler i hver node er stor, da straffen for lagring søsken pekere ikke er for tung. De fleste B-trær bruker denne metoden.

mange søke treet implementeringer holde en overordnet peker på hver node for å forenkle andre operasjoner. Hvis du har det, så kan du bruke en enkel peker til sist sett node som iterator tilstand. ved hver iterasjon, ser du for det neste barnet i det siste sett node foreldre. hvis det ikke er flere søsken, så du går opp ett nivå.

Hvis ingen av disse teknikkene passer deg, kan du bruke en stabel av noder, lagret i iterator. Dette serverer den samme funksjon som funksjon kallstakken ved gjentakelse gjennom søketreet som normalt, men i stedet for looping gjennom søsken og recursing på barn, presse deg barn på stakken og tilbake hver påfølgende søsken.

Svarte 03/01/2011 kl. 02:25
kilden bruker

stemmer
3

Ok, jeg vet dette er gammel, men jeg ble spurt om dette i et intervju med Microsoft en stund tilbake, og jeg bestemte meg for å jobbe med det litt. Jeg har testet dette, og det fungerer ganske bra.

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

};
Svarte 20/10/2012 kl. 04:53
kilden bruker

stemmer
15

Som TokenMacGuy nevnt kan du bruke en stabel lagret i iterator. Her er en rask testet implementering av dette i Java:

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

Annen variant ville være å traversere treet på byggetiden og lagre traversering inn i en liste. Du kan bruke listen iterator etterpå.

Svarte 30/07/2013 kl. 23:21
kilden bruker

stemmer
0

Hva med å bruke en dybde første søketeknikk. Iteratoren objekt bare må ha en stabel av de allerede besøkte noder.

Svarte 21/05/2014 kl. 21:02
kilden bruker

stemmer
0

Hvis du bruker stabelen, du bare oppnå "ekstra minnebruk O (h), er h høyden på treet". Men hvis du ønsker å bruke bare O (1) ekstra minne, må du registrere Her er analysen: - Hvis gjeldende node har riktig barn: finne min av riktig undertreet - Det nåværende node har ingen rett barn, du trenger å lete etter den fra roten, og holde oppdatere det laveste stamfar, som er det laveste siden node

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;
           }
       }
Svarte 24/04/2015 kl. 23:41
kilden bruker

stemmer
0

Bruk O (1) mellomrom, noe som betyr at vi ikke vil bruke O (h) stabel.

Å begynne:

  1. hasNext ()? current.val <= endNode.val for å kontrollere om treet er fullstendig traversert.

  2. Finn min via venstre mest: Vi kan alwasy lete etter lengst til venstre for å finne neste minimumsverdi.

  3. Når venstre mest min er avmerket (name it current). Neste min vil være 2 tilfeller: Hvis current.right = null, kan vi holde utkikk etter current.right venstre-mest barne, som neste min!. Eller må vi se bakover for foreldre. Bruk binært søketre for å finne aktuelle overordnede node.

Merk : når du gjør binære søk etter forelder, sørg for at den tilfredsstiller parent.left = strøm.

Fordi: Hvis parent.right == strøm, har det overordnede must blitt besøkt før. I binært søketre, vi vet at parent.val <parent.right.val. Vi må hoppe over dette spesielle tilfellet, siden det fører til ifinite loop.

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;
    }
}
Svarte 27/01/2016 kl. 16:42
kilden bruker

stemmer
0

Per definisjon er det ikke mulig for neste () og hasNext () for å kjøre i O (1) tid. Når du ser på en bestemt node i et BST, har du ingen anelse om høyde og struktur av de andre nodene er derfor du kan ikke bare "hoppe" til riktig neste node.

Imidlertid kan den plass kompleksitet reduseres til O (1) (med unntak av minnet for den BST seg selv). Her er måten jeg ville gjøre det i 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;
}

Trikset er å ha både en forelder link, og besøkte flagg for hver node. Etter min mening kan vi hevde at dette ikke er mer plassbruken, det er rett og slett en del av nettverksstrukturen. Og selvsagt må iter_next () kalles uten staten trestrukturen endring (selvfølgelig), men også at den "besøkt" flagg ikke endre verdier.

Her er tester funksjon som kaller iter_next () og skriver verdien hver gang for dette treet:

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

Som vil skrive ut verdiene i sortert rekkefølge:

15 16 20 21 25 27 40 62 71 
Svarte 13/02/2016 kl. 06:56
kilden bruker

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