├── .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 | ![Stacks - Reversing a String](https://dsvisualizer.net/assets/img/stack-reverse-string.gif) 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 | ![Binary Tree - Inorder traversal](https://dsvisualizer.net/assets/img/bt-inorder-traversal.gif) 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 |
2 |

Input:

3 | 4 |
5 | 6 | 7 |
The tree nodes should only contain the numbers or `null` followed by comma
8 |
9 | 10 | {% for input in page.inputs %} 11 | {% if input.type == 'string' %} 12 |
13 | 14 | 15 |
{{ input.feedback }}
16 |
17 | {% elsif input.type == 'number' %} 18 |
19 | 20 | 21 |
{{ input.feedback }}
22 |
23 | {% else %} 24 |
25 | 26 | 27 |
{{ input.feedback }}
28 |
29 | {% endif %} 30 | {% endfor %} 31 | 32 | {% if page.noinputtext %} 33 |
34 |
{{ page.noinputtext }}
35 |
36 | {% endif %} 37 | 38 |
39 | 40 |
41 | 42 | {% if page.no_output %} 43 | {% else %} 44 |

Output:

45 | 46 |
47 |   48 |
49 | {% endif %} 50 |
51 | 52 |
53 | 54 |
55 | 56 | Binary Tree Visualization 57 | 58 |
-------------------------------------------------------------------------------- /_includes/footer.html: -------------------------------------------------------------------------------- 1 |
2 | -------------------------------------------------------------------------------- /_includes/head-content.html: -------------------------------------------------------------------------------- 1 | 2 | {% if layout.for %} 3 | {{ layout.for }} - 4 | {% endif %} 5 | {{ page.title }} 6 | 7 | 8 | {% include seo.html %} 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /_includes/header.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | 9 | 10 | 14 |
15 |
-------------------------------------------------------------------------------- /_includes/seo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /_includes/stack-result-area.html: -------------------------------------------------------------------------------- 1 |
2 |

Input:

3 | {% for input in page.inputs %} 4 | {% if input.type == 'string' %} 5 |
6 | 7 | 8 |
{{ input.feedback }}
9 |
10 | {% elsif input.type == 'number' %} 11 |
12 | 13 | 14 |
This is the custom help text for this input
15 |
16 | {% else %} 17 |
18 | 19 | 20 |
Enter the array elements followed by a comma
21 |
22 | {% endif %} 23 | {% endfor %} 24 | 25 | {% if page.noinputtext %} 26 |
27 |
{{ page.noinputtext }}
28 |
29 | {% endif %} 30 | 31 |
32 | 33 |
34 | 35 |

Output:

36 | 37 |
38 |     39 |
40 |
41 | 42 |
43 | 44 |
45 |
-------------------------------------------------------------------------------- /_layouts/bt-visualizer.html: -------------------------------------------------------------------------------- 1 | --- 2 | for: Binary Trees 3 | --- 4 | 5 | 6 | 7 | 8 | {% include head-content.html %} 9 | 10 | 11 | 12 | {% include header.html %} 13 | 14 |
15 |
16 |
17 |

18 | {{ page.title }} 19 |

20 | 21 |
22 | 25 | 28 |
29 | 30 |
31 | {{ content }} 32 | 33 |
34 |
35 |
Result
36 |
Code
37 |
38 | 39 |
40 |
41 | {% include bt-result-area.html %} 42 |
43 | 44 |
45 |
46 |
solution.js
47 |
binary-tree.js
48 |
49 | 50 |
51 |
52 | {% highlight javascript %} 53 | {% include_relative assets/js/{{ page.slug }}.js %} 54 | {% endhighlight %} 55 |
56 |
57 | {% highlight javascript %} 58 | {% include_relative assets/js/binary-tree.js %} 59 | {% endhighlight %} 60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 | 68 | 84 |
85 |
86 | 87 | {% include footer.html %} 88 | 89 | 90 | 91 | 92 | 94 | 102 | 209 | 210 | -------------------------------------------------------------------------------- /_layouts/datastructure.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head-content.html %} 5 | 6 | 7 | {% include header.html %} 8 | 9 |
10 |
11 |
12 |

13 | {{ page.title }} 14 |

15 | 16 |
17 |

{{ page.subtitle }}

18 |
19 | 20 | {{ content }} 21 |
22 |
23 |
24 | 25 | {% include footer.html %} 26 | 27 | -------------------------------------------------------------------------------- /_layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | {% include head-content.html %} 5 | 6 | 7 | {% include header.html %} 8 | 9 |
10 |
11 |
12 |

13 | Data Structures - Learn by visualizing 14 |

15 | 16 |
17 |

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 |
21 | 22 |
23 | 24 |
25 | 26 |
27 | {{ content }} 28 |
29 |
30 |
31 |
32 | 33 | {% include footer.html %} 34 | 35 | -------------------------------------------------------------------------------- /_layouts/visualizer.html: -------------------------------------------------------------------------------- 1 | --- 2 | for: Stacks 3 | --- 4 | 5 | 6 | 7 | 8 | {% include head-content.html %} 9 | 10 | 11 | 12 | {% include header.html %} 13 | 14 |
15 |
16 |
17 |

18 | {{ page.title }} 19 |

