Iterative Binary Tree Traversals

April 18, 2022 7 min read

One common interview problem that commonly gets asked by interviewer is to write an in-order binary tree traversal algorithm. This deceptively simple problem in my own opinion is harder than it looks like. Although the recursive version is tidier, it hides the complexity that goes behind it. I, too, ended up memorizing those three lines, only to later realize that sometimes it was much harder to come up with a solution to a problem based on in-order traversal only using recursion.

Inorder Binary Search Tree Traversal

inorderTraversal.cpp
Copy

_14
class Solution {
_14
public:
_14
void inorderTraversal(TreeNode* root) {
_14
if (!root) {
_14
return;
_14
}
_14
//Traverse to the left most node
_14
inorder(root -> left);
_14
//Magic goes here
_14
cout << root -> val << endl;
_14
//Go to the right node
_14
inorder(root -> right);
_14
}
_14
};

Recall our good ol' recursive in-order traversal.

inorderTraversal.cpp
Copy

_15
class Solution {
_15
public:
_15
void inorderTraversal(TreeNode* root) {
_15
if (!root) {
_15
return;
_15
}
_15
stack<TreeNode*> nodes;
_15
//Traverse to the left most node
_15
inorder(root -> left);
_15
//Magic goes here
_15
cout << root -> val << endl;
_15
//Go to the right node
_15
inorder(root -> right);
_15
}
_15
};

Like a compiler, we can mechanically convert our function calls to use an explicit stack, but readibility/memoribility would suffer.

inorderTraversal.cpp
Copy

_22
class Solution {
_22
public:
_22
void inorderTraversal(TreeNode* root) {
_22
if (!root) {
_22
return;
_22
}
_22
stack<TreeNode*> nodes;
_22
_22
//Traverse to the left most node
_22
while(root || !nodes.empty()) {
_22
while(root) {
_22
nodes.push(root);
_22
root = root->left;
_22
}
_22
_22
//Magic goes here
_22
cout << root -> val << endl;
_22
//Go to the right node
_22
inorder(root -> right);
_22
}
_22
}
_22
};

The in-order traversal requires that we print the leftmost node first and the right most node at the end. So basically for each node we need to go as far as down and left as possible. So the line inorder(root->left) could be replaced with a while loop to have similar effect.

inorderTraversal.cpp
Copy

_23
class Solution {
_23
public:
_23
void inorderTraversal(TreeNode* root) {
_23
if (!root) {
_23
return;
_23
}
_23
stack<TreeNode*> nodes;
_23
_23
//Traverse to the left most node
_23
while(root || !nodes.empty()) {
_23
while(root) {
_23
nodes.push(root);
_23
root = root->left;
_23
}
_23
_23
root = nodes.top(); nodes.pop();
_23
//Magic goes here
_23
cout << root -> val << endl;
_23
//Go to the right node
_23
root = root->right;
_23
}
_23
}
_23
};

Now we need to retrieve the top node and store it's right child if it exists

An iterative solution can help us solve multiple tree problems, and the same template could be used for multiple problems.

Recall our good ol' recursive in-order traversal.

Like a compiler, we can mechanically convert our function calls to use an explicit stack, but readibility/memoribility would suffer.

The in-order traversal requires that we print the leftmost node first and the right most node at the end. So basically for each node we need to go as far as down and left as possible. So the line inorder(root->left) could be replaced with a while loop to have similar effect.

Now we need to retrieve the top node and store it's right child if it exists

An iterative solution can help us solve multiple tree problems, and the same template could be used for multiple problems.

inorderTraversal.cpp
CopyExpandClose

_14
class Solution {
_14
public:
_14
void inorderTraversal(TreeNode* root) {
_14
if (!root) {
_14
return;
_14
}
_14
//Traverse to the left most node
_14
inorder(root -> left);
_14
//Magic goes here
_14
cout << root -> val << endl;
_14
//Go to the right node
_14
inorder(root -> right);
_14
}
_14
};

inOrderTemplate.cpp
Copy

_22
class Solution {
_22
public:
_22
void inorderTraversal(TreeNode* root) {
_22
if (!root) {
_22
return;
_22
}
_22
stack<TreeNode*> nodes;
_22
_22
while(root || !nodes.empty()) {
_22
while(root) {
_22
nodes.push(root);
_22
root = root->left;
_22
}
_22
_22
root = nodes.top(); nodes.pop();
_22
//Magic goes here
_22
cout << root -> val << endl;
_22
_22
root = root->right;
_22
}
_22
}
_22
};

