NikBond
NikBond

Reputation: 763

How to traverse BTree in-order without recursion in iterative style?

I need B-Tree LNR traversal (in-order). I've found an algorithm for B-Tree traversal here. How I can implement it without recursion in iterative way? I've found this question but there is no answer and the code in the question is so unclear and seems incorrect. At least, this is not the LNR and it's not suitable for me. Also I've found a lot of example of simple binary tree traversal but I need exactly B-Tree.

I use Rust but I'll be happy to see answer in any language or pseudocode.

Upvotes: 2

Views: 1480

Answers (2)

Neil
Neil

Reputation: 1922

Though a stack/recursion is efficient, it takes O(log n) space. This is complex if one wants to have two or more iterators at the same time, (in the heap, at least.) An alternative is to make it take O(1) space at the cost of time, provided one has unique entries (strictly monotonic.) In pseudo-code:

iterator { tree root; { node; height; idx; } pos; };

boolean pin(iterator it) {
    if(!it.root) return false;
    /* Special case: off the left. */
    if(!it.pos.node) it.pos.node = it.root.node,
        it.pos.height = it.root.height, it.pos.idx = 0;
    /* Descend the tree. */
    while(it.pos.height) it.pos.height--,
        it.pos.node = it.pos.node.child[it.pos.idx], it.end.idx = 0;
    if(it.end.idx < it.end.node.size) return true; /* Likely. */
    if(!it->end.node->size) return false;
    /* Go down the tree again and note the next. */
    next.node = 0, x = it.pos.node.x[it.pos.node.size - 1];
    for(s = it.root; s.height; s.node = s.node.child[a0], s.height--) {
        a1 = s.node.size; a0 = 0;
        while(a0 < a1) {
            m = (a0 + a1) / 2;
            if(x > s.node->x[m]) a0 = m + 1; else a1 = m;
        }
        if(a0 < s.node.size)
            next.node = s.node, next.height = s.height, next.idx = a0;
    }
    if(!next.node) return false; /* Off the right. */
    it.pos = next;
    return true; /* Jumped nodes. */
}
iterator begin(tree tree) {
    iterator it;
    it.root = tree.root, it.pos.node = 0;
    return it;
}
entry next(iterator it) {
    return pin(it) ? it.pos.node[it.pos.idx++].to_entry() : null;
}

In the pin function, it takes an out-of-bounds access to the height-zero nodes and restarts from the root. It looks up the last value returned, but keeps a record of the next-key in the the descending nodes. The minimum-height node at which it found a next-key is the logical next-key. If it didn't find any next-key, the iteration is complete. This is slower, O(n log n) to complete the entire iteration, but may be more convenient.

Upvotes: 0

trincot
trincot

Reputation: 350232

As already mentioned in comments: if you do not want to use recursion -- which would be the way to go -- then you'll have to mimic it using a stack.

An entry on that stack would be a pair consisting of:

  • a reference to a node, and
  • a current index (0...m-1) within that node, where m is the number of branches at that node

As an example, I'll use the following B-tree:

enter image description here

...and use a JSON representation of it:

{
    "keys": [29],
    "children": [{
        "keys": [15, 21],
        "children": [{
            "keys": [11, 12]
        }, {
            "keys": [18, 20]
        }, {
            "keys": [23, 25, 27]
        }]
    }, {
        "keys": [35, 42],
        "children": [{
            "keys": [31, 33]
        }, {
            "keys": [36, 39]
        }, {
            "keys": [45, 47, 50, 55]
        }]
    }]
}

Here is an implementation of an iterate function in JavaScript, which returns an iterator over the keys in LNR order:

function * iterate(node) {
    let stack = []; // stack of [node, child-index] pairs
    
    stack.push([node, 0]);
    while (stack.length > 0) {
        let [node, i] = stack.pop();
        if (!("children" in node)) { // bottom level has no "children" member
            for (let key of node.keys) {
                yield key;
            }
        } else if (i < node.children.length) {
            if (i > 0) {
                yield node.keys[i-1];
            }
            stack.push([node, i+1]);
            stack.push([node.children[i], 0]);
        }
    }
}

// The example B-tree:
const btree = {"keys": [29],"children": [{"keys": [15, 21],"children": [{"keys": [11, 12]}, {"keys": [18, 20]}, {"keys": [23, 25, 27]}]}, {"keys": [35, 42],"children": [{"keys": [31, 33]}, {"keys": [36, 39]}, {"keys": [45, 47, 50, 55]}]}]};

// Consume the iterator and output
console.log(...iterate(btree));

Upvotes: 3

Related Questions