28

How can I write a Java iterator (i.e. needs the next and hasNext methods) that takes the root of a binary tree and iterates through the nodes of the binary tree in in-order fashion?

Paul S.
  • 4,362
  • 10
  • 35
  • 52
  • 7
    I think the question is clear enough, if you know what a binary tree is and have experience parsing them. – ilomambo Feb 19 '16 at 09:35
  • 3
    I think it’s real question alright. It should probably have been closed as too broad instead. – Ole V.V. May 10 '17 at 13:48
  • 8
    This is a real question. I really can't understand some of the moderators in SO. – aerin Mar 20 '19 at 14:32
  • I found this answer more helpful than the ones below: https://stackoverflow.com/a/2942598/1599699 – Andrew Jun 04 '20 at 18:20

3 Answers3

47

The first element of a subtree is always the leftmost one. The next element after an element is the first element of its right subtree. If the element does not have a right child, the next element is the element's first right ancestor. If the element has neither a right child nor a right ancestor, it is the rightmost element and it's last in iteration.

I hope my code is human readable and covers all cases.

public class TreeIterator {
    private Node next;

    public TreeIterator(Node root) {
        next = root;
        if(next == null)
            return;

        while (next.left != null)
           next = next.left;
    }

    public boolean hasNext(){
        return next != null;
    }

    public Node next(){
        if(!hasNext()) throw new NoSuchElementException();
        Node r = next;

        // If you can walk right, walk right, then fully left.
        // otherwise, walk up until you come from left.
        if(next.right != null) {
            next = next.right;
            while (next.left != null)
                next = next.left;
            return r;
        }

        while(true) {
            if(next.parent == null) {
                next = null;
                return r;
            }
            if(next.parent.left == next) {
                next = next.parent;
               return r;
            }
            next = next.parent;
        }
     }
 }

Consider the following tree:

     d
   /   \
  b     f
 / \   / \
a   c e   g
  • The first element is "fully left from the root"
  • a does not have a right child, so the next element is "up until you come from left"
  • b does have a right child, so iterate b's right subtree
  • c does not have a right child. It's parent is b, which has been traversed. The next parent is d, which has not been traversed, so stop here.
  • d has a right subtree. Its leftmost element is e.
  • ...
  • g has no right subtree, so walk up. f has been visited, since we've come from right. d has been visited. d has no parent, so we cannot move further up. We have come from the rightmost node and we're done iterating.
Gilad Green
  • 36,708
  • 7
  • 61
  • 95
John Dvorak
  • 26,799
  • 13
  • 69
  • 83
  • 2
    There was a reason I didn't post a valid iterator in my answer, and it was so the OP would have to do it themselves. Giving out answers like this doesn't benefit anyone. – Hunter McMillen Oct 12 '12 at 03:06
  • 4
    @HunterMcMillen good point. Howerer, this is not a copy-and-paste thing anyways. At least one line has to be changed ;-) but I confess it was not deliberate. My focus was readability, not correctness. I didn't even test it ;-) – John Dvorak Oct 12 '12 at 03:13
  • I love your idea to store next. I could only think to store the current node, not the next one, and I was having so much trouble coming up with a way to print the first element. Thank you. – Variadicism Jun 24 '14 at 01:46
  • Great answer, thank you! I do think you're missing a return statement in next(), just before the last closing curly brace. – Andor Nov 24 '14 at 15:35
  • 2
    what if you don't have a parent pointer? – Vic Jan 19 '15 at 14:25
  • 3
    @Vic then you need a stack of some sorts. Since Java does not have generators / coroutines (unless Java 8 introduced them), an explicit stack will have to do. Just remember the list of parents in a list, then pop one when you want to go up. – John Dvorak Jan 19 '15 at 16:53
  • 1
    you lose reference to root though...... – committedandroider Feb 25 '15 at 23:52
  • 1
    @committedandroider if I use an explicit stack, you mean? It's at the bottom of the stack. If I use parent pointers, it takes me o(log n) to reach it. If I use a linked list stack, it takes me o(log n) to peek at the bottom. If I use an arrayList stack, it takes me o(1) to peek at the bottom. And, as always, you can hold an explicit reference to it somewhere else. Also, the iterator doesn't need it as it's faster to crawl from the current node than from the root (asymptotically almost always, and always in the amortised sense) – John Dvorak Feb 26 '15 at 08:30
  • @JanDvorak this code makes sense to me until the "else while (true)" clause, I'm not familiar with this pattern. Could you clarify when this code fires? When I create and compile this (accounting for things like making a parent pointer), it only ever prints the left nodes on the left sub-tree (for me it would print "a, b, d") and then idles. – Ian Delairre Aug 03 '15 at 04:40
  • @IanDelairre when a node has a right child, the next node in-order is the left-most node of that right subtree. I guess you never enter a right subtree? – John Dvorak Aug 03 '15 at 07:31
  • 1
    I recommend having Node/TreeIterator have a simple function that retrieved the reference to the leftmost element of a given node - makes current code simpler to read and no code repetition. – Gilad Green May 10 '17 at 06:17
  • I think the most natural and general way to think about it is to maintain the call stack yourself. What needs to be stored on the stack frame is all of the parameters for the recursive function and whether to call or return. Example code: https://gist.github.com/qiuwei/1c52a9a679c11b99ba48fdb9416c70de – Wei Qiu Jun 22 '17 at 13:22
  • 2
    one catch with this answer is **Each TreeNode need to store parent**, otherwise we need to go through route of storing parents separately or use stack to form an iterator. – raksja Jul 28 '17 at 02:14
  • what if the tree node doesn't store/have a parent pointer? – techcraver Mar 14 '21 at 09:45