20 | 21 |
22 | 25 | 28 |
29 | 30 |
31 | {{ content }} 32 | 33 |
34 |
35 |
Result
36 |
Code
37 |
38 | 39 |
40 |
41 | {% include stack-result-area.html %} 42 |
43 | 44 |
45 |
46 |
solution.js
47 |
stack.js
48 |
stack-displayer.js
49 |
animator-module.js
50 |
51 | 52 |
53 |
54 | {% highlight javascript %} 55 | {% include_relative assets/js/{{ page.slug }}.js %} 56 | {% endhighlight %} 57 |
58 |
59 | {% highlight javascript %} 60 | {% include_relative assets/js/stack.js %} 61 | {% endhighlight %} 62 |
63 |
64 | {% highlight javascript %} 65 | {% include_relative assets/js/stack-displayer.js %} 66 | {% endhighlight %} 67 |
68 |
69 | {% highlight javascript %} 70 | {% include_relative assets/js/animator-module.js %} 71 | {% endhighlight %} 72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 | 80 | 96 |
97 |
98 | 99 | {% include footer.html %} 100 | 101 | 129 | 130 | 131 | 132 | 133 | 134 | 263 | 264 | -------------------------------------------------------------------------------- /_stacks/assets/css/animator.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --stack-item-color: #B4F0FA; 3 | --highlight-stack-item-color: #8CC8FA; 4 | --pop-item-color: #F5EDCB; 5 | } 6 | 7 | #stack-visualizer { 8 | display: flex; 9 | justify-content: center; 10 | margin: 0 20px; 11 | padding: 20px; 12 | } 13 | 14 | .text-center { 15 | text-align: center; 16 | } 17 | .hidden { 18 | opacity: 0; 19 | } 20 | .demo { 21 | width: 700px; 22 | margin: auto; 23 | } 24 | .stack-wrapper { 25 | width: 300px; 26 | margin: 0 20px; 27 | } 28 | .stack-wrapper > div { 29 | display: flex; 30 | align-items: end; 31 | font-size: 20px; 32 | justify-content: space-around; 33 | } 34 | .stack-wrapper > div > * { 35 | width: 100px; 36 | text-align: center; 37 | } 38 | .stack-ops { 39 | margin-bottom: 10px; 40 | } 41 | .stack-ops-values { 42 | margin-bottom: 30px; 43 | } 44 | .stack { 45 | border: 2px solid; 46 | border-top: 0; 47 | border-bottom-left-radius: 5px; 48 | border-bottom-right-radius: 5px; 49 | padding: 2px; 50 | padding-top: 12px; 51 | min-height: 150px; 52 | display: flex; 53 | flex-direction: column; 54 | justify-content: end; 55 | box-shadow: rgba(0, 0, 0, 0.07) 0px 1px 1px, rgba(0, 0, 0, 0.07) 0px 2px 2px, rgba(0, 0, 0, 0.07) 0px 4px 4px, rgba(0, 0, 0, 0.07) 0px 8px 8px, rgba(0, 0, 0, 0.07) 0px 16px 16px; 56 | } 57 | .stack-item { 58 | border: 2px solid; 59 | background: var(--stack-item-color); 60 | padding: 2px; 61 | margin-top: 1px; 62 | border-radius: 4px; 63 | position: relative; 64 | box-shadow: rgba(50, 50, 93, 0.25) 0px 6px 12px -2px, rgba(0, 0, 0, 0.3) 0px 3px 7px -3px; 65 | } 66 | .stack .stack-item:not(:last-child) { 67 | margin-bottom: 2px; 68 | } 69 | .stack-ops-values .stack-item { 70 | width: 90px; 71 | } 72 | .pop-element { 73 | background: var(--pop-item-color); 74 | } 75 | .stack-description { 76 | display: block !important; 77 | text-align: center; 78 | } 79 | .stack-description > * { 80 | width: 100% !important; 81 | } 82 | .stack-description h4 { 83 | margin-bottom: 5px; 84 | } 85 | .stack-description p { 86 | margin: 0; 87 | font-size: 16px; 88 | } 89 | .text-underline { 90 | text-decoration: underline; 91 | } 92 | 93 | .solution-result-display { 94 | max-width: 500px; 95 | margin: auto; 96 | } 97 | .solution-result-display label { 98 | display: block; 99 | font-weight: 700; 100 | } 101 | .solution-result-display input { 102 | border-radius: 4px; 103 | max-width: 500px; 104 | border: 1px solid; 105 | height: 28px; 106 | width: 100%; 107 | } 108 | .sv-comment { 109 | border-top: 1px solid; 110 | } 111 | .sv-comment-highlight { 112 | display: inline-block; 113 | letter-spacing: 5px; 114 | font-size: 20px; 115 | margin-top: 4px; 116 | padding: 5px; 117 | } 118 | .sv-comment-highlight-char { 119 | padding: 4px; 120 | border: 2px solid rgb(234,88,12); 121 | } 122 | .sv-comment-error { 123 | color: rgb(220, 53, 69); 124 | } 125 | .sv-input-feedback { 126 | font-size: 90%; 127 | color: rgb(220, 53, 69); 128 | } 129 | #final-output { 130 | width: 100%; 131 | border-radius: 4px; 132 | border: 2px solid rgb(234,88,12); 133 | } 134 | 135 | /* Binary Tree */ 136 | #bt-visualizer { 137 | text-align: center; 138 | } 139 | /* Binary Tree */ -------------------------------------------------------------------------------- /_stacks/assets/js/animator-module.js: -------------------------------------------------------------------------------- 1 | class AnimationList { 2 | animations = []; 3 | 4 | length() { 5 | return this.animations.length; 6 | } 7 | 8 | push(animation) { 9 | this.animations.push(animation); 10 | } 11 | 12 | shift() { 13 | return this.animations.shift(); 14 | } 15 | } 16 | 17 | let animatorModule = (function() { 18 | let animations = new AnimationList(); 19 | let animationsRunning = false; 20 | let animationSpeed = 1200; 21 | 22 | function getMoveDownAnimation(animation) { 23 | let topElement = animation.stackElement.children[0]; 24 | 25 | let distance = animation.stackElement.offsetHeight; 26 | if (topElement) { 27 | distance = topElement.getBoundingClientRect().y 28 | - animation.pushElement.getBoundingClientRect().y 29 | - animation.pushElement.getBoundingClientRect().height 30 | - 2; // Margin bottom 31 | } 32 | 33 | let moveDownAnimation = [ 34 | '0', 35 | '0', 36 | '0', 37 | '0', 38 | '0', 39 | '0', 40 | '0', 41 | '0', 42 | '0', 43 | '0', 44 | '0', 45 | '0', 46 | (0.2 * distance) + 'px', 47 | (0.37 * distance) + 'px', 48 | (0.55 * distance) + 'px', 49 | (0.7 * distance) + 'px', 50 | (0.87 * distance) + 'px', 51 | distance + 'px', 52 | distance + 'px' 53 | ]; 54 | 55 | return moveDownAnimation; 56 | } 57 | 58 | function getMoveUpAnimation(animation) { 59 | let topElement = animation.stackElement.children[0]; 60 | let distance = topElement.getBoundingClientRect().y 61 | - animation.pushElement.getBoundingClientRect().y; 62 | 63 | let moveUpAnimation = [ 64 | (distance - 0.95 * distance) + 'px', 65 | (distance - 0.87 * distance) + 'px', 66 | (distance - 0.7 * distance) + 'px', 67 | (distance - 0.55 * distance) + 'px', 68 | (distance - 0.37 * distance) + 'px', 69 | (distance - 0.2 * distance) + 'px', 70 | distance + 'px', 71 | distance + 'px', 72 | distance + 'px', 73 | distance + 'px', 74 | distance + 'px', 75 | distance + 'px', 76 | distance + 'px' 77 | ]; 78 | 79 | return moveUpAnimation; 80 | } 81 | 82 | function pushAnimate(animation) { 83 | animation.pushElement.textContent = animation.value; 84 | 85 | let pushAnimationKeyFrames = { 86 | // 25% - Show element 87 | opacity: ['0', '0.3', '0.7', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1', '1'], 88 | left: ['0', '0', '0', '0', '7px', '20px', '35px', '60px', '75px', '90px', '100px', '100px', '100px', '100px', '100px', '100px', '100px', '100px', '100px'], 89 | top: getMoveDownAnimation(animation), 90 | offset: [0, 0.08, 0.16, 0.25, 0.25, 0.31, 0.37, 0.43, 0.5, 0.56, 0.62, 0.68, 0.73, 0.78, 0.83, 0.88, 0.93, 0.98, 1] 91 | }; 92 | 93 | let duplicateElement = animation.pushElement.cloneNode(true); 94 | duplicateElement.classList.remove('hidden'); 95 | 96 | // TODO: Increase the height of the stack before pushing 97 | 98 | let pushAnimation = animation.pushElement.animate(pushAnimationKeyFrames, animationSpeed); 99 | 100 | return new Promise((resolve) => { 101 | return pushAnimation.finished.then(() => { 102 | animation.stackElement.prepend(duplicateElement); 103 | resolve(`Push Animation completed!`); 104 | }); 105 | }) 106 | } 107 | 108 | function popAnimate(animation) { 109 | let popAnimationKeyFrames = { 110 | bottom: getMoveUpAnimation(animation), 111 | left: ['0', '0', '0', '0', '0', '0', '7px', '20px', '35px', '60px', '75px', '90px', '100px'], 112 | offset: [0, 0.07, 0.15, 0.22, 0.3, 0.37, 0.45, 0.52, 0.6, 0.67, 0.75, 0.82, 0.9, 1] 113 | }; 114 | 115 | let topElement = animation.stackElement.children[0]; 116 | 117 | let popAnimation = topElement.animate(popAnimationKeyFrames, animationSpeed); 118 | // The animation is not smooth 119 | // So remove the item before animation is completed 120 | setTimeout(() => { 121 | topElement.remove(); 122 | animation.popElement.textContent = topElement.textContent; 123 | }, animationSpeed - 200); 124 | 125 | return new Promise((resolve) => { 126 | return popAnimation.finished.then(() => { 127 | // topElement.remove(); 128 | // animation.popElement.textContent = topElement.textContent; 129 | resolve(`Pop Animation completed!`); 130 | }); 131 | }) 132 | } 133 | 134 | function displayCommentAnimate(animation) { 135 | return new Promise((resolve) => { 136 | document.getElementsByClassName('sv-comment')[0] 137 | .innerHTML = animation.value; 138 | 139 | setTimeout(() => { 140 | resolve('Comment displayed!'); 141 | }, animation.timeout); 142 | }) 143 | } 144 | 145 | function displayOutputAnimate(animation) { 146 | return new Promise((resolve) => { 147 | let outputELement = document.getElementById('final-output'); 148 | outputELement.innerHTML = animation.value; 149 | resolve('Output displayed!'); 150 | }) 151 | } 152 | 153 | function runSingleAnimation(animation) { 154 | switch (animation.type) { 155 | case 'push': 156 | return pushAnimate(animation); 157 | break; 158 | case 'pop': 159 | return popAnimate(animation); 160 | break; 161 | case 'display-comment': 162 | return displayCommentAnimate(animation); 163 | break; 164 | case 'display-output': 165 | return displayOutputAnimate(animation); 166 | break; 167 | } 168 | } 169 | 170 | function runAnimations() { 171 | if (animationsRunning) return; 172 | 173 | animationsRunning = true; 174 | // Perform the animations one after the other 175 | let animation = animations.shift(); 176 | runSingleAnimation(animation) 177 | .then(() => { 178 | animationsRunning = false; 179 | if (animations.length()) { 180 | runAnimations(); 181 | } 182 | }); 183 | } 184 | 185 | function getStackElements(stackId) { 186 | let stackWrapper = document.getElementById(stackId); 187 | return { 188 | stackElement: stackWrapper.getElementsByClassName('stack')[0], 189 | pushElement: stackWrapper.getElementsByClassName('push-element')[0], 190 | popElement: stackWrapper.getElementsByClassName('pop-element')[0] 191 | } 192 | } 193 | 194 | let performPushAnimation = function (stackId, value) { 195 | let animationObject = Object.assign( 196 | {type: 'push', value: value}, 197 | getStackElements(stackId) 198 | ); 199 | animations.push(animationObject); 200 | 201 | runAnimations(); 202 | } 203 | 204 | let performPopAnimation = function (stackId, value) { 205 | let animationObject = Object.assign( 206 | {type: 'pop', value: value}, 207 | getStackElements(stackId) 208 | ); 209 | animations.push(animationObject); 210 | 211 | runAnimations(); 212 | } 213 | 214 | /** 215 | * Set the animation duration. 216 | * 217 | * @param flot speed Duration in seconds (1, 2.5...) 218 | */ 219 | let setAnimationSpeed = function (speed) { 220 | animationSpeed = speed * 1000; 221 | } 222 | 223 | let displayComment = function (comment, timeout = 0) { 224 | let animationObject = { 225 | type: 'display-comment', 226 | value: comment, 227 | timeout: timeout 228 | }; 229 | 230 | animations.push(animationObject); 231 | runAnimations(); 232 | } 233 | 234 | let displayOutput = function (output) { 235 | let animationObject = { 236 | type: 'display-output', 237 | value: output 238 | }; 239 | 240 | animations.push(animationObject); 241 | runAnimations(); 242 | } 243 | 244 | let clearAnimations = function () { 245 | animations = new AnimationList(); 246 | animationsRunning = false; 247 | } 248 | 249 | return { 250 | performPushAnimation: performPushAnimation, 251 | performPopAnimation: performPopAnimation, 252 | setAnimationSpeed: setAnimationSpeed, 253 | displayComment: displayComment, 254 | displayOutput: displayOutput, 255 | clearAnimations: clearAnimations 256 | } 257 | })(); -------------------------------------------------------------------------------- /_stacks/assets/js/browser-history.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let browserOps = [ 3 | 'visit(x.com)', 4 | 'visit(fb.com)', 5 | 'visit(ytb.in)', 6 | 'back(1)', 7 | 'back(1)', 8 | 'forward(1)', 9 | 'visit(one.com)', 10 | 'forward(1)', 11 | 'back(2)' 12 | ]; 13 | 14 | const stack = new Stack( 15 | ['wp.com'], 16 | { 17 | name: 'Stack', 18 | description: 'This stack holds the browser history.' 19 | } 20 | ); 21 | 22 | const forwardStack = new Stack( 23 | [], 24 | { 25 | name: 'Forward Stack', 26 | description: 'This stack holds the urls for forward operation.' 27 | } 28 | ); 29 | 30 | let browserOp, value; 31 | for (var i = 0; i < browserOps.length; i++) { 32 | browserOp = browserOps[i].substr(0, browserOps[i].indexOf('(')); 33 | value = browserOps[i].replace(browserOp, '').replaceAll(/[\(\)]/g, ''); 34 | 35 | if (browserOp == 'visit') { 36 | animatorModule.displayComment( 37 | 'Loop through the browser operations.

'+ browserOps[i] +'

' 38 | + 'For visit operation push url to the stack and clear the forward stack.', 39 | 500 40 | ); 41 | stack.push(value); 42 | 43 | while (! forwardStack.isEmpty()) { 44 | forwardStack.pop(); 45 | } 46 | } 47 | 48 | if (browserOp == 'back') { 49 | animatorModule.displayComment( 50 | 'Loop through the browser operations.

'+ browserOps[i] +'

' 51 | + 'For back operation move the urls from input Stack to Forward Stack.', 52 | 500 53 | ); 54 | 55 | value = parseInt(value); 56 | while (value > 0) { 57 | if (stack.isEmpty()) { 58 | break; 59 | } 60 | forwardStack.push(stack.pop()); 61 | value--; 62 | } 63 | 64 | if (value > 0 && ! stack.isEmpty()) { 65 | stack.push(forwardStack.pop()); 66 | } 67 | } 68 | 69 | if (browserOp == 'forward') { 70 | animatorModule.displayComment( 71 | 'Loop through the browser operations.

'+ browserOps[i] +'

' 72 | + 'For forward operation move the urls from Forward Stack to input Stack.', 73 | 500 74 | ); 75 | 76 | value = parseInt(value); 77 | while (value > 0) { 78 | if (forwardStack.isEmpty()) { 79 | animatorModule.displayComment( 80 | 'Loop through the browser operations.

'+ browserOps[i] +'

