├── .gitignore ├── CNAME ├── README.md ├── _binarytrees ├── assets │ └── js │ │ ├── binary-tree.js │ │ ├── height-of-binary-tree.js │ │ ├── inorder-traversal.js │ │ ├── level-order-traversal.js │ │ ├── lowest-common-ancestor.js │ │ ├── maximum-sum-path.js │ │ ├── minimum-depth-of-binary-tree.js │ │ ├── path-sum.js │ │ ├── postorder-traversal.js │ │ ├── preorder-traversal.js │ │ └── sum-of-left-leaves.js ├── height-of-binary-tree.md ├── inorder-traversal.md ├── level-order-traversal.md ├── lowest-common-ancestor.md ├── maximum-sum-path.md ├── minimum-depth-of-binary-tree.md ├── path-sum.md ├── postorder-traversal.md ├── preorder-traversal.md └── sum-of-left-leaves.md ├── _config.yml ├── _includes ├── bt-result-area.html ├── footer.html ├── head-content.html ├── header.html ├── seo.html └── stack-result-area.html ├── _layouts ├── bt-visualizer.html ├── datastructure.html ├── default.html └── visualizer.html ├── _stacks ├── assets │ ├── css │ │ └── animator.css │ └── js │ │ ├── animator-module.js │ │ ├── browser-history.js │ │ ├── expression-validator.js │ │ ├── index-of-closing-bracket.js │ │ ├── lexicographically-smallest-substring.js │ │ ├── longest-valid-parenthesis.js │ │ ├── next-bigger-number.js │ │ ├── path-simplifier.js │ │ ├── reverse-string.js │ │ ├── sort-stack.js │ │ ├── stack-displayer.js │ │ ├── stack.js │ │ └── stock-span-problem.js ├── browser-history.md ├── expression-validator.md ├── index-of-closing-bracket.md ├── lexicographically-smallest-substring.md ├── longest-valid-parenthesis.md ├── next-bigger-number.md ├── path-simplifier.md ├── reverse-string.md ├── sort-stack.md └── stock-span-problem.md ├── assets ├── css │ ├── main.css │ └── rouge-base16-monokai-dark.css ├── img │ ├── binary-tree.png │ ├── bt-inorder-traversal.gif │ ├── ds-visualizer.png │ ├── favicon.ico │ ├── logo.svg │ ├── stack-push-pop-operations.gif │ ├── stack-reverse-string.gif │ └── stack.png └── js │ └── binary-trees │ ├── bt-animator.js │ ├── canvas-displayer.js │ ├── height-of-binary-tree.js │ ├── inorder-traversal.js │ ├── level-order-traversal.js │ ├── lowest-common-ancestor.js │ ├── maximum-sum-path.js │ ├── minimum-depth-of-binary-tree.js │ ├── path-sum.js │ ├── postorder-traversal.js │ ├── preorder-traversal.js │ └── sum-of-left-leaves.js ├── binarytrees.html ├── canvas.html ├── index.html ├── stacks.html └── test.html /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | */.DS_Store 3 | notes.txt -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | dsvisualizer.net -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Data Structures - Learn by visualizing 2 | 3 | [https://dsvisualizer.net](https://dsvisualizer.net) 4 | 5 | **How about learning some of the data structures algorithms by visually seeing how they work? I've made a small effort in this site to show you how some algorithms work through the animations.** 6 | 7 | Data structures are ways of storing, managing, and organizing data in a computer so that it can be efficiently accessed, modified, and processed. Key algorithms and technologies, such as databases, web indexing, searching, and social networking, rely on data structures to manage underlying data effectively. 8 | 9 | The purpose of this site is to teach you some of the most common data structure algorithms through simple animations. Here, you will visually see and understand techniques such as traversing a binary tree and how basic stack operations, like push and pop, are used to implement different algorithms. 10 | 11 | For example in this simple animation you will see how a stack is used to reverse a string "Hello". You will also observe how basic `push` and `pop` operations are performed on the stack to implement this algorithm. 12 | 13 |  14 | 15 | Another example shows a technique for traversing binary trees. The animation clearly illustrates how nodes are visited and read in sequence. 16 | 17 |  18 | 19 | As you can see, visualizing how an algorithm works makes it much easier to understand. I’ve created visualizations for several stack and binary tree algorithms to help clarify their concepts and processes. I hope you find this site both informative and engaging! 20 | 21 | Visit [https://dsvisualizer.net](https://dsvisualizer.net) and explore the implemented algorithms and their solutions. -------------------------------------------------------------------------------- /_binarytrees/assets/js/binary-tree.js: -------------------------------------------------------------------------------- 1 | class TreeNode { 2 | constructor(data) { 3 | this.data = data; 4 | this.left = null; 5 | this.right = null; 6 | } 7 | } 8 | 9 | function buildTree(nodes, index, size) { 10 | if (index < size) { 11 | if (nodes[index] === null) { 12 | return null; 13 | } 14 | 15 | let root = new TreeNode(nodes[index]); 16 | root.left = buildTree(nodes, 2 * index + 1, size); 17 | root.right = buildTree(nodes, 2 * index + 2, size); 18 | 19 | return root; 20 | } 21 | 22 | return null; 23 | } -------------------------------------------------------------------------------- /_binarytrees/assets/js/height-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | 2 | function findMaxDepth(root) { 3 | if (root === null) return 0; 4 | 5 | let lDepth = findMaxDepth(root.left); 6 | let rDepth = findMaxDepth(root.right); 7 | 8 | return Math.max(lDepth, rDepth) + 1; 9 | } 10 | 11 | const nodes = [1, 2, 3, 4, 5, null, null, null, null, 10]; 12 | const root = buildTree(nodes); 13 | 14 | findMaxDepth(root); // 4 -------------------------------------------------------------------------------- /_binarytrees/assets/js/inorder-traversal.js: -------------------------------------------------------------------------------- 1 | 2 | function inOrderTraversal(root) { 3 | if (root !== null) { 4 | inOrderTraversal(root.left); 5 | 6 | console.log(root.data); 7 | 8 | inOrderTraversal(root.right); 9 | } 10 | } 11 | 12 | const nodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 13 | const root = buildTree(nodes); 14 | 15 | inOrderTraversal(root); -------------------------------------------------------------------------------- /_binarytrees/assets/js/level-order-traversal.js: -------------------------------------------------------------------------------- 1 | 2 | function levelOrderTraversal(root) { 3 | if (root !== null) { 4 | let queue = []; 5 | 6 | // Enqueue the root node. 7 | queue.push(root); 8 | 9 | // Loop through the Queue until it is not empty 10 | while (queue.length) { 11 | 12 | // Dequeue a node and visit it 13 | let node = queue.shift(); 14 | console.log(node.data); 15 | 16 | // Enqueue the left child 17 | if (node.left) { 18 | queue.push(node.left); 19 | } 20 | 21 | // Enqueue the right child 22 | if (node.right) { 23 | queue.push(node.right); 24 | } 25 | } 26 | } 27 | } 28 | 29 | const nodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 30 | const root = buildTree(nodes); 31 | 32 | levelOrderTraversal(root); -------------------------------------------------------------------------------- /_binarytrees/assets/js/lowest-common-ancestor.js: -------------------------------------------------------------------------------- 1 | 2 | // The idea is when a node is found matching p or q, 3 | // then stop propogating downward 4 | function findLCA(root, p, q, foundNodes) { 5 | if (root === null) return null; 6 | 7 | // Current node is p or q, 8 | // then current node could be an ancestor 9 | if ([p, q].includes(root.data)) { 10 | foundNodes.push(root); 11 | return root; 12 | } 13 | 14 | let left = null; 15 | let right = null; 16 | if (foundNodes.length < 2) { 17 | left = findLCA(root.left, p, q, foundNodes); 18 | } 19 | 20 | if (foundNodes.length < 2) { 21 | right = findLCA(root.right, p, q, foundNodes); 22 | } 23 | 24 | // If both subtrees are not null, 25 | // then the current node is the LCA 26 | if (left !== null && right !== null) { 27 | return root; 28 | } 29 | 30 | return left !== null ? left : right; 31 | } 32 | 33 | const nodes = [1,2,3,4,5,6,7,8,9,null,null,null,13]; 34 | const root = buildTree(nodes, 0, nodes.length); 35 | 36 | findLCA(root, 9, 13, []); // 1 37 | findLCA(root, 7, 13, []); // 3 -------------------------------------------------------------------------------- /_binarytrees/assets/js/maximum-sum-path.js: -------------------------------------------------------------------------------- 1 | function findMaximumSumPath(root, sum, maxSum, currentPathNodes, nodes) { 2 | if (root === null) return [maxSum, nodes]; 3 | 4 | sum += root.data; 5 | currentPathNodes.push(root); 6 | if (root.left === null && root.right === null) { 7 | if (sum > maxSum) { 8 | maxSum = sum; 9 | nodes = [...currentPathNodes]; 10 | currentPathNodes.pop(); 11 | return [maxSum, nodes]; 12 | } else { 13 | currentPathNodes.pop(); 14 | return [maxSum, nodes]; 15 | } 16 | } 17 | 18 | [maxSum, nodes] = findMaximumSumPath(root.left, sum, maxSum, currentPathNodes, nodes); 19 | [maxSum, nodes] = findMaximumSumPath(root.right, sum, maxSum, currentPathNodes, nodes); 20 | currentPathNodes.pop(); 21 | 22 | return [maxSum, nodes]; 23 | } 24 | 25 | const nodes = [1,2,3,4,null,6,7,8,9,null,null,null,13]; 26 | const root = buildTree(nodes, 0, nodes.length); 27 | 28 | let sum = 0; 29 | let pathNodes = []; 30 | let maxSum = 0; 31 | [maxSum, pathNodes] = findMaximumSumPath(root, sum, maxSum, [], pathNodes); 32 | 33 | let nodesData = []; 34 | pathNodes.forEach((node) => { 35 | nodesData.push(node.data); 36 | }) 37 | 38 | console.log(maxSum, nodesData); // 23, [1, 3, 6, 13] -------------------------------------------------------------------------------- /_binarytrees/assets/js/minimum-depth-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | 2 | function findMinDepth(root) { 3 | if (root === null) return 0; 4 | 5 | // If it is a leaf node, then the depth is 1 6 | if (root.left === null && root.right === null) { 7 | return 1; 8 | } 9 | 10 | // If left subtree is null, find the minimum depth of the right subtree 11 | if (root.left === null) { 12 | return findMinDepth(root.right) + 1; 13 | } 14 | 15 | // If right subtree is null, find the minimum depth of the left subtree 16 | if (root.right === null) { 17 | return findMinDepth(root.left) + 1; 18 | } 19 | 20 | // If both subtrees are present, then find the minimum of the two 21 | return Math.min(findMinDepth(root.left), findMinDepth(root.right)) + 1; 22 | } 23 | 24 | const nodes = [1, 2, 3, 4, 5, null, null, null, null, 10]; 25 | const root = buildTree(nodes); 26 | 27 | findMinDepth(root); // 2 28 | -------------------------------------------------------------------------------- /_binarytrees/assets/js/path-sum.js: -------------------------------------------------------------------------------- 1 | 2 | function checkIfPathExists(root, sum, targetSum, exists, pathNodes) { 3 | if (exists == 'TRUE') { 4 | return [exists, pathNodes]; 5 | } 6 | 7 | if (root === null) return ['FALSE', pathNodes]; 8 | 9 | sum += root.data; 10 | pathNodes.push(root); 11 | if (root.left === null && root.right === null) { 12 | if (sum == targetSum) { 13 | return ['TRUE', pathNodes]; 14 | } else { 15 | pathNodes.pop(); 16 | return ['FALSE', pathNodes]; 17 | } 18 | } 19 | 20 | [exists, pathNodes] = checkIfPathExists(root.left, sum, targetSum, exists, pathNodes); 21 | [exists, pathNodes] = checkIfPathExists(root.right, sum, targetSum, exists, pathNodes); 22 | if (exists == 'FALSE') { 23 | pathNodes.pop(); 24 | } 25 | 26 | return [exists, pathNodes]; 27 | } 28 | 29 | const nodes = [1,2,3,4,5,6,7,8,9,null,null,null,13]; 30 | const root = buildTree(nodes, 0, nodes.length); 31 | 32 | let pathNodes = []; 33 | [exists, pathNodes] = checkIfPathExists(root, 0, 23, 'FALSE', pathNodes); 34 | 35 | let nodeValues = []; 36 | pathNodes.forEach(node => { 37 | nodeValues.push(node.data); 38 | }); 39 | console.log(exists, pathNodes) // TRUE - 1, 3, 6, 13 40 | -------------------------------------------------------------------------------- /_binarytrees/assets/js/postorder-traversal.js: -------------------------------------------------------------------------------- 1 | 2 | function postOrderTraversal(root) { 3 | if (root !== null) { 4 | postOrderTraversal(root.left); 5 | 6 | postOrderTraversal(root.right); 7 | 8 | console.log(root.data); 9 | } 10 | } 11 | 12 | const nodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 13 | const root = buildTree(nodes); 14 | 15 | postOrderTraversal(root); -------------------------------------------------------------------------------- /_binarytrees/assets/js/preorder-traversal.js: -------------------------------------------------------------------------------- 1 | 2 | function preOrderTraversal(root) { 3 | if (root !== null) { 4 | console.log(root.data); 5 | 6 | preOrderTraversal(root.left); 7 | 8 | preOrderTraversal(root.right); 9 | } 10 | } 11 | 12 | const nodes = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; 13 | const root = buildTree(nodes); 14 | 15 | preOrderTraversal(root); -------------------------------------------------------------------------------- /_binarytrees/assets/js/sum-of-left-leaves.js: -------------------------------------------------------------------------------- 1 | 2 | function calculateSum(root, sum, isLeftNode) { 3 | if (root === null) return sum; 4 | 5 | if (isLeftNode && root.left === null && root.right === null) { 6 | sum += root.data; 7 | return sum; 8 | } 9 | 10 | sum = calculateSum(root.left, sum, 1); 11 | sum = calculateSum(root.right, sum, 0); 12 | 13 | return sum; 14 | } 15 | 16 | const nodes = [1, 2, 3, 4, 5, null, null, null, null, 10]; 17 | const root = buildTree(nodes); 18 | 19 | calculateSum(root, 0, 0); // 14 -------------------------------------------------------------------------------- /_binarytrees/height-of-binary-tree.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Find the maximun depth or height of a binary tree 4 | default_nodes: 1, 2, 3, 4, null, null, null, 8, 9 5 | --- 6 | 7 | #### Problem: 8 | 9 | Given a binary tree, find the maximun depth or height of a binary tree. The height of the tree is the number of vertices in the tree from the root to the deepest node. 10 | 11 | #### Algorithm: 12 | 13 | 1. If the tree is empty, return `0` (indicating no height). 14 | 2. Traverse the left and right subtrees, recursively calculating the height of each. 15 | 1. Recursively calculate the height of the left subtree. 16 | 2. Recursively calculate the height of the right subtree. 17 | 3. Determine the maximum height between the left and right subtrees, and add 1 to account for the current node’s height. 18 | 19 | This approach calculates the height by recursively determining the heights of the left and right subtrees and returning the greater of the two, plus one for the current node. 20 | 21 | #### Solution & Visualize: 22 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_binarytrees/inorder-traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Binary Trees - Inorder Traversal 4 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 5 | no_output: true 6 | --- 7 | 8 | > An inorder traversal technique follows the **Left -> Root -> Right** order. 9 | 10 | Inorder traversal is a method of visiting all the nodes in a binary tree in a specific order. In this traversal, the left subtree is processed first, then the root node is visited, and finally, the traversal continues through the right subtree. 11 | 12 | #### Algorithm: 13 | 14 | 1. Treverse the left subtree (recursively). 15 | 2. Visit the current root node 16 | 3. Treverse the right subtree (recursively). 17 | 18 | So the tree is traversed along the left subtree until the end node on the left subtree is reached. Then this end node is processed. Finally the same process is performed on the right subtree starting from this end node. 19 | 20 | #### Characteristics of Inorder Traversal 21 | - If the binary tree is a binary search tree, the inorder traversal will visit the nodes in ascending order. 22 | - **Time Complexity:** Inorder traversal of a tree with 𝑛 nodes takes 𝑂(𝑛) time, as it visits each node exactly once. 23 | 24 | #### Compare with other traversal algorithms 25 | 26 | > [Preorder Traversal]({% link _binarytrees/preorder-traversal.md %}): **Root -> Left -> Right** 27 | > 28 | > [Postorder Traversal]({% link _binarytrees/postorder-traversal.md %}): **Left -> Right -> Root** 29 | 30 | 31 | #### Solution & Visualize: 32 | The animation/visualization below demonstrates how the Inorder traversal works. -------------------------------------------------------------------------------- /_binarytrees/level-order-traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Binary Trees - Level Order Traversal 4 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 5 | no_output: true 6 | --- 7 | 8 | > Level order traversal is Breadth-first-search(BFS) algorithm, where the nodes at the same level are processed before moving to the next level. 9 | 10 | Level-order traversal is a method of visiting all the nodes in a binary tree, where nodes are processed level by level. In other words, all nodes at the same level are processed sequentially from left to right, before moving down to the next level. You can think of this as traversing the tree from top to bottom, processing each level from left to right. 11 | 12 | **Traversing Steps:** 13 | 1. Start with the root node. 14 | 2. Process the nodes at the current level from left to right. 15 | 3. Move to the next level, and repeat the process for each level. 16 | 17 | #### Algorithm: 18 | 19 | 1. Create an empty Queue. 20 | 2. Enqueue the root node. 21 | 3. Loop through the Queue until it is not empty and perform the below operations: 22 | 1. Dequeue a node and visit it. 23 | 2. Enqueue the left child of the node (if it exists). 24 | 3. Enqueue the right child of the node (if it exists). 25 | 26 | #### Characteristics of Level order Traversal 27 | - Level order traversal is useful in tree serialization and deserialization. 28 | - A queue data structure is used to implement the level order traversal. 29 | - **Time Complexity:** Level order traversal of a tree with 𝑛 nodes takes 𝑂(𝑛) time, as it visits each node exactly once. 30 | 31 | #### Solution & Visualize: 32 | The animation/visualization below demonstrates how the Levelorder traversal works. 33 | -------------------------------------------------------------------------------- /_binarytrees/lowest-common-ancestor.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Find the lowest common ancestor of two given nodes 4 | inputs: 5 | - type: number 6 | label: Please enter the p node value 7 | default: 4 8 | - type: number 9 | label: Please enter the q node value 10 | default: 11 11 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 12 | --- 13 | 14 | #### Problem: 15 | 16 | Given a binary tree, find the lowest common ancestor of two given nodes. All the nodes in the tree will be unique. 17 | 18 | > The lowest common ancestor is defined between two nodes p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself). 19 | 20 | #### Algorithm: 21 | 22 | 1. If the current node is `null`, return `null` 23 | 2. If the current node is either `p` or `q` return the current node, because it could be an ancestor. So return the current node. Use an array `foundNodes` to keep track if both nodes to be searched are found. 24 | 2. Recursively traverse both the left and right subtrees, storing the results from both subtrees. Continue this traversal until the foundNodes array has a length of 2 (indicating both nodes have been found). 25 | 3. If both `left` and `right` are not null, then the current node is LCA. (Because `p` and `q` are found in both sides.) 26 | 4. If only one subtree return a node, then propogate it upwward. 27 | 28 | The algorithm works on the principle that if nodes `p` and `q` are in different subtrees, the current node is their Lowest Common Ancestor (LCA). If both `p` and `q` are in the same subtree, the ancestor must be located in the same subtree. 29 | 30 | #### Solution & Visualize: 31 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_binarytrees/maximum-sum-path.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Maximum Sum Path 4 | default_nodes: 1,2,3,4,null,6,7,8,9,null,null,null,13 5 | --- 6 | 7 | #### Problem: 8 | 9 | Given a binary tree, find the maximum sum path from a leaf to a root. 10 | 11 | #### Algorithm: 12 | 13 | 1. Keep track of `maxSum` and the `nodes` array. The `nodes` array will store the nodes that form the maximum sum path. 14 | 2. Traverse the tree, recursively calculating the sum of the left and right subtrees. 15 | 3. At each node, add the node's value to the running sum and include the node in the `currentPathNodes` list. 16 | 4. When a leaf node is reached (both `left` and `right` children are null), compare the sum of the current path to the `maxSum`. 17 | 1. If sum of the current path is greater than the `maxSum`, update `maxSum` to this new sum. And set the `nodes` array to `currentPathNodes`. 18 | 2. If sum is less than the `maxSum`, remove this leaf from the `currentPathNodes` (pop the node) and continue traversing the other subtree. 19 | 5. Finally return the `maxSum` and `nodes` array representing the path of the maximum sum. 20 | 21 | This algorithm recursively explores each path, tracking the sum and nodes involved. When a leaf node is reached, it checks if the current path has a greater sum than the previously recorded maximum. If so, it updates the maximum sum and the corresponding path. 22 | 23 | #### Solution & Visualize: 24 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_binarytrees/minimum-depth-of-binary-tree.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Find the minimun depth of a binary tree 4 | default_nodes: 1, 2, 3, 4, 5, null, null, null, null, 10 5 | --- 6 | 7 | #### Problem: 8 | 9 | Given a binary tree, find the minimun depth of the tree. The minimum depth is the number of nodes along the shortest path from the root node down to the nearest leaf node. 10 | 11 | #### Algorithm: 12 | 13 | 1. Traverse the binary tree and perform the following operations 14 | 1. If the node is a leaf (both left and right children are null), return a depth as 1. 15 | 2. If the node is not a leaf and one of the subtrees is null, recursively traverse the other subtree to find its minimum depth. 16 | 3. If both subtrees are present, recursively find the minimum depth between the two subtrees. 17 | 18 | #### Solution & Visualize: 19 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_binarytrees/path-sum.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Path Sum 4 | inputs: 5 | - type: number 6 | label: Please enter the target sum 7 | default: 23 8 | default_nodes: 1,2,3,4,null,6,7,8,9,null,null,null,13 9 | --- 10 | 11 | #### Problem: 12 | 13 | Given a binary tree and an integer targetSum, return true if the tree has a root-to-leaf path such that adding up all the values along the path equals targetSum. 14 | 15 | #### Algorithm: 16 | 17 | 1. Keep track of `nodes` array to store the nodes that form the path with the target sum. Use a flag `exists`to indicate whether a valid path exists or not. 18 | 2. Traverse the binary tree and perform the following operations: 19 | 1. At each node, add the node's value to the running sum. 20 | 2. When a leaf node is reached (both `left` and `right` children are null), check if the running sum is equal to `targetSum`. 21 | - If the sum is equal to `targetSum`, the current path is the answer. Return the nodes along with the value `TRUE` for the `exists` flag. 22 | - If the sum does not equal to `targetSum`, remove the current leaf node from `nodes` list and continue traversal. 23 | 3. Finally, return the `exists` flag and `nodes` array representing the path that sums to the targetSum. 24 | 25 | This algorithm traverses the binary tree, calculating the sum of nodes along the path. When a leaf node is reached, it checks if the accumulated sum equals the target sum. If it does, it returns the path; if not, it continues traversing the other branches. The process is repeated until a valid path is found or all paths have been explored. 26 | 27 | #### Solution & Visualize: 28 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_binarytrees/postorder-traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Binary Trees - Postorder Traversal 4 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 5 | no_output: true 6 | --- 7 | 8 | > Postorder traversal technique follows the **Left -> Right -> Root** order. 9 | 10 | Postorder traversal is another method for visiting all the nodes in a binary tree. The recursive approach is similar to that of inorder traversal, with the key difference being that in postorder traversal, the root is processed last, after the left and right subtrees have been processed. 11 | 12 | #### Algorithm: 13 | 14 | 1. Treverse the left subtree (recursively). 15 | 2. Treverse the right subtree (recursively). 16 | 3. Visit the current root node 17 | 18 | So first, the left subtree is processed, followed by the right subtree. Finally, the current node is processed. 19 | 20 | #### Characteristics of Postorder Traversal 21 | - Postorder traversal is useful in following scenarios: 22 | - Deleting a tree, where the nodes are deleted after their subtrees. 23 | - To create the postfix expression of an expression tree. 24 | - In garbage collection algorithms 25 | - **Time Complexity:** Postorder traversal of a tree with 𝑛 nodes takes 𝑂(𝑛) time, as it visits each node exactly once. 26 | 27 | #### Compare with other traversal algorithms 28 | 29 | > [Inorder Traversal]({% link _binarytrees/inorder-traversal.md %}): **Left -> Root -> Right** 30 | > 31 | > [Preorder Traversal]({% link _binarytrees/preorder-traversal.md %}): **Root -> Left -> Right** 32 | 33 | #### Solution & Visualize: 34 | The animation/visualization below demonstrates how the Postorder traversal works. -------------------------------------------------------------------------------- /_binarytrees/preorder-traversal.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Binary Trees - Preorder Traversal 4 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 5 | no_output: true 6 | --- 7 | 8 | > Preorder traversal technique follows the **Root -> Left -> Right** order. 9 | 10 | Preorder traversal is another method for visiting all the nodes in a binary tree. The recursive approach is similar to that of inorder traversal, with the key difference being the order in which nodes are processed. In preorder traversal, the root node is processed first, followed by the left subtree, and then the right subtree. 11 | 12 | #### Algorithm: 13 | 14 | 1. Visit the current root node 15 | 2. Treverse the left subtree (recursively). 16 | 3. Treverse the right subtree (recursively). 17 | 18 | So, the current node is processed first, followed by a traversal of the left subtree. Once all the nodes in the left subtree are processed, the traversal then moves to the right subtree. 19 | 20 | #### Characteristics of Preorder Traversal 21 | - Preorder traversal is useful in creating a copy of tree. It is also used to create the prefix expressions of an expression tree. 22 | - **Time Complexity:** Preorder traversal of a tree with 𝑛 nodes takes 𝑂(𝑛) time, as it visits each node exactly once. 23 | 24 | #### Compare with other traversal algorithms 25 | 26 | > [Inorder Traversal]({% link _binarytrees/inorder-traversal.md %}): **Left -> Root -> Right** 27 | > 28 | > [Postorder Traversal]({% link _binarytrees/postorder-traversal.md %}): **Left -> Right -> Root** 29 | 30 | #### Solution & Visualize: 31 | The animation/visualization below demonstrates how the Preorder traversal works. 32 | -------------------------------------------------------------------------------- /_binarytrees/sum-of-left-leaves.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: bt-visualizer 3 | title: Sum of Left Leaves 4 | default_nodes: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 5 | --- 6 | 7 | #### Problem: 8 | 9 | Given the root of a binary tree, return the sum of all left leaves. Left leaf is a leaf node which has no children and is the left child of another node. 10 | 11 | #### Algorithm: 12 | 13 | 1. Traverse the binary tree and perform the following operations; 14 | 1. During the traversal, use a flag `isLeftNode` to identify left nodes. 15 | 2. Add the value of a left child node to the sum if it is a leaf node (i.e., it has no child nodes). 16 | 3. After completing the traversal, return the total sum of all leaf left nodes. 17 | 18 | This algorithm traverses the binary tree, checking each left child node. If the left child is a leaf (no children), its value is added to the total sum. The sum of all such leaf left nodes is then returned. 19 | 20 | #### Solution & Visualize: 21 | The animation/visualization below demonstrates how the algorithm works. -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | collections: 2 | stacks: 3 | output: true 4 | permalink: /:collection/:path 5 | binarytrees: 6 | output: true 7 | permalink: /:collection/:path -------------------------------------------------------------------------------- /_includes/bt-result-area.html: -------------------------------------------------------------------------------- 1 |
{{ page.noinputtext }}35 |
{{ page.noinputtext }}28 |
{{ page.subtitle }}
18 |18 | How about learning some of the data structures algorithms by visually seeing how they work? I've made a small effort in this site to show you how some algorithms work through the animations. 19 |
20 |10 | A binary tree is a hierarchical data structure in which each node has at most two children, commonly referred to as the left child and the right child. The topmost node in a binary tree is called the root, while the bottom-most nodes, which have no child nodes, are known as leaf nodes (or simply leaves). 11 |
12 | 13 |39 | Binary Tree Traversal involves visiting all the nodes of the binary tree in a specific order. Tree traversal algorithms are generally classified into two main categories: Depth-First Search (DFS) and Breadth-First Search (BFS). 40 |
55 | In the following problems, you will see visualizations demonstrating how these traversal algorithms work in practice. 56 |
57 | 58 | 59 |76 | Binary trees are fundamental in computer science because of their versatility in various applications like searching, balancing, and tree traversal. 77 |
78 | 79 |Go through some of these binarytree problems and their visualizations!
85 | 86 |7 | Data structures are ways of storing, managing, and organizing data in a computer so that it can be efficiently accessed, modified, and processed. Key algorithms and technologies, such as databases, web indexing, searching, and social networking, rely on data structures to manage underlying data effectively. Choosing the right and most efficient data structure is crucial to the success of these technologies and algorithms. 8 |
9 | 10 |11 | For instance, in databases, efficient algorithms for searching and retrieving data are essential. The performance of any database management system (DBMS) relies on how quickly and efficiently it can read and write data. Thus, implementing the appropriate data structure is key to the success of a DBMS. 12 |
13 | 14 |15 | There are various types of data structures, each designed to solve specific kinds of problems. Some common examples include arrays, stacks, queues, linked lists, trees, and hash tables. The choice of data structure depends on the specific operations that need to be performed on the data. 16 |
17 | 18 |20 | While there are many resources available online to learn about data structures, the purpose of this site is to teach you some of the most common data structure algorithms through simple animations. Here, you will visually see and understand techniques such as traversing a binary tree and how basic stack operations, like push and pop, are used to implement different algorithms. 21 |
22 | 23 |24 | When learning an algorithm, it’s essential to mentally picture each step to fully understand it. But if you can see how the algorithm works through an animation then you can quickly grasp it. And you are making the work easy for your logical brain. 25 |
26 | 27 |28 | For example in this simple animation you will see how a stack is used to reverse a string "Hello". You will also observe how basic push and pop operations are performed on the stack to implement this algorithm. 29 |
30 | 31 |37 | Another example shows a technique for traversing binary trees. The animation clearly illustrates how nodes are visited and read in sequence. 38 |
39 | 40 |46 | As you can see, visualizing how an algorithm works makes it much easier to understand. I’ve created visualizations for several stack and binary tree algorithms to help clarify their concepts and processes. I hope you find this site both informative and engaging! 47 |
48 | 49 |50 | Feel free to explore the Stacks and Binary Trees sections to review the implemented algorithms and their solutions. 51 |
52 | 53 | 57 | -------------------------------------------------------------------------------- /stacks.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: datastructure 3 | permalink: /stacks/ 4 | title: Stacks - Learn by visualizing 5 | subtitle: Stack works on the basis LIFO or FILO principal. 6 | --- 7 | 8 |10 | A Stack is a linear data structure that stores an ordered sequence of elements based on the LIFO (Last-In-First-Out) principle. This means that the last element inserted into the stack is the first one to be removed. It is also referred to as the FILO (First-In-Last-Out) principle, where the element that was inserted first is the one that will be removed last. 11 |
12 | 13 |19 | As shown in the diagram, a stack has only one open end, and elements can only be accessed from the top. To implement a stack, you only need a top pointer that points to the top-most element in the stack. Sometimes, a limit is imposed on the number of elements that can be added to the stack. If you attempt to insert more elements than the allowed limit, a condition known as stack overflow occurs. Have you ever wondered how Stackoverflow got its name? 20 |
21 | 22 | 23 |There are two main operations that can be performed on the stack:
25 |36 | There are other operations like Peek, which is to retrieve the top most element of the stack without deleting it. You can also perform other operations like to check if the stack is empty or full. 37 |
38 | 39 |40 | Stacks are commonly used in expression evaluation, parenthesis matching, and syntax parsing, which are widely used in calculator programs. Additionally, stacks can be utilized to implement efficient backtracking algorithms. 41 |
42 | 43 |44 | Questions related to stacks are frequently asked in technical interviews and coding challenges. Below, I’ve implemented solutions to some of these problems, along with animations to help visualize the algorithms and enhance your understanding. 45 |
46 |Take a look at some of these stack problems and their visualizations!
52 | 53 |