Wdrożenie iterator nad wyszukiwania binarnego drzewa

głosy
30

Byłem kodowania się kilka różnych implementacjach binarne drzewo poszukiwań niedawno (AVL, splay, treap) i jestem ciekaw, czy jest jakiś szczególnie „dobry” sposób pisać iterator do przechodzenia tych struktur. Rozwiązanie Użyłem teraz ma mieć każdy węzeł w wskaźników sklepowych BST do następnych i poprzednich elementów w drzewie, co zmniejsza iteracji do standardowego linkowane listy iteracji. Jednak nie jestem zadowolony z tej odpowiedzi. Zwiększa wykorzystanie miejsca w każdym węźle przez najbliższe dwa wskaźniki (i wcześniejszych), w pewnym sensie to jest po prostu oszustwo.

Znam sposób budowania przeszukiwania binarnego drzewa iterator, który używa O (h) pomocniczy przestrzeni składowania (gdzie h jest wysokością drzewa) za pomocą stosu śledzić węzłów granicznych zbadać później, ale jestem ve opór kodowanie to się ze względu na zużycie pamięci. Miałem nadzieję, że istnieje jakiś sposób, aby zbudować iterator, który używa tylko stałą przestrzeń.

To jest moje pytanie - czy jest jakiś sposób, aby zaprojektować iterator nad wyszukiwania binarnego drzewa z następujących właściwości?

  1. Elementy są odwiedzane w kolejności rosnącej (czyli przejścia Inorder)
  2. next()i hasNext()zapytań uruchomić w O (1) czasu.
  3. Wykorzystanie pamięci O (1)

Żeby było łatwiej, to jest w porządku, jeśli przyjąć, że struktura drzewa nie zmienia kształtu podczas iteracji (to znaczy nie insercje, delecje, lub obroty), ale byłoby naprawdę super, gdyby nie było to rozwiązanie, które rzeczywiście może sobie z tym poradzić.

Utwórz 03/01/2011 o 02:54
źródło użytkownik
W innych językach...                            


8 odpowiedzi

głosy
1

Przechodzenie drzewa z Wikipedii:

Wszystkie przykładowe implementacje będzie wymagać połączenia przestrzeń stosu proporcjonalny do wysokości drzewa. W źle zbilansowanej drzewie, to może być dość znaczna.

Możemy usunąć wymóg stosu utrzymanie wskaźników dominującą w każdym węźle, lub gwintowania drzewa. W przypadku korzystania z wątków, pozwoli to na znacznie poprawiła Inorder przechodzenia, chociaż pobieranie węzeł nadrzędny wymaganego do przedsprzedaży i postorder przechodzenie będzie wolniejsze niż algorytm prosty stos oparte.

W artykule pewne pseudokod dla iteracji z O (1) stanie, który może być łatwo przystosowany do iteracyjnej.

Odpowiedział 03/01/2011 o 03:09
źródło użytkownik

głosy
30

Najprostsze sklepy możliwe iteracyjnej ostatni widział klucz, a następnie na następnej iteracji, przeszukuje drzewo dla kres górny dla tego klucza. Iteracji O (log n). Ma to tę zaletę, że jest bardzo prosta. Jeżeli klawisze są małe wówczas iteratory są również małe. Oczywiście, że ma tę wadę, że jest stosunkowo powolny sposób iteracja drzewa. To również nie będzie działać non-unikalnych sekwencji.

Niektóre drzewa użyć dokładnie realizację już używać, ponieważ jest to ważne dla ich szczególne przeznaczenie, że skanowanie jest bardzo szybki. Jeżeli liczba klawiszy w każdym węźle jest duża, wówczas kara przechowywania rodzeństwa wskazówek nie jest zbyt uciążliwe. Większość B-Drzewa korzystać z tej metody.

wiele implementacji wyszukiwania drzewo utrzymać wskaźnik nadrzędny na każdym węźle, aby uprościć inne operacje. Jeśli masz, wtedy można użyć prostego wskaźnik do węzła ostatnio widziany jako podanie iteratora. przy każdej iteracji, szukać kolejnego dziecka w rodzica Ostatnio widziany węzła. jeśli nie ma więcej rodzeństwo, a następnie udać się jeszcze jeden poziom.

Jeśli żadna z tych technik Ci odpowiadać, można użyć stos węzłów, przechowywana w iteracyjnej. To serwuje taką samą funkcję jak wywołanie funkcji stosu gdy iteracja drzewie poszukiwań normalnie, ale zamiast zapętlenie przez rodzeństwo i recursing na dzieci, kiedy pchasz dzieci na stos i powrót każdego kolejnego rodzeństwa.