' 81 | + 'For forward operation move the urls from Forward Stack to input Stack.
' 82 | + 'No URLs to forward.', 83 | 1000 84 | ); 85 | break; 86 | } 87 | stack.push(forwardStack.pop()); 88 | value--; 89 | } 90 | } 91 | } 92 | 93 | animatorModule.displayComment( 94 | 'The current active page: ' + stack.top(), 95 | 500 96 | ); 97 | animatorModule.displayOutput(stack.top()); 98 | return stack.top(); 99 | } -------------------------------------------------------------------------------- /_stacks/assets/js/expression-validator.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let expression = args[0]; 3 | 4 | const stack = new Stack( 5 | [], 6 | { 7 | name: 'Stack', 8 | description: 'This stack is used to hold the brackets.' 9 | } 10 | ); 11 | 12 | let bracketMapping = { 13 | ')': '(', 14 | ']': '[', 15 | '}': '{' 16 | }; 17 | 18 | for (let i = 0; i < expression.length; i++) { 19 | animatorModule.displayComment( 20 | 'Loop through the expression and push all opening brackets onto the stack.
' 21 | + highlightCharacter(expression, i), 22 | 500 23 | ); 24 | 25 | char = expression[i]; 26 | charToMatch = bracketMapping[char] ?? null; 27 | 28 | if (charToMatch) { 29 | animatorModule.displayComment( 30 | 'When a closing bracket is encountered, pop the top element from the stack.
' 31 | + highlightCharacter(expression, i), 32 | 500 33 | ); 34 | 35 | if (stack.isEmpty() || stack.pop() !== charToMatch) { 36 | animatorModule.displayComment( 37 | 'There is no corresponding opening bracket, so the expression is invalid.
' 38 | + highlightCharacter(expression, i), 39 | 1000 40 | ); 41 | 42 | break; 43 | } else { 44 | animatorModule.displayComment( 45 | 'There is a matching opening bracket.
' 46 | + highlightCharacter(expression, i), 47 | 1000 48 | ); 49 | } 50 | 51 | } else { 52 | if (Object.values(bracketMapping).includes(char)) { 53 | stack.push(char); 54 | } 55 | } 56 | } 57 | 58 | let result = stack.isEmpty() ? 'TRUE' : 'FALSE'; 59 | 60 | animatorModule.displayOutput(result); 61 | if (stack.isEmpty()) { 62 | animatorModule.displayComment( 63 | 'It is valid expression.', 64 | 500 65 | ); 66 | } else { 67 | animatorModule.displayComment( 68 | 'The expression has unmatched brackets, so it is invalid.', 69 | 500 70 | ); 71 | } 72 | 73 | return result; 74 | } -------------------------------------------------------------------------------- /_stacks/assets/js/index-of-closing-bracket.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let string = args[0]; 3 | let startIndex = parseInt(args[1]); 4 | let endIndex = -1; 5 | 6 | if (string[startIndex] !== '[') { 7 | animatorModule.displayComment( 8 | 'Error: There is no opening bracket ([) at the given index.', 9 | 15000 10 | ); 11 | } 12 | 13 | const stack = new Stack( 14 | [], 15 | { 16 | name: 'Stack', 17 | description: 'This stack will hold the opening brackets.' 18 | } 19 | ); 20 | 21 | for (var i = startIndex; i < string.length; i++) { 22 | animatorModule.displayComment( 23 | 'Loop through the characters and push all opening brackets onto the stack.
' 24 | + highlightCharacter(string, i), 25 | 500 26 | ); 27 | if (string[i] == '[') { 28 | stack.push('['); 29 | } 30 | 31 | if (string[i] == ']') { 32 | animatorModule.displayComment( 33 | 'When a closing bracket is encountered, pop the top element from the stack.
' 34 | + highlightCharacter(string, i), 35 | 500 36 | ); 37 | 38 | // If stack is empty, then the brackets are not valid 39 | if (stack.isEmpty()) { 40 | animatorModule.displayComment( 41 | 'Error: The string does not contain the valid pair of brackets.', 42 | 15000 43 | ); 44 | return; 45 | } 46 | 47 | stack.pop(); 48 | 49 | if (stack.isEmpty()) { 50 | animatorModule.displayComment( 51 | 'If stack is empty then we have found the end index.', 52 | 1500 53 | ); 54 | endIndex = i; 55 | break; 56 | } 57 | } 58 | } 59 | 60 | animatorModule.displayOutput(endIndex); 61 | animatorModule.displayComment(''); 62 | return endIndex; 63 | } -------------------------------------------------------------------------------- /_stacks/assets/js/lexicographically-smallest-substring.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let string = args[0]; 3 | let smallestString = ''; 4 | 5 | const stack = new Stack( 6 | string.split(''), 7 | { 8 | name: 'Stack', 9 | description: 'This stack will hold the characters of the given string.' 10 | } 11 | ); 12 | 13 | let topChar = ''; 14 | let charCount = {}; 15 | let i = string.length - 1; 16 | while (! stack.isEmpty()) { 17 | animatorModule.displayComment( 18 | 'Pop each element from the stack and compare it with first letter of string.
' 19 | + highlightCharacter(string, i) 20 | + '
Current smallest subsequence:
' 21 | + highlightCharacter(smallestString, 0) +'
', 22 | 200 23 | ); 24 | 25 | topChar = stack.pop(); 26 | 27 | // Increment the count or set the count to 1 28 | charCount[topChar] = (charCount[topChar] || 0) + 1; 29 | 30 | if (smallestString.length > 0 && charCount[topChar] > 1) { 31 | if (topChar > smallestString[0]) { 32 | animatorModule.displayComment( 33 | 'Popped element ('+ topChar +') > first letter ('+ smallestString[0] +'). So ignore it.
' 34 | + highlightCharacter(string, i) 35 | + '
Current smallest subsequence:
' 36 | + highlightCharacter(smallestString, 0) +'
', 37 | 2000 38 | ); 39 | } else { 40 | smallestString = smallestString.replace(topChar, ''); 41 | smallestString = topChar + smallestString; 42 | 43 | animatorModule.displayComment( 44 | 'Popped element ('+ topChar +') <= first letter ('+ smallestString[0] +'). So ignore the duplicate character already present in the string.
' 45 | + highlightCharacter(string, i) 46 | + '
Current smallest subsequence:
' 47 | + highlightCharacter(smallestString, 0) +'
', 48 | 2000 49 | ); 50 | } 51 | 52 | } else { 53 | if (smallestString.length > 0) { 54 | animatorModule.displayComment( 55 | 'Pop each element from the stack and compare it with first letter of string.
' 56 | + highlightCharacter(string, i) 57 | + '
Current smallest subsequence:
' 58 | + highlightCharacter(smallestString, 0) +'
', 59 | 200 60 | ); 61 | } 62 | 63 | smallestString = topChar + smallestString; 64 | } 65 | 66 | i--; 67 | } 68 | 69 | animatorModule.displayOutput(smallestString); 70 | animatorModule.displayComment( 71 | 'Lexicographically smallest subsequence:
' 72 | + highlightSubstring(smallestString, 0, smallestString.length) 73 | ); 74 | return smallestString; 75 | } -------------------------------------------------------------------------------- /_stacks/assets/js/longest-valid-parenthesis.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let string = args[0]; 3 | let length = 0; 4 | let maxLength = 0; 5 | let startIndex = 0; 6 | let validParentheses = {}; 7 | 8 | const stack = new Stack( 9 | [], 10 | { 11 | name: 'Stack', 12 | description: 'This stack will hold the opening parentheses.' 13 | } 14 | ); 15 | 16 | for (var i = 0; i < string.length; i++) { 17 | if (string[i] == '(') { 18 | animatorModule.displayComment( 19 | 'Loop through the string and push all opening brackets onto the stack.
' 20 | + highlightCharacter(string, i), 21 | 500 22 | ); 23 | 24 | stack.push('('); 25 | startIndex++; 26 | } else { 27 | if (! stack.isEmpty()) { 28 | animatorModule.displayComment( 29 | 'When a closing parentheses is encountered, pop the top element from the stack.
' 30 | + highlightCharacter(string, i), 31 | 500 32 | ); 33 | 34 | stack.pop(); 35 | length +=2; 36 | startIndex--; 37 | 38 | animatorModule.displayComment( 39 | 'Current valid substring.
' 40 | + highlightSubstring(string, startIndex, length), 41 | 750 42 | ); 43 | } else { 44 | if (length > 0) { 45 | animatorModule.displayComment( 46 | 'Unmatched closing parentheses.
' 47 | + highlightCharacter(string, i), 48 | 1500 49 | ); 50 | 51 | animatorModule.displayComment( 52 | 'A valid substring is found, continue searching for the next substring.
' 53 | + highlightSubstring(string, startIndex, length), 54 | 1500 55 | ); 56 | } 57 | 58 | startIndex = i + 1; 59 | length = 0; 60 | } 61 | } 62 | 63 | validParentheses[startIndex] = length; 64 | } 65 | 66 | let key = Object.keys(validParentheses) 67 | .reduce((a, b) => validParentheses[a] > validParentheses[b] ? a : b); 68 | 69 | maxLength = validParentheses[key]; 70 | 71 | animatorModule.displayOutput(maxLength); 72 | animatorModule.displayComment( 73 | 'Longest valid parentheses:
' + highlightSubstring(string, parseInt(key), maxLength) 74 | ); 75 | return maxLength; 76 | } -------------------------------------------------------------------------------- /_stacks/assets/js/next-bigger-number.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) {console.log(args); 2 | let array = args[0]; 3 | let referenceNumber = args[1]; 4 | 5 | const stack = new Stack( 6 | array, 7 | { 8 | name: 'Stack', 9 | description: 'This stack holds the input array elements.' 10 | } 11 | ); 12 | 13 | const tempStack = new Stack( 14 | [], 15 | { 16 | name: 'Temp Stack', 17 | description: 'Temporary stack to get the compare the numbers.' 18 | } 19 | ); 20 | 21 | animatorModule.displayComment( 22 | 'Pop from the input stack until the reference number('+ referenceNumber +') is found.', 23 | 1000 24 | ); 25 | 26 | let nextBiggerNumber = -1; 27 | while (! stack.isEmpty()) { 28 | if (stack.top() == referenceNumber) { 29 | animatorModule.displayComment( 30 | 'The reference number('+ referenceNumber +') is found.', 31 | 1000 32 | ); 33 | break; 34 | } 35 | 36 | tempStack.push(stack.pop()); 37 | } 38 | 39 | animatorModule.displayComment( 40 | 'Now pop from the second stack and check if the popped number is bigger than reference number.', 41 | 1000 42 | ); 43 | 44 | let topElement = ''; 45 | while (! tempStack.isEmpty()) { 46 | topElement = tempStack.pop(); 47 | 48 | if (topElement > referenceNumber) { 49 | animatorModule.displayComment( 50 | 'Next bigger number is found: ' + topElement, 51 | 1000 52 | ); 53 | nextBiggerNumber = topElement; 54 | break; 55 | } 56 | } 57 | 58 | if (nextBiggerNumber == -1) { 59 | animatorModule.displayComment( 60 | 'There is no next bigger number.', 61 | 1000 62 | ); 63 | } 64 | 65 | animatorModule.displayOutput(nextBiggerNumber); 66 | return nextBiggerNumber; 67 | } -------------------------------------------------------------------------------- /_stacks/assets/js/path-simplifier.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let string = args[0]; 3 | let paths = string.split('/'); 4 | 5 | const stack = new Stack( 6 | [], 7 | { 8 | name: 'Stack', 9 | description: 'This stack holds the path segments.' 10 | } 11 | ); 12 | 13 | animatorModule.displayComment('Push the individual path segments to the stack.'); 14 | for (var i = 0; i < paths.length; i++) { 15 | if (paths[i] !== '') { 16 | stack.push(paths[i]); 17 | } 18 | } 19 | 20 | let simplifiedPath = ''; 21 | let skipNextPath = false; 22 | while (! stack.isEmpty()) { 23 | if (skipNextPath) { 24 | animatorModule.displayComment('If ".." is encountered skip the next path.', 500); 25 | } else { 26 | animatorModule.displayComment( 27 | 'Pop individual path segments and prepend them.
'+ simplifiedPath +'
' 28 | ); 29 | } 30 | path = stack.pop(); 31 | 32 | if (path == '..') { 33 | skipNextPath = true; 34 | } else { 35 | if (path == '.') { 36 | animatorModule.displayComment('If "." is encountered ignore it.', 1500); 37 | } 38 | 39 | if (path !== '.' && ! skipNextPath) { 40 | // The first letter in simplifiedPath is '/' 41 | simplifiedPath = '/' + path + simplifiedPath; 42 | } 43 | 44 | animatorModule.displayOutput(simplifiedPath); 45 | skipNextPath = false; 46 | } 47 | } 48 | 49 | simplifiedPath = (simplifiedPath == '') ? '/' : simplifiedPath; 50 | 51 | animatorModule.displayComment('Final path:
' + simplifiedPath + '
', 1000); 52 | animatorModule.displayOutput(simplifiedPath); 53 | animatorModule.displayComment(''); 54 | return simplifiedPath; 55 | } -------------------------------------------------------------------------------- /_stacks/assets/js/reverse-string.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let string = args[0]; 3 | 4 | const stack = new Stack( 5 | [], 6 | { 7 | name: 'Stack', 8 | description: 'This stack is used to hold the string characters.' 9 | } 10 | ); 11 | 12 | animatorModule.displayComment('Push individual characters of the string to the stack.'); 13 | for (var i = 0; i < string.length; i++) { 14 | stack.push(string[i]); 15 | } 16 | 17 | animatorModule.displayComment('Now pop the characters from stack and concatenate them.'); 18 | let reversedString = ''; 19 | while (! stack.isEmpty()) { 20 | reversedString += stack.pop(); 21 | animatorModule.displayOutput(reversedString); 22 | } 23 | 24 | animatorModule.displayComment(''); 25 | return reversedString; 26 | } -------------------------------------------------------------------------------- /_stacks/assets/js/sort-stack.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let array = args[0]; 3 | 4 | const stack = new Stack( 5 | array, 6 | { 7 | name: 'Input Stack', 8 | description: 'This stack holds the input array elements.' 9 | } 10 | ); 11 | 12 | const tempStack = new Stack( 13 | [], 14 | { 15 | name: 'Temp Stack', 16 | description: 'This stack will hold the sorted array elements.' 17 | } 18 | ); 19 | 20 | let temp = ''; 21 | while (! stack.isEmpty()) { 22 | animatorModule.displayComment('Pop the element from the input stack.', 500); 23 | temp = stack.pop(); 24 | 25 | while (! tempStack.isEmpty() && tempStack.top() < temp) { 26 | animatorModule.displayComment( 27 | 'If the popped element('+ temp +') is greater than the elements in the tempStack, then push the elements from tempStack to input stack.', 28 | 700 29 | ); 30 | stack.push(tempStack.pop()); 31 | } 32 | 33 | animatorModule.displayComment('Add the popped element to the tempStack.', 500); 34 | tempStack.push(temp); 35 | } 36 | 37 | let sortedArray = []; 38 | while (! tempStack.isEmpty()) { 39 | animatorModule.displayComment( 40 | 'Finally we get the sorted list in the tempStack. Pop and get the sorted list.', 41 | 500 42 | ); 43 | animatorModule.displayOutput([...sortedArray]); 44 | sortedArray.push(tempStack.pop()); 45 | } 46 | 47 | animatorModule.displayOutput(sortedArray); 48 | animatorModule.displayComment(''); 49 | return sortedArray; 50 | } -------------------------------------------------------------------------------- /_stacks/assets/js/stack-displayer.js: -------------------------------------------------------------------------------- 1 | class StackDisplayer { 2 | constructor(initialData, metaData) { 3 | this.initialData = initialData; 4 | this.metaData = metaData; 5 | } 6 | 7 | display() { 8 | // Display stack on the screen 9 | const stackTemplate = document.getElementById('stack-template'); 10 | const clone = stackTemplate.content.cloneNode(true); 11 | const stackCount = document.getElementsByClassName('stack-wrapper').length + 1; 12 | 13 | // Add an unique ID to the clone 14 | const stackWrapperId = 'stack-' + stackCount; 15 | const stackWrapper = clone.querySelector('.stack-wrapper'); 16 | stackWrapper.id = stackWrapperId; 17 | 18 | // Add initial stack items 19 | const stackElement = clone.querySelector('.stack'); 20 | while(this.initialData.length) { 21 | stackElement.insertAdjacentHTML( 22 | 'beforeend', 23 | '
'+ this.initialData.pop() +'
' 24 | ) 25 | } 26 | 27 | clone.querySelector('.stack-description h4') 28 | .textContent = this.metaData.name; 29 | clone.querySelector('.stack-description p') 30 | .textContent = this.metaData.description; 31 | 32 | document.getElementById('stack-visualizer') 33 | .appendChild(clone); 34 | 35 | return stackWrapperId; 36 | } 37 | } -------------------------------------------------------------------------------- /_stacks/assets/js/stack.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | metaData = { 3 | 'name': 'Stack', 4 | 'description': '', 5 | 'animation_duration': 1500, 6 | } 7 | 8 | constructor(initialData, metaData) { 9 | this.initialData = [...initialData]; 10 | this.metaData = Object.assign({}, this.metaData, metaData); 11 | 12 | this.stackDisplayer = new StackDisplayer(initialData, this.metaData); 13 | this.stackId = this.stackDisplayer.display(); 14 | } 15 | 16 | getStackData() { 17 | return this.initialData; 18 | } 19 | 20 | push(value) { 21 | this.initialData.push(value); 22 | 23 | // Perform the animation 24 | animatorModule.performPushAnimation(this.stackId, value); 25 | } 26 | 27 | pop() { 28 | if (this.initialData.length == 0) return; 29 | 30 | const value = this.initialData.pop(); 31 | 32 | // Perform the animation 33 | animatorModule.performPopAnimation(this.stackId, value); 34 | 35 | return value; 36 | } 37 | 38 | top() { 39 | if (this.initialData.length == 0) return; 40 | 41 | const value = this.initialData[this.initialData.length - 1]; 42 | 43 | return value; 44 | } 45 | 46 | isEmpty() { 47 | return this.initialData.length === 0; 48 | } 49 | } -------------------------------------------------------------------------------- /_stacks/assets/js/stock-span-problem.js: -------------------------------------------------------------------------------- 1 | function stackSolution(args) { 2 | let array = args[0]; 3 | 4 | const stack1 = new Stack( 5 | [], 6 | { 7 | name: 'Stack 1', 8 | description: 'Stack 1.' 9 | } 10 | ); 11 | 12 | const stack2 = new Stack( 13 | [], 14 | { 15 | name: 'Stack 2', 16 | description: 'Stack 2' 17 | } 18 | ); 19 | 20 | let popOpsCount = 0; 21 | let currentValue = null; 22 | let stockSpan = []; 23 | let currentStockSpan = 1; 24 | while (array.length) { 25 | animatorModule.displayComment( 26 | 'Loop through the array and compare the previous values in stack.
' 27 | + highlightArray([...array], 0) 28 | + '
Stock Span: ' + currentStockSpan, 29 | 500 30 | ); 31 | 32 | currentValue = array[0]; 33 | currentStockSpan = 1; 34 | 35 | while (! stack1.isEmpty()) { 36 | if (stack1.top() > currentValue) { 37 | break; 38 | } 39 | 40 | animatorModule.displayComment( 41 | 'If the top element of Stack 1 is smaller then push it Stack 2.
' 42 | + highlightArray([...array], 0) 43 | + '
Stock Span: ' + currentStockSpan, 44 | 500 45 | ); 46 | 47 | stack2.push(stack1.pop()); 48 | popOpsCount++; 49 | currentStockSpan++; 50 | } 51 | 52 | while (popOpsCount > 0) { 53 | if (stack1.isEmpty()) { 54 | animatorModule.displayComment( 55 | 'Stack 1 is empty. Now push the elements back to Stack 1
' 56 | + highlightArray([...array], 0) 57 | + '
Stock Span: ' + currentStockSpan, 58 | 500 59 | ); 60 | } else { 61 | animatorModule.displayComment( 62 | 'We hit the larger value. Now push the elements back to Stack 1
' 63 | + highlightArray([...array], 0) 64 | + '
Stock Span: ' + currentStockSpan, 65 | 500 66 | ); 67 | } 68 | 69 | stack1.push(stack2.pop()); 70 | popOpsCount--; 71 | } 72 | 73 | array.shift(); 74 | stack1.push(currentValue); 75 | stockSpan.push(currentStockSpan); 76 | animatorModule.displayOutput([...stockSpan]); 77 | } 78 | 79 | animatorModule.displayOutput(stockSpan); 80 | animatorModule.displayComment(''); 81 | return stockSpan; 82 | } -------------------------------------------------------------------------------- /_stacks/browser-history.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Design Browser History 4 | noinputtext: | 5 | Homepage: wp.com 6 | [ 7 | 'visit(x.com)', 8 | 'visit(fb.com)', 9 | 'visit(ytb.in)', 10 | 'back(1)', 11 | 'back(1)', 12 | 'forward(1)', 13 | 'visit(one.com)', 14 | 'forward(1)', 15 | 'back(2)' 16 | ] 17 | --- 18 | 19 | #### Problem: 20 | 21 | You have a browser of one tab where you start on the homepage and you can visit another url, get back in the history number of steps or move forward in the history number of steps. So these operations can be performed on the browser history: 22 | 23 | - visit(url): Visits a URL given as string 24 | - forward(steps): Forward the given number of steps 25 | - backward(steps): Backward the given number of steps 26 | 27 | When a set of operations is given, find the active page the user is on at the end of these operations. 28 | 29 | Example: 30 | 31 | **Home Pate:** wp.com 32 | 33 | **Input Array (Browser operations):** 34 | 35 | [ 36 | 'visit(x.com)' 37 | 'visit(fb.com)' 38 | 'visit(ytb.in)' 39 | 'back(1)' 40 | 'back(1)' 41 | 'forward(1)' 42 | 'visit(one.com)' 43 | 'forward(1)' 44 | 'back(2)' 45 | ] 46 | 47 | **Output (The current page user is on):** 'x.com' 48 | 49 | #### Algorithm: 50 | 51 | We can solve this using two stacks. When the user performs `visit` operations we can keep track of the active urls in stack 1. And when the user performs the `back` operation then we need to pop the urls from stack 1 and push them onto stack 2. The urls in the stack 2 will be used to perform the `forward` opearion. Finally the url at the top of the stack 1 is the current active url. 52 | 53 | **Steps:** 54 | 55 | 1. Loop through the operations and perform these steps: 56 | 1. For `visit` operation push the url to the stack 1. 57 | 2. For `backward` operation pop the url from stack 1 and push to stack 2. 58 | 3. For `forward` operation pop the url from stack 2 and push to stack 1. 59 | 2. Finally the url at the top of the stack 1 is the current active url. 60 | 61 | #### Solution & Visualize: 62 | 63 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 64 | -------------------------------------------------------------------------------- /_stacks/expression-validator.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Check for valid parenthesis 4 | inputs: 5 | - type: string 6 | label: Please enter the input string 7 | regex: ^([\d\s+\-\*\/\(\)\[\]\{\}]+)$ 8 | default: 5 + [7 * (8 + 5]) 9 | --- 10 | 11 | #### Problem: 12 | 13 | Given an expression string containing the bracket characters {, }, (, ), [, and ], check whether the expression is valid. A valid expression must have matching pairs of brackets and maintain the correct order. 14 | 15 | Example: 16 | 17 | **Input String:** '{[()]}' 18 | 19 | **Output:** TRUE 20 | 21 | The output is TRUE because all the brackets are properly closed. 22 | 23 | **Input String:** '{[(])}' 24 | 25 | **Output:** FALSE 26 | 27 | The output is FALSE because the bracket pairs are not closed in the correct order. 28 | 29 | **Input String:** '[5 * (6 + 7)]' 30 | 31 | **Output:** TRUE 32 | 33 | The output is TRUE because all the brackets are properly closed. 34 | 35 | **Input String:** '5 + [7 * (8 + 5])' 36 | 37 | **Output:** FALSE 38 | 39 | The output is FALSE because the square bracket is closed before the parenthesis. 40 | 41 | #### Algorithm: 42 | 43 | In this problem, we need to ensure that each opening bracket is closed by the corresponding type of closing bracket and that the brackets are closed in the correct order. Additionally, every closing bracket must have a matching opening bracket. 44 | 45 | **Steps:** 46 | 47 | 1. Loop through the characters in the expression and push all opening brackets onto the stack. 48 | 2. When encountering a closing bracket, check if the opening bracket at the top of the stack corresponds to it. 49 | 1. If a matching opening bracket is found at the top of the stack, remove it and continue the loop. 50 | 2. If no matching opening bracket is found, the brackets are not properly closed, and the expression is invalid. 51 | 3. At the end of the loop, if the stack is not empty, it indicates that there are unmatched opening brackets, so the expression is invalid. 52 | 53 | #### Solution & Visualize: 54 | 55 | The animation/visualization below illustrates the algorithm. Feel free to experiment with different input values. 56 | -------------------------------------------------------------------------------- /_stacks/index-of-closing-bracket.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Find the index of closing bracket for a given opening bracket 4 | inputs: 5 | - type: string 6 | label: Please enter the input string 7 | regex: .?\[.+\] 8 | default: "[X[YZ][AB]][MN]" 9 | - type: number 10 | label: Please enter the start index 11 | default: 0 12 | --- 13 | 14 | #### Problem: 15 | 16 | Given a string containing square brackets, if the index of an opening bracket is provided, find the index of its corresponding closing bracket. Assume that only square brackets ([ and ]) are used. 17 | 18 | Example: 19 | 20 | **Input string:** '[X[YZ][AB]][MN]' 21 | 22 | **Input (Opening bracket Index):** 0 23 | 24 | **Output:** 10 25 | 26 | The closing bracket at index 10 matches the opening bracket at index 0. 27 | 28 | **Input (Opening bracket Index):** 6 29 | 30 | **Output:** 9 31 | 32 | The closing bracket at index 9 matches the opening bracket at index 6. 33 | 34 | #### Algorithm: 35 | 36 | To solve this, we will start from the given index of the opening bracket and push brackets onto the stack. When we encounter a closing bracket, we pop the opening bracket from the stack. If the stack becomes empty after popping, the current index will be the index of the closing bracket. 37 | 38 | **Steps:** 39 | 40 | 1. Loop through the characters starting from the index of the opening bracket. 41 | 2. Push all opening brackets onto the stack. 42 | 3. When a closing bracket is encountered, pop the top element from the stack. If the stack is empty after this operation, the current index will be the index of the closing bracket. 43 | 44 | #### Solution & Visualize: 45 | 46 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 47 | -------------------------------------------------------------------------------- /_stacks/lexicographically-smallest-substring.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Find the lexicographically smallest substring with distinct characters 4 | inputs: 5 | - type: string 6 | label: Please enter the input string 7 | regex: ^[a-z]+$ 8 | default: bcabc 9 | feedback: The string should only contain the lowercase English letters. 10 | --- 11 | 12 | #### Problem: 13 | 14 | Given a string, find the lexicographically smallest subsequence that can be formed using all distinct characters only once from the given string. The input string will only contain lowercase English letters. 15 | 16 | Example: 17 | 18 | **Input string:** 'bcabc' 19 | 20 | **Output:** 'abc' 21 | 22 | **Input string:** 'cbacdcbc' 23 | 24 | **Output:** 'acdb' 25 | 26 | #### Algorithm: 27 | 28 | To solve this, we will start from the given index of the opening bracket and push brackets onto the stack. When we encounter a closing bracket, we pop the opening bracket from the stack. If the stack becomes empty after popping, the current index will be the index of the closing bracket. 29 | 30 | **Steps:** 31 | 32 | 1. Loop through the characters of the string in reverse order to construct the smallest subsequence. 33 | 2. If duplicate characters are encountered, compare the current character with the first character of the subsequence. 34 | 1. If the current character is larger than the first character of the subsequence, ignore it. 35 | 2. If the current character is smaller or equal, prepend it to the subsequence and remove the duplicate character already present in the subsequence. 36 | 3. After completing these steps, the resulting subsequence will be the smallest possible subsequence. 37 | 38 | #### Solution & Visualize: 39 | 40 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 41 | -------------------------------------------------------------------------------- /_stacks/longest-valid-parenthesis.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Longest Valid Parentheses 4 | inputs: 5 | - type: string 6 | label: Please enter the input string 7 | regex: ^([\(-\)]+)$ 8 | default: ())(()()()) 9 | --- 10 | 11 | #### Problem: 12 | 13 | Given a string containing just the characters '(' and ')', return the length of the longest valid (well-formed) parentheses substring. 14 | 15 | Example: 16 | 17 | **Input String:** '(' 18 | 19 | **Output:** 0 20 | 21 | There is no closing parentheses. 22 | 23 | **Input String:** '))' 24 | 25 | **Output:** 0 26 | 27 | There is no valid parentheses substring. 28 | 29 | **Input String:** '))()' 30 | 31 | **Output:** 2 32 | 33 | The longest valid parentheses substring is "()". 34 | 35 | **Input String:** ')()())(()()())' 36 | 37 | **Output:** 8 38 | 39 | The longest valid parentheses substring is "(()()())". 40 | 41 | #### Algorithm: 42 | 43 | To solve this, we use a stack to find the valid parentheses substring. When encountering an unmatched closing parenthesis, we check for the next valid substring. By keeping track of the length and the starting index of each valid substring, we can identify the longest valid substring. 44 | 45 | **Steps:** 46 | 47 | 1. Loop through the string and push all opening parentheses onto the stack until a closing parenthesis is encountered. 48 | 2. For every opening parenthesis, increment the startIndex. 49 | 3. When a closing parenthesis is encountered: 50 | 1. If the stack is not empty, pop the opening parenthesis from the stack. This forms a matching parentheses pair. Decrement the startIndex and increase the length of the valid substring by 2. 51 | 2. If the stack is empty, it means the valid substring ends at the current index. Continue searching for the next valid substring from the next index. 52 | 4. Finally, find the maximum length among all valid substrings. 53 | 54 | #### Solution & Visualize: 55 | 56 | The animation/visualization below illustrates the algorithm. Feel free to experiment with different input values. 57 | -------------------------------------------------------------------------------- /_stacks/next-bigger-number.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Find The Next Bigger Number 4 | alt: Find The Next Greater Element 5 | inputs: 6 | - type: array 7 | label: Please enter the input array 8 | default: 5, 9, 3, 21, 18 9 | - type: number 10 | label: Please enter the reference number 11 | default: 9 12 | --- 13 | 14 | #### Problem: 15 | 16 | Given an array of numbers, find the next bigger number to the right of a specified number. That is, for each number in the array, determine the next larger number that appears to its right. If no such number exists, return -1. You can assume that the array contains unique numbers. 17 | 18 | Example: 19 | 20 | **Input Array:** [5, 9, 3, 21, 18] 21 | 22 | **Input Number:** 5 23 | 24 | **Output:** 9 25 | 26 | 9 is the next larger number after 5. 27 | 28 | **Input Number:** 9 29 | 30 | **Output:** 21 31 | 32 | In the same array, after 9, we have 3, which is not larger than 9. Therefore, we move to the next element, 21, which is larger than 9, and 21 is returned. 33 | 34 | **Input Number:** 21 35 | 36 | **Output:** -1 37 | 38 | After 21, there are no numbers larger than 21 in the array. 39 | 40 | #### Algorithm: 41 | 42 | To solve this, we need to traverse the array from the end and push each element onto the stack until we encounter the reference number. Once the reference number is found, we begin popping elements from the stack and checking if each popped number is greater than the reference number. 43 | 44 | **Steps:** 45 | 46 | 1. Loop through the array from the end and push each element onto the stack until the reference number is encountered. 47 | 2. Once the reference number is found, pop elements from the stack and compare each with the reference number. 48 | 3. If a popped number is greater than the reference number, that number is the answer. 49 | 4. If the stack becomes empty and no greater number is found, return -1. 50 | 51 | #### Solution & Visualize: 52 | 53 | The animation/visualization below illustrates the algorithm. Feel free to experiment with different input values. 54 | -------------------------------------------------------------------------------- /_stacks/path-simplifier.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Simplify Absolute UNIX Path 4 | inputs: 5 | - type: string 6 | label: Please enter the UNIX path 7 | regex: ^((?!\\).)*$ 8 | default: /home/user//Documents/../Pictures 9 | --- 10 | 11 | #### Problem: 12 | 13 | You are given an absolute path for a Unix-style file system, which always begins with a slash '/'. Your task is to transform this absolute path into its simplified canonical path. 14 | 15 | The Unix-style file system has following rules: 16 | 17 | - A single period '.' represents the current directory. 18 | - A double period '..' represents the previous/parent directory. 19 | - Multiple consecutive slashes such as '//' and '///' are treated as a single slash '/'. 20 | - Any sequence of periods that does not match the rules above should be treated as a valid directory or file name. For example, '...' and '....' are valid directory or file names. 21 | 22 | The simplified canonical path should follow these rules: 23 | 24 | - The path must start with a single slash '/'. 25 | - Directories within the path must be separated by exactly one slash '/'. 26 | - The path must not end with a slash '/', unless it is the root directory. 27 | - The path must not have any single or double periods ('.' and '..') used to denote current or parent directories. 28 | 29 | Example: 30 | 31 | **Input Path:** '/home/' 32 | 33 | **Output (Simplified Path):** '/home' 34 | 35 | **Input Path:** '/home/user//Documents/../Pictures' 36 | 37 | **Output (Simplified Path):** '/home/user/Pictures' 38 | 39 | **Input Path:** '/.../a/../b/c/../d/./' 40 | 41 | **Output (Simplified Path):** '/.../b/d' 42 | 43 | #### Algorithm: 44 | 45 | To solve this, we need to split the string at each forward slash ('/') and process each path segment individually. If we encounter '.' or '..', we skip the corresponding path segments. 46 | 47 | **Steps:** 48 | 49 | 1. Split the string at each forward slash ('/') and push each segment onto the stack. 50 | 2. When processing the segments, if '.' is encountered, skip it. If '..' is encountered, pop the top element from the stack to move one level up in the path. 51 | 3. After processing all segments, reconstruct the path by popping from the stack and joining the valid segments. 52 | 53 | #### Solution & Visualize: 54 | 55 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 56 | -------------------------------------------------------------------------------- /_stacks/reverse-string.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Reverse a String Using Stack 4 | inputs: 5 | - type: string 6 | label: Please enter the input string 7 | default: World 8 | --- 9 | 10 | #### Problem: 11 | 12 | Given a string, reverse it using stack. Do not use any built in string or array reverse functions. 13 | 14 | Example: 15 | 16 | **Input String:** JavaScript 17 | 18 | **Output String:** tpircSavaJ 19 | 20 | #### Algorithm: 21 | 22 | This is a relatively simple problem. The approach involves pushing all the characters of the string onto a stack and then reading them by popping them off. Since a stack follows the Last-In-First-Out (LIFO) principle, the string is automatically reversed during the pop operations. 23 | 24 | **Steps:** 25 | 26 | 1. Create an empty stack and push all the characters of the string onto it. 27 | 2. Pop the characters from the stack one by one and concatenate them to form the reversed string. 28 | 29 | #### Solution & Visualize: 30 | 31 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 32 | -------------------------------------------------------------------------------- /_stacks/sort-stack.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Sort A Given Stack 4 | inputs: 5 | - type: array 6 | label: Please enter the input array 7 | default: 36, 9, 22, 75, 92 8 | --- 9 | 10 | #### Problem: 11 | 12 | Given a stack of integers, sort it in ascending order. 13 | 14 | Example: 15 | 16 | **Input Array:** [36, 9, 22, 75, 92, 23] 17 | 18 | **Output Array:** [9, 22, 23, 36, 75, 92] 19 | 20 | #### Algorithm: 21 | 22 | To solve this, we will use an additional temporary stack. We will pop elements from the input stack and push them to the temporary stack, comparing each element with those already in the temporary stack to maintain the correct order. 23 | 24 | **Steps:** 25 | 26 | 1. Create a temporary stack, tempStack. 27 | 2. While the input stack is not empty, do the following: 28 | 1. Pop an element from the input stack and call it temp. 29 | 2. While the temporary stack is not empty and the top element of tempStack is greater than temp, pop elements from tempStack and push them back to the input stack. 30 | 3. Push temp into tempStack. 31 | 3. After the loop ends, the sorted elements will be in tempStack. 32 | 33 | #### Solution & Visualize: 34 | 35 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 36 | -------------------------------------------------------------------------------- /_stacks/stock-span-problem.md: -------------------------------------------------------------------------------- 1 | --- 2 | layout: visualizer 3 | title: Stock Span Problem 4 | inputs: 5 | - type: array 6 | label: Please enter the input array 7 | default: 150, 120, 140, 160, 140, 145, 150 8 | --- 9 | 10 | #### Problem: 11 | 12 | Given a series of daily price quotes for a stock over N days, calculate the span of the stock’s price for each day. The span S(i) of the stock’s price on a given day i is defined as the maximum number of consecutive days before day i, including day i itself, for which the price of the stock on the current day is greater than or equal to the price on the previous days. 13 | 14 | Example: 15 | 16 | **Input Array (Stock price):** [150, 120, 140, 160, 140, 145, 150] 17 | 18 | **Output (Stock Span):** [1, 1, 2, 4, 1, 2, 3] 19 | 20 | For the first value, 150, there are no previous values, so the stock span is 1. For the value 120, the previous value is 150, which is greater than 120, so the stock span is 1. However, for 140, the previous value 120 is smaller, and the stock span extends to 2, as 140 is greater than 120. Similarly, for 150 at the end of the array, there are 3 values (including the current value itself) that are less than or equal to 150, so the stock span is 3. 21 | 22 | #### Algorithm: 23 | 24 | To solve this, we need two stacks. We will loop through the price array and push each item onto Stack 1. For each item pushed onto Stack 1, we will check if the previous values are less than or equal to the current value being pushed. To achieve this, we can use a second stack (Stack 2). After finding the stock span for each value, we can restore Stack 1 by transferring the elements from Stack 2 back to it. 25 | 26 | **Steps:** 27 | 28 | 1. Loop through the price array and push each price onto Stack 1. 29 | 2. For each price pushed onto Stack 1, compare it with the previous values in Stack 1. 30 | 3. To find the stock span, pop values from Stack 1 and push them onto Stack 2 until you find a value that is greater than the current price. The number of popped elements gives the stock span for that day. 31 | 4. Once Stack 1 is empty, push all the elements from Stack 2 back into Stack 1 and continue processing the next price in the array. 32 | 33 | #### Solution & Visualize: 34 | 35 | The animation/visualization below demonstrates how the algorithm to reverse a string works. Feel free to experiment by changing the input strings. 36 | -------------------------------------------------------------------------------- /assets/css/main.css: -------------------------------------------------------------------------------- 1 | /************ Layout ************/ 2 | :root { 3 | --text-primary: rgb(234,88,12); 4 | --text-normal: rgb(39, 39, 42); 5 | --text-hover: rgb(194, 65, 12); 6 | --text-muted: rgba(39, 39, 42, 0.85); 7 | --text-code: rgb(126, 34, 206); 8 | } 9 | .d-none { 10 | display: none; 11 | } 12 | .d-flex { 13 | display: flex; 14 | } 15 | .flex-column { 16 | flex-direction: column; 17 | } 18 | .justify-center { 19 | justify-content: center; 20 | } 21 | .align-center { 22 | align-items: center; 23 | } 24 | .gap-3 { 25 | gap: 1rem; 26 | } 27 | .gap-4 { 28 | gap: 1.5rem; 29 | } 30 | .p-2 { 31 | padding: 0.5rem; 32 | } 33 | .py-2 { 34 | padding-top: 0.5rem; 35 | padding-bottom: 0.5rem; 36 | } 37 | .pb-5 { 38 | padding-bottom: 3rem; 39 | } 40 | .m-0 { 41 | margin: 0; 42 | } 43 | .mt-0 { 44 | margin-top: 0; 45 | } 46 | .mb-2 { 47 | margin-bottom: 0.5rem; 48 | } 49 | .mb-4 { 50 | margin-bottom: 1rem; 51 | } 52 | .me-2 { 53 | margin-right: 0.5rem; 54 | } 55 | .m-2 { 56 | margin-top: 0.5rem; 57 | margin-bottom: 0.5rem; 58 | } 59 | .mx-auto { 60 | margin: auto; 61 | } 62 | .w-100 { 63 | width: 100%; 64 | } 65 | .text-primary { 66 | color: var(--text-primary); 67 | } 68 | .text-muted { 69 | color: var(--text-muted); 70 | } 71 | .text-center { 72 | text-align: center; 73 | } 74 | .text-right { 75 | text-align: right; 76 | } 77 | .ff-main { 78 | font-family: "Montserrat", sans-serif; 79 | } 80 | .fw-bold { 81 | font-weight: bold; 82 | } 83 | .fw-600 { 84 | font-weight: 600; 85 | } 86 | .fs-2 { 87 | font-size: 1.75rem; 88 | line-height: 1.25; 89 | } 90 | .fs-4 { 91 | font-size: 1.25rem; 92 | } 93 | .btn { 94 | cursor: pointer; 95 | padding: 0.5rem 0.75rem; 96 | border-radius: 6px; 97 | border: 1px solid; 98 | } 99 | .btn-primary { 100 | background-color: rgb(126, 34, 206); 101 | color: #FFF; 102 | } 103 | .btn-primary:hover { 104 | background-color: rgba(126, 34, 206, 0.9); 105 | } 106 | /********************************/ 107 | 108 | 109 | body { 110 | margin: 0; 111 | font-family: "Lato", sans-serif; 112 | font-weight: 400; 113 | font-size: 18px; 114 | color: var(--text-normal); 115 | line-height: 24px; 116 | } 117 | a { 118 | color: var(--text-code); 119 | } 120 | a:hover { 121 | color: var(--text-hover); 122 | } 123 | h4 { 124 | margin: 1rem 0; 125 | } 126 | p { 127 | margin: 0.75rem 0; 128 | } 129 | header { 130 | background-image: linear-gradient(rgb(255, 247, 237), rgb(255,251,247)); 131 | position: sticky; 132 | top: 0; 133 | } 134 | header .container { 135 | height: 100px; 136 | justify-content: space-between; 137 | } 138 | .navbar-logo a { 139 | text-decoration: none; 140 | } 141 | .nav-item { 142 | text-decoration: none; 143 | } 144 | .container { 145 | max-width: 768px; 146 | padding: 0 25px; 147 | } 148 | figure img { 149 | max-width: 100%; 150 | } 151 | .main-img { 152 | border-radius: 10px; 153 | } 154 | main { 155 | background-image: linear-gradient(0deg, rgb(255, 247, 237), rgba(255, 247, 237, 0) 45px); 156 | } 157 | footer { 158 | /*background-color: rgb(255, 247, 237);*/ 159 | background-color: rgb(255, 242, 232); 160 | } 161 | footer .container { 162 | min-height: 100px; 163 | } 164 | code { 165 | font-size: 14px; 166 | } 167 | code.language-plaintext { 168 | color: var(--text-code); 169 | font-weight: bold; 170 | } 171 | .solution-tab-panel { 172 | box-shadow: rgba(60, 64, 67, 0.3) 0px 1px 2px 0px, rgba(60, 64, 67, 0.15) 0px 1px 3px 1px; 173 | } 174 | .solutions-list li { 175 | padding: 5px 0; 176 | } 177 | .solutions-list li, 178 | .solutions-list li a { 179 | color: var(--text-code); 180 | } 181 | .post-tag { 182 | /* background-color: rgba(126, 34, 206, 0.15); 183 | color: rgb(126, 34, 206);*/ 184 | background-color: rgba(234,88,12, 0.05); 185 | color: rgb(234,88,12); 186 | padding: 2px 8px; 187 | border-radius: 4px; 188 | font-size: 90%; 189 | font-weight: 600; 190 | } 191 | .tab-btn { 192 | cursor: pointer; 193 | } 194 | .solution-tab { 195 | font-size: 18px; 196 | font-weight: 700; 197 | } 198 | .solution-tab.active { 199 | border-bottom: 2px solid var(--text-primary); 200 | color: var(--text-primary); 201 | } 202 | .tab-panel { 203 | display: none; 204 | } 205 | .tab-panel.active { 206 | display: block; 207 | } 208 | #solution-code { 209 | background-color: rgb(30, 31, 31); 210 | color: rgb(237, 237, 237); 211 | } 212 | figure.highlight { 213 | margin: 10px; 214 | } 215 | .highlight, .highlight .w { 216 | background-color: rgb(30, 31, 31); 217 | } 218 | .code-tab { 219 | } 220 | .code-tab.active { 221 | border-bottom: 4px solid #DEA74A; 222 | } 223 | .code-tab-panel { 224 | max-height: 500px; 225 | overflow: auto; 226 | width: 100%; 227 | } 228 | .prev-next-links { 229 | margin-top: 30px; 230 | margin-bottom: 20px; 231 | } 232 | .prev-next-links a { 233 | width: 50%; 234 | } 235 | 236 | @media only screen and (max-width: 768px) { 237 | header .container { 238 | flex-direction: column; 239 | justify-content: space-around; 240 | padding: 8px 25px; 241 | } 242 | } -------------------------------------------------------------------------------- /assets/css/rouge-base16-monokai-dark.css: -------------------------------------------------------------------------------- 1 | /* 2 | generated by rouge http://rouge.jneen.net/ 3 | */ 4 | 5 | .highlight table td { padding: 5px; } 6 | .highlight table pre { margin: 0; } 7 | .highlight, .highlight .w { 8 | color: #f8f8f2; 9 | background-color: #272822; 10 | } 11 | .highlight .err { 12 | color: #272822; 13 | background-color: #f92672; 14 | } 15 | .highlight .c, .highlight .ch, .highlight .cd, .highlight .cm, .highlight .cpf, .highlight .c1, .highlight .cs { 16 | color: #75715e; 17 | } 18 | .highlight .cp { 19 | color: #f4bf75; 20 | } 21 | .highlight .nt { 22 | color: #f4bf75; 23 | } 24 | .highlight .o, .highlight .ow { 25 | color: #f8f8f2; 26 | } 27 | .highlight .p, .highlight .pi { 28 | color: #f8f8f2; 29 | } 30 | .highlight .gi { 31 | color: #a6e22e; 32 | } 33 | .highlight .gd { 34 | color: #f92672; 35 | } 36 | .highlight .gh { 37 | color: #66d9ef; 38 | background-color: #272822; 39 | font-weight: bold; 40 | } 41 | .highlight .k, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kv { 42 | color: #ae81ff; 43 | } 44 | .highlight .kc { 45 | color: #fd971f; 46 | } 47 | .highlight .kt { 48 | color: #fd971f; 49 | } 50 | .highlight .kd { 51 | color: #fd971f; 52 | } 53 | .highlight .s, .highlight .sb, .highlight .sc, .highlight .dl, .highlight .sd, .highlight .s2, .highlight .sh, .highlight .sx, .highlight .s1 { 54 | color: #a6e22e; 55 | } 56 | .highlight .sa { 57 | color: #ae81ff; 58 | } 59 | .highlight .sr { 60 | color: #a1efe4; 61 | } 62 | .highlight .si { 63 | color: #cc6633; 64 | } 65 | .highlight .se { 66 | color: #cc6633; 67 | } 68 | .highlight .nn { 69 | color: #f4bf75; 70 | } 71 | .highlight .nc { 72 | color: #f4bf75; 73 | } 74 | .highlight .no { 75 | color: #f4bf75; 76 | } 77 | .highlight .na { 78 | color: #66d9ef; 79 | } 80 | .highlight .m, .highlight .mb, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo, .highlight .mx { 81 | color: #a6e22e; 82 | } 83 | .highlight .ss { 84 | color: #a6e22e; 85 | } -------------------------------------------------------------------------------- /assets/img/binary-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/binary-tree.png -------------------------------------------------------------------------------- /assets/img/bt-inorder-traversal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/bt-inorder-traversal.gif -------------------------------------------------------------------------------- /assets/img/ds-visualizer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/ds-visualizer.png -------------------------------------------------------------------------------- /assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/favicon.ico -------------------------------------------------------------------------------- /assets/img/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /assets/img/stack-push-pop-operations.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/stack-push-pop-operations.gif -------------------------------------------------------------------------------- /assets/img/stack-reverse-string.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/stack-reverse-string.gif -------------------------------------------------------------------------------- /assets/img/stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/raghavendra89/ds-visualizer/7f75e940941aa5d1cdf573fd7e78fd8fcad76d6d/assets/img/stack.png -------------------------------------------------------------------------------- /assets/js/binary-trees/bt-animator.js: -------------------------------------------------------------------------------- 1 | let animatorModule = (function() { 2 | let animations = []; 3 | let animationsRunning = false; 4 | let animationSpeed = 500; 5 | 6 | function highlightNodeAnimate(animation) { 7 | return new Promise((resolve) => { 8 | ctx.beginPath(); 9 | ctx.arc(animation.nodePoint.x, animation.nodePoint.y, 25, 0, Math.PI * 2, true); 10 | ctx.stroke(); 11 | ctx.fillStyle = '#81C784'; 12 | // ctx.fillStyle = '#66BB6A'; 13 | ctx.fill(); 14 | 15 | ctx.fillStyle = 'black'; 16 | ctx.fillText(animation.data, animation.nodePoint.x, animation.nodePoint.y); 17 | 18 | setTimeout(() => { 19 | resolve('Node highlighted!'); 20 | }, animationSpeed); 21 | }); 22 | } 23 | 24 | function highlightAnswerNodesAnimate(animation) { 25 | return new Promise((resolve) => { 26 | AnimationSwitch.animate = false; 27 | 28 | for (let i = 0; i < animation.nodes.length; i++) { 29 | let node = animation.nodes[i]; 30 | ctx.beginPath(); 31 | ctx.arc(node.nodePoint.x, node.nodePoint.y, 25, 0, Math.PI * 2, true); 32 | ctx.stroke(); 33 | ctx.fillStyle = '#F06292'; 34 | ctx.fill(); 35 | 36 | ctx.fillStyle = 'black'; 37 | ctx.fillText(node.data, node.nodePoint.x, node.nodePoint.y); 38 | } 39 | 40 | AnimationSwitch.animate = true; 41 | 42 | setTimeout(() => { 43 | resolve('Node highlighted!'); 44 | }, animationSpeed); 45 | }); 46 | } 47 | 48 | function highlightLineAnimate(animation) { 49 | return new Promise((resolve) => { 50 | drawLine(animation.nodePoint1, animation.nodePoint2, '#FF3D00'); 51 | 52 | setTimeout(() => { 53 | resolve('Node highlighted!'); 54 | }, 250); 55 | }); 56 | } 57 | 58 | function displayOutputAnimate(animation) { 59 | return new Promise((resolve) => { 60 | let outputELement = document.getElementById('final-output'); 61 | outputELement.innerHTML = animation.value; 62 | 63 | ctx.textAlign = 'left'; 64 | ctx.fillText(animation.text, 20, 30); 65 | ctx.textAlign = 'center'; 66 | 67 | resolve('Output displayed!'); 68 | }) 69 | } 70 | 71 | function runSingleAnimation(animation) { 72 | switch (animation.type) { 73 | case 'highlight-node': 74 | return highlightNodeAnimate(animation); 75 | break; 76 | case 'highlight-answer-nodes': 77 | return highlightAnswerNodesAnimate(animation); 78 | break; 79 | case 'highlight-line': 80 | return highlightLineAnimate(animation); 81 | break; 82 | case 'display-output': 83 | return displayOutputAnimate(animation); 84 | break; 85 | } 86 | } 87 | 88 | function runAnimations() { 89 | if (animationsRunning) return; 90 | 91 | animationsRunning = true; 92 | // Perform the animations one after the other 93 | let animation = animations.shift(); 94 | runSingleAnimation(animation) 95 | .then(() => { 96 | animationsRunning = false; 97 | if (animations.length) { 98 | runAnimations(); 99 | } 100 | }); 101 | } 102 | 103 | let highlightNode = function (nodePoint, data) { 104 | let animation = { 105 | type: 'highlight-node', 106 | nodePoint: nodePoint, 107 | data: data 108 | } 109 | 110 | animations.push(animation); 111 | 112 | runAnimations(); 113 | } 114 | 115 | let highlightAnswerNodes = function (nodes) { 116 | let animation = { 117 | type: 'highlight-answer-nodes', 118 | nodes: nodes 119 | } 120 | 121 | animations.push(animation); 122 | 123 | runAnimations(); 124 | } 125 | 126 | let highlightLine = function (nodePoint1, nodePoint2) { 127 | let animation = { 128 | type: 'highlight-line', 129 | nodePoint1: nodePoint1, 130 | nodePoint2: nodePoint2 131 | } 132 | 133 | animations.push(animation); 134 | 135 | runAnimations(); 136 | } 137 | 138 | let displayOutput = function (output, outputText) { 139 | let animationObject = { 140 | type: 'display-output', 141 | value: output, 142 | text: outputText 143 | }; 144 | 145 | animations.push(animationObject); 146 | runAnimations(); 147 | } 148 | 149 | let clearAnimations = function () { 150 | animations = []; 151 | animationsRunning = false; 152 | } 153 | 154 | return { 155 | highlightNode: highlightNode, 156 | highlightAnswerNodes: highlightAnswerNodes, 157 | highlightLine: highlightLine, 158 | displayOutput: displayOutput, 159 | clearAnimations: clearAnimations 160 | } 161 | })(); -------------------------------------------------------------------------------- /assets/js/binary-trees/canvas-displayer.js: -------------------------------------------------------------------------------- 1 | // Animator switch: Controls whether to animate access to the tree 2 | class AnimationSwitch { 3 | static animate = false; 4 | } 5 | 6 | class TreeNode { 7 | #data = null; 8 | #left = null; 9 | #right = null; 10 | 11 | constructor(data, nodePoint) { 12 | this.#data = data; 13 | this.#left = null; 14 | this.#right = null; 15 | 16 | this.nodePoint = nodePoint; 17 | } 18 | 19 | set data(data) { 20 | this.#data = data; 21 | } 22 | 23 | set left(left) { 24 | this.#left = left; 25 | } 26 | 27 | set right(right) { 28 | this.#right = right; 29 | } 30 | 31 | get data() { 32 | if (AnimationSwitch.animate) { 33 | animatorModule.highlightNode(this.nodePoint, this.#data); 34 | } 35 | 36 | return this.#data; 37 | } 38 | 39 | get left() { 40 | if (AnimationSwitch.animate && this.#left !== null) { 41 | animatorModule.highlightLine(this.nodePoint, this.#left.nodePoint); 42 | } 43 | 44 | return this.#left; 45 | } 46 | 47 | get right() { 48 | if (AnimationSwitch.animate && this.#right !== null) { 49 | animatorModule.highlightLine(this.nodePoint, this.#right.nodePoint); 50 | } 51 | 52 | return this.#right; 53 | } 54 | 55 | // Read the node data without highlighting or animation 56 | dryRead() { 57 | return this.#data; 58 | } 59 | } 60 | 61 | function buildTree(nodes, nodePoints, index, size) { 62 | if (index < size) { 63 | if (nodes[index] === null) { 64 | return null; 65 | } 66 | 67 | let root = new TreeNode(nodes[index], nodePoints[index]); 68 | root.left = buildTree(nodes, nodePoints, 2 * index + 1, size); 69 | root.right = buildTree(nodes, nodePoints, 2 * index + 2, size); 70 | 71 | return root; 72 | } 73 | 74 | return null; 75 | } 76 | 77 | function drawBinaryTree(nodes) { 78 | AnimationSwitch.animate = false; 79 | 80 | let nodePoints = []; 81 | 82 | // Circle center coordinates for the top node 83 | let x = 340; 84 | let y = 50; 85 | nodePoints.push({x: x, y: y}); 86 | 87 | // Circle center coordinates for the 2nd level nodes 88 | x = 180; 89 | y = 150; 90 | for (var i = 0; i < 2; i++) { 91 | nodePoints.push({x: x, y: y}); 92 | x += 334; 93 | } 94 | 95 | // Circle center coordinates for the 3rd level nodes 96 | x = 100; 97 | y = 250; 98 | for (var i = 0; i < 4; i++) { 99 | nodePoints.push({x: x, y: y}); 100 | x += 166.67; 101 | } 102 | 103 | // Circle center coordinates for the 4th level nodes 104 | x = 60; 105 | y = 350; 106 | for (var i = 0; i < 8; i++) { 107 | nodePoints.push({x: x, y: y}); 108 | x += 83; 109 | } 110 | 111 | let root = buildTree(nodes, nodePoints, 0, nodes.length); 112 | 113 | traverseToDraw(root); 114 | 115 | // Enable animations in the solution functions 116 | AnimationSwitch.animate = true; 117 | 118 | return root; 119 | } 120 | 121 | // Do the inorder traversal to draw the tree on the canvas 122 | function traverseToDraw(root) { 123 | if (root !== null) { 124 | drawTree(root, root.left); 125 | traverseToDraw(root.left); 126 | 127 | drawTree(root, root.right); 128 | traverseToDraw(root.right); 129 | } 130 | } 131 | 132 | function drawTree(node, child) { 133 | if (node !== null && child !== null) { 134 | drawCircle(node); 135 | drawCircle(child); 136 | 137 | drawLine(node.nodePoint, child.nodePoint); 138 | } 139 | } 140 | 141 | function drawCircle(node) { 142 | let nodePoint = node.nodePoint; 143 | 144 | ctx.beginPath(); 145 | ctx.arc(nodePoint.x, nodePoint.y, 25, 0, Math.PI * 2, true); 146 | ctx.stroke(); 147 | ctx.fillStyle = '#ECECEC'; 148 | // ctx.fillStyle = '#FFFAE6'; 149 | // ctx.fillStyle = '#D6EBF2'; 150 | // ctx.fillStyle = '#ADD8E6'; 151 | ctx.fill(); 152 | 153 | ctx.fillStyle = 'black'; 154 | ctx.fillText(node.data, nodePoint.x, nodePoint.y); 155 | } 156 | 157 | function drawLine(c1, c2, strokeColor = 'black') { 158 | let p1 = getPointOnCircle(c1, c2); 159 | let p2 = getPointOnCircle(c2, c1); 160 | 161 | ctx.beginPath(); 162 | ctx.moveTo(p1.x, p1.y); 163 | ctx.lineTo(p2.x, p2.y); 164 | ctx.strokeStyle = strokeColor; 165 | ctx.stroke(); 166 | ctx.strokeStyle = 'black'; 167 | } 168 | 169 | function getPointOnCircle(c1, c2) { 170 | let vector = {x: c2.x - c1.x, y: c2.y - c1.y} 171 | 172 | // Determine length b/w the centers 173 | let length = Math.sqrt(Math.pow(vector.x,2) + Math.pow(vector.y,2)); 174 | 175 | let radius = 25; 176 | 177 | // Determine the ratio between the radius and the full hypotenuse. 178 | let ratio = radius / length; 179 | 180 | let newX = c1.x + vector.x * ratio; 181 | let newY = c1.y + vector.y * ratio; 182 | 183 | return {x: newX, y: newY}; 184 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/height-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | let maxDepth = findMaxDepth(root); 4 | 5 | // Display the max depth 6 | animatorModule.displayOutput(maxDepth, 'Max Depth: ' + maxDepth); 7 | } 8 | 9 | function findMaxDepth(root) { 10 | if (root === null) return 0; 11 | 12 | 13 | let lDepth = findMaxDepth(root.left); 14 | let rDepth = findMaxDepth(root.right); 15 | 16 | return Math.max(lDepth, rDepth) + 1; 17 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/inorder-traversal.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | inOrderTraversal(root); 4 | } 5 | 6 | function inOrderTraversal(root) { 7 | if (root !== null) { 8 | 9 | inOrderTraversal(root.left); 10 | 11 | console.log(root.data); 12 | 13 | inOrderTraversal(root.right); 14 | } 15 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/level-order-traversal.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | levelOrderTraversal(root); 4 | } 5 | 6 | function levelOrderTraversal(root) { 7 | if (root !== null) { 8 | let queue = []; 9 | 10 | // Enqueue the root node. 11 | queue.push(root); 12 | 13 | // Loop through the Queue until it is not empty 14 | while (queue.length) { 15 | 16 | // Dequeue a node and visit it 17 | let node = queue.shift(); 18 | console.log(node.data); 19 | 20 | // Enqueue the left child 21 | if (node.left) { 22 | queue.push(node.left); 23 | } 24 | 25 | // Enqueue the right child 26 | if (node.right) { 27 | queue.push(node.right); 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/lowest-common-ancestor.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root, args) 2 | { 3 | let p = parseInt(args[1]); 4 | let q = parseInt(args[2]); 5 | 6 | let feedbackElement; 7 | if (! args[0].includes(p)) { 8 | feedbackElement = document.getElementsByClassName('sv-input')[1].nextElementSibling 9 | feedbackElement.innerText = 'The values of p and q must exist within the tree.'; 10 | feedbackElement.classList.remove('d-none'); 11 | return; 12 | } 13 | if (! args[0].includes(q)) { 14 | feedbackElement = document.getElementsByClassName('sv-input')[2].nextElementSibling 15 | feedbackElement.innerText = 'The values of p and q must exist within the tree.'; 16 | feedbackElement.classList.remove('d-none'); 17 | return; 18 | } 19 | 20 | let foundNodes = []; 21 | let lca = findLCA(root, p, q, foundNodes); 22 | 23 | // Display the max depth 24 | animatorModule.displayOutput(lca.data, 'LCA of nodes '+ p +' & '+ q +' is: ' + lca.data); 25 | animatorModule.highlightAnswerNodes([lca]); 26 | animatorModule.highlightAnswerNodes(foundNodes); 27 | } 28 | 29 | // The idea is when a node is found matching p or q, 30 | // then stop propogating downward 31 | function findLCA(root, p, q, foundNodes) { 32 | if (root === null) return null; 33 | 34 | // Current node is p or q, 35 | // then current node could be an ancestor 36 | if ([p, q].includes(root.data)) { 37 | foundNodes.push(root); 38 | return root; 39 | } 40 | 41 | let left = null; 42 | let right = null; 43 | if (foundNodes.length < 2) { 44 | left = findLCA(root.left, p, q, foundNodes); 45 | } 46 | 47 | if (foundNodes.length < 2) { 48 | right = findLCA(root.right, p, q, foundNodes); 49 | } 50 | 51 | // If both subtrees are not null, 52 | // then the current node is the LCA 53 | if (left !== null && right !== null) { 54 | return root; 55 | } 56 | 57 | // If only one subtree 58 | return left !== null ? left : right; 59 | } 60 | -------------------------------------------------------------------------------- /assets/js/binary-trees/maximum-sum-path.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | let sum = 0; 4 | let nodes = []; 5 | let maxSum = 0; 6 | [maxSum, nodes] = findMaximumSumPath(root, sum, maxSum, [], nodes); 7 | 8 | // Display the max depth 9 | animatorModule.displayOutput(maxSum, 'Max Sum: ' + maxSum); 10 | animatorModule.highlightAnswerNodes(nodes); 11 | } 12 | 13 | function findMaximumSumPath(root, sum, maxSum, currentPathNodes, nodes) { 14 | if (root === null) return [maxSum, nodes]; 15 | 16 | sum += root.data; 17 | currentPathNodes.push(root); 18 | if (root.left === null && root.right === null) { 19 | if (sum > maxSum) { 20 | maxSum = sum; 21 | nodes = [...currentPathNodes]; 22 | currentPathNodes.pop(); 23 | return [maxSum, nodes]; 24 | } else { 25 | currentPathNodes.pop(); 26 | return [maxSum, nodes]; 27 | } 28 | } 29 | 30 | [maxSum, nodes] = findMaximumSumPath(root.left, sum, maxSum, currentPathNodes, nodes); 31 | [maxSum, nodes] = findMaximumSumPath(root.right, sum, maxSum, currentPathNodes, nodes); 32 | currentPathNodes.pop(); 33 | 34 | return [maxSum, nodes]; 35 | } 36 | -------------------------------------------------------------------------------- /assets/js/binary-trees/minimum-depth-of-binary-tree.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | let minDepth = findMinDepth(root); 4 | 5 | // Display the max depth 6 | animatorModule.displayOutput(minDepth, 'Min Depth: ' + minDepth); 7 | } 8 | 9 | function findMinDepth(root) { 10 | if (root === null) return 0; 11 | 12 | if (root.left === null && root.right === null) { 13 | return 1; 14 | } 15 | 16 | if (root.left === null) { 17 | return findMinDepth(root.right) + 1; 18 | } 19 | 20 | if (root.right === null) { 21 | return findMinDepth(root.left) + 1; 22 | } 23 | 24 | return Math.min(findMinDepth(root.left), findMinDepth(root.right)) + 1; 25 | } 26 | -------------------------------------------------------------------------------- /assets/js/binary-trees/path-sum.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root, args) 2 | { 3 | let sum = 0; 4 | let targetSum = args[1]; 5 | let nodes = []; 6 | let exists = 'FALSE'; 7 | [exists, nodes] = checkIfPathExists(root, sum, targetSum, exists, nodes); 8 | 9 | // Display the max depth 10 | animatorModule.displayOutput(exists, 'Path Exists: ' + exists); 11 | animatorModule.highlightAnswerNodes(nodes); 12 | } 13 | 14 | function checkIfPathExists(root, sum, targetSum, exists, nodes) { 15 | if (exists == 'TRUE') { 16 | return [exists, nodes]; 17 | } 18 | 19 | if (root === null) return ['FALSE', nodes]; 20 | 21 | sum += root.data; 22 | nodes.push(root); 23 | if (root.left === null && root.right === null) { 24 | if (sum == targetSum) { 25 | return ['TRUE', nodes]; 26 | } else { 27 | nodes.pop(); 28 | return ['FALSE', nodes]; 29 | } 30 | } 31 | 32 | [exists, nodes] = checkIfPathExists(root.left, sum, targetSum, exists, nodes); 33 | [exists, nodes] = checkIfPathExists(root.right, sum, targetSum, exists, nodes); 34 | if (exists == 'FALSE') { 35 | nodes.pop(); 36 | } 37 | 38 | return [exists, nodes]; 39 | } 40 | -------------------------------------------------------------------------------- /assets/js/binary-trees/postorder-traversal.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | postOrderTraversal(root); 4 | } 5 | 6 | function postOrderTraversal(root) { 7 | if (root !== null) { 8 | postOrderTraversal(root.left); 9 | 10 | postOrderTraversal(root.right); 11 | 12 | console.log(root.data); 13 | } 14 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/preorder-traversal.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | preOrderTraversal(root); 4 | } 5 | 6 | function preOrderTraversal(root) { 7 | if (root !== null) { 8 | console.log(root.data); 9 | 10 | preOrderTraversal(root.left); 11 | 12 | preOrderTraversal(root.right); 13 | } 14 | } -------------------------------------------------------------------------------- /assets/js/binary-trees/sum-of-left-leaves.js: -------------------------------------------------------------------------------- 1 | function binaryTreeSolution(root) 2 | { 3 | let sum = calculateSum(root, 0, 0); 4 | 5 | // Display the max depth 6 | animatorModule.displayOutput(sum, 'Sum of Left Leaves: ' + sum); 7 | } 8 | 9 | function calculateSum(root, sum, isLeftNode) { 10 | if (root === null) return sum; 11 | 12 | if (isLeftNode && root.left === null && root.right === null) { 13 | sum += root.data; 14 | return sum; 15 | } 16 | 17 | sum = calculateSum(root.left, sum, 1); 18 | sum = calculateSum(root.right, sum, 0); 19 | 20 | return sum; 21 | } 22 | -------------------------------------------------------------------------------- /binarytrees.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: datastructure 3 | permalink: /binarytrees/ 4 | title: Binary Trees - Learn by visualizing 5 | subtitle: A Binary Tree is a fundamental concept in computer science used in various algorithms and data structures, such as binary search trees, heaps, and syntax trees. 6 | --- 7 | 8 |
9 |

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 |
14 | 15 |
Binary Tree
16 |
17 | 18 |

Key Features of a Binary Tree:

19 |
    20 |
  1. 21 | Nodes: Each node in a Binary Tree has three parts: Data and references (or pointers) to its left and right children. 22 |
  2. 23 |
  3. 24 | Root: The top node in the tree. It is the starting point of the tree structure. 25 |
  4. 26 |
  5. 27 | Leaf Nodes: Nodes that do not have any children (i.e., both their left and right pointers are null). 28 |
  6. 29 |
  7. 30 | Internal Nodes: Nodes that have at least one child (left or right). 31 |
  8. 32 |
  9. 33 | Height: The height of a binary tree is the length of the longest path from the root node to a leaf. 34 |
  10. 35 |
36 | 37 |

Binary Tree Traversal:

38 |

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 |

    41 |
  • 42 | In Depth-First Search (DFS), we aim to visit the nodes as deep as possible, starting from the root. DFS has three main traversal techniques: 43 |
      44 |
    1. Inorder: Visit the left subtree, then the current node, and then the right subtree.
    2. 45 |
    3. Preorder: Visit the current node, then the left subtree, and finally the right subtree.
    4. 46 |
    5. Postorder: Visit the left subtree, then the right subtree, and finally the current node.
    6. 47 |
    48 |
  • 49 |
  • 50 | In Breadth-First Search (BFS), we prioritize visiting the node closest to the root before moving on to nodes further away. This approach typically uses a queue to explore each level of the tree from top to bottom. 51 |
  • 52 |
53 |

54 |

55 | In the following problems, you will see visualizations demonstrating how these traversal algorithms work in practice. 56 |

57 | 58 | 59 |

Applications of Binary Trees:

60 |
    61 |
  • 62 | Searching: Efficient searching (in structures like Binary Search Trees). 63 |
  • 64 |
  • 65 | Sorting: Binary trees are used in some sorting algorithms (like heaps). 66 |
  • 67 |
  • 68 | Expression Parsing: Expression trees (used in compilers) represent mathematical expressions. 69 |
  • 70 |
  • 71 | Hierarchical Data Representation: File systems or organization charts can be represented using binary trees. 72 |
  • 73 |
74 | 75 |

76 | Binary trees are fundamental in computer science because of their versatility in various applications like searching, balancing, and tree traversal. 77 |

78 | 79 |
80 | 81 |
82 |

BinaryTrees Problems

83 | 84 |

Go through some of these binarytree problems and their visualizations!

85 | 86 |
    87 | {% for binarytree in site.binarytrees %} 88 |
  1. 89 | {{ binarytree.title }} 90 |
  2. 91 | {% endfor %} 92 |
93 |
-------------------------------------------------------------------------------- /canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Binary Tree in a Canvase 5 | 21 | 22 | 23 | 24 |
25 |     26 |
27 | 28 |
29 | 30 | Binary Tree Visualization 31 | 32 |
33 | 34 | 35 | 43 | 44 | 45 | 46 | 47 | 55 | 56 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Data Structures Visualizer 4 | --- 5 | 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. 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 |

Why this site?

19 |

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 |
32 | 33 |
Stacks - Reversing a String
34 |
35 | 36 |

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 |
41 | 42 |
Binary Tree - Inorder traversal
43 |
44 | 45 |

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 |
54 | Stacks
55 | Binary Trees 56 |
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 |
9 |

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 |
14 | 15 |
Stack has only one open end
16 |
17 | 18 |

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 |

Stack Operations

24 |

There are two main operations that can be performed on the stack:

25 |
    26 |
  1. Push: Insert an element onto the top of the stack.
  2. 27 |
  3. Pop: Remove the top-most element from the stack.
  4. 28 |
29 | 30 |
31 | 32 |
Stack operations - Push and Pop
33 |
34 | 35 |

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 |
47 | 48 |
49 |

Stack Problems

50 | 51 |

Take a look at some of these stack problems and their visualizations!

52 | 53 |
    54 | {% for stack in site.stacks %} 55 |
  1. 56 | {{ stack.title }} 57 |
  2. 58 | {% endfor %} 59 |
60 |
-------------------------------------------------------------------------------- /test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Stack Test 5 | 6 | 41 | 42 | 43 | 44 |
45 |
46 |

Input:

47 |
48 | 49 | 50 |
Enter the array elements followed by a comma
51 |
52 | 53 |
54 | 55 | 56 |
This is the custom help text for this input
57 |
58 | 59 |
60 | 61 | 62 |
This is the custom help text for this input
63 |
64 | 65 |
66 | 67 |
68 | 69 |

Output:

70 | 71 |
72 |     73 |
74 |
75 | 76 |
77 | 78 |
79 |
80 |
81 | 82 | 110 | 111 | 112 | 113 | 114 | 115 | 176 | 179 | 180 | --------------------------------------------------------------------------------