From f5c43c914e891c79988dfa8f2a94b5f2f0bbfe2a Mon Sep 17 00:00:00 2001 From: Jidong Xiao Date: Fri, 28 Mar 2025 02:35:30 -0400 Subject: [PATCH] adding the iterative approach using stacks --- lectures/21_trees_IV/README.md | 50 +++++++++++++++++--- lectures/21_trees_IV/inorder_iterative.cpp | 47 ++++++++++++++++++ lectures/21_trees_IV/postorder_iterative.cpp | 50 ++++++++++++++++++++ lectures/21_trees_IV/preorder_iterative.cpp | 45 ++++++++++++++++++ 4 files changed, 186 insertions(+), 6 deletions(-) create mode 100644 lectures/21_trees_IV/inorder_iterative.cpp create mode 100644 lectures/21_trees_IV/postorder_iterative.cpp create mode 100644 lectures/21_trees_IV/preorder_iterative.cpp diff --git a/lectures/21_trees_IV/README.md b/lectures/21_trees_IV/README.md index a7f211b..fcae436 100644 --- a/lectures/21_trees_IV/README.md +++ b/lectures/21_trees_IV/README.md @@ -2,9 +2,44 @@ ## Today’s Lecture -- Morris Traversal +- Binary Tree In-order, Pre-order, Post-order Iterative Traversal +- Binary Tree Morris Traversal -## 21.1 Morris Traversal +## 21.1 Binary Tree In-order, Pre-order, Post-order Iterative Traversal + +A common way to traverse a binary tree without recursion, is using an extra container, such as a stack or a queue. Here we will use a stack to perform an in-order traversal of a binary tree; and use a stack to perform a pre-order traversal of a binary tree; but we will need to use two stacks to perform a post-order traversal of a binary tree. + +We will use this binary tree as a test case: + +![alt text](binaryTree.png "Binary Tree Test Case") + +## 21.1.1 In-order Iteratively + +An in-order traversal program is provided here: [inorder_iterative.cpp](inorder_iterative.cpp). + +## 21.1.2 Pre-order Iteratively + +A pre-order traversal program is provided here: [preorder_iterative.cpp](preorder_iterative.cpp). + +## 21.1.3 Post-order Iteratively + +A post-order traversal program is provided here: [postorder_iterative.cpp](postorder_iterative.cpp). + +The best way to understand these programs is to walk through the code using the test case. + +## 21.2 Existing Binary Tree Traversals + +In-order, Pre-order, Post-Order recursively: Time Complexity: O(n), Space Complexity: best case: O(log n), balanced tree; worst case: O(n), completely skewed tree. The space consumption is mostly because of the recursive call stack usage. + +In-order, Pre-order, Post-Order iteratively: Time Complexity: O(n); Space Complexity: best case: O(log n), worst case: O(n). The space consumption is mostly because of the stack usage. + +Level-order, iteratively: Time Complexity: O(n), Space Complexity: best case: O(1), for a skewed tree; worst case: O(n), if every node either has zero children, or exactly two children. + +Can we traverse a binary tree with an O(n) time complexity and O(1) space complexity? + +**Note**: when considering space complexity of the traverse, we do not count the memory space used by the tree itself; meaning that the space refers to the extra space, which is introduced by the traverse function. + +## 21.3 Morris Traversal Morris Traversal is a tree traversal algorithm that allows in-order, pre-order, and post-order traversal of a binary tree without using recursion or a stack/queue, achieving O(1) space complexity. It modifies the tree temporarily but restores it afterward. @@ -16,7 +51,11 @@ Instead of using extra memory (like recursion stack or an explicit stack), Morri - Using these links to traverse back instead of a recursive call. -## 21.2 Morris Traversal - In Order +- In Morris Traversal: + - for each node which has no left subtree, we visit this node once; + - for each node which has a left subtree, we visit this node twice; and in between the first visit and the second visit of this node, the traverse of the entire left subtree occurs. + +## 21.4 Morris Traversal - In Order - Start from the root. @@ -61,7 +100,6 @@ You can test the above function using this program: [inorder_main.cpp](inorder_m For this test case, -![alt text](binaryTree.png "Binary Tree Test Case") The testing program prints: @@ -72,7 +110,7 @@ Inorder Traversal using Morris Traversal: 4 2 6 5 7 1 3 9 8 ``` -## 21.3 Morris Traversal - Pre Order +## 21.5 Morris Traversal - Pre Order To perform preorder traversal: @@ -116,7 +154,7 @@ Preorder Traversal using Morris Traversal: 1 2 4 5 6 7 3 8 9 ``` -## 21.4 Morris Traversal - Post Order +## 21.6 Morris Traversal - Post Order Post order is different, and we need to write some helper functions here. diff --git a/lectures/21_trees_IV/inorder_iterative.cpp b/lectures/21_trees_IV/inorder_iterative.cpp new file mode 100644 index 0000000..28bbb08 --- /dev/null +++ b/lectures/21_trees_IV/inorder_iterative.cpp @@ -0,0 +1,47 @@ +#include +#include + +class TreeNode { +public: + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +// in order traverse a binary tree, iteratively. +void inorderTraversal(TreeNode* root) { + std::stack st; + TreeNode* current = root; + + while (current != nullptr || !st.empty()) { + while (current != nullptr) { // reach leftmost node + st.push(current); + current = current->left; + } + + current = st.top(); // process node + st.pop(); + std::cout << current->val << " "; + + current = current->right; // move to right subtree + } +} + +int main() { + TreeNode* root = new TreeNode(1); + root->left = new TreeNode(2); + root->right = new TreeNode(3); + root->left->left = new TreeNode(4); + root->left->right = new TreeNode(5); + root->left->right->left = new TreeNode(6); + root->left->right->right = new TreeNode(7); + root->right->right = new TreeNode(8); + root->right->right->left = new TreeNode(9); + + std::cout << "Inorder Traversal: "; + inorderTraversal(root); + std::cout << std::endl; + + return 0; +} diff --git a/lectures/21_trees_IV/postorder_iterative.cpp b/lectures/21_trees_IV/postorder_iterative.cpp new file mode 100644 index 0000000..0c035e6 --- /dev/null +++ b/lectures/21_trees_IV/postorder_iterative.cpp @@ -0,0 +1,50 @@ +#include +#include + +class TreeNode { +public: + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +// pre order traverse a binary tree, iteratively. +void postorderTraversal(TreeNode* root) { + if (root == nullptr) return; + + std::stack st1, st2; + st1.push(root); + + while (!st1.empty()) { + TreeNode* current = st1.top(); + st1.pop(); + st2.push(current); + + if (current->left) st1.push(current->left); + if (current->right) st1.push(current->right); + } + + while (!st2.empty()) { + std::cout << st2.top()->val << " "; + st2.pop(); + } +} + +int main() { + TreeNode* root = new TreeNode(1); + root->left = new TreeNode(2); + root->right = new TreeNode(3); + root->left->left = new TreeNode(4); + root->left->right = new TreeNode(5); + root->left->right->left = new TreeNode(6); + root->left->right->right = new TreeNode(7); + root->right->right = new TreeNode(8); + root->right->right->left = new TreeNode(9); + + std::cout << "Inorder Traversal: "; + postorderTraversal(root); + std::cout << std::endl; + + return 0; +} diff --git a/lectures/21_trees_IV/preorder_iterative.cpp b/lectures/21_trees_IV/preorder_iterative.cpp new file mode 100644 index 0000000..3babfac --- /dev/null +++ b/lectures/21_trees_IV/preorder_iterative.cpp @@ -0,0 +1,45 @@ +#include +#include + +class TreeNode { +public: + int val; + TreeNode* left; + TreeNode* right; + TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} +}; + +// pre order traverse a binary tree, iteratively. +void preorderTraversal(TreeNode* root) { + if (root == nullptr) return; + + std::stack st; + st.push(root); + + while (!st.empty()) { + TreeNode* current = st.top(); + st.pop(); + std::cout << current->val << " "; + + if (current->right) st.push(current->right); // push right first + if (current->left) st.push(current->left); // then push left + } +} + +int main() { + TreeNode* root = new TreeNode(1); + root->left = new TreeNode(2); + root->right = new TreeNode(3); + root->left->left = new TreeNode(4); + root->left->right = new TreeNode(5); + root->left->right->left = new TreeNode(6); + root->left->right->right = new TreeNode(7); + root->right->right = new TreeNode(8); + root->right->right->left = new TreeNode(9); + + std::cout << "Inorder Traversal: "; + preorderTraversal(root); + std::cout << std::endl; + + return 0; +}