Let's use the same template we created earlier to solve multiple leetcode problem using the same pattern

inOrderTemplate.cpp
Solution.cpp
Copy

_24
class Solution {
_24
public:
_24
vector<int> inorderTraversal(TreeNode* root) {
_24
if (!root) {
_24
return {};
_24
}
_24
stack<TreeNode*> nodes;
_24
vector<int> result;
_24
_24
while(root || !nodes.empty()) {
_24
while(root) {
_24
nodes.push(root);
_24
root = root->left;
_24
}
_24
_24
root = nodes.top(); nodes.pop();
_24
_24
result.push_back(root->val);
_24
_24
root = root->right;
_24
}
_24
return result;
_24
}
_24
};

For leetcode problem Binary Tree Inorder Traversal, we need to return a list of node so rather than printing. This could be solved by storing the values in a list and returning it.

inOrderTemplate.cpp
Solution.cpp
Copy

_25
class Solution {
_25
public:
_25
bool isValidBST(TreeNode* root) {
_25
if (!root) {
_25
return true;
_25
}
_25
stack<TreeNode*> nodes;
_25
TreeNode* pre = nullptr;
_25
while(root || !nodes.empty()) {
_25
while(root) {
_25
nodes.push(root);
_25
root = root->left;
_25
}
_25
_25
root = nodes.top(); nodes.pop();
_25
//Magic goes here
_25
if(pre && root->val <= pre->val) return false;
_25
pre = root;
_25
_25
_25
root = root->right;
_25
}
_25
return true;
_25
}
_25
};

Similarly, in order to Validate Binary Search Tree could be solved by verifying if the in-order traversal is in ascending order

inOrderTemplate.cpp
Solution.cpp
Copy

_22
class Solution {
_22
public:
_22
int kthSmallest(TreeNode* root, int k) {
_22
stack<TreeNode*> nodes;
_22
_22
while(root || !nodes.empty()) {
_22
while(root) {
_22
nodes.push(root);
_22
root = root->left;
_22
}
_22
_22
root = nodes.top(); nodes.pop();
_22
//Magic goes here
_22
k--;
_22
if (k == 0) return root->val;
_22
_22
_22
root = root->right;
_22
}
_22
return -1;
_22
}
_22
};

Find Kth Smallest Element in a BST by simply breaking at kth element

inOrderTemplate.cpp
Solution.cpp
Copy

_21
class Solution {
_21
public:
_21
TreeNode* searchBST(TreeNode* root, int val) {
_21
stack<TreeNode*> nodes;
_21
_21
while(root || !nodes.empty()) {
_21
while(root) {
_21
nodes.push(root);
_21
root = root->left;
_21
}
_21
_21
root = nodes.top(); nodes.pop();
_21
//Magic goes here
_21
if (root->val == val) return root;
_21
_21
_21
root = root->right;
_21
}
_21
return NULL;
_21
}
_21
};

Search in a Binary Search Tree becomes much simpler and one needs to simply check for val you are looking for.

inOrderTemplate.cpp
Solution.cpp
Copy

_27
class Solution {
_27
public:
_27
TreeNode* increasingBST(TreeNode* root, int val) {
_27
TreeNode* head = new TreeNode(-1);
_27
TreeNode* prev = head;
_27
_27
stack<TreeNode*> nodes;
_27
_27
while(root || !nodes.empty()) {
_27
_27
while(root) {
_27
nodes.push(root);
_27
root = root->left;
_27
}
_27
_27
root = nodes.top(); nodes.pop();
_27
//Magic goes here
_27
prev->right = root;
_27
prev = prev->right;
_27
root->left = NULL;
_27
_27
_27
root = root->right;
_27
}
_27
return head->right;
_27
}
_27
};

Increasing Order Search Tree has similar intuition. Each time we meet a node, link it like a linked list using the right pointer. To facilitate the linking, create a dummy head

inOrderTemplate.cpp
Solution.cpp
Copy