Odpowiedział 03/01/2011 o 03:25
źródło użytkownik

głosy
3

Ok, wiem, że to jest stary, ale poproszono mnie o to w wywiadzie z Microsoft jakiś czas temu i postanowiłem pracować na nim trochę. Ja testowałem to i działa całkiem dobrze.

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

};
Odpowiedział 20/10/2012 o 05:53
źródło użytkownik

głosy
15

Jak wspomniano TokenMacGuy można użyć stosu przechowywane w iteracyjnej. Oto krótki testowane wdrożenie w 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);
    }
}

Inny wariant będzie przechodzić przez drzewo w czasie budowy i zapisać przechodzenie do listy. Można użyć listy iterator później.

Odpowiedział 31/07/2013 o 00:21
źródło użytkownik

głosy
0

Co za pomocą głębokości pierwszy technikę wyszukiwania. Przedmiotem iterator po prostu musi mieć stos już odwiedzonych węzłów.

Odpowiedział 21/05/2014 o 22:02
źródło użytkownik

głosy
0

Jeśli używasz stosu, tylko osiągnięcia „Użycie pamięci Extra O (h), h jest wysokością drzewa”. Jednakże, jeśli chcesz używać tylko O ​​(1) dodatkowej pamięci, trzeba nagrać Oto analiza: - Jeśli bieżący węzeł ma prawo dziecka: znaleźć min z prawego poddrzewa - To bieżący węzeł nie ma prawa dziecka, trzeba spojrzeć na niego z korzenia i utrzymać aktualizacją to najniższy przodek, który jest najniższy następny węzeł

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;
           }
       }
Odpowiedział 24/04/2015 o 23:41
źródło użytkownik

głosy
0

Używać O (1) przestrzeń, co oznacza, że ​​nie będzie korzystał O (h) stosu.

Aby rozpocząć:

  1. hasNext ()? current.val <= endNode.val aby sprawdzić, czy drzewo jest w pełni wykonany ruch.

  2. Znajdź min poprzez lewy najbardziej: Możemy zawsze pełen szukać lewej najbardziej na znalezienie kolejnej wartości minimalnej.

  3. Po lewej najbardziej min jest zaznaczone (nazwać current). Następny min będzie 2 przypadki: Jeśli current.right = null, możemy zachować patrząc na lewej skrajnej dziecka current.right, tak jak najbliższego min!. Albo musimy spojrzeć wstecz dla rodzica. Użyj binarne drzewo poszukiwań znaleźć na bieżący węzeł nadrzędny.

Uwaga : podczas wykonywania przeszukiwanie binarne dla rodzica, upewnij się, że spełnia parent.left = prąd.

Ponieważ: Jeśli parent.right == prądu, że rodzic musi odwiedzono już wcześniej. W binarne drzewo poszukiwań, wiemy, że parent.val <parent.right.val. Musimy pominąć ten szczególny przypadek, ponieważ prowadzi do ifinite pętlę.

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;
    }
}
Odpowiedział 27/01/2016 o 16:42
źródło użytkownik

głosy
0

Zgodnie z definicją, nie jest możliwe do next () i hasNext (), aby uruchomić w czasie O (1) czas. Kiedy szukasz w szczególności węzła w BST, nie masz pojęcia, wysokość i struktura pozostałych węzłów, więc może nie tylko „skok” do prawidłowego następnego węzła.

Jednak złożoność przestrzeń może być zmniejszona do O (1) (z wyjątkiem pamięci dla samego BST). Oto sposób, by to zrobić w 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;
}

Sztuką jest mieć zarówno łącza nadrzędnego i odwiedził flagę dla każdego węzła. Moim zdaniem, możemy twierdzić, że nie jest to dodatkowe zużycie miejsca, to jest po prostu częścią struktury węzła. I oczywiście, iter_next () musi być wywołana bez stanu struktury drzewa zmieniającym się (oczywiście), ale również, że „odwiedził” flagi nie zmieniają wartości.

Oto funkcja tester, który wywołuje iter_next () i drukuje wartości każdorazowo na tym drzewie:

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

Które zostaną wydrukowane wartości posortowanych:

15 16 20 21 25 27 40 62 71 
Odpowiedział 13/02/2016 o 06:56
źródło użytkownik

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