Reputation: 794
given the code below, I am bit confused as to how the order of operations happens to get an in order traversal of a binary search tree.
BinarySearchTree.prototype.inOrder = function() {
if (this.root == null) {
return null;
} else {
var result = new Array();
function traverseInOrder(node) {
node.left && traverseInOrder(node.left);
result.push(node.data);
node.right && traverseInOrder(node.right);
}
traverseInOrder(this.root);
return result;
};
}
I tried to add a debugger statement and follow but I get lost inside:
function traverseInOrder(node) {
node.left && traverseInOrder(node.left); //step 1
result.push(node.data); //step 2
node.right && traverseInOrder(node.right); //step 3
}
node.left && traverseInOrder(node.left);
(Step 1) gets run then gets run again then gets run again. Finally when there is no node.left
, Step 2 gets called : result.push(node.data);
This is the part where it loses me. Now it tries to run node.right && traverseInOrder(node.right)
but there is no node.right
but instead of moving out of the function, it goes back up to Step 2, result.push(node.data);
.
Is this queued up from the multiple recursive calls in Step 1?
Upvotes: 4
Views: 1693
Reputation: 50807
tenkmilan already did an excellent job of showing how to envision that code.
Here I go a different path, and write a simpler inorder
traversal. It should be fairly clear how that can map to the code supplied.
An inorder
traversal is simple enough. preorder
and postorder
are the other most common tree traversals and they work on arbitrary finite trees. Inorder is only defined for binary trees, and uses the left
- and right
-children of your node. The traversal order is to (recursively) traverse the left child, then visit the node itself, and finally to (recursively) traverse the right child.
We can write such a traversal simply:
const inorder = (tree) =>
tree
? [
... inorder (tree .left),
tree .data,
... inorder (tree .right)
]
: []
We have a base-case where the node we're looking at is empty, and we just return an empty array. In the general case we just concatenate together a recursive call on left .tree
with our current node's value and a recursive call on right .tree
.
That's all there is to inorder
traversal. You can see it in action in this snippet:
const inorder = (tree) =>
tree
? [
... inorder (tree .left),
tree .data,
... inorder (tree .right)
]
: []
const tree = {
data: 'F',
left: {
data: 'B',
left: {data: 'A'},
right: {
data: 'D',
left: {data: 'C'},
right: {data: 'E'}
}
},
right: {
data: 'H',
left: {data: 'G'},
right: {data: 'I'}
}
}
console .log (inorder (tree))
Of course, this is for a simple tree stored as a plain JS object. But mapping back your sample code is easy enough. I'm guessing that if you can follow this one, you can quickly follow that one.
Upvotes: 4
Reputation: 2715
Let's look at an example. Let it be the below tree which will be traversed in order
.
traverseInOrder
is called with this.root
, it means the
parameter from the above example is A
. The node.left
exists, it follows traverseInOrder
is called with parameter B
.
B
the node.left
exists, it follows traverseInOrder
is called with parameter D
.
D
node.left
does not exist (node.left
is falsy), so result.push(node.data);
is called with parameter D
. In the next step node.right
is falsy, it follows the traversInOrder
with parameter D
is finished. We get back to the B
.B
, and because the traversInOrder
with D
just finished, result.push(node.data)
will be called with parameter B
. And as next node.right
is truthy so traverseInOrder
is called with the node.right
so with E
.
E
node.left
is falsy, result.push
is called with parameter E
. node.right
is falsy is the call with E
ended here. We get back to call A
, because as we return from here to the call of B
it finishes at this point.A
we just finished the left node, result.push(node.data);
is called for A
and next node.right
is truthy so result.push(node.data)
is called with the right node witch means parameter C
.And it goes on the same with the C,F,G.
Upvotes: 5