_30
class Solution {
_30
public:
_30
void recoverTree(TreeNode* head) {
_30
TreeNode* first = NULL;
_30
TreeNode* second = NULL;
_30
TreeNode* root = head;
_30
TreeNode* prev = NULL;
_30
_30
stack<TreeNode*> nodes;
_30
_30
while(root || !nodes.empty()) {
_30
while(root) {
_30
nodes.push(root);
_30
root = root->left;
_30
}
_30
_30
root = nodes.top(); nodes.pop();
_30
//Magic goes here
_30
if (prev != NULL && prev->val > root->val) {
_30
if (first == NULL) {
_30
first = prev;
_30
}
_30
second = root;
_30
}
_30
prev = root;
_30
_30
root = root->right;
_30
}
_30
swap(first->val, second->val);
_30
};

For Recover Binary Search Tree, the question boils down to to find "2" elements in a given sorted arrangement , such that both of these elements violate the "sorted" order, and swap them back to their original places.

inOrderTemplate.cpp
Solution.cpp
Copy

_28
class Solution {
_28
public:
_28
TreeNode* convertBST(TreeNode* root, int val) {
_28
if (!root) return NULL;
_28
TreeNode* head = root;
_28
int sum = 0;
_28
int current_val;
_28
_28
stack<TreeNode*> nodes;
_28
_28
while(root || !nodes.empty()) {
_28
while(root) {
_28
nodes.push(root);
_28
root = root->right;
_28
}
_28
_28
root = nodes.top(); nodes.pop();
_28
//Magic goes here
_28
current_val = root->val;
_28
root->val = root->val + sum;
_28
sum += current_val;
_28
_28
_28
root = root->left;
_28
}
_28
return head;
_28
}
_28
};

For Convert BST to Greater Tree, The solution is the modification of inorder travel. Namely, travel right subtree, change the root value, and travel left subtree.

Let's use the same template we created earlier to solve multiple leetcode problem using the same pattern

For leetcode problem Binary Tree Inorder Traversal, we need to return a list of node so rather than printing. This could be solved by storing the values in a list and returning it.

Similarly, in order to Validate Binary Search Tree could be solved by verifying if the in-order traversal is in ascending order

Find Kth Smallest Element in a BST by simply breaking at kth element

Search in a Binary Search Tree becomes much simpler and one needs to simply check for val you are looking for.

Increasing Order Search Tree has similar intuition. Each time we meet a node, link it like a linked list using the right pointer. To facilitate the linking, create a dummy head

For Recover Binary Search Tree, the question boils down to to find "2" elements in a given sorted arrangement , such that both of these elements violate the "sorted" order, and swap them back to their original places.

For Convert BST to Greater Tree, The solution is the modification of inorder travel. Namely, travel right subtree, change the root value, and travel left subtree.

inOrderTemplate.cpp
CopyExpandClose

_22
class Solution {
_22
public:
_22
void inorderTraversal(TreeNode* root) {
_22
if (!root) {
_22
return;
_22
}
_22
stack<TreeNode*> nodes;
_22
_22
while(root || !nodes.empty()) {
_22
while(root) {
_22
nodes.push(root);
_22
root = root->left;
_22
}
_22
_22
root = nodes.top(); nodes.pop();
_22
//Magic goes here
_22
cout << root -> val << endl;
_22
_22
root = root->right;
_22
}
_22
}
_22
};

Once you have realized the pattern, you could see Binary Search Tree Iterator problem has the exact same structure

  1. Some initialization.
  2. A while-loop with a condition that tells whether there is more.
  3. The loop body gets the next value and does something with it.
BSTIterator.cpp
Copy

_33
class BSTIterator {
_33
stack<TreeNode*> nodes;
_33
TreeNode* head;
_33
int result;
_33
public:
_33
BSTIterator(TreeNode* root) {
_33
head = root;
_33
_33
while(head) {
_33
s.push(head);
_33
head = head->left;
_33
}
_33
}
_33
_33
int next() {
_33
head = s.top(); s.pop();
_33
result = head->val;
_33
if (head->right) {
_33
head = head->right;
_33
_33
while(head) {
_33
s.push(head);
_33
head = head->left;
_33
}
_33
}
_33
_33
return result;
_33
}
_33
_33
bool hasNext() {
_33
return !s.empty();
_33
}
_33
};

That’s it! I hope you enjoyed the explanation, not only we managed to understand inorder binary search tree traversal but also managed to solve handful leetcode problems as well and feel free to reach out to me on how I could improve its clarity. Next great steps after you think you’ve understood this is to try and implement the iterative solutions for preorder binary tree traversal and postorder binary tree traversal!

Happy coding.


 Configure Nginx Proxy Manager with BitwardenBinary Search 

Want more?

Subscribe to get my latest content via email. I won’t send you spam, and you can unsubscribe at any time.