3

To get the next entry, 'nextEntry()', for the iterator, I looked at snippets from java.util.TreeMap pasted below. In plain English, I'd say you make sure the root node is not null first else return null. If it is not, vist the right node if it is not null. Then visit the left (if not null) and visit that one's left repeatedly in a while loop until hitting null. If the originial right node is null then instead of all that, visit the parent node if that is not null. Now enter a while loop where you vist the parent until it's either null or the node you're currently visiting has its right (child) node equal to your last position. Now return whatever entry you're sittng on. Failing all those options, return the (original) root node. 'HasNext()' merely checks if this "next entry" you are returning is null or not.

public final boolean hasNext() {
     return next != null;
}

final TreeMap.Entry<K,V> nextEntry() {
    TreeMap.Entry<K,V> e = next;
    if (e == null || e.key == fenceKey)
        throw new NoSuchElementException();
    if (m.modCount != expectedModCount)
        throw new ConcurrentModificationException();
    next = successor(e);
    lastReturned = e;
    return e;
}

static <K,V> TreeMap.Entry<K,V> successor(Entry<K,V> t) {
    if (t == null)
        return null;
    else if (t.right != null) {
        Entry<K,V> p = t.right;
        while (p.left != null)
            p = p.left;
        return p;
    } else {
        Entry<K,V> p = t.parent;
        Entry<K,V> ch = t;
        while (p != null && ch == p.right) {
            ch = p;
            p = p.parent;
        }
        return p;
    }
}
apcris
  • 153
  • 9
  • Yes, I did. Please publish yours and may that be the accepted answer if equal or better to my explanation. It actually states in the TreeMap.java file packaged with the jdk that the code is properietary. If that is the case, I will say that ~30 lines out of 2400 is fair use for educational purposes. – apcris Oct 12 '12 at 02:43
  • I did publish my answer. – John Dvorak Oct 12 '12 at 02:44
  • Not sure the transcription helps understanding... – John Dvorak Oct 12 '12 at 02:45
  • I agree with the fair use policy, I'm saying it's easy to overlook the attribution. In fact, I did. – John Dvorak Oct 12 '12 at 02:48
  • 1
    I see what you mean, it's reasonable to assume the user pasting code wrote it. I'm not sure that I could write a binary tree data structure myself but I've used TreeMaps and noticed the relevant source to be much simpler than I expected. I think the transcription is helpful. I do like your a-g tree and seeing two implementations of the same thing. – apcris Oct 12 '12 at 03:00
-2

It's pretty straight forward, for in-order traversal you visit the left child if there is one, then the root node, then the right child:

visit_node(node)
   if node.left: visit_node(node.left)
   // visit the root node
   if node.right: visit_node(node.right)

Diagram:

     a 
   /   \        (in-order traversal would give bac)
  b     c
Hunter McMillen
  • 59,865
  • 24
  • 119
  • 170