├── .circleci └── config.yml ├── .coveralls.yml ├── .editorconfig ├── .eslintrc.json ├── .gitattributes ├── .github ├── dsa.jpeg ├── logo.png └── workflows │ └── node.js.yml ├── .gitignore ├── .husky ├── pre-commit └── pre-push ├── .prettierrc.js ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── TOC.md ├── package-lock.json ├── package.json └── src ├── _Algorithms_ ├── lru-cache │ ├── LRUCache.test.js │ └── index.js └── path-finder │ └── a-star │ └── index.js ├── _Classics_ ├── caeser_cipher │ └── index.js ├── fibonacci │ └── index.js └── knuth-morris-pratt │ ├── index.js │ └── knuth-morris-pratt.test.js ├── _DataStructures_ ├── BloomFilters │ └── index.js ├── DoublyLinkedList │ ├── Node.js │ ├── doublyLinkedList.test.js │ └── index.js ├── Graphs │ └── index.js ├── HashTable │ ├── HashEntry.js │ └── index.js ├── Heaps │ ├── MaxHeap │ │ ├── MaxHeap.test.js │ │ └── index.js │ ├── MinHeap │ │ ├── MinHeap.test.js │ │ └── index.js │ ├── k-largest-in-array │ │ └── index.js │ └── k-smallest-in-array │ │ └── index.js ├── LinkedList │ ├── LinkedList.test.js │ ├── element-from-last │ │ ├── element-from-last.test.js │ │ └── index.js │ ├── index.js │ ├── loop-in-list │ │ ├── index.js │ │ └── loop-in-list.test.js │ ├── middle-node │ │ ├── index.js │ │ └── midle-node.test.js │ └── reverse-linked-list │ │ ├── index.js │ │ └── reverse-linked-list.test.js ├── Queue │ ├── Queue.test.js │ ├── QueueUsingArray.js │ ├── generate-binary-number │ │ └── index.js │ ├── index.js │ ├── queue-using-stack │ │ └── index.js │ ├── reverse-first-k │ │ └── index.js │ └── weave │ │ ├── index.js │ │ └── weave.test.js ├── Set │ ├── index.js │ └── setTest.js ├── Stack │ ├── 2-stacks-using1-array │ │ └── index.js │ ├── Stack.test.js │ ├── balanced-parenthesis │ │ └── index.js │ ├── baseball-game │ │ └── index.js │ ├── immitate-queue-using-stack │ │ ├── immitate-queue-using-stack.test.js │ │ └── index.js │ ├── index.js │ ├── min-stack │ │ └── index.js │ ├── postfix-expression-evaluation │ │ ├── index.js │ │ └── postfix-expression-evaluation.test.js │ ├── remove-consecutive-repeated-digits │ │ └── index.js │ └── sort-a-stack │ │ └── index.js └── Trees │ ├── BinarySearchTree │ ├── Node.js │ ├── find-ancestors │ │ └── index.js │ ├── find-k-nodes-from-root │ │ └── index.js │ ├── find-kth-max │ │ └── index.js │ ├── find-kth-min │ │ └── index.js │ ├── height-of-bst │ │ ├── height-of-bst.test.js │ │ └── index.js │ ├── index.js │ ├── index.test.js │ ├── lowest-common-ancestor │ │ ├── index.js │ │ └── index.test.js │ └── utils.js │ ├── BinaryTree │ ├── Node.js │ ├── bottom-view-binary-tree │ │ ├── BottomViewBinaryTree.test.js │ │ └── index.js │ ├── btree-traversals.test.js │ ├── check-binary-tree-subtree-another-binary-tree │ │ └── index.js │ └── index.js │ ├── SuffixTree │ ├── SuffixTree.test.js │ └── index.js │ └── Trie │ ├── Node.js │ ├── all-words-in-trie │ ├── all-words-in-trie.test.js │ └── index.js │ ├── get-unique-words │ ├── get-unique-words.test.js │ └── index.js │ ├── index.js │ ├── search.test.js │ ├── total-words-in-trie │ ├── index.js │ └── total-words-in-trie.test.js │ └── unique-word-count │ ├── index.js │ └── index.test.js ├── _PathFinder_ └── AStar │ ├── AStar.test.js │ └── index.js ├── _Problems_ ├── 3Sum │ ├── 3sum.js │ └── 3sum.test.js ├── anagrams │ ├── anagrams.test.js │ └── index.js ├── array-chunk │ ├── array-chunk.test.js │ └── index.js ├── bfs-bst │ └── index.js ├── binary-tree-to-binary-search-tree │ ├── binary-tree-to-binary-search-tree.test.js │ └── index.js ├── compose-largest-number │ ├── compose-largest.test.js │ └── index.js ├── count-vowels │ ├── count-vowels.test.js │ └── index.js ├── factorial │ ├── factorial.test.js │ └── index.js ├── find-2-nums-adding-to-n │ ├── find-2-nums-adding-to-n.test.js │ └── index.js ├── find-2nd-max │ ├── find-2nd-max.test.js │ └── index.js ├── fizzbuzz │ ├── fizzbuzz.test.js │ └── index.js ├── get-mazePath │ ├── get-mazePath.test.js │ └── index.js ├── get-smallest-common-number │ ├── get-smallest-common-number.test.js │ └── index.js ├── get-string-permutations │ ├── get-string-permutations.test.js │ └── index.js ├── get-subsequence │ ├── index.js │ └── subsequence.test.js ├── max-consecutive-1s │ ├── index.js │ └── max-consecutive-1s.test.js ├── max-product-of-3-numbers │ ├── index.js │ └── max-product-of-3-numbers.test.js ├── maxchar │ ├── index.js │ └── maxchar.test.js ├── merge-two-sorted-arrays │ ├── index.js │ └── merge-two-sorted-arrays.test.js ├── next-greater-element │ ├── index.js │ └── next-greater-element.test.js ├── palindrome │ ├── index.js │ └── palindrome.test.js ├── product-of-elements │ ├── index.js │ └── product-of-elements.test.js ├── remove-duplicates │ ├── index.js │ └── remove-duplicates.test.js ├── reverse-number │ ├── index.js │ └── reverse-number.test.js ├── reverse_string │ ├── index.js │ └── string-reverse.test.js └── rotate-image │ ├── index.js │ └── rotate-image.test.js └── _Searching_ ├── BinarySearch ├── BinarySearch.test.js └── index.js ├── JumpSearch ├── JumpSearch.test.js └── index.js └── TernarySearch ├── TernarySearch.test.js └── index.js /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # This config is equivalent to both the '.circleci/extended/orb-free.yml' and the base '.circleci/config.yml' 2 | version: 2.1 3 | 4 | # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects. 5 | # See: https://circleci.com/docs/2.0/orb-intro/ 6 | orbs: 7 | node: circleci/node@4.7 8 | 9 | # Invoke jobs via workflows 10 | # See: https://circleci.com/docs/2.0/configuration-reference/#workflows 11 | workflows: 12 | sample: # This is the name of the workflow, feel free to change it to better match your workflow. 13 | # Inside the workflow, you define the jobs you want to run. 14 | jobs: 15 | - node/test: 16 | # This is the node version to use for the `cimg/node` tag 17 | # Relevant tags can be found on the CircleCI Developer Hub 18 | # https://circleci.com/developer/images/image/cimg/node 19 | version: '16.10' 20 | # If you are using yarn, change the line below from "npm" to "yarn" 21 | pkg-manager: npm 22 | -------------------------------------------------------------------------------- /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis 2 | repo_token: yCWrqxWMQS88cTEJ0SCr79BSMPV5AawTz 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { 3 | "node": true, 4 | "jest": true 5 | }, 6 | "extends": ["airbnb", "prettier"], 7 | "plugins": ["prettier"], 8 | "rules": { 9 | "prettier/prettier": ["error"], 10 | "no-underscore-dangle": "off", 11 | "no-param-reassign": "off", 12 | "no-console": "warn", 13 | "consistent-return": "warn", 14 | "max-classes-per-file": "off", 15 | "no-bitwise": "warn", 16 | "no-restricted-syntax": "warn", 17 | "no-continue": "warn" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=crlf -------------------------------------------------------------------------------- /.github/dsa.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knaxus/problem-solving-javascript/5bebc4b976643e3fded5d422e2d194c2b3363140/.github/dsa.jpeg -------------------------------------------------------------------------------- /.github/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/knaxus/problem-solving-javascript/5bebc4b976643e3fded5d422e2d194c2b3363140/.github/logo.png -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | # This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node 2 | # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs 3 | 4 | name: Node.js CI 5 | 6 | on: 7 | push: 8 | branches: [ "master" ] 9 | pull_request: 10 | branches: [ "master" ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | strategy: 18 | matrix: 19 | node-version: [14.x, 16.x, 18.x] 20 | # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ 21 | 22 | steps: 23 | - uses: actions/checkout@v3 24 | - name: Use Node.js ${{ matrix.node-version }} 25 | uses: actions/setup-node@v3 26 | with: 27 | node-version: ${{ matrix.node-version }} 28 | cache: 'npm' 29 | - run: npm ci 30 | - run: npm run build --if-present 31 | - run: npm test 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | coverage/ 3 | .vs/ -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run format && npm run lint 5 | -------------------------------------------------------------------------------- /.husky/pre-push: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npm run format && npm run lint && npm run test 5 | -------------------------------------------------------------------------------- /.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | semi: true, 3 | trailingComma: 'all', 4 | singleQuote: true, 5 | printWidth: 120, 6 | tabWidth: 2, 7 | arrowParens: 'always', 8 | endOfLine: 'lf', 9 | }; 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '8.9.3' 4 | after_success: 5 | - npm install -D coveralls 6 | - npm run coverage 7 | - npm run coveralls 8 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guide 2 | 3 | Thanks for taking interest and I appreciate your efforts towards making this project even better. 4 | 5 | ## How to setup? 6 | 7 | This is the most simple project when it comes to contributions, setup, opening issues/pull requests. So let's get started. 8 | 9 | - Clone the repo using the command `git clone git@github.com:knaxus/problem-solving-javascript.git`1 10 | - Install the packages to get support for linter using `npm install` 11 | 12 | 1: If you do not have **ssh** setup for github, while cloning go with **https** 13 | 14 | ### Before you start, keep the following things in mind: 15 | 16 | - We use ESLint for code linting 17 | - The linter follows [Airbnb JavaScript Style Guide](https://github.com/airbnb/javascript) 18 | - Go through the folder structure carefully and follow the same 19 | - Go through the format and file conventions used while adding tests (both test case and test files) 20 | 21 | ## How to pick up an Issue 22 | 23 | - Comment on the issue first so that we can assign you the issue. 24 | - If you raise a Pull Request for an issue and the Issue was not assigned to you, your PR will be marked as **Invalid** 25 | 26 | ## Submittng a Pull Request (PR) 27 | 28 | - Add yourself to the assignee section 29 | - Add meaningful heading and description to your PR 30 | - Also mention the issue number in the description using **'#'**, e.g: **#12** 31 | - Not following the above will mark your PR invalid 32 | 33 | ## Adding your code 34 | 35 | - When adding a new problem with a solution 36 | 37 | - Take care of the filename convention (Very Important) 38 | - A problem statement should be there and support it with some examples 39 | - Make sure you've added the **Run Time Complexity** of your solution 40 | - Please take care of the segregation of the Problems as per the given Folder Structure 41 | - It's great if you can add the Unit Tests to verify your solutions as well 42 | - Do not forget to update **[TOC.md](TOC.md)** with your new problem or data structure 43 | 44 | - When adding a Unit Test 45 | - Take care of the file name convention 46 | - Make sure CI (Travis) is passing 47 | - Expanding the test suite until (close to) 100 percentage code coverage is achieved 48 | 49 | ### Notes 50 | 51 | - Keep an eye on this guide, it's subjected to change frequently. 52 | - Please do not break the ESLint rules 53 | - Todo 54 | - Issue Template 55 | - PR Template 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Ashok Dey @ Knaxus 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # Problem Solving using Javascript 4 | 5 | [![Build Status](https://travis-ci.org/knaxus/problem-solving-javascript.svg?branch=master)](https://travis-ci.org/knaxus/problem-solving-javascript) 6 | [![Coverage Status](https://coveralls.io/repos/github/knaxus/problem-solving-javascript/badge.svg?branch=master)](https://coveralls.io/github/knaxus/problem-solving-javascript?branch=master) 7 | ![GitHub stars](https://img.shields.io/github/stars/knaxus/problem-solving-javascript) 8 | ![GitHub contributors](https://img.shields.io/github/contributors/knaxus/problem-solving-javascript) 9 | [![contributions welcome](https://img.shields.io/badge/contributions-welcome-brightgreen.svg?style=flat)](https://github.com/knaxus/problem-solving-javascript/issues) 10 | ![GitHub](https://img.shields.io/github/license/knaxus/problem-solving-javascript) 11 | 12 | Collection of interview questions with Unit Tests. Problems include Data Structures, Logical and few Classical problems. 13 | 14 | ![DSA](.github/dsa.jpeg) 15 | 16 | ## Overview 17 | 18 | This repo contains the following sections implemented in **JavaScript** 19 | 20 | - [Data Structures](src/_DataStructures_) 21 | - [Algorithms](src/_Algorithms_) 22 | - [Logical Problems](src/_Problems_) 23 | - [Classics (Few of the classical questions)](src/_Classics_) 24 | 25 | Find the detailed contents and problem list here: [Table Of Contents](TOC.md) 26 | 27 | ## Maintainers 28 | 29 | | Name | Twitter | LinkedIn | Website | 30 | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- | ------------------------------------------ | 31 | | [Ashok Dey](https://github.com/ashokdey) | ![Twitter Follow](https://img.shields.io/twitter/follow/ashokdey_?label=%40ashokdey_&style=social) | [Ashok Dey](https://linkedin.com/in/ashokdey) | [https://ashokdey.in](https://ashokdey.in) | 32 | | [Ashu Deshwal](https://github.com/TheSTL) | ![Twitter Follow](https://img.shields.io/twitter/follow/_TheSTL_?label=%40_TheSTL__&style=social) | [Ashu Deshwal](https://www.linkedin.com/in/ashu-deshwal/) | - | 33 | 34 | ### Contributors 35 | 36 | 37 | 38 | 39 | 40 | 41 | ## Contribution Guide 42 | 43 | It's great to know that you want to contribute to this repo. Thanks for taking interest. Please find the [guide here](https://github.com/knaxus/problem-solving-javascript/blob/master/CONTRIBUTING.md) 44 | 45 | Keep an eye on this guide. It's subject to frequent change. 46 | -------------------------------------------------------------------------------- /TOC.md: -------------------------------------------------------------------------------- 1 | # Table of Contents 2 | 3 | ## Data Structures 4 | 5 | - [Singly Linked List](src/_DataStructures_/LinkedList) 6 | 7 | - [N Element From Last](src/_DataStructures_/LinkedList/element-from-last) 8 | - [Middle Node](src/_DataStructures_/LinkedList/middle-node) 9 | - [Detect Loop](src/_DataStructures_/LinkedList/loop-in-list) 10 | - [Reverse Linked List](src/_DataStructures_/LinkedList/reverse-linked-list) 11 | 12 | - [Stack](src/_DataStructures_/Stack) 13 | 14 | - [Implement Queue Using Stack](src/_DataStructures_/Stack/immitate-queue-using-stack) 15 | - [Baseball Game](src/_DataStructures_/Stack/baseball-game) 16 | - [Find minimum in the Stack](src/_DataStructures_/Stack/min-stack) 17 | - [Balanced Parenthesis](src/_DataStructures_/Stack/balanced-parenthesis) 18 | - [Postfix Expression Evaluation](src/_DataStructures_/Stack/postfix-expression-evaluation) 19 | - [Remove Consecutive Repeated Digits](src/_DataStructures_/Stack/remove-consecutive-repeated-digits) 20 | - [Implement 2 Stacks using Single Array](src/_DataStructures_/Stack/2-stacks-using1-array) 21 | - [Sort a Stack](src/_DataStructures_/Stack/sort-a-stack) 22 | 23 | - [Queue](src/_DataStructures_/Queue) 24 | 25 | - [Weave](src/_DataStructures_/Queue/weave) 26 | - [Reverse First K Elements of a Queue](src/_DataStructures_/Queue/reverse-first-k) 27 | - [Generate all Binary Numbers from 1 to N](src/_DataStructures_/Queue/generate-binary-number) 28 | - [Queue using Stack](src/_DataStructures_/Queue/queue-using-stack) 29 | 30 | - [Doubly Linked List](src/_DataStructures_/DoublyLinkedList) 31 | 32 | - [Trees](src/_DataStructures_/Trees) 33 | - [Binary Tree (creation using level order)](src/_DataStructures_/Trees/BinaryTree) 34 | - [Binary Search Tree](src/_DataStructures_/Trees/BinarySearchTree) 35 | - [Find kth maximum in a BinarySearchTree](src/_DataStructures_/Trees/BinarySearchTree/find-kth-max) 36 | - [Find kth minimum in a BinarySearchTree](src/_DataStructures_/Trees/BinarySearchTree/find-kth-min) 37 | - [Find Ancestors of a Node](src/_DataStructures_/Trees/BinarySearchTree/find-ancestors) 38 | - [Find Height of BST](src/_DataStructures_/Trees/BinarySearchTree/height-of-bst) 39 | - [Find k Nodes from Root of BST](src/_DataStructures_/Trees/BinarySearchTree/find-k-nodes-from-root) 40 | - [Suffix Tree](src/_DataStructures_/SuffixTree) 41 | - [Trie](src/_DataStructures_/Trees/Trie) 42 | - [Total words count count in a Trie](src/_DataStructures_/Trees/Trie/total-words-in-trie) 43 | - [Unique words count in a Trie](src/_DataStructures_/Trees/Trie/unique-word-count) 44 | - [All the words from a Trie](src/_DataStructures_/Trees/Trie/all-words-in-trie) 45 | - [Unique words in a Trie](src/_DataStructures_/Trees/Trie/get-unique-words) 46 | - [Heaps](src/_DataStructures_/Heaps) 47 | - [MaxHeap](src/_DataStructures_/Heaps/MaxHeap) 48 | - [MinHeap](src/_DataStructures_/Heaps/MinHeap) 49 | - Problems 50 | - [K Largest Elements](src/_DataStructures_/Heaps/k-largest-in-array) 51 | - [K Smallest Elements](src/_DataStructures_/Heaps/k-smallest-in-array) 52 | - [Hash Table](src/_DataStructures_/HashTable) 53 | - [Set](src/_DataStructures_/Set) 54 | - [Bloom Filters](src/_DataStructures_/BloomFilters) 55 | 56 | ### Logical Problems 57 | 58 | - [Anagrams](src/_Problems_/anagrams) 59 | - [Array Chunk](src/_Problems_/array-chunk) 60 | - [Count Vowels](src/_Problems_/count-vowels) 61 | - [Find 2 numbers that add upto N](src/_Problems_/find-2-nums-adding-to-n) 62 | - [Find 2nd Maxinum from an Array](src/_Problems_/find-2nd-max) 63 | - [FizzBuzz](src/_Problems_/fizzbuzz) 64 | - [String Permutations](src/_Problems_/get-string-permutations) 65 | - [Get Subsequence](src/_Problems_/get_subsequence) 66 | - [Get Maze Path](src/_Problems_/get-mazePath) 67 | - [Get longest consecutive 1s](src/_Problems_/max-consecutive-1s) 68 | - [Get Max Char](src/_Problems_/maxchar) 69 | - [Get Smallest Common Number](src/_Problems_/get-smallest-common-number) 70 | - [Merge 2 Sorted Arrays](src/_Problems_/merge-two-sorted-arrays) 71 | - [Palindrome](src/_Problems_/palindrome) 72 | - [Product of Elements](src/_Problems_/product-of-elements) 73 | - [Remove Duplicates](src/_Problems_/remove-duplicates) 74 | - [Reverse String](src/_Problems_/reverse_string) 75 | - [Maximum Product of Three Numbers](src/_Problems_/max-product-of-3-numbers) 76 | - [Next Greater for Every Element in an Array](src/_Problems_/next-greater-element) 77 | - [Compose Largest Number](src/_Problems_/compose-largest-number) 78 | - [Rotate Image](src/_Problems_/rotate-image) 79 | - [3 Sum](src/_Problems_/3Sum/) 80 | ### Searching 81 | 82 | - [Binary Search](src/_Searching_/BinarySearch) 83 | 84 | ### Algorithms 85 | 86 | - [LRU Cache](src/_Algorithms_/lru-cache) 87 | - Path Finders 88 | - [A\*](src/_Algorithms_/path-finder/a-star) 89 | 90 | ### Classics 91 | 92 | - [Caeser Cipher](src/_Classics_/caeser_cipher) 93 | - [Fibonacci](src/_Classics_/fibonacci) 94 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "problem-solving-in-js", 3 | "version": "1.0.0", 4 | "engines": { 5 | "node": "^14" 6 | }, 7 | "description": "", 8 | "main": "index.js", 9 | "scripts": { 10 | "prepare": "husky install", 11 | "coverage": "jest --coverage", 12 | "coveralls": "cat ./coverage/lcov.info | ./node_modules/.bin/coveralls", 13 | "test": "jest --verbose", 14 | "test:watch": "jest --watchAll", 15 | "format": "prettier --write .", 16 | "lint": "eslint --fix ." 17 | }, 18 | "jest": { 19 | "testEnvironment": "node" 20 | }, 21 | "husky": { 22 | "hooks": { 23 | "pre-commit": "npm run format && npm run lint" 24 | } 25 | }, 26 | "keywords": [], 27 | "author": "Ashok Dey (http://ashokdey.in)", 28 | "license": "MIT", 29 | "devDependencies": { 30 | "eslint": "^8.2.0", 31 | "eslint-config-airbnb": "^19.0.4", 32 | "eslint-config-prettier": "^8.5.0", 33 | "eslint-plugin-import": "^2.25.3", 34 | "eslint-plugin-jsx-a11y": "^6.5.1", 35 | "eslint-plugin-prettier": "^4.2.1", 36 | "eslint-plugin-react": "^7.28.0", 37 | "eslint-plugin-react-hooks": "^4.3.0", 38 | "husky": "^8.0.1", 39 | "jest": "^25.0.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/_Algorithms_/lru-cache/LRUCache.test.js: -------------------------------------------------------------------------------- 1 | const { LRUCache } = require('.'); 2 | 3 | describe('Algorithms: LRU Cache', () => { 4 | describe('LRUCache Instance', () => { 5 | it('Should be a class', () => { 6 | expect(typeof LRUCache.prototype.constructor).toEqual('function'); 7 | }); 8 | }); 9 | 10 | describe('LRUCache API', () => { 11 | let lruCache = new LRUCache(4); 12 | 13 | beforeEach(() => { 14 | lruCache = new LRUCache(4); 15 | }); 16 | 17 | describe('get(key)', () => { 18 | it('Should return false if the LRUCache is empty', () => { 19 | expect(lruCache.get('foo')).toEqual(false); 20 | }); 21 | 22 | it('Should return cached value if the key exists in the LRUCache', () => { 23 | lruCache.set('foo', 'bar'); 24 | expect(lruCache.get('foo')).toEqual('bar'); 25 | }); 26 | 27 | it('Should move least recently used key to the beginning of the list', () => { 28 | lruCache.set('key1', 'value1'); 29 | lruCache.set('key2', 'value2'); 30 | 31 | expect(lruCache.list.head.next.data.key).toEqual('key2'); 32 | expect(lruCache.list.head.next.data.value).toEqual('value2'); 33 | 34 | // The least recently used key is moved at the beginning of the list 35 | lruCache.get('key1'); 36 | expect(lruCache.list.head.next.data.key).toEqual('key1'); 37 | expect(lruCache.list.head.next.data.value).toEqual('value1'); 38 | }); 39 | }); 40 | 41 | describe('set(key, value)', () => { 42 | it('Should append each pair to the beginning of list', () => { 43 | lruCache.set('foo', 'bar'); 44 | expect(lruCache.list.head.next.data.key).toEqual('foo'); 45 | expect(lruCache.list.head.next.data.value).toEqual('bar'); 46 | }); 47 | 48 | it('Should update value if key already exists', () => { 49 | lruCache.set('foo', 'bar'); 50 | lruCache.set('foo', 'newBar'); 51 | expect(lruCache.get('foo')).toEqual('newBar'); 52 | }); 53 | 54 | it('Should remove node at the end if the LRUCache capacity is filled', () => { 55 | lruCache.set('key5', 'value5'); 56 | lruCache.set('key4', 'value4'); 57 | lruCache.set('key3', 'value3'); 58 | lruCache.set('key2', 'value2'); 59 | lruCache.set('key1', 'value1'); 60 | 61 | expect(lruCache.list.length()).toEqual(lruCache.size); 62 | expect(lruCache.list.head.next.data.key).toEqual('key1'); 63 | expect(lruCache.list.head.next.data.value).toEqual('value1'); 64 | expect(lruCache.list.head.next.next.data.key).toEqual('key2'); 65 | expect(lruCache.list.head.next.next.data.value).toEqual('value2'); 66 | expect(lruCache.list.head.next.next.next.data.key).toEqual('key3'); 67 | expect(lruCache.list.head.next.next.next.data.value).toEqual('value3'); 68 | expect(lruCache.list.head.next.next.next.next.data.key).toEqual('key4'); 69 | expect(lruCache.list.head.next.next.next.next.data.value).toEqual('value4'); 70 | }); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/_Algorithms_/lru-cache/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Least recently used (LRU) - cache implementation 3 | 4 | get(key) – Get the value (will always be positive) of the key if the key exists in the cache, otherwise return false. 5 | Complexity: O(1) 6 | 7 | set(key, value) – Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. 8 | Complexity: O(1) 9 | */ 10 | 11 | const DoublyLinkedList = require('../../_DataStructures_/DoublyLinkedList/index'); 12 | 13 | class LRUCache { 14 | constructor(n) { 15 | this.size = n; 16 | this.map = new Map(); 17 | this.list = new DoublyLinkedList(); 18 | } 19 | 20 | // this method will work in O(1) 21 | set(key, value) { 22 | const data = { 23 | key, 24 | value, 25 | }; 26 | if (!this.map.has(key)) { 27 | this.list.addAtBeginning(data); 28 | this.map.set(key, this.list.head.next); 29 | 30 | if (this.list.length() > this.size) { 31 | const lastNode = this.list.tail.previous.data; 32 | this.map.delete(lastNode.key); 33 | this.list.removeAtEnd(); 34 | } 35 | } else { 36 | this.list.remove(this.map.get(key)); 37 | this.list.addAtBeginning(data); 38 | this.map.set(key, this.list.head.next); 39 | } 40 | } 41 | 42 | // this method will work in O(1) 43 | get(key) { 44 | if (this.map.has(key)) { 45 | const node = this.map.get(key); 46 | const { value } = node.data; 47 | this.list.remove(node); 48 | this.list.addAtBeginning({ 49 | key, 50 | value, 51 | }); 52 | this.map.set(key, this.list.head.next); 53 | return value; 54 | } 55 | return false; 56 | } 57 | } 58 | 59 | // const lru = new LRUCache(3); 60 | // lru.set(1, 1); 61 | // lru.set(2, 2); 62 | // lru.set(3, 3); 63 | // lru.set(4, 4); 64 | // lru.set(5, 5); 65 | // lru.set(2, 2); 66 | // lru.get(5, 5); 67 | // lru.list.display(); 68 | 69 | module.exports = { 70 | LRUCache, 71 | }; 72 | -------------------------------------------------------------------------------- /src/_Classics_/caeser_cipher/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Most simplest encryption scheme. Read more: [http://practicalcryptography.com/ciphers/caesar-cipher/] 3 | * @param {String} str 4 | * @param {Number} num 5 | */ 6 | 7 | function caesarCipher(str, num) { 8 | if (!num) throw new Error('Missing argument: num'); 9 | 10 | const lowerCaseString = str.toLowerCase(); 11 | const alphabets = 'abcdefghijklmnopqrstuvwxyz'.split(''); 12 | const totalAlphabets = alphabets.length; 13 | let result = ''; 14 | 15 | // handle large number, like 300 or -300 16 | num %= totalAlphabets; 17 | 18 | const alphabetsMap = new Map(); 19 | 20 | for (const index in alphabets) { 21 | if (index) { 22 | alphabetsMap[alphabets[index]] = index; 23 | } 24 | } 25 | 26 | for (const index in lowerCaseString) { 27 | if (index) { 28 | // get the current character 29 | const currentCharacter = lowerCaseString[index]; 30 | 31 | // if character is space, add it to the result and continue to next 32 | if (currentCharacter === ' ') { 33 | result += currentCharacter; 34 | continue; 35 | } 36 | 37 | // determine the new index 38 | /** 39 | * const currentIndex = alphabets.indexOf(currentCharacter); 40 | * 41 | * With indexOf complexity will be O(n*26) 42 | * With Map complexity will be O(n). 43 | */ 44 | const currentIndex = Number(alphabetsMap[currentCharacter]); 45 | let newIndex = currentIndex + num; 46 | 47 | // if the index passes 25, restart from 0 48 | if (newIndex > totalAlphabets - 1) { 49 | newIndex -= totalAlphabets; 50 | } 51 | 52 | if (newIndex < 0) { 53 | newIndex = totalAlphabets + newIndex; 54 | } 55 | 56 | // check if the character in original string was upper case 57 | if (str[index] === str[index].toUpperCase()) { 58 | result += alphabets[newIndex].toUpperCase(); 59 | } else { 60 | result += alphabets[newIndex]; 61 | } 62 | } 63 | } 64 | return result; 65 | } 66 | 67 | module.exports = { 68 | caesarCipher, 69 | }; 70 | -------------------------------------------------------------------------------- /src/_Classics_/fibonacci/index.js: -------------------------------------------------------------------------------- 1 | // The Fibonacci sequence: 0, 1, 1, 2, 3, 5, 8, 13, 21 2 | // the algorithm has time complexity of O(n^2), very bad! 3 | function fibonacci(position) { 4 | // if position is 1 or 2, the number in fibonacci sequence will be 1 5 | if (position === 1 || position === 0) { 6 | return position; 7 | } 8 | if (position < 0) { 9 | throw new Error('Invalid Position'); 10 | } 11 | 12 | // else the element in fibonacci sequence will be the sum of 13 | // element at position(p) (p -1) and (p - 2) 14 | return fibonacci(position - 2) + fibonacci(position - 1); 15 | } 16 | 17 | /** 18 | * Memoization. In computing, memoization or memoisation is an 19 | * optimization technique used primarily to speed up computer 20 | * programs by storing the results of expensive function 21 | * calls and returning the cached result when the 22 | * same inputs occur again 23 | */ 24 | 25 | // Linear time, test with index as 510 for both the functions 26 | function fibonacciMemoized(index, cache) { 27 | cache = cache || []; 28 | 29 | if (cache[index]) { 30 | return cache[index]; 31 | } 32 | if (index === 1 || index === 0) { 33 | return index; 34 | } 35 | if (index < 0) { 36 | throw new Error('Invalid Position'); 37 | } else { 38 | cache[index] = fibonacciMemoized(index - 1, cache) + fibonacciMemoized(index - 2, cache); 39 | } 40 | 41 | return cache[index]; 42 | } 43 | 44 | /** 45 | * Using the bottom up approach, also known as tabular method 46 | */ 47 | 48 | function fibonacciTabular(n) { 49 | const table = [0, 1]; 50 | if (n === 1 || n === 0) { 51 | return n; 52 | } 53 | if (n < 0) { 54 | throw new Error('Invalid Position'); 55 | } 56 | for (let i = 2; i <= n; i += 1) { 57 | table[i] = table[i - 1] + table[i - 2]; 58 | } 59 | 60 | return table[n]; 61 | } 62 | 63 | // const number = 50; 64 | 65 | // console.log(`Fib normal - ${fibonacci(number)}`); 66 | // console.log('--'); 67 | // console.log(`Fib memo - ${fibonacciMemoized(number)}`); 68 | // console.log(`Fib table - ${fibonacciTabular(number)}`); 69 | 70 | module.exports = { 71 | fibonacci, 72 | fibonacciMemoized, 73 | fibonacciTabular, 74 | }; 75 | -------------------------------------------------------------------------------- /src/_Classics_/knuth-morris-pratt/index.js: -------------------------------------------------------------------------------- 1 | /* * 2 | * The time complexity of KMP algorithm is O(n) in the worst case 3 | * Example use case: Pattern = ABCABCACA Text = AAABCBAABCABCACACABBCA 4 | * LPSArray = [ 0, 0, 0, 1, 2, 3, 4, 0, 1 ] 5 | * Found = true, at index 7 6 | * */ 7 | 8 | // Longest prefix suffix - generate an array of the lps for each pattern array value 9 | const createLPS = (pattern, patternLength) => { 10 | // initialise the current longest prefix suffix length and iterator index values 11 | const lps = [patternLength]; 12 | lps[0] = 0; 13 | 14 | let length = 0; 15 | let i = 1; 16 | // while there is still pattern to iterate over - calculate the lps for i = 1 to patternLength - 1 17 | while (i < patternLength) { 18 | /* * 19 | * if the pattern character at position i matches the pattern character at position length, 20 | * then increment length, update 21 | * the lps to the incremted length value and iterate to the next index i. 22 | * */ 23 | if (pattern.charAt(i) === pattern.charAt(length)) { 24 | length += 1; 25 | lps[i] = length; 26 | i += 1; 27 | // if not matching 28 | } else if (length !== 0) { 29 | // if the length value is not 0, then set the length to be the lps value of index length - 1 30 | length = lps[length - 1]; 31 | } else { 32 | // else if length is 0, then set the lps at position i to length, i.e. 0 and increment i. 33 | lps[i] = length; 34 | i += 1; 35 | } 36 | } 37 | return lps; 38 | }; 39 | 40 | /* * 41 | * Invoke the Knuth-Morris-Pratt pattern matching algorithm to find a Pattern with a Text - this 42 | * uses a precomputed prefix-suffix array/table to essentially skip chunks of the text that we 43 | * know will match the pattern. This algorithm will return true if the pattern is a subset of 44 | * the text, else it will return false. 45 | * This algorithm accepts two strings, the pattern and text. 46 | * The time complexity of the KMP algorithm is O(n) in the worst case. 47 | * */ 48 | const KMPSearch = (pattern, text) => { 49 | const patternLength = pattern.length; // Often referred to as M 50 | const textLength = text.length; // Often referred to as N 51 | 52 | // Longest Pattern Suffix - array containing the lps for all pattern value positions 53 | const lps = createLPS(pattern, patternLength); // This is preprocessed. 54 | // console.log({ lpsArray: lps }) 55 | 56 | let patternIndex = 0; // Referred to as P 57 | let textIndex = 0; // Referred to as T 58 | let found = false; 59 | 60 | // While there is still text left to iterate over and the pattern has not yet been found 61 | while (textIndex < textLength && found === false) { 62 | // if the pattern char at index pos P equals the text char at text pos T, then increment indexes 63 | if (pattern.charAt(patternIndex) === text.charAt(textIndex)) { 64 | textIndex += 1; 65 | patternIndex += 1; 66 | } 67 | /* * 68 | * if the pattern index equals the pattern length then the pattern has been successfully 69 | * found, as such the pattern is a subset of the text the pattern index is set to the longest 70 | * pattern suffix value (the index is decremented due to being zero indexed). 71 | * */ 72 | if (patternIndex === patternLength) { 73 | // console.log(`Pattern found at index ${textIndex-patternIndex}`); 74 | patternIndex = lps[patternIndex - 1]; 75 | found = true; 76 | } else if (textIndex < textLength && pattern.charAt(patternIndex) !== text.charAt(textIndex)) { 77 | /* * 78 | * else if there is still text left to iterate over and the pattern character does not match 79 | * the text characterat their respective index positions, then check of the pattern Index is 0, 80 | * i.e. if it is the first pattern position. If so then jump to the next text character, else 81 | * (this is not the first pattern position), then update the pattern index using the generated 82 | * longest prefix suffix, to skip ahead of matching values. This logic will only be encountered 83 | * after T number of mismatches. 84 | * */ 85 | if (patternIndex === 0) textIndex += 1; 86 | else patternIndex = lps[patternIndex - 1]; 87 | } 88 | } 89 | // Pattern has not been found, return false. Else return true. 90 | if (!found) { 91 | // console.log('The pattern was not found!') 92 | return false; 93 | } 94 | return true; 95 | }; 96 | 97 | module.exports = { 98 | KMPSearch, 99 | }; 100 | -------------------------------------------------------------------------------- /src/_Classics_/knuth-morris-pratt/knuth-morris-pratt.test.js: -------------------------------------------------------------------------------- 1 | const { KMPSearch } = require('.'); 2 | 3 | describe('Pattern Matching Classic Algorithm: Knuth-Morris-Pratt', () => { 4 | describe('KMPSearch', () => { 5 | it('Should return true when the pattern equals the text', () => { 6 | expect(KMPSearch('A', 'A')).toEqual(true); 7 | }); 8 | it('Should return true when the pattern is a single character and is contained within the text', () => { 9 | expect(KMPSearch('S', 'TEST')).toEqual(true); 10 | }); 11 | it('Should return true when the pattern is multiple characters and in the middle of the text', () => { 12 | expect(KMPSearch('WORLD', 'TESTWORLDTEST')).toEqual(true); 13 | }); 14 | it('Should return true when the pattern is present multiple times within the text', () => { 15 | expect(KMPSearch('ST', 'TESTWORLDTEST')).toEqual(true); 16 | }); 17 | it('Should return true when the pattern is a single character and is present at the start of the text', () => { 18 | expect(KMPSearch('A', 'ABABABAABCABCABC')).toEqual(true); 19 | }); 20 | it('Should return true when the pattern is multiple characters and is present at the start of the text', () => { 21 | expect(KMPSearch('AB', 'ABABABAABCABCABC')).toEqual(true); 22 | }); 23 | it('Should return true when the pattern contains repeating characters, and is present in the middle of the text', () => { 24 | expect(KMPSearch('AAABAAAA', 'AAAAAAAAAAABAAAAAA')).toEqual(true); 25 | }); 26 | it('Should return true when the pattern is contained within the text and the pattern contains non alphabetic characters', () => { 27 | expect(KMPSearch('AAA123! ', 'AAAAAA123! AAAAABAAAAAA')).toEqual(true); 28 | }); 29 | it('Should return false when the pattern does not equal the text', () => { 30 | expect(KMPSearch('A', 'B')).toEqual(false); 31 | }); 32 | it('Should return false when the pattern is not contained within the text', () => { 33 | expect(KMPSearch('AD', 'ABABABAABCABCABC')).toEqual(false); 34 | }); 35 | it('Should return false when the pattern is longer than the text', () => { 36 | expect(KMPSearch('AAAAAAAA', 'AAAAAA')).toEqual(false); 37 | }); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /src/_DataStructures_/BloomFilters/index.js: -------------------------------------------------------------------------------- 1 | class BloomFilters { 2 | constructor(size = 101) { 3 | this.size = size; 4 | this.data = this.getStorage(size); 5 | } 6 | 7 | add(element) { 8 | const indices = this.getIndices(element); 9 | 10 | for (let i = 0; i < indices.length; i += 1) { 11 | this.data.setBit(indices[i]); 12 | } 13 | } 14 | 15 | contains(element) { 16 | const indices = this.getIndices(element); 17 | 18 | for (let i = 0; i < indices.length; i += 1) { 19 | const index = indices[i]; 20 | if (!this.data.getBit(index)) { 21 | return false; // item is definately not there 22 | } 23 | } 24 | return true; // item may be there 25 | } 26 | 27 | getIndices(element) { 28 | return [this.hashOne(element), this.hashTwo(element), this.hashThree(element)]; 29 | } 30 | 31 | hashOne(value) { 32 | const stringValue = String(value); 33 | let hashVal = 0; 34 | 35 | for (let i = 0; i < stringValue.length; i += 1) { 36 | hashVal += stringValue.charCodeAt(i) - 96; 37 | } 38 | 39 | // eslint-disable-next-line no-bitwise 40 | // To get a better hash. It may look useless but here is the explanation: https://stackoverflow.com/questions/38356644/why-is-the-bitwise-and-of-two-of-the-same-value-producing-a-different-value 41 | hashVal &= hashVal; 42 | 43 | return Math.abs(hashVal % this.size); 44 | } 45 | 46 | hashTwo(value) { 47 | const stringValue = String(value); 48 | const PRIME_MULTIPLIER = 1801; // Random prime number 49 | let hashVal = 0; 50 | 51 | for (let i = 0; i < stringValue.length; i += 1) { 52 | hashVal += stringValue.charCodeAt(i) - 96; 53 | hashVal *= PRIME_MULTIPLIER; 54 | } 55 | 56 | return Math.abs(hashVal % this.size); 57 | } 58 | 59 | hashThree(value) { 60 | const stringValue = String(value); 61 | const PRIME_MULTIPLIER = 1801; // Random prime number 62 | const PRIME_ADDER = 2029; // Random prime number 63 | let hashVal = 0; 64 | 65 | for (let i = 0; i < stringValue.length; i += 1) { 66 | hashVal += stringValue.charCodeAt(i) - 96; 67 | hashVal *= PRIME_MULTIPLIER; 68 | hashVal += PRIME_ADDER; 69 | } 70 | // eslint-disable-next-line no-bitwise 71 | hashVal &= hashVal; 72 | return Math.abs(hashVal % this.size); 73 | } 74 | 75 | // eslint-disable-next-line class-methods-use-this 76 | getStorage(size) { 77 | const data = new Array(size).fill(0); 78 | 79 | return { 80 | setBit(index) { 81 | data[index] = 1; 82 | }, 83 | getBit(index) { 84 | return data[index]; 85 | }, 86 | }; 87 | } 88 | } 89 | 90 | // const b = new BloomFilters(); 91 | 92 | // b.add('React.js'); 93 | // b.add('Node.js'); 94 | 95 | // console.log(b.contains('JavaScript')); 96 | // console.log(b.contains('React.js')); 97 | 98 | module.exports = BloomFilters; 99 | -------------------------------------------------------------------------------- /src/_DataStructures_/DoublyLinkedList/Node.js: -------------------------------------------------------------------------------- 1 | class Node { 2 | constructor(data, previous, next) { 3 | this.data = data; 4 | this.previous = previous; 5 | this.next = next; 6 | } 7 | } 8 | module.exports = Node; 9 | -------------------------------------------------------------------------------- /src/_DataStructures_/DoublyLinkedList/doublyLinkedList.test.js: -------------------------------------------------------------------------------- 1 | const DLL = require('.'); 2 | 3 | describe('Doubly Linked List', () => { 4 | it('Doubly linked list should be class', () => { 5 | expect(typeof DLL.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | const doublyLinkedList = new DLL(); 9 | 10 | it('It should create a DLL', () => { 11 | expect(doublyLinkedList.head.next).toEqual(doublyLinkedList.tail); 12 | expect(doublyLinkedList.tail.previous).toEqual(doublyLinkedList.head); 13 | expect(doublyLinkedList.length()).toEqual(0); 14 | }); 15 | 16 | it('It should add at beginning (addAtBeginning)', () => { 17 | doublyLinkedList.addAtBeginning(1); 18 | doublyLinkedList.addAtBeginning(2); 19 | doublyLinkedList.addAtBeginning(3); 20 | expect(doublyLinkedList.traverse()).toEqual([3, 2, 1]); 21 | }); 22 | 23 | it('It should add at end (addAtEnd)', () => { 24 | doublyLinkedList.addAtEnd(1); 25 | doublyLinkedList.addAtEnd(2); 26 | doublyLinkedList.addAtEnd(3); 27 | expect(doublyLinkedList.traverse()).toEqual([3, 2, 1, 1, 2, 3]); 28 | }); 29 | 30 | it('It should remove at beginning (removeAtBeginning)', () => { 31 | doublyLinkedList.removeAtBeginning(); 32 | doublyLinkedList.removeAtBeginning(); 33 | doublyLinkedList.removeAtBeginning(); 34 | expect(doublyLinkedList.traverse()).toEqual([1, 2, 3]); 35 | }); 36 | 37 | it('It should remove at end (removeAtEnd)', () => { 38 | doublyLinkedList.removeAtEnd(); 39 | doublyLinkedList.removeAtEnd(); 40 | doublyLinkedList.removeAtEnd(); 41 | 42 | expect(doublyLinkedList.traverse()).toEqual([]); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/_DataStructures_/DoublyLinkedList/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable class-methods-use-this */ 2 | const Node = require('./Node'); 3 | 4 | class DoublyLinkedList { 5 | constructor() { 6 | // head -> tail 7 | // head <- tail 8 | this.head = new Node(null, null, null); 9 | this.tail = new Node(null, null, null); 10 | this.head.next = this.tail; // head next point to tail 11 | this.tail.previous = this.head; // tail previous point to head 12 | this.size = 0; 13 | } 14 | 15 | addAtBeginning(value) { 16 | const newNode = new Node(value, this.head, this.head.next); 17 | this.head.next.previous = newNode; 18 | this.head.next = newNode; 19 | this.size += 1; 20 | } 21 | 22 | addAtEnd(value) { 23 | const newNode = new Node(value, this.tail.previous, this.tail); 24 | this.tail.previous.next = newNode; 25 | this.tail.previous = newNode; 26 | this.size += 1; 27 | } 28 | 29 | removeAtBeginning() { 30 | this.remove(this.head.next); 31 | this.size -= 1; 32 | } 33 | 34 | removeAtEnd() { 35 | this.remove(this.tail.previous); 36 | this.size -= 1; 37 | } 38 | 39 | remove(node) { 40 | const previousNode = node.previous; 41 | const nextNode = node.next; 42 | previousNode.next = nextNode; 43 | nextNode.previous = previousNode; 44 | } 45 | 46 | length() { 47 | return this.size; 48 | } 49 | 50 | traverse() { 51 | let address = this.head.next; 52 | const elements = []; 53 | while (address !== this.tail) { 54 | elements.push(address.data); 55 | address = address.next; 56 | } 57 | return elements; 58 | } 59 | } 60 | 61 | module.exports = DoublyLinkedList; 62 | -------------------------------------------------------------------------------- /src/_DataStructures_/Graphs/index.js: -------------------------------------------------------------------------------- 1 | const { LinkedList } = require('../LinkedList'); 2 | 3 | class Graph { 4 | constructor() { 5 | this.data = this.getStorage(); 6 | } 7 | 8 | addVertex(v) { 9 | this.data.addVertex(v); 10 | } 11 | 12 | addEdge(v, e) { 13 | this.data.addEdge(v, e); 14 | } 15 | 16 | removeEdge(v, e) { 17 | this.data.removeEdge(v, e); 18 | } 19 | 20 | removeVertex(v) { 21 | this.data.removeVertex(v); 22 | } 23 | 24 | getEdges(v) { 25 | return this.data.getEdges(v); 26 | } 27 | 28 | display() { 29 | return this.data.displayMatrix(); 30 | } 31 | 32 | // eslint-disable-next-line class-methods-use-this 33 | getStorage() { 34 | const map = {}; 35 | 36 | return { 37 | addVertex(v) { 38 | if (!map[v]) map[v] = new LinkedList(); 39 | }, 40 | addEdge(v, e) { 41 | if (map[v]) { 42 | map[v].addAtEnd(e); 43 | } 44 | }, 45 | removeEdge(v, e) { 46 | if (map[v]) { 47 | map[v].filter(e); 48 | } 49 | }, 50 | removeVertex(v) { 51 | if (map[v]) { 52 | delete map[v]; 53 | 54 | const vertices = Object.keys(map); 55 | const edge = v; // this vertex may be an edge for other vertices 56 | vertices.forEach((vertex) => this.removeEdge(vertex, edge)); 57 | } 58 | }, 59 | getEdges(v) { 60 | if (map[v]) { 61 | return map[v].traverseList(); 62 | } 63 | }, 64 | displayMatrix() { 65 | const vertices = Object.keys(map); 66 | const result = {}; 67 | 68 | vertices.forEach((v) => { 69 | result[v] = map[v].traverseList(); 70 | }); 71 | return result; 72 | }, 73 | }; 74 | } 75 | } 76 | 77 | // const g = new Graph(); 78 | 79 | // g.addVertex('Noida'); 80 | // console.log(g.display()); 81 | 82 | // g.addEdge('Noida', 'Greater Noida'); 83 | // g.addEdge('Noida', 'Ghaziabaad'); 84 | // g.addEdge('Noida', 'Meerut'); 85 | // g.addEdge('Noida', 'Greater Noida'); 86 | // g.addEdge('Noida', 'Mathura'); 87 | 88 | // g.addVertex('Mathura'); 89 | // g.addEdge('Mathura', 'Noida'); 90 | // g.addEdge('Mathura', 'Meerut'); 91 | // console.log(g.display()); 92 | // // g.data['Noida'].size = 10; 93 | 94 | // // console.log(g.data['Noida']); 95 | // // g.filter('Noida', 'Greater Noida'); 96 | // console.log(g.display()); 97 | // console.log('removing Mathura'); 98 | 99 | // g.removeVertex('Mathura'); 100 | // console.log(g.display()); 101 | // console.log(g.getEdges('Noida')); 102 | 103 | module.exports = Graph; 104 | -------------------------------------------------------------------------------- /src/_DataStructures_/HashTable/HashEntry.js: -------------------------------------------------------------------------------- 1 | class HashEntry { 2 | constructor({ key, value }) { 3 | this.key = key; 4 | this.value = value; 5 | this.next = null; 6 | } 7 | } 8 | 9 | module.exports = HashEntry; 10 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/MaxHeap/MaxHeap.test.js: -------------------------------------------------------------------------------- 1 | const MaxHeap = require('.'); 2 | 3 | describe('MaxHeap', () => { 4 | it('Should be a class', () => { 5 | expect(typeof MaxHeap.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | const mh = new MaxHeap(); 9 | 10 | beforeEach(() => { 11 | mh.destroy(); 12 | }); 13 | 14 | it('Should create an instance of MaxHeap', () => { 15 | expect(mh instanceof MaxHeap).toEqual(true); 16 | }); 17 | 18 | it('Should create a MaxHeap using collection', () => { 19 | const mHBulk = new MaxHeap([1, 3, 21, 9, 101, 0]); 20 | expect(mHBulk.getMax()).toEqual(101); 21 | }); 22 | 23 | it('Should add an element to the MaxHeap', () => { 24 | mh.add(10); 25 | expect(mh.getMax()).toEqual(10); 26 | }); 27 | 28 | it('Should keep the largest element at the root', () => { 29 | [12, 5, 34].forEach((el) => mh.add(el)); 30 | expect(mh.getMax()).toEqual(34); 31 | }); 32 | 33 | it('Should retain Heap properties after removal of an element', () => { 34 | [12, 45, 1, 34].forEach((el) => mh.add(el)); 35 | expect(mh.getMax()).toEqual(45); 36 | mh.remove(); 37 | expect(mh.getMax()).toEqual(34); 38 | }); 39 | 40 | it('Should return `null` when heap is empty', () => { 41 | [1, 34, 43, 54, 123].forEach((el) => mh.add(el)); 42 | mh.remove(); 43 | mh.remove(); 44 | mh.remove(); 45 | mh.remove(); 46 | mh.remove(); 47 | mh.remove(); 48 | expect(mh.getMax()).toEqual(null); 49 | }); 50 | 51 | it('Should return the elelment value on `remove()`', () => { 52 | [1, 34].forEach((el) => mh.add(el)); 53 | expect(mh.getMax()).toEqual(34); 54 | expect(mh.remove()).toEqual(34); 55 | expect(mh.remove()).toEqual(1); 56 | expect(mh.getMax()).toEqual(null); 57 | }); 58 | 59 | it('Should return `null` on `remove() called on empty heap`', () => { 60 | expect(mh.getMax()).toEqual(null); 61 | }); 62 | }); 63 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/MaxHeap/index.js: -------------------------------------------------------------------------------- 1 | class MaxHeap { 2 | constructor(collection) { 3 | this.heap = []; 4 | if (collection) { 5 | collection.forEach((element) => { 6 | this.add(element); 7 | }); 8 | } 9 | } 10 | 11 | add(element) { 12 | this.heap.push(element); 13 | // check for the parent element & swap if required 14 | // eslint-disable-next-line no-underscore-dangle 15 | this.__traverseUpAndSwap(this.heap.length - 1); 16 | } 17 | 18 | getMax() { 19 | return this.heap[0] !== undefined ? this.heap[0] : null; 20 | } 21 | 22 | remove() { 23 | const max = this.heap[0] !== undefined ? this.heap[0] : null; 24 | // return the element at the root 25 | if (this.heap.length === 1) { 26 | this.heap.pop(); 27 | } 28 | 29 | if (this.heap.length > 1) { 30 | // move the leaf to the root 31 | this.heap[0] = this.heap[this.heap.length - 1]; 32 | this.heap.pop(); 33 | // restore the heapify property 34 | // eslint-disable-next-line no-underscore-dangle 35 | this.__heapify(0); 36 | } 37 | return max; 38 | } 39 | 40 | __heapify(index) { 41 | const left = index * 2; 42 | const right = index * 2 + 1; 43 | let largest = index; 44 | 45 | if (this.heap.length > left && this.heap[largest] < this.heap[left]) { 46 | largest = left; 47 | } 48 | 49 | if (this.heap.length > right && this.heap[largest] < this.heap[right]) { 50 | largest = right; 51 | } 52 | 53 | if (largest !== index) { 54 | const temp = this.heap[largest]; 55 | this.heap[largest] = this.heap[index]; 56 | this.heap[index] = temp; 57 | // eslint-disable-next-line no-underscore-dangle 58 | this.__heapify(largest); 59 | } 60 | } 61 | 62 | __traverseUpAndSwap(index) { 63 | if (index <= 0) return; 64 | const parent = Math.floor(index / 2); 65 | 66 | if (this.heap[parent] < this.heap[index]) { 67 | const temp = this.heap[parent]; 68 | this.heap[parent] = this.heap[index]; 69 | this.heap[index] = temp; 70 | // eslint-disable-next-line no-underscore-dangle 71 | this.__traverseUpAndSwap(parent); 72 | } 73 | } 74 | 75 | destroy() { 76 | this.heap = []; 77 | } 78 | } 79 | 80 | module.exports = MaxHeap; 81 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/MinHeap/MinHeap.test.js: -------------------------------------------------------------------------------- 1 | const MinHeap = require('.'); 2 | 3 | describe('MinHeap', () => { 4 | it('Should be a class', () => { 5 | expect(typeof MinHeap.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | const mh = new MinHeap(); 9 | 10 | beforeEach(() => { 11 | mh.destroy(); 12 | }); 13 | 14 | it('Should create an instance of MinHeap', () => { 15 | expect(mh instanceof MinHeap).toEqual(true); 16 | }); 17 | 18 | it('Should create a MinHeap using collection', () => { 19 | const mHBulk = new MinHeap([112, 3, 21, 9, 10, 0]); 20 | expect(mHBulk.getMin()).toEqual(0); 21 | }); 22 | 23 | it('Should add an element to the MinHeap', () => { 24 | mh.add(10); 25 | expect(mh.getMin()).toEqual(10); 26 | }); 27 | 28 | it('Should keep the smallest element at the root', () => { 29 | [12, 5, 34].forEach((el) => mh.add(el)); 30 | expect(mh.getMin()).toEqual(5); 31 | }); 32 | 33 | it('Should retain Heap properties after removal of an element', () => { 34 | [12, 45, 1, 34].forEach((el) => mh.add(el)); 35 | expect(mh.getMin()).toEqual(1); 36 | mh.remove(); 37 | expect(mh.getMin()).toEqual(12); 38 | }); 39 | 40 | it('Should return `null` when heap is empty', () => { 41 | [1, 34, 43, 54, 123].forEach((el) => mh.add(el)); 42 | expect(mh.getMin()).toEqual(1); 43 | mh.remove(); 44 | mh.remove(); 45 | mh.remove(); 46 | mh.remove(); 47 | mh.remove(); 48 | mh.remove(); 49 | expect(mh.getMin()).toEqual(null); 50 | }); 51 | 52 | it('Should return the elelment value on `remove()`', () => { 53 | [1, 34].forEach((el) => mh.add(el)); 54 | expect(mh.getMin()).toEqual(1); 55 | expect(mh.remove()).toEqual(1); 56 | expect(mh.remove()).toEqual(34); 57 | expect(mh.getMin()).toEqual(null); 58 | }); 59 | 60 | it('Should return `null` on `remove() called on empty heap`', () => { 61 | expect(mh.getMin()).toEqual(null); 62 | }); 63 | }); 64 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/MinHeap/index.js: -------------------------------------------------------------------------------- 1 | class MinHeap { 2 | constructor(collection) { 3 | this.heap = []; 4 | 5 | if (collection) { 6 | collection.forEach((element) => { 7 | this.add(element); 8 | }); 9 | } 10 | } 11 | 12 | add(element) { 13 | this.heap.push(element); 14 | // check for the parent element & swap if required 15 | // eslint-disable-next-line no-underscore-dangle 16 | this.__traverseUpAndSwap(this.heap.length - 1); 17 | } 18 | 19 | getMin() { 20 | return this.heap[0] !== undefined ? this.heap[0] : null; 21 | } 22 | 23 | remove() { 24 | const min = this.heap[0] !== undefined ? this.heap[0] : null; 25 | if (this.heap.length === 1) { 26 | this.heap.pop(); 27 | } 28 | if (this.heap.length > 1) { 29 | this.heap[0] = this.heap[this.heap.length - 1]; 30 | this.heap.pop(); 31 | // eslint-disable-next-line no-underscore-dangle 32 | this.__heapify(0); 33 | } 34 | return min; 35 | } 36 | 37 | destroy() { 38 | this.heap = []; 39 | } 40 | 41 | // eslint-disable-next-line consistent-return 42 | __traverseUpAndSwap(index) { 43 | if (index <= 0) return null; 44 | 45 | const parent = Math.floor(index / 2); 46 | 47 | if (this.heap[parent] > this.heap[index]) { 48 | const temp = this.heap[parent]; 49 | this.heap[parent] = this.heap[index]; 50 | this.heap[index] = temp; 51 | // eslint-disable-next-line no-underscore-dangle 52 | this.__traverseUpAndSwap(parent); 53 | } 54 | } 55 | 56 | __heapify(index) { 57 | const left = index * 2; 58 | const right = index * 2 + 1; 59 | 60 | let smallest = index; 61 | 62 | if (this.heap.length > left && this.heap[smallest] > this.heap[left]) { 63 | smallest = left; 64 | } 65 | if (this.heap.length > right && this.heap[smallest] > this.heap[right]) { 66 | smallest = right; 67 | } 68 | if (smallest !== index) { 69 | const tmp = this.heap[smallest]; 70 | this.heap[smallest] = this.heap[index]; 71 | this.heap[index] = tmp; 72 | // eslint-disable-next-line no-underscore-dangle 73 | this.__heapify(smallest); 74 | } 75 | } 76 | } 77 | 78 | module.exports = MinHeap; 79 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/k-largest-in-array/index.js: -------------------------------------------------------------------------------- 1 | const MaxHeap = require('../MaxHeap'); 2 | 3 | /** 4 | * Find the 4 largest elements from an array 5 | */ 6 | 7 | function findKLargest(collection, k) { 8 | if (!collection || !Array.isArray(collection)) { 9 | throw new Error('Invalid / missing collection'); 10 | } 11 | 12 | // create a MaxHeap using the collection 13 | const mh = new MaxHeap(collection); 14 | const result = []; 15 | 16 | for (let i = 0; i < k; i += 1) { 17 | result.push(mh.remove()); 18 | } 19 | return result; 20 | } 21 | 22 | module.exports = findKLargest; 23 | -------------------------------------------------------------------------------- /src/_DataStructures_/Heaps/k-smallest-in-array/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Find 4 smallest elements in an array 3 | */ 4 | 5 | const MinHeap = require('../MinHeap'); 6 | 7 | function findKSmallest(collection, k) { 8 | if (!collection || !Array.isArray(collection)) { 9 | throw new Error('Invalid / missing collection'); 10 | } 11 | 12 | // create a MinHeap using the collection 13 | const mh = new MinHeap(collection); 14 | const result = []; 15 | for (let i = 0; i < k; i += 1) { 16 | result.push(mh.remove()); 17 | } 18 | return result; 19 | } 20 | 21 | module.exports = findKSmallest; 22 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/element-from-last/element-from-last.test.js: -------------------------------------------------------------------------------- 1 | const { getElementFromlast } = require('.'); 2 | const { LinkedList } = require('../index'); 3 | 4 | describe('Find the middle node of a LinkedList', () => { 5 | let list = null; 6 | beforeEach(() => { 7 | list = new LinkedList(); 8 | list.addAtBeginning('Hello'); 9 | list.addAtEnd('World!'); 10 | list.addAtEnd('Welcome'); 11 | list.addAtEnd('to'); 12 | list.addAtEnd('the'); 13 | list.addAtEnd('world'); 14 | list.addAtEnd('of'); 15 | list.addAtEnd('JavaScript'); 16 | }); 17 | 18 | it('Should return `null` for empty list', () => { 19 | list.delete(); 20 | expect(getElementFromlast(list, 10)).toEqual(null); 21 | }); 22 | 23 | it('Should return `world` as 3rd from last of the list', () => { 24 | expect(getElementFromlast(list, 3).data).toEqual('world'); 25 | }); 26 | 27 | it('Should return `Welcome` as 6th from last of the list', () => { 28 | expect(getElementFromlast(list, 6).data).toEqual('Welcome'); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/element-from-last/index.js: -------------------------------------------------------------------------------- 1 | function getElementFromlast(linkedList, index) { 2 | let normal = linkedList.getFirst(); 3 | let nth = linkedList.getFirst(); 4 | let count = 0; 5 | 6 | if (!normal) { 7 | return null; 8 | } 9 | 10 | while (normal) { 11 | if (count >= index) { 12 | nth = nth.next; 13 | } 14 | normal = normal.next; 15 | count += 1; 16 | } 17 | return nth; 18 | } 19 | 20 | module.exports = { 21 | getElementFromlast, 22 | }; 23 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/loop-in-list/index.js: -------------------------------------------------------------------------------- 1 | // Floyd’s Cycle-Finding Algorithm 2 | 3 | function detectLoop(linkedList) { 4 | let slow = linkedList.getFirst(); 5 | let fast = linkedList.getFirst(); 6 | 7 | while (fast.next && fast.next.next) { 8 | slow = slow.next; 9 | fast = fast.next.next; 10 | 11 | if (slow === fast) { 12 | return true; 13 | } 14 | } 15 | return false; 16 | } 17 | 18 | module.exports = { 19 | detectLoop, 20 | }; 21 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/loop-in-list/loop-in-list.test.js: -------------------------------------------------------------------------------- 1 | const { LinkedList } = require('../index'); 2 | const { detectLoop } = require('.'); 3 | 4 | describe('Loop a LinkedList', () => { 5 | let loopList = null; 6 | let last = null; 7 | beforeEach(() => { 8 | loopList = new LinkedList(); 9 | loopList.addAtBeginning('1'); 10 | loopList.addAtEnd('2'); 11 | loopList.addAtEnd('3'); 12 | loopList.addAtEnd('4'); 13 | loopList.addAtEnd('5'); 14 | // Create loop in list 15 | last = loopList.getLast(); 16 | last.next = loopList.getFirst(); 17 | }); 18 | 19 | it('Should break for empty list', () => { 20 | loopList.delete(); 21 | expect(() => detectLoop(loopList)).toThrow(TypeError); 22 | }); 23 | 24 | it('Should return `true` when looping list', () => { 25 | expect(detectLoop(loopList)).toEqual(true); 26 | }); 27 | 28 | it('Should return `false` for non loop list', () => { 29 | last.next = null; // remove loop in list 30 | expect(detectLoop(loopList)).toEqual(false); 31 | }); 32 | 33 | it('Should return `false` for non loop list', () => { 34 | const list = new LinkedList(); 35 | list.addAtBeginning('1'); 36 | list.addAtEnd('1'); 37 | list.addAtEnd('1'); 38 | expect(detectLoop(list)).toEqual(false); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/middle-node/index.js: -------------------------------------------------------------------------------- 1 | function getMiddleNode(linkedList) { 2 | let slow = linkedList.getFirst(); 3 | let fast = linkedList.getFirst(); 4 | 5 | if (!slow) { 6 | return null; 7 | } 8 | 9 | while (fast.next && fast.next.next) { 10 | slow = slow.next; 11 | fast = fast.next.next; 12 | } 13 | 14 | return slow; 15 | } 16 | 17 | module.exports = { 18 | getMiddleNode, 19 | }; 20 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/middle-node/midle-node.test.js: -------------------------------------------------------------------------------- 1 | const { LinkedList } = require('../index'); 2 | const { getMiddleNode } = require('.'); 3 | 4 | describe('Find the middle node of a LinkedList', () => { 5 | let list = null; 6 | beforeEach(() => { 7 | list = new LinkedList(); 8 | list.addAtBeginning('Hello'); 9 | list.addAtEnd('World!'); 10 | list.addAtEnd('Welcome'); 11 | list.addAtEnd('to'); 12 | list.addAtEnd('the'); 13 | list.addAtEnd('world'); 14 | list.addAtEnd('of'); 15 | list.addAtEnd('JavaScript'); 16 | }); 17 | 18 | it('Should return `null` for empty list', () => { 19 | list.delete(); 20 | expect(getMiddleNode(list)).toEqual(null); 21 | }); 22 | 23 | it('Should return `to` for the given list', () => { 24 | expect(getMiddleNode(list).data).toEqual('to'); 25 | }); 26 | 27 | it('Should return `Welcome` after deleting 3 last nodes of the list', () => { 28 | list.removeFromEnd(); 29 | list.removeFromEnd(); 30 | list.removeFromEnd(); 31 | expect(getMiddleNode(list).data).toEqual('Welcome'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/reverse-linked-list/index.js: -------------------------------------------------------------------------------- 1 | function reverseLinkedList(linkedList) { 2 | let current = linkedList.getFirst(); 3 | let prev = null; 4 | let keeper = null; 5 | do { 6 | keeper = current.next; 7 | current.next = prev; 8 | prev = current; 9 | current = keeper; 10 | } while (current.next != null); 11 | 12 | return current; 13 | } 14 | module.exports = { 15 | reverseLinkedList, 16 | }; 17 | -------------------------------------------------------------------------------- /src/_DataStructures_/LinkedList/reverse-linked-list/reverse-linked-list.test.js: -------------------------------------------------------------------------------- 1 | const { LinkedList } = require('../index'); 2 | const { reverseLinkedList } = require('.'); 3 | 4 | describe('Reverse a LinkedList', () => { 5 | let list = null; 6 | beforeEach(() => { 7 | list = new LinkedList(); 8 | list.addAtBeginning('1'); 9 | list.addAtEnd('2'); 10 | list.addAtEnd('3'); 11 | list.addAtEnd('4'); 12 | list.addAtEnd('5'); 13 | }); 14 | 15 | it.skip('Should return `null` for empty list', () => { 16 | list.delete(); 17 | expect(reverseLinkedList(list)).toEqual(null); 18 | }); 19 | 20 | it.skip('Should return `5`->`4`->`3`->`2`->`1` for the given list', () => { 21 | const reversedList = reverseLinkedList(list); 22 | expect(reversedList.data).toEqual('5'); 23 | expect(reversedList.next.data).toEqual('4'); 24 | expect(reversedList.next.next.data).toEqual('3'); 25 | expect(reversedList.next.next.next.data).toEqual('2'); 26 | expect(reversedList.next.next.next.next.data).toEqual('1'); 27 | }); 28 | 29 | it.skip('Should return `3`->`2`->`1` after deleting 2 last nodes of the list', () => { 30 | list.removeFromEnd(); 31 | list.removeFromEnd(); 32 | const reversedList2 = reverseLinkedList(list); 33 | expect(reversedList2.data).toEqual('3'); 34 | expect(reversedList2.next.data).toEqual('2'); 35 | expect(reversedList2.next.next.data).toEqual('1'); 36 | }); 37 | }); 38 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/Queue.test.js: -------------------------------------------------------------------------------- 1 | const Queue = require('.'); 2 | 3 | describe('Data Structure : Queue', () => { 4 | it('Queue should be class', () => { 5 | expect(typeof Queue.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | describe('Queue API', () => { 9 | let queue = null; 10 | 11 | beforeEach(() => { 12 | queue = new Queue(); 13 | }); 14 | 15 | it('Should add element to a queue', () => { 16 | queue.enqueue(5); 17 | expect(queue.peek()).toEqual(5); 18 | }); 19 | 20 | it('Should dequeue() an element from the queue', () => { 21 | queue.enqueue(2); 22 | queue.enqueue(3); 23 | 24 | expect(queue.dequeue()).toEqual(2); 25 | expect(queue.peek()).toEqual(3); 26 | expect(queue.length()).toEqual(1); 27 | }); 28 | 29 | describe('peek()', () => { 30 | beforeEach(() => { 31 | queue.enqueue(2); 32 | queue.enqueue(5); 33 | }); 34 | 35 | it('Should return the elemet to be removed using peek()', () => { 36 | expect(queue.peek()).toEqual(2); 37 | }); 38 | 39 | it('Should not remove the element', () => { 40 | expect(queue.peek()).toEqual(2); 41 | expect(queue.dequeue()).toEqual(2); 42 | }); 43 | }); 44 | 45 | it('Should maintain the order of elements', () => { 46 | // first in first out 47 | queue.enqueue(2); 48 | queue.enqueue(1); 49 | queue.enqueue(4); 50 | queue.enqueue(3); 51 | 52 | expect(queue.dequeue()).toEqual(2); 53 | expect(queue.dequeue()).toEqual(1); 54 | expect(queue.dequeue()).toEqual(4); 55 | expect(queue.dequeue()).toEqual(3); 56 | expect(queue.dequeue()).toEqual(null); 57 | }); 58 | 59 | it('Shoud return size of Queue', () => { 60 | const queue2 = new Queue(); 61 | queue2.enqueue(2); 62 | queue2.enqueue(1); 63 | queue2.enqueue(4); 64 | queue2.enqueue(3); 65 | expect(queue2.length()).toEqual(4); 66 | }); 67 | 68 | it('Should Destroy Queue', () => { 69 | queue.destroy(); 70 | expect(queue.length()).toEqual(0); 71 | }); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/QueueUsingArray.js: -------------------------------------------------------------------------------- 1 | class Queue { 2 | constructor() { 3 | this.data = []; 4 | } 5 | 6 | add(element) { 7 | // add element to the start of the data 8 | return this.data.unshift(element); 9 | } 10 | 11 | peek() { 12 | return this.data[this.data.length - 1]; 13 | } 14 | 15 | remove() { 16 | return this.data.pop(); 17 | } 18 | } 19 | 20 | module.exports = Queue; 21 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/generate-binary-number/index.js: -------------------------------------------------------------------------------- 1 | const Queue = require('../index'); 2 | 3 | function generateBinaryNumber(n) { 4 | const result = []; 5 | const q = new Queue(); 6 | 7 | // add `1` to the queue 8 | q.enqueue('1'); 9 | 10 | // iterate till the given number 11 | for (let i = 0; i < n; i += 1) { 12 | // push the item in the queue to the array 13 | result.push(q.dequeue()); 14 | 15 | // append `0` & `1` respectively 16 | const s1 = `${result[i]}0`; 17 | const s2 = `${result[i]}1`; 18 | 19 | // push the combinations in the queue 20 | q.enqueue(s1); 21 | q.enqueue(s2); 22 | } 23 | // return the result containing all the binary numbers 24 | return result; 25 | } 26 | 27 | // console.log(generateBinaryNumber(5)); 28 | 29 | module.exports = generateBinaryNumber; 30 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/index.js: -------------------------------------------------------------------------------- 1 | const { LinkedList: SLL } = require('../LinkedList'); 2 | 3 | class Queue { 4 | constructor() { 5 | this.data = this.getStorage(); 6 | } 7 | 8 | enqueue(element) { 9 | this.data.enqueue(element); 10 | } 11 | 12 | dequeue() { 13 | return this.data.dequeue(); 14 | } 15 | 16 | peek() { 17 | return this.data.peek(); 18 | } 19 | 20 | length() { 21 | return this.data.length(); 22 | } 23 | 24 | destroy() { 25 | return this.data.destroy(); 26 | } 27 | 28 | // eslint-disable-next-line class-methods-use-this 29 | getStorage() { 30 | // encapsulating the internal implementation here 31 | const storage = new SLL(); 32 | 33 | return { 34 | enqueue(element) { 35 | return storage.addAtEnd(element); 36 | }, 37 | dequeue() { 38 | const node = storage.removeFromBeginning(); 39 | return node ? node.data : node; 40 | }, 41 | peek() { 42 | const node = storage.getFirst(); 43 | return node ? node.data : node; 44 | }, 45 | length() { 46 | return storage.size; 47 | }, 48 | destroy() { 49 | storage.delete(); 50 | }, 51 | }; 52 | } 53 | } 54 | 55 | module.exports = Queue; 56 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/queue-using-stack/index.js: -------------------------------------------------------------------------------- 1 | const Stack = require('../../Stack'); 2 | 3 | class Queue { 4 | constructor() { 5 | this.queue = new Stack(); 6 | this.temp = new Stack(); 7 | } 8 | 9 | enqueue(data) { 10 | this.queue.push(data); 11 | } 12 | 13 | dequeue() { 14 | if (!this.queue.peek()) { 15 | return null; 16 | } 17 | 18 | // pop all the element to the temp stack 19 | while (this.queue.peek()) this.temp.push(this.queue.pop()); 20 | const el = this.temp.pop(); 21 | 22 | // push all the temp items to the queue again 23 | while (this.temp.peek()) this.queue.push(this.temp.pop()); 24 | return el; 25 | } 26 | } 27 | 28 | module.exports = Queue; 29 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/reverse-first-k/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const Queue = require('../index'); 3 | const Stack = require('../../Stack'); 4 | 5 | function reverseFirstKElelments(q, k) { 6 | const s = new Stack(); 7 | 8 | // push all the k elements ot the stack 9 | for (let i = 0; i < k; i += 1) { 10 | s.push(q.dequeue()); 11 | } 12 | 13 | // push the stack items to the queue 14 | for (let i = 0; i < k; i += 1) { 15 | q.enqueue(s.pop()); 16 | } 17 | 18 | // empty the queue and push the same queue 19 | const remaining = q.length() - k; 20 | for (let i = 0; i < remaining; i += 1) { 21 | q.enqueue(q.dequeue()); 22 | } 23 | 24 | // return the queue 25 | return q; 26 | } 27 | 28 | module.exports = reverseFirstKElelments; 29 | 30 | // let q = new Queue(); 31 | 32 | // q.enqueue(1); 33 | // q.enqueue(2); 34 | // q.enqueue(3); 35 | // q.enqueue(4); 36 | // q.enqueue(5); 37 | // q.enqueue(6); 38 | // q.enqueue(7); 39 | // q.enqueue(8); 40 | // q.enqueue(9); 41 | 42 | // q = reverseFirstKElelments(q, 4); 43 | 44 | // const arr = []; 45 | // while (q.length()) { 46 | // arr.push(q.dequeue()); 47 | // } 48 | // console.log(arr); 49 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/weave/index.js: -------------------------------------------------------------------------------- 1 | const Queue = require('../index'); 2 | 3 | function weaveQueues(first, second) { 4 | const weaved = new Queue(); 5 | 6 | while (first.peek() || second.peek()) { 7 | if (first.peek()) { 8 | weaved.enqueue(first.dequeue()); 9 | } 10 | 11 | if (second.peek()) { 12 | weaved.enqueue(second.dequeue()); 13 | } 14 | } 15 | return weaved; 16 | } 17 | 18 | module.exports = { 19 | weaveQueues, 20 | }; 21 | -------------------------------------------------------------------------------- /src/_DataStructures_/Queue/weave/weave.test.js: -------------------------------------------------------------------------------- 1 | const { weaveQueues } = require('.'); 2 | const Queue = require('../index'); 3 | 4 | describe('Weave two queues using weaveQueues()', () => { 5 | it('Should weave be a function', () => { 6 | expect(typeof weaveQueues).toEqual('function'); 7 | }); 8 | 9 | it('Should weave 2 queues', () => { 10 | const q1 = new Queue(); 11 | const q2 = new Queue(); 12 | 13 | q1.enqueue('Hello'); 14 | q2.enqueue(1); 15 | q1.enqueue('World'); 16 | q2.enqueue(2); 17 | q2.enqueue(3); 18 | 19 | const q3 = weaveQueues(q1, q2); 20 | 21 | expect(q3.dequeue()).toEqual('Hello'); 22 | expect(q3.dequeue()).toEqual(1); 23 | expect(q3.dequeue()).toEqual('World'); 24 | expect(q3.dequeue()).toEqual(2); 25 | expect(q3.dequeue()).toEqual(3); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /src/_DataStructures_/Set/index.js: -------------------------------------------------------------------------------- 1 | // XSet because ES6 already has a Set class 2 | class XSet { 3 | constructor() { 4 | this.data = this.getStore(); 5 | } 6 | 7 | add(element) { 8 | this.data.push(element); 9 | } 10 | 11 | remove(element) { 12 | this.data.pop(element); 13 | } 14 | 15 | has(element) { 16 | return this.data.contains(element); 17 | } 18 | 19 | values() { 20 | return this.data.val(); 21 | } 22 | 23 | union(givenSet) { 24 | const result = new XSet(); 25 | const firstSetValues = this.values(); 26 | const givenSetValues = givenSet.values(); 27 | 28 | // eslint-disable-next-line no-restricted-syntax 29 | for (const e of firstSetValues) result.add(e); 30 | 31 | // eslint-disable-next-line no-restricted-syntax 32 | for (const e of givenSetValues) result.add(e); 33 | 34 | return result; 35 | } 36 | 37 | // eslint-disable-next-line class-methods-use-this 38 | getStore() { 39 | const store = {}; 40 | 41 | return { 42 | push(el) { 43 | if (!store[el]) { 44 | store[el] = true; 45 | } 46 | }, 47 | pop(el) { 48 | if (store[el]) { 49 | delete store[el]; 50 | } 51 | }, 52 | contains(el) { 53 | return !!store[el]; 54 | }, 55 | val() { 56 | return Object.keys(store); 57 | }, 58 | }; 59 | } 60 | } 61 | 62 | // const s = new XSet(); 63 | 64 | // s.add(10); 65 | // s.add(20); 66 | // s.add(90); 67 | 68 | // console.log(s.has(1)); 69 | // console.log(s.has(10)); 70 | // console.log(s.has(90)); 71 | 72 | // console.log(s.values()); 73 | // s.remove(90); 74 | // console.log(s.has(90)); 75 | // console.log(s.data); 76 | 77 | module.exports = XSet; 78 | -------------------------------------------------------------------------------- /src/_DataStructures_/Set/setTest.js: -------------------------------------------------------------------------------- 1 | const letters = require('./index'); 2 | 3 | describe('Data Structure : Set', () => { 4 | 5 | 6 | it('X Set should be a Class', () => { 7 | expect(typeof XSet.prototype.constructor).toEqual('function'); 8 | }); 9 | 10 | describe('Creation of Set', () => { 11 | 12 | it('Should create a new Set with no elements', () => { 13 | letters = new XSet(); 14 | expect(letters === 0); 15 | }); 16 | 17 | it('Should add letter A', () => { 18 | letters.add('a'); 19 | expect(letters.has('a'); 20 | }); 21 | 22 | it('Should add letter B', () => { 23 | letters.add('b'); 24 | expect(letters.has('b'); 25 | }); 26 | 27 | it('Should add letter C', () => { 28 | letters.add('c'); 29 | expect(letters.has('c'); 30 | }); 31 | 32 | it('Should remove letter A', () => { 33 | letters.remove('a'); 34 | expect(!letters.has('a'); 35 | }); 36 | 37 | it('Should remove letter B', () => { 38 | letters.remove('b'); 39 | expect(!letters.has('b'); 40 | }); 41 | 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/2-stacks-using1-array/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Revision to PR #35 where I implemented bullshit thinking of 3 | * new breakthrough :D 4 | */ 5 | 6 | class TwoStacks { 7 | constructor(capacity) { 8 | this.data = []; 9 | this.top1 = -1; 10 | this.top2 = capacity; 11 | this.overflow = new Error('Overflow: Stack is full'); 12 | 13 | this.capacity = capacity; 14 | } 15 | 16 | push1(value) { 17 | if (this.top1 < this.top2 - 1) { 18 | this.top1 += 1; 19 | this.data[this.top1] = value; 20 | } else { 21 | throw this.overflow; 22 | } 23 | } 24 | 25 | push2(value) { 26 | if (this.top1 < this.top2 - 1) { 27 | this.top2 -= 1; 28 | this.data[this.top2] = value; 29 | } else { 30 | throw this.overflow; 31 | } 32 | } 33 | 34 | pop1() { 35 | if (this.top1 >= 0) { 36 | const item = this.data[this.top1]; 37 | delete this.data[this.top1]; 38 | this.top1 -= 1; 39 | return item; 40 | } 41 | return -1; 42 | } 43 | 44 | pop2() { 45 | if (this.top2 < this.capacity) { 46 | const item = this.data[this.top2]; 47 | delete this.data[this.top2]; 48 | this.top2 += 1; 49 | return item; 50 | } 51 | return -1; 52 | } 53 | } 54 | 55 | module.exports = TwoStacks; 56 | 57 | /** Test cases */ 58 | 59 | /* 60 | const s = new TwoStacks(4); 61 | 62 | s.push1('a'); 63 | console.log(s.data); 64 | 65 | s.push2('a2'); 66 | console.log(s.data); 67 | 68 | s.push1('b'); 69 | console.log(s.data); 70 | 71 | s.push2('b2'); 72 | console.log(s.data); 73 | 74 | s.push2('d2'); 75 | console.log(s.data); 76 | 77 | s.push2('c23'); 78 | console.log(s.data); 79 | 80 | console.log(s.pop2()); 81 | console.log(s.data); 82 | 83 | console.log(s.pop1()); 84 | console.log(s.data); 85 | */ 86 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/Stack.test.js: -------------------------------------------------------------------------------- 1 | const Stack = require('.'); 2 | 3 | describe('Data Structure : Stack', () => { 4 | it('Should be class', () => { 5 | expect(typeof Stack.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | describe('Stack API', () => { 9 | let stack = null; 10 | 11 | beforeEach(() => { 12 | stack = new Stack(); 13 | }); 14 | 15 | it('Should add() element to a stack', () => { 16 | stack.push(5); 17 | expect(stack.data).toEqual([5]); 18 | }); 19 | 20 | it('Should remove() an element from the stack', () => { 21 | stack.push(2); 22 | stack.push(3); 23 | 24 | expect(stack.pop()).toEqual(3); 25 | expect(stack.data).toEqual([2]); 26 | }); 27 | 28 | describe('peek()', () => { 29 | beforeEach(() => { 30 | stack.push(2); 31 | stack.push(5); 32 | }); 33 | 34 | it('Should return the elemet to be removed using peek()', () => { 35 | expect(stack.peek()).toEqual(5); 36 | }); 37 | 38 | it('Should not remove the element', () => { 39 | expect(stack.peek()).toEqual(5); 40 | expect(stack.pop()).toEqual(5); 41 | }); 42 | }); 43 | 44 | it('Should maintain the FILO order of elements', () => { 45 | // first in last out 46 | stack.push(2); 47 | stack.push(1); 48 | stack.push(4); 49 | stack.push(3); 50 | 51 | expect(stack.pop()).toEqual(3); 52 | expect(stack.pop()).toEqual(4); 53 | expect(stack.pop()).toEqual(1); 54 | expect(stack.pop()).toEqual(2); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/balanced-parenthesis/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | Given an expression string exp , write a program to examine whether the pairs and 3 | the orders of “{“,”}”,”(“,”)”,”[“,”]” are correct in expression. 4 | 5 | Example: 6 | Input: exp = “[()]{}{[()()]()}” 7 | Output: true 8 | 9 | Input: exp = “[(])” 10 | Output: false 11 | */ 12 | 13 | const Stack = require('../index'); 14 | 15 | function checkBalancedParenthesis(expression) { 16 | const s = new Stack(); 17 | for (let i = 0; i < expression.length; i += 1) { 18 | const char = expression[i]; 19 | if (char === '{' || char === '(' || char === '[') { 20 | // If current character is a starting bracket (‘(‘ or ‘{‘ or ‘[‘) then push it to stack 21 | s.push(char); 22 | } else { 23 | if (s.isEmpty()) { 24 | // when we have only right parenthesis or brackets in expresion 25 | return false; 26 | } 27 | if ( 28 | (char === '}' && s.peek() !== '{') || 29 | (char === ')' && s.peek() !== '(') || 30 | (char === ']' && s.peek() !== '[') 31 | ) { 32 | return false; 33 | } 34 | // If the current character is a closing bracket (‘)’ or ‘}’ or ‘]’) then pop it from stack 35 | s.pop(); 36 | } 37 | } 38 | if (s.isEmpty()) { 39 | // expression has balanced parenthesis 40 | return true; 41 | } 42 | return false; 43 | } 44 | 45 | module.exports = { 46 | checkBalancedParenthesis, 47 | }; 48 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/baseball-game/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | You're now a baseball game point recorder. 4 | 5 | Given a list of strings, each string can be one of the 4 following types: 6 | 7 | Integer (one round's score): Directly represents the number of points you get in this round. 8 | "+" (one round's score): Represents that the points you get in this round are the sum of the last two valid round's points. 9 | "D" (one round's score): Represents that the points you get in this round are the doubled data of the last valid round's points. 10 | "C" (an operation, which isn't a round's score): Represents the last valid round's points you get were invalid and should be removed. 11 | Each round's operation is permanent and could have an impact on the round before and the round after. 12 | 13 | You need to return the sum of the points you could get in all the rounds. 14 | 15 | */ 16 | 17 | const Stack = require('../index'); 18 | 19 | // The given solution is on O(n) 20 | 21 | function sumOfPoints(arr) { 22 | const pointsTracker = new Stack(); 23 | let sum = 0; 24 | 25 | if (!Array.isArray(arr)) { 26 | throw new Error('Invalid Argument!'); 27 | } 28 | // Track the value of `sum` accordingly 29 | // eslint-disable-next-line no-restricted-syntax 30 | for (const el of arr) { 31 | // console.log('data : ', pointsTracker.data); 32 | // console.log('sum : ', sum); 33 | 34 | if (el === 'C') { 35 | // pop if the value is `C` 36 | const top = pointsTracker.pop(); 37 | if (top) { 38 | sum -= top; 39 | } 40 | } else if (el === 'D') { 41 | // Double the top id the value is 'D' 42 | const top = pointsTracker.peek(); 43 | sum += top * 2; 44 | pointsTracker.push(top * 2); 45 | } else if (el === '+') { 46 | const top = pointsTracker.pop(); 47 | const secondTop = pointsTracker.peek(); 48 | 49 | const result = top + secondTop; 50 | sum += result; 51 | 52 | pointsTracker.push(top); 53 | pointsTracker.push(result); 54 | } else { 55 | // push to the Stack if the value is integer 56 | sum += parseInt(el, 10); 57 | pointsTracker.push(parseInt(el, 10)); 58 | } 59 | } 60 | return sum; 61 | } 62 | 63 | /** 64 | * Let's Try it out 65 | 66 | console.log(sumOfPoints(['5', '2', 'C', 'D', '+'])); 67 | console.log(sumOfPoints(['5', '-2', '4', 'C', 'D', '9', '+', '+'])); 68 | */ 69 | 70 | /** 71 | 72 | Input: ["5","2","C","D","+"] 73 | Output: 30 74 | 75 | Explanation: 76 | 77 | Round 1: You could get 5 points. The sum is: 5. 78 | Round 2: You could get 2 points. The sum is: 7. 79 | Operation 1: The round 2's data was invalid. The sum is: 5. 80 | Round 3: You could get 10 points (the round 2's data has been removed). The sum is: 15. 81 | Round 4: You could get 5 + 10 = 15 points. The sum is: 30. 82 | Example 2: 83 | Input: ["5","-2","4","C","D","9","+","+"] 84 | Output: 27 85 | 86 | Explanation: 87 | 88 | Round 1: You could get 5 points. The sum is: 5. 89 | Round 2: You could get -2 points. The sum is: 3. 90 | Round 3: You could get 4 points. The sum is: 7. 91 | Operation 1: The round 3's data is invalid. The sum is: 3. 92 | Round 4: You could get -4 points (the round 3's data has been removed). The sum is: -1. 93 | Round 5: You could get 9 points. The sum is: 8. 94 | Round 6: You could get -4 + 9 = 5 points. The sum is 13. 95 | Round 7: You could get 9 + 5 = 14 points. The sum is 27. 96 | 97 | Note: 98 | 99 | The size of the input list will be between 1 and 1000. 100 | Every integer represented in the list will be between -30000 and 30000. 101 | 102 | */ 103 | 104 | module.exports = { 105 | sumOfPoints, 106 | }; 107 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/immitate-queue-using-stack/immitate-queue-using-stack.test.js: -------------------------------------------------------------------------------- 1 | const Queue = require('.'); 2 | 3 | describe('Immitated Queue using 2 Stacks', () => { 4 | it('Should be class', () => { 5 | expect(typeof Queue.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | describe('Queue API', () => { 9 | let queue = null; 10 | 11 | beforeEach(() => { 12 | queue = new Queue(); 13 | }); 14 | 15 | it('Should add() element to a queue', () => { 16 | queue.add(5); 17 | expect(queue.data).toEqual([5]); 18 | }); 19 | 20 | it('Should remove() an element from the queue', () => { 21 | queue.add(2); 22 | queue.add(3); 23 | 24 | expect(queue.remove()).toEqual(2); 25 | expect(queue.data).toEqual([3]); 26 | }); 27 | 28 | describe('peek()', () => { 29 | beforeEach(() => { 30 | queue.add(2); 31 | queue.add(5); 32 | }); 33 | 34 | it('Should return the elemet to be removed using peek()', () => { 35 | expect(queue.peek()).toEqual(2); 36 | }); 37 | 38 | it('Should not remove the element', () => { 39 | expect(queue.peek()).toEqual(2); 40 | expect(queue.remove()).toEqual(2); 41 | }); 42 | }); 43 | 44 | it('Should maintain the order of elements', () => { 45 | // first in first out 46 | queue.add(2); 47 | queue.add(1); 48 | queue.add(4); 49 | queue.add(3); 50 | 51 | expect(queue.remove()).toEqual(2); 52 | expect(queue.remove()).toEqual(1); 53 | expect(queue.remove()).toEqual(4); 54 | expect(queue.remove()).toEqual(3); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/immitate-queue-using-stack/index.js: -------------------------------------------------------------------------------- 1 | const Stack = require('../index'); 2 | 3 | class ImmitateQueue { 4 | constructor() { 5 | this.stackA = new Stack(); 6 | this.stackB = new Stack(); 7 | this.data = this.stackA.data; 8 | } 9 | 10 | add(element) { 11 | this.stackA.push(element); 12 | this.data = this.stackA.data; 13 | } 14 | 15 | peek() { 16 | while (this.stackA.peek()) { 17 | this.stackB.push(this.stackA.pop()); 18 | } 19 | 20 | const element = this.stackB.peek(); 21 | 22 | while (this.stackB.peek()) { 23 | this.stackA.push(this.stackB.pop()); 24 | } 25 | this.data = this.stackA.data; 26 | return element; 27 | } 28 | 29 | remove() { 30 | while (this.stackA.peek()) { 31 | this.stackB.push(this.stackA.pop()); 32 | } 33 | 34 | const element = this.stackB.pop(); 35 | 36 | while (this.stackB.peek()) { 37 | this.stackA.push(this.stackB.pop()); 38 | } 39 | 40 | this.data = this.stackA.data; 41 | return element; 42 | } 43 | } 44 | 45 | module.exports = ImmitateQueue; 46 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/index.js: -------------------------------------------------------------------------------- 1 | class Stack { 2 | constructor() { 3 | this.data = []; 4 | } 5 | 6 | push(element) { 7 | // add element to the last 8 | this.data.push(element); 9 | } 10 | 11 | pop() { 12 | return this.data.pop(); 13 | } 14 | 15 | peek() { 16 | return this.data[this.data.length - 1]; 17 | } 18 | 19 | isEmpty() { 20 | // check if stack is empty 21 | return this.data.length === 0; 22 | } 23 | } 24 | 25 | module.exports = Stack; 26 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/min-stack/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | You have to implement the minStack class which will have a min() function. 3 | Whenever min() is called, the minimum value of the stack is returned in O(1) time. 4 | The element is not popped from the stack, its value is simply returned. 5 | 6 | Keep in mind that the min function should work in O(1) and should not pop 7 | the minimum element out of the stack. It simply returns its value. 8 | */ 9 | 10 | const Stack = require('../index'); 11 | 12 | class MinStack { 13 | constructor() { 14 | this.main = new Stack(); 15 | this.minStack = new Stack(); 16 | } 17 | 18 | push(element) { 19 | this.main.push(element); 20 | 21 | if (!this.minStack.peek()) { 22 | return this.minStack.push(element); 23 | } 24 | if (element > this.minStack.peek()) { 25 | return this.minStack.push(this.minStack.peek()); 26 | } 27 | return this.minStack.push(element); 28 | } 29 | 30 | pop() { 31 | this.minStack.pop(); 32 | return this.main.pop(); 33 | } 34 | 35 | getMin() { 36 | return this.minStack.peek(); 37 | } 38 | } 39 | 40 | const ms = new MinStack(); 41 | 42 | ms.push(1); 43 | ms.push(10); 44 | ms.push(21); 45 | ms.push(3); 46 | ms.push(9); 47 | ms.push(-11); 48 | ms.push(32); 49 | 50 | // eslint-disable-next-line no-console 51 | console.log(ms.minStack.data); 52 | // eslint-disable-next-line no-console 53 | console.log(ms.getMin()); 54 | 55 | ms.pop(); 56 | ms.pop(); 57 | 58 | // eslint-disable-next-line no-console 59 | console.log(ms.getMin()); 60 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/postfix-expression-evaluation/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Evaluation of Postfix Expression 3 | * Input:456*+ 4 | * Output:34 5 | */ 6 | 7 | const Stack = require('../index'); 8 | 9 | const ERROR_STRING = 'Expression is not in order'; 10 | 11 | function evaluatePostfixExpression(expression) { 12 | // eslint-disable-next-line no-param-reassign 13 | expression = expression.trim(); 14 | 15 | if (expression.length === 0 || expression.length === 1) { 16 | throw new Error(ERROR_STRING); 17 | } 18 | 19 | const s = new Stack(); 20 | // eslint-disable-next-line no-plusplus 21 | for (let i = 0; i < expression.length; i++) { 22 | const char = expression[i]; 23 | // eslint-disable-next-line no-restricted-globals 24 | if (!isNaN(char)) { 25 | // if number push the char onto stack 26 | s.push(Number(char)); 27 | } else { 28 | // if char is an operator then pop two elements from stack, evaluate them accordingly based on operator. 29 | // push the result to stack 30 | const val1 = s.pop(); 31 | const val2 = s.pop(); 32 | switch (char) { 33 | case '+': 34 | s.push(val2 + val1); 35 | break; 36 | case '-': 37 | s.push(val2 - val1); 38 | break; 39 | case '*': 40 | s.push(val2 * val1); 41 | break; 42 | case '/': 43 | s.push(val2 / val1); 44 | break; 45 | default: 46 | throw new Error('Operation is not valid'); 47 | } 48 | } 49 | } 50 | // pop the value from stack 51 | const result = s.pop(); 52 | if (s.isEmpty()) { 53 | return result; 54 | } 55 | throw new Error(ERROR_STRING); 56 | } 57 | 58 | module.exports = { 59 | evaluatePostfixExpression, 60 | ERROR_STRING, 61 | }; 62 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/postfix-expression-evaluation/postfix-expression-evaluation.test.js: -------------------------------------------------------------------------------- 1 | const { evaluatePostfixExpression, ERROR_STRING } = require('.'); 2 | 3 | describe('Postfix expression evaluation', () => { 4 | it('should be a function', () => { 5 | expect(typeof evaluatePostfixExpression).toEqual('function'); 6 | }); 7 | 8 | it('should return a number', () => { 9 | const expression = '11+'; 10 | 11 | expect(typeof evaluatePostfixExpression(expression)).toEqual('number'); 12 | }); 13 | 14 | it('should handle addition', () => { 15 | const expression = '23+'; 16 | const expected = 5; 17 | 18 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 19 | }); 20 | 21 | it('should handle subtraction', () => { 22 | const expression = '54-'; 23 | const expected = 1; 24 | 25 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 26 | }); 27 | 28 | it('should handle multiplication', () => { 29 | const expression = '34*'; 30 | const expected = 12; 31 | 32 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 33 | }); 34 | 35 | it('should handle division', () => { 36 | const expression = '62/'; 37 | const expected = 3; 38 | 39 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 40 | }); 41 | 42 | it('should handle negative numbers', () => { 43 | const expression = '25-'; 44 | const expected = -3; 45 | 46 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 47 | }); 48 | 49 | it('should handle multiple operators', () => { 50 | const expression = '123*+'; 51 | const expected = 7; 52 | 53 | expect(evaluatePostfixExpression(expression)).toEqual(expected); 54 | }); 55 | 56 | describe('should throw error on invalid expressions', () => { 57 | const invalidExpressions = ['12', '1', '+', '1+2', '+12']; 58 | test.each(invalidExpressions)('running for %p', (expression) => { 59 | expect(() => evaluatePostfixExpression(expression)).toThrow(ERROR_STRING); 60 | }); 61 | 62 | expect(() => evaluatePostfixExpression('1&2')).toThrow('Operation is not valid'); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/remove-consecutive-repeated-digits/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an integer N, remove consecutive repeated digits from it. 3 | * Input:133445 4 | * Output:1345 5 | */ 6 | 7 | const Stack = require('../index'); 8 | 9 | function removeConsecutiveDigits(no) { 10 | const s = new Stack(); 11 | let newNo = ''; 12 | // initally push first digit into stack 13 | newNo += no[0]; 14 | s.push(no[0]); 15 | for (let i = 1; i < no.length; i += 1) { 16 | const digit = no[i]; 17 | // if stack top and incoming digit is same ignore it else append to newNo. 18 | if (s.peek() !== digit) { 19 | newNo += digit; 20 | s.push(digit); 21 | } 22 | } 23 | return newNo; 24 | } 25 | 26 | module.exports = { 27 | removeConsecutiveDigits, 28 | }; 29 | -------------------------------------------------------------------------------- /src/_DataStructures_/Stack/sort-a-stack/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sort a stack with the help of a temporary stack. 3 | * Input:[1,10,21,3,9,-11,32] 4 | * Output:[32,21,10,9,3,1,-11] 5 | * Time Complexity:O(N^2) 6 | */ 7 | const Stack = require('../index'); 8 | 9 | function sortStack(stack) { 10 | const tempStack = new Stack(); 11 | while (!stack.isEmpty()) { 12 | // pop the first element from stack 13 | const temp = stack.pop(); 14 | // for ascending order (tempStack.peek() < temp) 15 | while (!tempStack.isEmpty() && tempStack.peek() > temp) { 16 | stack.push(tempStack.pop()); 17 | } 18 | // push the first element(temp) onto tempStack if tempStack.peek() root.value) { 24 | const right = findAncestors(root.rightChild, value); 25 | if (right) { 26 | return [...right, root.value]; 27 | } 28 | return false; 29 | } 30 | 31 | if (value === root.value) return []; 32 | return false; 33 | } 34 | 35 | // create a BST 36 | // const myBST = new BST(6); 37 | // myBST.add(4); 38 | // myBST.add(9); 39 | // myBST.add(2); 40 | // myBST.add(5); 41 | // myBST.add(14); 42 | // myBST.add(8); 43 | // myBST.add(12); 44 | // myBST.add(10); 45 | 46 | // console.log(findAncestors(myBST.root, 10)); 47 | // console.log(findAncestors(myBST.root, 101)); 48 | 49 | module.exports = findAncestors; 50 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/find-k-nodes-from-root/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const BST = require('../index'); 3 | 4 | function findKNodes(root, k) { 5 | let arr = []; 6 | 7 | if (root === null) return []; 8 | if (k === 0) return [...arr, root.value]; 9 | 10 | const left = findKNodes(root.leftChild, k - 1); 11 | arr = [...arr, ...left]; 12 | 13 | const right = findKNodes(root.rightChild, k - 1); 14 | arr = [...arr, ...right]; 15 | return arr; 16 | } 17 | 18 | // create a BST 19 | // const myBST = new BST(6); 20 | 21 | // myBST.add(2); 22 | // myBST.add(19); 23 | // myBST.add(14); 24 | // myBST.add(8); 25 | // myBST.add(5); 26 | // myBST.add(12); 27 | // myBST.add(33); 28 | // myBST.add(52); 29 | // myBST.add(1); 30 | 31 | // console.log(findKNodes(myBST.root, 2)); 32 | 33 | module.exports = findKNodes; 34 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/find-kth-max/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const BST = require('../index'); 3 | 4 | // Inorder traversal returns a sorted array 5 | function inOrderTraversal(root) { 6 | if (root === null) return []; 7 | let arr = []; 8 | // traverse left 9 | const left = inOrderTraversal(root.leftChild); 10 | arr = [...left, root.value]; 11 | const right = inOrderTraversal(root.rightChild); 12 | return [...arr, ...right]; 13 | } 14 | 15 | function findKthMax(rootNode, k) { 16 | const arr = inOrderTraversal(rootNode); 17 | if (k <= 0 || k > arr.lenth) { 18 | throw new Error('Invalid value for K'); 19 | } 20 | return arr[arr.length - k]; 21 | } 22 | 23 | // // create a BST 24 | // const myBST = new BST(6); 25 | 26 | // myBST.add(2); 27 | // myBST.add(19); 28 | // myBST.add(14); 29 | // myBST.add(8); 30 | // myBST.add(5); 31 | // myBST.add(12); 32 | // myBST.add(33); 33 | // myBST.add(52); 34 | // myBST.add(1); 35 | 36 | // // find 3rd max 37 | // console.log(findKthMax(myBST.root, 3)); 38 | 39 | module.exports = findKthMax; 40 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/find-kth-min/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const BST = require('../index'); 3 | 4 | // Inorder traversal returns a sorted array 5 | function inOrderTraversal(root) { 6 | if (root === null) return []; 7 | let arr = []; 8 | // traverse left 9 | const left = inOrderTraversal(root.leftChild); 10 | arr = [...left, root.value]; 11 | const right = inOrderTraversal(root.rightChild); 12 | return [...arr, ...right]; 13 | } 14 | 15 | function findKthMin(rootNode, k) { 16 | const arr = inOrderTraversal(rootNode); 17 | if (k <= 0 || k > arr.lenth) { 18 | throw new Error('Invalid value for K'); 19 | } 20 | return arr[k - 1]; 21 | } 22 | 23 | // // create a BST 24 | // const myBST = new BST(6); 25 | 26 | // myBST.add(2); 27 | // myBST.add(19); 28 | // myBST.add(14); 29 | // myBST.add(8); 30 | // myBST.add(5); 31 | // myBST.add(12); 32 | // myBST.add(33); 33 | // myBST.add(52); 34 | // myBST.add(1); 35 | // myBST.add(0); 36 | 37 | // console.log(findKthMin(myBST.root, 3)); 38 | 39 | module.exports = findKthMin; 40 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/height-of-bst/height-of-bst.test.js: -------------------------------------------------------------------------------- 1 | const BinarySearchTree = require('../index'); 2 | const heightOfBST = require('./index'); 3 | 4 | describe('Binary search tree traversals', () => { 5 | let bst; 6 | 7 | describe('Creates BST', () => { 8 | it('should create BST', () => { 9 | // Creates BST 10 | bst = new BinarySearchTree(6); 11 | const keys = [4, 9, 2, 5, 8, 12]; 12 | keys.forEach((el) => bst.add(el)); 13 | }); 14 | }); 15 | 16 | describe('Check bst was created as expected', () => { 17 | it('Inorder traversal of the created bst should be [ 2, 4, 5, 6, 8, 9, 12 ]', () => { 18 | expect(bst.inorder()).toEqual([2, 4, 5, 6, 8, 9, 12]); 19 | }); 20 | 21 | it('Preorder traversal of the created bst should be [ 6, 4, 2, 5, 9, 8, 12 ]', () => { 22 | expect(bst.preorder()).toEqual([6, 4, 2, 5, 9, 8, 12]); 23 | }); 24 | 25 | it('Postorder traversal of the created bst should be [ 2, 5, 4, 8, 12, 9, 6 ]', () => { 26 | expect(bst.postorder()).toEqual([2, 5, 4, 8, 12, 9, 6]); 27 | }); 28 | }); 29 | 30 | describe('BST node deletions', () => { 31 | it('should check height of bst to be 3 prior deletion', () => { 32 | expect(heightOfBST(bst.root)).toEqual(3); 33 | }); 34 | 35 | it('deleting leaf element does not affect tree height if it has sibling', () => { 36 | bst.remove(2); 37 | bst.remove(8); 38 | expect(heightOfBST(bst.root)).toEqual(3); 39 | }); 40 | 41 | it('deleting leaf element does affect tree height if it has no-sibling', () => { 42 | bst.remove(5); 43 | bst.remove(12); 44 | expect(heightOfBST(bst.root)).toEqual(2); 45 | }); 46 | }); 47 | 48 | describe('When root left subtree height is greater than right', () => { 49 | const bst2 = new BinarySearchTree(10); 50 | const keys = [11, 20, 9, 8, 7, 6, 5, 4]; 51 | keys.forEach((el) => bst2.add(el)); 52 | 53 | it('should return height of BST ', () => { 54 | expect(heightOfBST(bst2.root)).toEqual(7); 55 | }); 56 | }); 57 | }); 58 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/height-of-bst/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const BST = require('../index'); 3 | 4 | function findHeightOfBST(root) { 5 | let leftHeight = 0; 6 | let rightHeight = 0; 7 | 8 | if (root === null) return 0; 9 | leftHeight = findHeightOfBST(root.leftChild); 10 | rightHeight = findHeightOfBST(root.rightChild); 11 | 12 | if (leftHeight > rightHeight) { 13 | return leftHeight + 1; 14 | } 15 | return rightHeight + 1; 16 | } 17 | 18 | // create a BST 19 | // const myBST = new BST(6); 20 | // myBST.add(4); 21 | // myBST.add(9); 22 | // myBST.add(2); 23 | // myBST.add(5); 24 | // myBST.add(14); 25 | // myBST.add(8); 26 | // myBST.add(12); 27 | // myBST.add(10); 28 | 29 | // // console.log(myBST.root); 30 | // console.log(myBST.preorder()); 31 | // console.log(findHeightOfBST(myBST.root)); 32 | 33 | module.exports = findHeightOfBST; 34 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/index.js: -------------------------------------------------------------------------------- 1 | const Node = require('./Node'); 2 | const BSTUtils = require('./utils'); 3 | 4 | class BinarySearchTree { 5 | constructor(value) { 6 | if (!value) throw new Error('Root node value required'); 7 | this.root = new Node(value); 8 | } 9 | 10 | isEmpty() { 11 | return this.root === null; 12 | } 13 | 14 | /** Layered methods to simplify the BST API using utils under the hood */ 15 | 16 | add(value) { 17 | return BSTUtils.insert(this.root, value); 18 | } 19 | 20 | preorder() { 21 | return BSTUtils.preorder(this.root, []); 22 | } 23 | 24 | postorder() { 25 | return BSTUtils.postorder(this.root, []); 26 | } 27 | 28 | inorder() { 29 | return BSTUtils.inorder(this.root, []); 30 | } 31 | 32 | search(value) { 33 | return BSTUtils.search(this.root, value); 34 | } 35 | 36 | getMinimum() { 37 | const minNode = BSTUtils.findMinNode(this.root); 38 | return minNode.value; 39 | } 40 | 41 | getMaximum() { 42 | const maxNode = BSTUtils.findMaxNode(this.root); 43 | return maxNode.value; 44 | } 45 | 46 | remove(value) { 47 | this.root = BSTUtils.delete(this.root, value); 48 | } 49 | } 50 | 51 | // const bst = new BinarySearchTree(6); 52 | // [4, 9, 2, 5, 8, 12].forEach(el => bst.add(el)); 53 | 54 | // const preorder = bst.preorder(); 55 | // console.log('Preorder Traversal - ', preorder); 56 | 57 | // const inorder = bst.inorder(); 58 | // console.log('Inorder Traversal - ', inorder); 59 | 60 | // const postorder = bst.postorder(); 61 | // console.log('Postorder Traversal - ', postorder); 62 | 63 | // const search = 18; 64 | // console.log(`Search for ${search}`, bst.search(search)); 65 | 66 | // const minNode = bst.getMinimum(); 67 | // console.log('Minimum value =>', minNode); 68 | 69 | // const maxNode = bst.getMaximum(); 70 | // console.log('Maximum value =>', maxNode); 71 | 72 | // bst.remove(4); 73 | // console.log(bst.preorder()); 74 | 75 | // console.log(bst.root); 76 | 77 | module.exports = BinarySearchTree; 78 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/lowest-common-ancestor/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Lowest Common Ancestor in a Binary Search Tree. 3 | * 4 | * Given values of two values n1 and n2 in a Binary Search Tree, find the Lowest Common Ancestor (LCA). You may assume that both the values exist in the tree. 5 | */ 6 | 7 | function lowestCommonAncestor(node, n1, n2) { 8 | if (node === null) return null; 9 | 10 | // If both n1 and n2 are smaller than root, then LCA lies in left 11 | if (node.value > n1 && node.value > n2) { 12 | return lowestCommonAncestor(node.leftChild, n1, n2); 13 | } 14 | 15 | // If both n1 and n2 are greater than root, then LCA lies in right 16 | if (node.value < n1 && node.value < n2) { 17 | return lowestCommonAncestor(node.rightChild, n1, n2); 18 | } 19 | 20 | return node; 21 | } 22 | 23 | module.exports = { 24 | lowestCommonAncestor, 25 | }; 26 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/lowest-common-ancestor/index.test.js: -------------------------------------------------------------------------------- 1 | const { lowestCommonAncestor } = require('.'); 2 | const BinarySearchTree = require('../index'); 3 | 4 | // Quick JSON equivalent 5 | // {"left":{"left":{"data":4},"right":{"left":{"data":10},"right":{"data":14},"data":12},"data":8},"right":{"data":22},"data":20} 6 | 7 | describe('Lowest Common Ancestor in BST', () => { 8 | const bst = new BinarySearchTree(20); 9 | const keys = [22, 8, 12, 4, 14, 10]; 10 | keys.forEach((el) => bst.add(el)); 11 | 12 | it('Should return Lowest Common Ancestor Node ', () => { 13 | expect(lowestCommonAncestor(bst.root, 10, 14).value).toEqual(12); 14 | expect(lowestCommonAncestor(bst.root, 14, 8).value).toEqual(8); 15 | expect(lowestCommonAncestor(bst.root, 10, 22).value).toEqual(20); 16 | }); 17 | 18 | const bst2 = new BinarySearchTree(6); 19 | bst2.remove(6); 20 | 21 | it('Should return Null when root is null', () => { 22 | expect(lowestCommonAncestor(bst2.root, 10, 22)).toEqual(null); 23 | }); 24 | }); 25 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinarySearchTree/utils.js: -------------------------------------------------------------------------------- 1 | const Node = require('./Node'); 2 | 3 | const utils = { 4 | // eslint-disable-next-line consistent-return 5 | insert(root, value) { 6 | if (root === null) { 7 | const newNode = new Node(value); 8 | // eslint-disable-next-line no-param-reassign 9 | root = newNode; 10 | return root; 11 | } 12 | 13 | if (value < root.value) { 14 | // eslint-disable-next-line no-param-reassign 15 | root.leftChild = this.insert(root.leftChild, value); 16 | return root; 17 | } 18 | if (value > root.value) { 19 | // eslint-disable-next-line no-param-reassign 20 | root.rightChild = this.insert(root.rightChild, value); 21 | return root; 22 | } 23 | }, 24 | 25 | preorder(root, array) { 26 | if (root === null) return array; 27 | array.push(root.value); 28 | this.preorder(root.leftChild, array); 29 | this.preorder(root.rightChild, array); 30 | return array; 31 | }, 32 | 33 | inorder(root, array) { 34 | if (root === null) return array; 35 | this.inorder(root.leftChild, array); 36 | array.push(root.value); 37 | this.inorder(root.rightChild, array); 38 | return array; 39 | }, 40 | 41 | postorder(root, array) { 42 | if (root === null) return array; 43 | this.postorder(root.leftChild, array); 44 | this.postorder(root.rightChild, array); 45 | array.push(root.value); 46 | return array; 47 | }, 48 | 49 | // eslint-disable-next-line consistent-return 50 | search(root, value) { 51 | if (root === null) return false; 52 | if (value === root.value) return true; 53 | 54 | if (value < root.value) { 55 | return this.search(root.leftChild, value); 56 | } 57 | if (value > root.value) { 58 | return this.search(root.rightChild, value); 59 | } 60 | }, 61 | 62 | delete(root, value) { 63 | if (root === null) { 64 | return root; 65 | } 66 | 67 | if (value > root.value) { 68 | // eslint-disable-next-line no-param-reassign 69 | root.rightChild = this.delete(root.rightChild, value); 70 | } else if (value < root.value) { 71 | // eslint-disable-next-line no-param-reassign 72 | root.leftChild = this.delete(root.leftChild, value); 73 | } else { 74 | // found the node 75 | if (root.leftChild === null) { 76 | // there is a right sub-tree 77 | return root.rightChild; 78 | } 79 | if (root.rightChild === null) { 80 | // there is a left sub-tree 81 | return root.leftChild; 82 | } 83 | /** 84 | * the root contain 2 childs, we got 2 options: 85 | * 1. We can either find the Node with minimum value at from the right sub-tree 86 | * 2. Or, we can find the Node with maximum value from the left sub-tree 87 | * 88 | * I'm picking up 1 here 89 | */ 90 | const minRightNode = this.findMinNode(root.rightChild); 91 | // eslint-disable-next-line no-param-reassign 92 | root.value = minRightNode.value; 93 | // eslint-disable-next-line no-param-reassign 94 | root.rightChild = this.delete(root.rightChild, minRightNode.value); 95 | return root; 96 | } 97 | return root; 98 | }, 99 | 100 | findMinNode(root) { 101 | /** The minnimum values is the let most leaf node in BST */ 102 | if (root.leftChild === null) return root; 103 | return this.findMinNode(root.leftChild); 104 | }, 105 | 106 | findMaxNode(root) { 107 | if (root.rightChild === null) return root; 108 | return this.findMaxNode(root.rightChild); 109 | }, 110 | }; 111 | 112 | module.exports = utils; 113 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/Node.js: -------------------------------------------------------------------------------- 1 | module.exports = class Node { 2 | constructor(value) { 3 | this.value = value; 4 | this.leftChild = null; // will be a node 5 | this.rightChild = null; // will be a node 6 | } 7 | }; 8 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/bottom-view-binary-tree/BottomViewBinaryTree.test.js: -------------------------------------------------------------------------------- 1 | const BinaryTree = require('../index'); 2 | const bottomView = require('.'); 3 | 4 | describe('Bottom View Binary Tree', () => { 5 | let btree; 6 | 7 | beforeEach(() => { 8 | btree = new BinaryTree([1, 2, 3, 4, 5, 6]); 9 | }); 10 | 11 | it('Should determine the bottom view of a binary tree', () => { 12 | expect(bottomView(btree)).toEqual([6, 2, 3, 4]); 13 | }); 14 | it('Should handle null binary tree', () => { 15 | expect(bottomView(null)).toEqual([]); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/bottom-view-binary-tree/index.js: -------------------------------------------------------------------------------- 1 | const Queue = require('../../../Queue'); 2 | 3 | // Determines the bottom view of a binary tree 4 | // Takes a BinaryTree as a parameter 5 | // Returns an integer array 6 | // Time complexity: O(n) where n is the number of nodes in the tree 7 | 8 | module.exports = function bottomView(binaryTree) { 9 | if (binaryTree == null || binaryTree.root == null) { 10 | return []; 11 | } 12 | 13 | // root's horizontal distance = 0 14 | const horizontalDistance = 0; 15 | 16 | // create a map to track most recent visited nodes per hd 17 | const hdToNodeValue = new Map(); 18 | 19 | // perform bfs 20 | const q = new Queue(); 21 | q.enqueue([binaryTree.root, horizontalDistance]); 22 | 23 | while (q.length() > 0) { 24 | const currentNodeTuple = q.dequeue(); 25 | const currentNode = currentNodeTuple[0]; 26 | const currentHd = currentNodeTuple[1]; 27 | hdToNodeValue.set(currentHd, currentNode.value); 28 | 29 | if (currentNode.leftChild != null && currentNode.leftChild.value != null) { 30 | q.enqueue([currentNode.leftChild, currentHd - 1]); 31 | } 32 | 33 | if (currentNode.rightChild != null && currentNode.rightChild.value != null) { 34 | q.enqueue([currentNode.rightChild, currentHd + 1]); 35 | } 36 | } 37 | 38 | return Array.from(hdToNodeValue.values()); 39 | }; 40 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/btree-traversals.test.js: -------------------------------------------------------------------------------- 1 | const BinaryTree = require('./index'); 2 | 3 | describe('Binary Tree Preorder Traversal', () => { 4 | let btree; 5 | let preOrderTraversal; 6 | 7 | describe('Creates BTree', () => { 8 | it('Should throw error if argument is not array', () => { 9 | expect(() => { 10 | btree = new BinaryTree('Hello tree'); 11 | }).toThrow('Invalid argument to create a Binary Tree'); 12 | }); 13 | btree = new BinaryTree([1, 2, 3, 4, 5, 6]); 14 | }); 15 | 16 | describe('BTree Traversals', () => { 17 | it('Should compute the Preorder traversal for the above created binary tree', () => { 18 | preOrderTraversal = btree.preOrder(); 19 | expect(preOrderTraversal).toEqual([1, 2, 4, 5, 3, 6]); 20 | }); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/check-binary-tree-subtree-another-binary-tree/index.js: -------------------------------------------------------------------------------- 1 | // Check if a binary tree is subtree of another binary tree 2 | // Root of the source tree and the tree to be matched are provided in the parameters. 3 | // Return true/false based on whether or not the given tree is the subtree of the source tree 4 | // Time complexity : O(m*n) m is the number of nodes of the original tree, 5 | // n is the number of nodes in the tree to be matched 6 | 7 | function areIdentical(rootOfOriginalTree, rootOfMatchingTree) { 8 | if (rootOfOriginalTree === null && rootOfMatchingTree === null) { 9 | return true; 10 | } 11 | 12 | if (rootOfOriginalTree === null || rootOfMatchingTree === null) { 13 | return false; 14 | } 15 | 16 | return ( 17 | rootOfOriginalTree.value === rootOfMatchingTree.value && 18 | areIdentical(rootOfOriginalTree.leftChild, rootOfMatchingTree.leftChild) && 19 | areIdentical(rootOfOriginalTree.rightChild, rootOfMatchingTree.rightChild) 20 | ); 21 | } 22 | 23 | function isSubtree(rootOfOriginalTree, rootOfMatchingTree) { 24 | if (rootOfMatchingTree === null) { 25 | return true; 26 | } 27 | 28 | if (rootOfOriginalTree === null) { 29 | return false; 30 | } 31 | 32 | if (areIdentical(rootOfOriginalTree, rootOfMatchingTree)) { 33 | return true; 34 | } 35 | 36 | return ( 37 | isSubtree(rootOfOriginalTree.leftChild, rootOfMatchingTree) || 38 | isSubtree(rootOfMatchingTree.rightChild, rootOfMatchingTree) 39 | ); 40 | } 41 | 42 | module.exports = { 43 | isSubtree, 44 | }; 45 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/BinaryTree/index.js: -------------------------------------------------------------------------------- 1 | const Node = require('./Node'); 2 | 3 | class BinaryTree { 4 | constructor(arr) { 5 | if (!Array.isArray(arr) || !arr.length) { 6 | throw new Error('Invalid argument to create a Binary Tree'); 7 | } 8 | this.root = this.createBinaryTree((this.root = null), arr, 0); 9 | } 10 | 11 | // eslint-disable-next-line class-methods-use-this 12 | createBinaryTree(root, arr, i) { 13 | if (i < arr.length && arr[i]) { 14 | // eslint-disable-next-line no-param-reassign 15 | root = new Node(arr[i]); 16 | // eslint-disable-next-line no-param-reassign 17 | root.leftChild = this.createBinaryTree(root.leftChild, arr, 2 * i + 1); 18 | // eslint-disable-next-line no-param-reassign 19 | root.rightChild = this.createBinaryTree(root.rightChild, arr, 2 * i + 2); 20 | } 21 | return root; 22 | } 23 | 24 | preorder(root) { 25 | let arr = []; 26 | 27 | if (root === null) return arr; 28 | // push node to arr 29 | arr.push(root.value); 30 | 31 | // push left node 32 | const left = this.preorder(root.leftChild); 33 | arr = [...arr, ...left]; 34 | 35 | // push right node 36 | const right = this.preorder(root.rightChild); 37 | arr = [...arr, ...right]; 38 | 39 | return arr; 40 | } 41 | 42 | preOrder() { 43 | return this.preorder(this.root); 44 | } 45 | } 46 | 47 | // const bt = new BinaryTree([1, 2, 3, 4, 5, 6]); 48 | // console.log(bt.root); 49 | // console.log(bt.preOrder()); 50 | 51 | module.exports = BinaryTree; 52 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/SuffixTree/SuffixTree.test.js: -------------------------------------------------------------------------------- 1 | const SuffixTree = require('.'); 2 | 3 | describe('Data Structure : Suffix Tree', () => { 4 | it('Should be class', () => { 5 | expect(typeof SuffixTree.prototype.constructor).toEqual('function'); 6 | }); 7 | 8 | it('Should correctly construct Suffix Tree from string', () => { 9 | const banana = new SuffixTree('banana'); 10 | banana.constructSuffixTree(); 11 | 12 | expect(banana.findSubstring('banana')).toBe(0); 13 | expect(banana.findSubstring('nana')).toBe(2); 14 | expect(banana.findSubstring('na')).toBe(4); 15 | expect(banana.findSubstring('an')).toBe(-1); 16 | 17 | const suffix = new SuffixTree('suffix'); 18 | suffix.constructSuffixTree(); 19 | 20 | expect(suffix.findSubstring('fix')).toBe(3); 21 | 22 | const kebab = new SuffixTree('kebab'); 23 | kebab.constructSuffixTree(); 24 | 25 | expect(kebab.findSubstring('horse')).toBe(-1); 26 | 27 | const mississippi = new SuffixTree('mississippi'); 28 | mississippi.constructSuffixTree(); 29 | 30 | expect(mississippi.findSubstring('ssippi')).toBe(5); 31 | expect(mississippi.findSubstring('ppi')).toBe(8); 32 | expect(mississippi.findSubstring('mis')).toBe(-1); 33 | expect(mississippi.findSubstring('pi')).toBe(9); 34 | 35 | const linkedList = new SuffixTree('aaaaaaaaaaa'); 36 | linkedList.constructSuffixTree(); 37 | 38 | expect(linkedList.findSubstring('a')).toBe(10); 39 | expect(linkedList.findSubstring('aaa')).toBe(8); 40 | expect(linkedList.findSubstring('b')).toBe(-1); 41 | expect(linkedList.findSubstring('')).toBe(-1); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/SuffixTree/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable max-len */ 2 | /* eslint-disable no-restricted-syntax */ 3 | /* eslint-disable no-plusplus */ 4 | /* 5 | Implemented by watching this conceptually video: https://www.youtube.com/watch?v=VA9m_l6LpwI 6 | 7 | Suffix for banana are : 8 | banana 9 | anana 10 | nana 11 | ana 12 | na 13 | a 14 | 15 | Constructing a suffix tree is O(n*d) where d is length of max string 16 | 17 | Searching a suffix of a string is O(d) where d is length of suffix string. 18 | If found then return the index, else return -1 19 | 20 | */ 21 | 22 | const alphabets = 'abcdefghijklmnopqrstuvwxyz'; 23 | class Node { 24 | constructor(value, isEnd, index) { 25 | this.data = value; 26 | this.isEnd = isEnd; 27 | this.index = index; 28 | this.next = new Map(); 29 | } 30 | } 31 | 32 | class SuffixTree { 33 | constructor(string) { 34 | this.head = new Node(); 35 | this.string = string.toLowerCase(); 36 | } 37 | 38 | constructSuffixTree() { 39 | const { string } = this; 40 | let currentString = ''; 41 | for (let i = string.length - 1; i >= 0; i -= 1) { 42 | currentString = string[i] + currentString; 43 | let j = 0; 44 | let currentNode = this.head; 45 | while (j < currentString.length) { 46 | if (!currentNode.next.has(currentString[j])) { 47 | let nextString = ''; 48 | while (j < currentString.length) { 49 | nextString += currentString[j]; 50 | j++; 51 | } 52 | currentNode.next.set(nextString[0], new Node(nextString, true, i)); 53 | break; 54 | } else { 55 | let k = 0; 56 | const partialMatchNode = currentNode.next.get(currentString[j]); 57 | const partialMatchString = partialMatchNode.data; 58 | 59 | let matchString = ''; 60 | while ( 61 | k < partialMatchString.length && 62 | j < currentString.length && 63 | partialMatchString[k] === currentString[j] 64 | ) { 65 | matchString += currentString[j]; 66 | k++; 67 | j++; 68 | } 69 | 70 | let diffString = ''; 71 | while (k < partialMatchString.length) { 72 | diffString += partialMatchString[k]; 73 | k++; 74 | } 75 | 76 | partialMatchNode.data = matchString; 77 | if (diffString) { 78 | const oldMap = partialMatchNode.next; 79 | const newNode = new Node(diffString, partialMatchNode.isEnd, partialMatchNode.index); 80 | const alphabetsArray = alphabets.split(''); 81 | 82 | for (const char of alphabetsArray) { 83 | if (oldMap.has(char)) { 84 | newNode.next.set(char, oldMap.get(char)); 85 | } 86 | } 87 | partialMatchNode.next = new Map(); 88 | partialMatchNode.next.set(diffString[0], newNode); 89 | partialMatchNode.isEnd = false; 90 | partialMatchNode.index = null; 91 | } 92 | 93 | if (partialMatchNode.next.has(currentString[j])) { 94 | currentNode = partialMatchNode; 95 | } else { 96 | let nextString = ''; 97 | while (j < currentString.length) { 98 | nextString += currentString[j]; 99 | j++; 100 | } 101 | partialMatchNode.next.set(nextString[0], new Node(nextString, true, i)); 102 | break; 103 | } 104 | } 105 | } 106 | } 107 | } 108 | 109 | findSubstring(string) { 110 | string = string.toLowerCase(); 111 | if (!this.head.next.has(string[0])) { 112 | return -1; 113 | } 114 | 115 | let currentNode = this.head.next.get(string[0]); 116 | let currentNodeValue = currentNode.data; 117 | 118 | let i = 0; 119 | let j = 0; 120 | 121 | while (i < string.length) { 122 | j = 0; 123 | while (i < string.length && j < currentNodeValue.length && string[i++] === currentNodeValue[j++]); 124 | 125 | if (i === string.length && j === currentNodeValue.length && currentNode.isEnd) { 126 | return currentNode.index; 127 | } 128 | 129 | if (currentNode.next.has(string[i])) { 130 | currentNode = currentNode.next.get(string[i]); 131 | currentNodeValue = currentNode.data; 132 | } else { 133 | return -1; 134 | } 135 | } 136 | return -1; 137 | } 138 | } 139 | 140 | // const st = 'CatatecheeseMouseatecheesetooCatatemousetoo'; 141 | // const s = new SuffixTree(st); 142 | // s.constructSuffixTree(); 143 | 144 | // for (let i = 0; i < st.length; i++) { 145 | // const e = st.substring(i); 146 | // if (s.findSubstring(e) !== i) { 147 | // console.log(e, i, s.findSubstring(e)); 148 | // } 149 | // } 150 | 151 | module.exports = SuffixTree; 152 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/Node.js: -------------------------------------------------------------------------------- 1 | class TrieNode { 2 | constructor(char) { 3 | this.char = char; 4 | this.children = []; 5 | this.isEndOfWord = false; 6 | this.wordCount = 0; 7 | 8 | // mark all the alphabets as null 9 | for (let i = 0; i < 26; i += 1) this.children[i] = null; 10 | } 11 | 12 | markAsLeaf() { 13 | this.isEndOfWord = true; 14 | } 15 | 16 | increaseCount() { 17 | this.wordCount += 1; 18 | } 19 | } 20 | 21 | module.exports = TrieNode; 22 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/all-words-in-trie/all-words-in-trie.test.js: -------------------------------------------------------------------------------- 1 | const allWordsInTrie = require('./index'); 2 | const Trie = require('../index'); 3 | 4 | describe('Data Structure : Trie : All Words In Tree', () => { 5 | it('Should return empty array', () => { 6 | const trie = new Trie(); 7 | const result = allWordsInTrie(trie.root); 8 | expect(result.length).toEqual(0); 9 | }); 10 | 11 | it('Should return all words sorted alphabetically', () => { 12 | const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 13 | const trie = new Trie(); 14 | 15 | words.forEach((word) => trie.insert(word)); 16 | 17 | const result = allWordsInTrie(trie.root); 18 | 19 | const expected = ['apple', 'ball', 'bed', 'bed', 'java', 'javascript']; 20 | expect(expected).toEqual(result); 21 | }); 22 | 23 | it('Should retain duplicates', () => { 24 | const words = ['bed', 'bed', 'bed']; 25 | const trie = new Trie(); 26 | 27 | words.forEach((word) => trie.insert(word)); 28 | 29 | const result = allWordsInTrie(trie.root); 30 | expect(result.length).toBe(3); 31 | }); 32 | 33 | it('passing an empty array of words returns an empty array', () => { 34 | const words = []; 35 | const trie = new Trie(); 36 | 37 | words.forEach((word) => trie.insert(word)); 38 | 39 | const result = allWordsInTrie(trie.root); 40 | expect(result).toEqual([]); 41 | }); 42 | 43 | it('passing an empty Trie will throw an error ', () => { 44 | const trie = new Trie(); 45 | expect(() => { 46 | allWordsInTrie(trie); 47 | }).toThrow('Invalid argument: Root of Trie is required'); 48 | }); 49 | 50 | it('passing an empty array will throw an error ', () => { 51 | expect(() => { 52 | allWordsInTrie([]); 53 | }).toThrow('Invalid argument: Root of Trie is required'); 54 | }); 55 | 56 | it('passing null will throw an error ', () => { 57 | expect(() => { 58 | allWordsInTrie([]); 59 | }).toThrow('Invalid argument: Root of Trie is required'); 60 | }); 61 | 62 | it('passing an array not in a Trie will throw an error ', () => { 63 | expect(() => { 64 | allWordsInTrie(['bed', 'ball', 'apple']); 65 | }).toThrow('Invalid argument: Root of Trie is required'); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/all-words-in-trie/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const Trie = require('../index'); 3 | const TrieNode = require('../Node'); 4 | 5 | function getAllWords(root, level, word) { 6 | let result = []; 7 | 8 | if (root.isEndOfWord) { 9 | let temp = ''; 10 | for (let i = 0; i < level; i += 1) { 11 | temp += String(word[i]); 12 | } 13 | // get the count and push all the occurences 14 | const res = []; 15 | for (let i = 0; i < root.wordCount; i += 1) { 16 | res.push(temp); 17 | } 18 | result = [...result, ...res]; 19 | } 20 | 21 | for (let i = 0; i < 26; i += 1) { 22 | if (root.children[i] !== null) { 23 | // eslint-disable-next-line no-param-reassign 24 | word[level] = String.fromCharCode(i + 'a'.charCodeAt(0)); 25 | result = [...result, ...getAllWords(root.children[i], level + 1, word)]; 26 | } 27 | } 28 | return result; 29 | } 30 | 31 | function allWordsFromTrie(root) { 32 | if (!(root instanceof TrieNode)) { 33 | throw new Error('Invalid argument: Root of Trie is required'); 34 | } 35 | 36 | const word = []; // char arr to store a word 37 | for (let i = 0; i < 26; i += 1) { 38 | word[i] = null; 39 | } 40 | return getAllWords(root, 0, word); 41 | } 42 | 43 | // const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 44 | // const trie = new Trie(); 45 | 46 | // words.forEach(word => trie.insert(word)); 47 | // console.log(allWordsFromTrie(trie.root)); 48 | 49 | module.exports = allWordsFromTrie; 50 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/get-unique-words/get-unique-words.test.js: -------------------------------------------------------------------------------- 1 | const getUniqueWords = require('./index'); 2 | const Trie = require('../index'); 3 | 4 | describe('Data Structure : Trie : Get unique words', () => { 5 | it('Should returns unique words (no duplicates), sorted alphabetically', () => { 6 | const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 7 | const trie = new Trie(); 8 | 9 | words.forEach((word) => trie.insert(word)); 10 | 11 | const result = getUniqueWords(trie.root); 12 | 13 | const expected = ['apple', 'ball', 'bed', 'java', 'javascript']; 14 | expect(result).toEqual(expected); 15 | }); 16 | 17 | it('removes duplicates', () => { 18 | const words = ['bed', 'bed', 'bed']; 19 | const trie = new Trie(); 20 | 21 | words.forEach((word) => trie.insert(word)); 22 | 23 | const result = getUniqueWords(trie.root); 24 | expect(result.length).toBe(1); 25 | }); 26 | 27 | it('passing an empty array of words returns an empty array', () => { 28 | const words = []; 29 | const trie = new Trie(); 30 | 31 | words.forEach((word) => trie.insert(word)); 32 | 33 | const result = getUniqueWords(trie.root); 34 | expect(result).toEqual([]); 35 | }); 36 | 37 | it('passing an empty Trie will throw an error ', () => { 38 | const trie = new Trie(); 39 | expect(() => { 40 | getUniqueWords(trie); 41 | }).toThrow('Invalid argument: Root of Trie is required'); 42 | }); 43 | 44 | it('passing an empty array will throw an error ', () => { 45 | expect(() => { 46 | getUniqueWords([]); 47 | }).toThrow('Invalid argument: Root of Trie is required'); 48 | }); 49 | 50 | it('passing null will throw an error ', () => { 51 | expect(() => { 52 | getUniqueWords([]); 53 | }).toThrow('Invalid argument: Root of Trie is required'); 54 | }); 55 | 56 | it('passing an array not in a Trie will throw an error ', () => { 57 | expect(() => { 58 | getUniqueWords(['bed', 'ball', 'apple']); 59 | }).toThrow('Invalid argument: Root of Trie is required'); 60 | }); 61 | }); 62 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/get-unique-words/index.js: -------------------------------------------------------------------------------- 1 | const TrieNode = require('../Node'); 2 | 3 | function getAllUniqueWords(root, level, word) { 4 | let result = []; 5 | 6 | if (root.isEndOfWord) { 7 | let temp = ''; 8 | for (let i = 0; i < level; i += 1) { 9 | temp += String(word[i]); 10 | } 11 | result = [...result, temp]; 12 | } 13 | 14 | for (let i = 0; i < 26; i += 1) { 15 | if (root.children[i]) { 16 | // eslint-disable-next-line no-param-reassign 17 | word[level] = String.fromCharCode(i + 'a'.charCodeAt(0)); 18 | result = [...result, ...getAllUniqueWords(root.children[i], level + 1, word)]; 19 | } 20 | } 21 | return result; 22 | } 23 | 24 | function allUniqueWordsFromTrie(root) { 25 | if (!(root instanceof TrieNode)) { 26 | throw new Error('Invalid argument: Root of Trie is required'); 27 | } 28 | const word = []; // char arr to store a word 29 | for (let i = 0; i < 26; i += 1) { 30 | word[i] = null; 31 | } 32 | return getAllUniqueWords(root, 0, word); 33 | } 34 | 35 | // const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 36 | // const trie = new Trie(); 37 | 38 | // words.forEach(word => trie.insert(word)); 39 | // console.log(allUniqueWordsFromTrie(trie.root)); 40 | 41 | module.exports = allUniqueWordsFromTrie; 42 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/index.js: -------------------------------------------------------------------------------- 1 | const Node = require('./Node'); 2 | 3 | class Trie { 4 | constructor() { 5 | this.root = new Node(''); 6 | } 7 | 8 | // helper to get the index of a character 9 | // eslint-disable-next-line class-methods-use-this 10 | getIndexOfChar(char) { 11 | return char.charCodeAt(0) - 'a'.charCodeAt(0); 12 | } 13 | 14 | insert(key) { 15 | if (!key) { 16 | return false; 17 | } 18 | 19 | // convert to lower case 20 | // keys are basically words 21 | const word = key.toLowerCase(); 22 | let currentNode = this.root; 23 | 24 | for (let level = 0; level < word.length; level += 1) { 25 | const index = this.getIndexOfChar(word[level]); 26 | if (!currentNode.children[index]) { 27 | currentNode.children[index] = new Node(word[level]); 28 | } 29 | currentNode = currentNode.children[index]; 30 | } 31 | 32 | // when we are done with inserting all the character of the word, 33 | // mark the node as end leaf 34 | currentNode.markAsLeaf(); 35 | currentNode.increaseCount(); 36 | return true; 37 | } 38 | 39 | search(key) { 40 | if (!key) { 41 | return false; 42 | } 43 | 44 | // convert word to lower case 45 | const word = key.toLowerCase(); 46 | let currentNode = this.root; 47 | 48 | for (let level = 0; level < word.length; level += 1) { 49 | const index = this.getIndexOfChar(word[level]); 50 | if (!currentNode.children[index]) { 51 | return false; 52 | } 53 | currentNode = currentNode.children[index]; 54 | } 55 | if (currentNode !== null && currentNode.isEndOfWord) { 56 | return true; 57 | } 58 | return false; 59 | } 60 | } 61 | 62 | // const words = ['bed', 'ball', 'apple', 'java', 'javascript']; 63 | // const trie = new Trie(); 64 | 65 | // words.forEach(word => trie.insert(word)); 66 | 67 | // console.log(trie.root); 68 | 69 | // console.log(trie.search(words[3])); 70 | // console.log(trie.search('word')); 71 | // console.log(trie.search(words[4])); 72 | // console.log(trie.search('random')); 73 | 74 | module.exports = Trie; 75 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/search.test.js: -------------------------------------------------------------------------------- 1 | const Trie = require('./index'); 2 | 3 | describe('Data Structure : Trie', () => { 4 | describe('Trie Instance', () => { 5 | it('Should be a class', () => { 6 | expect(typeof Trie.prototype.constructor).toEqual('function'); 7 | }); 8 | }); 9 | 10 | describe('Trie API', () => { 11 | const words = ['bed', 'ball', 'apple', 'java', 'javascript']; 12 | let trie; 13 | it('Should insert string', () => { 14 | trie = new Trie(); 15 | words.forEach((word) => trie.insert(word)); 16 | }); 17 | 18 | it('Should return `True` if string present', () => { 19 | expect(trie.search(words[0])).toEqual(true); 20 | }); 21 | 22 | it('Should return `False` if string present', () => { 23 | expect(trie.search('Ashu')).toEqual(false); 24 | expect(trie.search('be')).toEqual(false); 25 | }); 26 | 27 | it('Should return `False` if argument is not pass', () => { 28 | expect(trie.search()).toEqual(false); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/total-words-in-trie/index.js: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line no-unused-vars 2 | const Trie = require('../index'); 3 | 4 | function totalWords(root) { 5 | let result = 0; 6 | if (root.isEndOfWord) { 7 | result += root.wordCount; 8 | } 9 | for (let i = 0; i < 26; i += 1) { 10 | if (root.children[i] !== null) { 11 | result += totalWords(root.children[i]); 12 | } 13 | } 14 | return result; 15 | } 16 | 17 | // const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 18 | // const trie = new Trie(); 19 | 20 | // words.forEach(word => trie.insert(word)); 21 | // console.log(totalWords(trie.root)); 22 | 23 | module.exports = totalWords; 24 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/total-words-in-trie/total-words-in-trie.test.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const totalWordsInTrie = require('./index'); 3 | const Trie = require('../index'); 4 | 5 | describe('Data Structure : Trie', () => { 6 | it('Should be class of type Trie', () => { 7 | assert.equal(typeof Trie.prototype.constructor, 'function'); 8 | // expect(typeof Trie.prototype.constructor).toEqual('function'); 9 | }); 10 | 11 | describe('Trie', () => { 12 | it('Should return 6.', () => { 13 | const newTrie = new Trie(); 14 | const words = ['bed', 'ball', 'apple', 'java', 'javascript', 'bed']; 15 | words.forEach((word) => newTrie.insert(word)); 16 | const result = totalWordsInTrie(newTrie.root); 17 | assert.equal(result, 6); 18 | }); 19 | 20 | it('Should return 0.', () => { 21 | const newTrie = new Trie(); 22 | const result = totalWordsInTrie(newTrie.root); 23 | assert.equal(result, 0); 24 | }); 25 | 26 | it('Should return 6.', () => { 27 | const newTrie = new Trie(); 28 | const words = ['bed', 'ball', '', 'apple', 'java', 'javascript', 'bed']; 29 | words.forEach((word) => newTrie.insert(word)); 30 | const result = totalWordsInTrie(newTrie.root); 31 | assert.equal(result, 6); 32 | }); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/unique-word-count/index.js: -------------------------------------------------------------------------------- 1 | function uniqueWordCount(root) { 2 | let result = 0; 3 | if (root.isEndOfWord) { 4 | result += 1; 5 | } 6 | for (let i = 0; i < 26; i += 1) { 7 | if (root.children[i]) { 8 | result += uniqueWordCount(root.children[i]); 9 | } 10 | } 11 | return result; 12 | } 13 | 14 | module.exports = uniqueWordCount; 15 | -------------------------------------------------------------------------------- /src/_DataStructures_/Trees/Trie/unique-word-count/index.test.js: -------------------------------------------------------------------------------- 1 | const Trie = require('../index'); 2 | const uniqueWordCount = require('.'); 3 | 4 | describe('Trie Unique Word Count', () => { 5 | it('counts an empty trie', () => { 6 | const trie = new Trie(); 7 | const wordCount = uniqueWordCount(trie.root); 8 | expect(wordCount).toEqual(0); 9 | }); 10 | 11 | it('counts unique words', () => { 12 | const trie = new Trie(); 13 | const words = ['one', 'two', 'three', 'four']; 14 | words.forEach((word) => trie.insert(word)); 15 | const wordCount = uniqueWordCount(trie.root); 16 | expect(wordCount).toEqual(4); 17 | }); 18 | 19 | it('does not count duplicate words', () => { 20 | const trie = new Trie(); 21 | const words = ['one', 'one', 'two', 'three']; 22 | words.forEach((word) => trie.insert(word)); 23 | const wordCount = uniqueWordCount(trie.root); 24 | expect(wordCount).toEqual(3); 25 | }); 26 | }); 27 | -------------------------------------------------------------------------------- /src/_Problems_/3Sum/3sum.js: -------------------------------------------------------------------------------- 1 | const threeSum = function(nums) { 2 | // sort the array 3 | nums = nums.sort((a, b) => a - b); 4 | 5 | let result = []; 6 | // iterate through the array and use two pointers to find the sum 7 | for (let i = 0; i < nums.length; ++i) { 8 | let left = i + 1; 9 | let right = nums.length - 1; 10 | while (left < right) { 11 | let sum = nums[i] + nums[left] + nums[right]; 12 | if (sum == 0) { 13 | result.push([nums[i], nums[left], nums[right]]); 14 | left++; 15 | right--; 16 | } 17 | else if (sum < 0) { 18 | left++; 19 | } 20 | else { 21 | right--; 22 | } 23 | } 24 | // skip duplicates 25 | while (i < nums.length - 1 && nums[i] == nums[i + 1]) { 26 | i++; 27 | } 28 | } 29 | 30 | // initialize set to remove duplicate 31 | const set = new Set(result.map(JSON.stringify)); 32 | // final output array 33 | output = (new Array(...set).map(JSON.parse)); 34 | return output; 35 | }; 36 | 37 | 38 | module.exports = threeSum; 39 | 40 | -------------------------------------------------------------------------------- /src/_Problems_/3Sum/3sum.test.js: -------------------------------------------------------------------------------- 1 | const threeSum = require("./3sum"); 2 | 3 | describe("threeSum", () => { 4 | it("Should return [[-1, -1, 2], [-1, 0, 1]]", () => { 5 | expect(threeSum([-1, 0, 1, 2, -1, -4])).toEqual([ 6 | [-1, -1, 2], 7 | [-1, 0, 1], 8 | ]); 9 | }); 10 | 11 | it("Should return [[0, 0, 0]]", () => { 12 | expect(threeSum([0, 0, 0])).toEqual([[0, 0, 0]]); 13 | }); 14 | 15 | it("Should return [[-1, -1, 2]]", () => { 16 | expect(threeSum([-1, 2, -1, -4])).toEqual([[-1, -1, 2]]); 17 | }); 18 | 19 | }); 20 | -------------------------------------------------------------------------------- /src/_Problems_/anagrams/anagrams.test.js: -------------------------------------------------------------------------------- 1 | const { checkAnagrams, checkAnagramUsingHelpers } = require('.'); 2 | 3 | describe('Anagrams', () => { 4 | describe('Using cutom methods and character map', () => { 5 | it('Should return TRUE for `rail safety` & `fairy tales`', () => { 6 | expect( 7 | checkAnagrams({ 8 | firstString: 'rail safety', 9 | secondString: 'fairy tales', 10 | }), 11 | ).toBe(true); 12 | }); 13 | 14 | it('Should return TRUE for `FAIRY tales` & `rail SAFETY`', () => { 15 | expect( 16 | checkAnagrams({ 17 | firstString: 'FAIRY tales', 18 | secondString: 'rail SAFETY', 19 | }), 20 | ).toBe(true); 21 | }); 22 | 23 | it('Should return FALSE for `Hello World` & `Bye`', () => { 24 | expect( 25 | checkAnagrams({ 26 | firstString: 'Hello World', 27 | secondString: 'Bye', 28 | }), 29 | ).toBe(false); 30 | }); 31 | 32 | it('Should ignore special characters', () => { 33 | expect( 34 | checkAnagrams({ 35 | firstString: 'hello world!!', 36 | secondString: 'hello - world', 37 | }), 38 | ).toBe(true); 39 | }); 40 | }); 41 | 42 | describe('Using in built methods and sorting', () => { 43 | it('Should return TRUE for `rail safety` & `fairy tales`', () => { 44 | expect( 45 | checkAnagramUsingHelpers({ 46 | firstString: 'rail safety', 47 | secondString: 'fairy tales', 48 | }), 49 | ).toBe(true); 50 | }); 51 | 52 | it('Should return TRUE for `FAIRY tales` & `rail SAFETY`', () => { 53 | expect( 54 | checkAnagramUsingHelpers({ 55 | firstString: 'FAIRY tales', 56 | secondString: 'rail SAFETY', 57 | }), 58 | ).toBe(true); 59 | }); 60 | 61 | it('Should return FALSE for `Hello World` & `Bye`', () => { 62 | expect( 63 | checkAnagramUsingHelpers({ 64 | firstString: 'Hello World', 65 | secondString: 'Bye', 66 | }), 67 | ).toBe(false); 68 | }); 69 | 70 | it('Should ignore special characters', () => { 71 | expect( 72 | checkAnagramUsingHelpers({ 73 | firstString: 'hello world!!', 74 | secondString: 'hello - world', 75 | }), 76 | ).toBe(true); 77 | }); 78 | 79 | it('Should return FALSE for `Hello` & `Hallo`', () => { 80 | expect( 81 | checkAnagrams({ 82 | firstString: 'Hello', 83 | secondString: 'Hallo', 84 | }), 85 | ).toBe(false); 86 | }); 87 | }); 88 | }); 89 | -------------------------------------------------------------------------------- /src/_Problems_/anagrams/index.js: -------------------------------------------------------------------------------- 1 | const pattern = /[^\w]/g; 2 | 3 | function createCharMap(str) { 4 | const charMap = {}; 5 | const sanitizedString = str.replace(pattern, '').toLowerCase(); 6 | sanitizedString.split('').forEach((char) => { 7 | if (!charMap[char]) { 8 | charMap[char] = 1; 9 | } else { 10 | charMap[char] += 1; 11 | } 12 | return 0; 13 | }); 14 | return charMap; 15 | } 16 | 17 | function sanitizeAndSortString(str) { 18 | return str.replace(pattern, '').toLowerCase().split('').sort().join(''); 19 | } 20 | 21 | function checkAnagrams({ firstString, secondString }) { 22 | const charMapFirst = createCharMap(firstString); 23 | const charMapSecond = createCharMap(secondString); 24 | 25 | if (Object.keys(charMapFirst).length !== Object.keys(charMapSecond).length) { 26 | return false; 27 | } 28 | 29 | // eslint-disable-next-line no-restricted-syntax 30 | for (const char in charMapFirst) { 31 | if (charMapFirst[char] !== charMapSecond[char]) { 32 | return false; 33 | } 34 | } 35 | 36 | return true; 37 | } 38 | 39 | function checkAnagramUsingHelpers({ firstString, secondString }) { 40 | return sanitizeAndSortString(firstString) === sanitizeAndSortString(secondString); 41 | } 42 | 43 | module.exports = { 44 | checkAnagrams, 45 | checkAnagramUsingHelpers, 46 | }; 47 | -------------------------------------------------------------------------------- /src/_Problems_/array-chunk/array-chunk.test.js: -------------------------------------------------------------------------------- 1 | const { arrayChunk, errFirstArgument, errSecondArguemnt, chunkUsingSlice } = require('.'); 2 | 3 | describe('Chunk of Arrays', () => { 4 | describe('Using normal itteration', () => { 5 | it('Should throw an error for invalid `array` argument', () => { 6 | expect(() => arrayChunk({ array: 'hello', size: 3 })).toThrow(errFirstArgument); 7 | }); 8 | 9 | it('Should throw an error for invalid `size` value', () => { 10 | expect(() => arrayChunk({ array: [1, 2, 3, 4, 5], size: 'A' })).toThrow(errSecondArguemnt); 11 | }); 12 | 13 | it('Should return 5 chunks of size 2 of array with 10 elements', () => { 14 | expect( 15 | arrayChunk({ 16 | array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 17 | size: 2, 18 | }).length, 19 | ).toEqual(5); 20 | }); 21 | 22 | it('Should return the given array as chunk if the chunk size >= given array', () => { 23 | const array = [1, 2, 3, 4, 5]; 24 | expect( 25 | arrayChunk({ 26 | array, 27 | size: 8, 28 | }), 29 | ).toEqual([array]); 30 | }); 31 | 32 | it('Should return [[1,2], [3,4], [5]] for [1, 2, 3, 4, 5] with chunk size of 2', () => { 33 | const array = [1, 2, 3, 4, 5]; 34 | const output = [[1, 2], [3, 4], [5]]; 35 | 36 | expect( 37 | arrayChunk({ 38 | array, 39 | size: 2, 40 | }), 41 | ).toEqual(output); 42 | }); 43 | }); 44 | 45 | describe('Using Array.slice()', () => { 46 | it('Should throw an error for invalid `array` argument', () => { 47 | expect(() => chunkUsingSlice({ array: 'hello', size: 3 })).toThrow(errFirstArgument); 48 | }); 49 | 50 | it('Should throw an error for invalid `size` value', () => { 51 | expect(() => chunkUsingSlice({ array: [1, 2, 3, 4, 5], size: 'A' })).toThrow(errSecondArguemnt); 52 | }); 53 | 54 | it('Should return 5 chunks of size 2 of array with 10 elements', () => { 55 | expect( 56 | chunkUsingSlice({ 57 | array: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 58 | size: 2, 59 | }).length, 60 | ).toEqual(5); 61 | }); 62 | 63 | it('Should return the given array as chunk if the chunk size >= given array', () => { 64 | const array = [1, 2, 3, 4, 5]; 65 | expect( 66 | chunkUsingSlice({ 67 | array, 68 | size: 8, 69 | }), 70 | ).toEqual([array]); 71 | }); 72 | 73 | it('Should return [[1,2], [3,4], [5]] for [1, 2, 3, 4, 5] with chunk size of 2', () => { 74 | const array = [1, 2, 3, 4, 5]; 75 | const output = [[1, 2], [3, 4], [5]]; 76 | 77 | expect( 78 | chunkUsingSlice({ 79 | array, 80 | size: 2, 81 | }), 82 | ).toEqual(output); 83 | }); 84 | }); 85 | }); 86 | -------------------------------------------------------------------------------- /src/_Problems_/array-chunk/index.js: -------------------------------------------------------------------------------- 1 | const errFirstArgument = 'Invalid Argument: Expected an array as first argument'; 2 | const errSecondArguemnt = 'Invalid Argument: Expected a positive number as second argument'; 3 | 4 | function validateArguments(array, size) { 5 | if (!Array.isArray(array)) { 6 | throw new Error(errFirstArgument); 7 | } 8 | 9 | if (typeof size !== 'number' || size < 0) { 10 | throw new Error(errSecondArguemnt); 11 | } 12 | 13 | if (size > array.length) { 14 | return [array]; 15 | } 16 | return 0; 17 | } 18 | 19 | function arrayChunk({ array, size }) { 20 | validateArguments(array, size); 21 | 22 | const result = []; 23 | 24 | for (let i = 0; i < array.length; i += 1) { 25 | const lastChunk = result[result.length - 1]; 26 | 27 | if (!lastChunk || lastChunk.length === size) { 28 | result.push([array[i]]); 29 | } else { 30 | lastChunk.push(array[i]); 31 | } 32 | } 33 | 34 | return result; 35 | } 36 | 37 | function chunkUsingSlice({ array, size }) { 38 | validateArguments(array, size); 39 | 40 | let index = 0; 41 | const result = []; 42 | 43 | while (index < array.length) { 44 | result.push(array.slice(index, index + size)); 45 | index += size; 46 | } 47 | 48 | return result; 49 | } 50 | 51 | module.exports = { 52 | errFirstArgument, 53 | errSecondArguemnt, 54 | arrayChunk, 55 | chunkUsingSlice, 56 | }; 57 | -------------------------------------------------------------------------------- /src/_Problems_/bfs-bst/index.js: -------------------------------------------------------------------------------- 1 | const BST = require('../../_DataStructures_/Trees/BinarySearchTree'); 2 | const Queue = require('../../_DataStructures_/Queue'); 3 | 4 | function traverseBFS(root) { 5 | let temp = root; 6 | const arr = []; 7 | const nodeQueue = new Queue(); 8 | 9 | if (root === null) { 10 | return arr; 11 | } 12 | 13 | while (temp !== null) { 14 | arr.push(temp.value); 15 | if (temp.leftChild) nodeQueue.enqueue(temp.leftChild); 16 | if (temp.rightChild) nodeQueue.enqueue(temp.rightChild); 17 | temp = nodeQueue.dequeue(); 18 | } 19 | return arr; 20 | } 21 | 22 | const myBST = new BST(51); 23 | 24 | [10, 34, 32, 12, 90, 54, 61, 2, 71, 9].forEach((e) => myBST.add(e)); 25 | 26 | const preOrderElements = myBST.preorder(); 27 | const levelOrderElements = traverseBFS(myBST.root); 28 | 29 | // eslint-disable-next-line no-console 30 | console.log(preOrderElements); 31 | // eslint-disable-next-line no-console 32 | console.log(levelOrderElements); 33 | -------------------------------------------------------------------------------- /src/_Problems_/binary-tree-to-binary-search-tree/binary-tree-to-binary-search-tree.test.js: -------------------------------------------------------------------------------- 1 | const { binaryTreeToBST, storeInorder } = require('.'); 2 | const BinaryTree = require('../../_DataStructures_/Trees/BinaryTree'); 3 | 4 | describe('Binary tree to binary search tree', () => { 5 | let tree; 6 | 7 | it('Should return `null` if root is null', () => { 8 | tree = new BinaryTree([1]); 9 | tree.root = null; 10 | expect(binaryTreeToBST(tree)).toEqual(null); 11 | }); 12 | 13 | it('Should converted binary tree to binary search tree', () => { 14 | tree = new BinaryTree([10, 30, 15, 20, null, null, 5]); 15 | const bTree = binaryTreeToBST(tree); 16 | expect(storeInorder(bTree)).toEqual([5, 10, 15, 20, 30]); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /src/_Problems_/binary-tree-to-binary-search-tree/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given a Binary Tree, convert it to a Binary Search Tree. 3 | * The conversion must be done in such a way that keeps the original structure of Binary Tree. 4 | * Example 1 5 | Input: 6 | 10 7 | / \ 8 | 2 7 9 | / \ 10 | 8 4 11 | Output: 12 | 8 13 | / \ 14 | 4 10 15 | / \ 16 | 2 7 17 | */ 18 | 19 | const Node = require('../../_DataStructures_/Trees/BinaryTree/Node'); 20 | // Helper function to store inorder traversal of a binary tree 21 | function storeInorder(root) { 22 | /** left - root - right */ 23 | if (root === null) return []; 24 | 25 | // First store the left subtree 26 | let arr = []; 27 | const left = storeInorder(root.leftChild); 28 | arr = [...left, ...arr]; 29 | 30 | // Append root's data 31 | arr = [...arr, root.value]; 32 | 33 | // Store right subtree 34 | const right = storeInorder(root.rightChild); 35 | arr = [...arr, ...right]; 36 | return arr; 37 | } 38 | 39 | // Helper function to copy elements from sorted array to make BST while keeping same structure 40 | // Runtime complexity iof this function is O(n) where n is number of nodes, as we are each node of tree one time. 41 | function arrayToBST(arr, root) { 42 | const node = root; 43 | // Base case 44 | if (!node) return null; 45 | 46 | const bstNode = new Node(); 47 | // First update the left subtree 48 | const leftChild = arrayToBST(arr, node.leftChild); 49 | if (leftChild) { 50 | bstNode.leftChild = leftChild; 51 | } 52 | 53 | // update the root's data and remove it from sorted array 54 | // eslint-disable-next-line no-param-reassign 55 | bstNode.value = arr.shift(); 56 | 57 | // Finally update the right subtree 58 | const rightChild = arrayToBST(arr, node.rightChild); 59 | if (rightChild) { 60 | bstNode.rightChild = rightChild; 61 | } 62 | 63 | return bstNode; 64 | } 65 | 66 | function binaryTreeToBST(bTree) { 67 | // Tree is empty 68 | if (!bTree.root) return null; 69 | const arr = bTree.preOrder(); 70 | arr.sort((a, b) => a - b); 71 | const bst = arrayToBST(arr, bTree.root); 72 | return bst; 73 | } 74 | 75 | module.exports = { 76 | binaryTreeToBST, 77 | storeInorder, 78 | }; 79 | -------------------------------------------------------------------------------- /src/_Problems_/compose-largest-number/compose-largest.test.js: -------------------------------------------------------------------------------- 1 | const { composeHighest, compare, ErrorMessage } = require('.'); 2 | 3 | /** 4 | * Test cases 5 | * [3, 6, 0, 9] -> 9630 6 | * [60, 548] -> 60548 7 | * [1, 34, 3, 98, 9, 76, 45, 4] -> 998764543431 8 | */ 9 | 10 | describe('Compose Largest Number', () => { 11 | describe('The main function returning the Highest NUmber', () => { 12 | it('Should throw error for invalid argument', () => { 13 | expect(() => composeHighest('abcd')).toThrow(ErrorMessage); 14 | }); 15 | 16 | it('Should return 9630 for `[3, 6, 0, 9]`', () => { 17 | expect(composeHighest([3, 6, 0, 9])).toEqual(9630); 18 | }); 19 | 20 | it('Should return 60548 for `[60, 548]`', () => { 21 | expect(composeHighest([60, 548])).toEqual(60548); 22 | }); 23 | 24 | it('Should return 998764543431 for `[1, 34, 3, 98, 9, 76, 45, 4]`', () => { 25 | expect(composeHighest([1, 34, 3, 98, 9, 76, 45, 4])).toEqual(998764543431); 26 | }); 27 | }); 28 | 29 | describe('Testing custom `compare()` for `sort()`', () => { 30 | it('Should return [60, 548] instead of [548, 60]', () => { 31 | expect([60, 548].sort(compare)).toEqual([60, 548]); 32 | }); 33 | 34 | it('Should return [9, 81, 548] instead of [548, 81, 9]', () => { 35 | expect([548, 9, 81].sort(compare)).toEqual([9, 81, 548]); 36 | }); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/_Problems_/compose-largest-number/index.js: -------------------------------------------------------------------------------- 1 | /** Given an array of numbers, return the highest number that can be made out of it */ 2 | 3 | /** 4 | * Test cases 5 | * [3, 6, 0, 9] -> 9630 6 | * [60, 548] -> 60548 7 | * [1, 34, 3, 98, 9, 76, 45, 4] -> 998764543431 8 | */ 9 | 10 | /** At first glance, the below solution may seem the answer */ 11 | // const hightestNumber = Number([60, 548].sort((a, b) => b - a).join('')); 12 | 13 | /** The above will fail for test case 2 & 3 */ 14 | 15 | /** We need a custom compare funtion */ 16 | function compare(a, b) { 17 | const x = Number(String(a) + String(b)); 18 | const y = Number(String(b) + String(a)); 19 | return x > y ? -1 : 1; 20 | } 21 | 22 | const ErrorMessage = 'Invalid array/missing argument'; 23 | 24 | /** final function */ 25 | function composeHighest(arr) { 26 | if (!arr || !Array.isArray(arr)) { 27 | throw new Error(ErrorMessage); 28 | } 29 | 30 | return Number(arr.sort(compare).join('')); 31 | } 32 | 33 | /** tests */ 34 | 35 | // console.log(composeHighest([3, 6, 0, 9]) === 9630); 36 | // console.log(composeHighest([60, 548]) === 60548); 37 | // console.log(composeHighest([1, 34, 3, 98, 9, 76, 45, 4]) === 998764543431); 38 | 39 | module.exports = { composeHighest, compare, ErrorMessage }; 40 | -------------------------------------------------------------------------------- /src/_Problems_/count-vowels/count-vowels.test.js: -------------------------------------------------------------------------------- 1 | const { countVowelsItteratively, countVowelsItterativelyES6, countVowelsUsingRegex } = require('.'); 2 | 3 | describe('Count Vowels', () => { 4 | const apple = 'AppLe'; 5 | const education = 'education'; 6 | const myths = 'myths'; 7 | 8 | describe('Count by regular itteration', () => { 9 | it('Should return 2 for `Apple`', () => { 10 | expect(countVowelsItteratively(apple)).toEqual(2); 11 | }); 12 | 13 | it('Should return 5 for `Education`', () => { 14 | expect(countVowelsItteratively(education)).toEqual(5); 15 | }); 16 | 17 | it('Should return 0 for `Myths`', () => { 18 | expect(countVowelsItteratively(myths)).toEqual(0); 19 | }); 20 | }); 21 | 22 | describe('Count by ES6 itteration', () => { 23 | it('Should return 2 for `Apple`', () => { 24 | expect(countVowelsItterativelyES6(apple)).toEqual(2); 25 | }); 26 | 27 | it('Should return 5 for `Education`', () => { 28 | expect(countVowelsItterativelyES6(education)).toEqual(5); 29 | }); 30 | 31 | it('Should return 0 for `Myths`', () => { 32 | expect(countVowelsItterativelyES6(myths)).toEqual(0); 33 | }); 34 | }); 35 | 36 | describe('Count using REGEX', () => { 37 | it('Should return 2 for `Apple`', () => { 38 | expect(countVowelsUsingRegex(apple)).toEqual(2); 39 | }); 40 | 41 | it('Should return 5 for `Education`', () => { 42 | expect(countVowelsUsingRegex(education)).toEqual(5); 43 | }); 44 | 45 | it('Should return 0 for `Myths`', () => { 46 | expect(countVowelsUsingRegex(myths)).toEqual(0); 47 | }); 48 | }); 49 | }); 50 | -------------------------------------------------------------------------------- /src/_Problems_/count-vowels/index.js: -------------------------------------------------------------------------------- 1 | const pattern = /[^\w]/g; 2 | 3 | const cleanString = (str) => str.replace(pattern, '').toLowerCase(); 4 | const isVowel = (char) => char === 'a' || char === 'e' || char === 'i' || char === 'o' || char === 'u'; 5 | 6 | function countVowelsItteratively(str) { 7 | const cleanedString = cleanString(str); 8 | let count = 0; 9 | for (let i = 0; i < cleanedString.length; i += 1) { 10 | if (isVowel(cleanedString[i])) { 11 | count += 1; 12 | } 13 | } 14 | return count; 15 | } 16 | 17 | function countVowelsItterativelyES6(str) { 18 | const cleanedString = cleanString(str); 19 | const vowels = ['a', 'e', 'i', 'o', 'u']; 20 | let count = 0; 21 | 22 | // eslint-disable-next-line no-restricted-syntax 23 | for (const char of cleanedString) { 24 | if (vowels.includes(char)) { 25 | count += 1; 26 | } 27 | } 28 | return count; 29 | } 30 | 31 | function countVowelsUsingRegex(str) { 32 | const match = str.match(/[aeiou]/gi); 33 | return match ? match.length : 0; 34 | } 35 | 36 | module.exports = { 37 | countVowelsItteratively, 38 | countVowelsItterativelyES6, 39 | countVowelsUsingRegex, 40 | }; 41 | -------------------------------------------------------------------------------- /src/_Problems_/factorial/factorial.test.js: -------------------------------------------------------------------------------- 1 | const { factorial } = require('.'); 2 | 3 | describe('Factorial', () => { 4 | it('Should return 24', () => { 5 | expect(factorial(4)).toEqual(24); 6 | }); 7 | 8 | it('Should return 1', () => { 9 | expect(factorial(1)).toEqual(1); 10 | }); 11 | 12 | it('Should return 120', () => { 13 | expect(factorial(5)).toEqual(120); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/_Problems_/factorial/index.js: -------------------------------------------------------------------------------- 1 | function factorial(num) { 2 | if (num === 1) return num; 3 | return num * factorial(num - 1); 4 | } 5 | 6 | module.exports = { 7 | factorial, 8 | }; 9 | -------------------------------------------------------------------------------- /src/_Problems_/find-2-nums-adding-to-n/find-2-nums-adding-to-n.test.js: -------------------------------------------------------------------------------- 1 | const { findTwoNumsAddingToN, findTwoNumsAddingToN2 } = require('.'); 2 | 3 | describe('Find two numbers adding to N', () => { 4 | [findTwoNumsAddingToN, findTwoNumsAddingToN2].forEach((func) => { 5 | describe(func.name, () => { 6 | it('Should return an array with length two', () => { 7 | expect(findTwoNumsAddingToN2([1, 2], 3).length).toBe(2); 8 | }); 9 | 10 | it('Should return false when there is no solution', () => { 11 | expect(findTwoNumsAddingToN2([1, 2], 5)).toBe(false); 12 | }); 13 | 14 | it('Should return false input array length is less than 2', () => { 15 | expect(findTwoNumsAddingToN2([5], 5)).toBe(false); 16 | }); 17 | 18 | it('Should return negative values', () => { 19 | expect(findTwoNumsAddingToN2([-2, 1, 2, 6, 7], 5)).toEqual(expect.arrayContaining([-2, 7])); 20 | }); 21 | 22 | it('Should return two numbers that sum to N', () => { 23 | expect(findTwoNumsAddingToN([1, 2, 3, 5], 5)).toEqual(expect.arrayContaining([2, 3])); 24 | }); 25 | }); 26 | }); 27 | 28 | describe('function differences findTwoNumsAddingToN and findTwoNumsAddingToN2', () => { 29 | it('Should return different arrays', () => { 30 | expect(findTwoNumsAddingToN([1, 2, 3, 4], 5)).toEqual( 31 | expect.not.arrayContaining(findTwoNumsAddingToN2([1, 2, 3, 4], 5)), 32 | ); 33 | }); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/_Problems_/find-2-nums-adding-to-n/index.js: -------------------------------------------------------------------------------- 1 | // the best case [O(n)] using SET data structure 2 | function findTwoNumsAddingToN(arr, number) { 3 | const pair = []; 4 | const store = new Set(); 5 | 6 | for (let i = 0; i < arr.length; i += 1) { 7 | // check if the set contains one of the element that sum upto the given number 8 | if (store.has(number - arr[i])) { 9 | pair.push(number - arr[i]); 10 | pair.push(arr[i]); 11 | break; 12 | } 13 | // push the element in the set 14 | store.add(arr[i]); 15 | } 16 | return pair.length ? pair : false; 17 | } 18 | 19 | // the Brute force approach 20 | function findTwoNumsAddingToN2(arr, number) { 21 | for (let i = 0; i < arr.length; i += 1) { 22 | for (let j = i + 1; j < arr.length; j += 1) { 23 | if (arr[i] + arr[j] === number) { 24 | return [arr[i], arr[j]]; 25 | } 26 | } 27 | } 28 | 29 | return false; 30 | } 31 | 32 | module.exports = { 33 | findTwoNumsAddingToN, 34 | findTwoNumsAddingToN2, 35 | }; 36 | -------------------------------------------------------------------------------- /src/_Problems_/find-2nd-max/find-2nd-max.test.js: -------------------------------------------------------------------------------- 1 | const { findSecondMax } = require('.'); 2 | 3 | describe('FindSecondMax', () => { 4 | it('Should return 6', () => { 5 | expect(findSecondMax([9, 2, 3, 6])).toEqual(6); 6 | }); 7 | 8 | it('Should return -1', () => { 9 | expect(findSecondMax([0, -1, -2, 0])).toEqual(-1); 10 | }); 11 | 12 | it('Should return 0', () => { 13 | expect(findSecondMax([-2, -1, 0, 1])).toEqual(0); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/_Problems_/find-2nd-max/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * You may find it easy but it's tricky for few 3 | * Input - [9, 2, 3, 6] 4 | * Output - 6 5 | */ 6 | 7 | function findSecondMax(arr) { 8 | let max = arr[0]; 9 | let max2 = Number.MIN_SAFE_INTEGER; 10 | 11 | for (const el of arr) { 12 | if (el > max) { 13 | max2 = max; 14 | max = el; 15 | } 16 | 17 | if (el < max && el > max2) { 18 | max2 = el; 19 | } 20 | } 21 | return max2; 22 | } 23 | 24 | module.exports = { 25 | findSecondMax, 26 | }; 27 | -------------------------------------------------------------------------------- /src/_Problems_/fizzbuzz/fizzbuzz.test.js: -------------------------------------------------------------------------------- 1 | const { fizzBuzz } = require('.'); 2 | 3 | describe('FizBuzz...', () => { 4 | it('Should return `Fizz` for a multiple of 3 only', () => { 5 | expect(fizzBuzz(21)).toEqual('Fizz'); 6 | }); 7 | 8 | it('Should print `Buzz` for a multiple of 5 only', () => { 9 | expect(fizzBuzz(35)).toEqual('Buzz'); 10 | }); 11 | 12 | it('Should return `FizzBuzz` for multiple of 3 & 5', () => { 13 | expect(fizzBuzz(45)).toEqual('FizzBuzz'); 14 | }); 15 | 16 | it('Should return the number if its not a multiple of 3, 5 or both 3 & 5', () => { 17 | expect(fizzBuzz(7)).toEqual(7); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/_Problems_/fizzbuzz/index.js: -------------------------------------------------------------------------------- 1 | function fizzBuzz(number) { 2 | if (number % 15 === 0) { 3 | return 'FizzBuzz'; 4 | } 5 | if (number % 5 === 0) { 6 | return 'Buzz'; 7 | } 8 | if (number % 3 === 0) { 9 | return 'Fizz'; 10 | } 11 | return number; 12 | } 13 | 14 | module.exports = { 15 | fizzBuzz, 16 | }; 17 | -------------------------------------------------------------------------------- /src/_Problems_/get-mazePath/get-mazePath.test.js: -------------------------------------------------------------------------------- 1 | const { getMazePath } = require('.'); 2 | 3 | describe('Get maze path', () => { 4 | it('returns all possible solutions for a 2x2 grid', () => { 5 | const expectedSolutions = ['HHVV', 'HVHV', 'HVVH', 'VHHV', 'VHVH', 'VVHH']; 6 | 7 | expect(getMazePath(0, 0, 2, 2)).toEqual(expectedSolutions); 8 | }); 9 | 10 | it('returns an even amount of horizontal and vertical movements', () => { 11 | const solutions = getMazePath(0, 0, 3, 3); 12 | 13 | solutions.forEach((solution) => { 14 | expect(solution.length).toEqual(6); 15 | 16 | expect(solution.match(/H/g).length).toEqual(3); 17 | expect(solution.match(/V/g).length).toEqual(3); 18 | }); 19 | }); 20 | 21 | it('returns the expected number of solutions based on given grids', () => { 22 | expect(getMazePath(0, 0, 1, 1).length).toEqual(2); 23 | expect(getMazePath(0, 0, 2, 2).length).toEqual(6); 24 | expect(getMazePath(0, 0, 3, 3).length).toEqual(20); 25 | expect(getMazePath(0, 0, 4, 4).length).toEqual(70); 26 | 27 | expect(getMazePath(1, 1, 4, 4).length).toEqual(20); 28 | }); 29 | 30 | it('returns an empty array when the start and end coordinates are equal', () => { 31 | const solutions = getMazePath(2, 2, 2, 2); 32 | 33 | expect(solutions).toEqual(['']); 34 | }); 35 | 36 | it('returns an empty array when the start coordinates are greater than the end coordinates', () => { 37 | const solutions = getMazePath(2, 2, 1, 1); 38 | 39 | expect(solutions).toEqual([]); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/_Problems_/get-mazePath/index.js: -------------------------------------------------------------------------------- 1 | //= =====================================Problem Statement============================================= 2 | // --->> Print all possible path to reach the end of the GRID/MAZE (N x N) from starting point to ending point 3 | // --->> One horizontal move will be represented by H and one vertical move will be represented by V 4 | // --->> Complexity = Complexity will be exponential as there are many overlapping solutions 5 | // --->> cr = current row 6 | // --->> cc = current column 7 | // --->> er = end row 8 | // --->> ec = end column 9 | 10 | const getMazePath = (cr, cc, er, ec) => { 11 | if (cr === er && cc === ec) { 12 | //= ===========POSITIVE BASE CASE=========== 13 | const br = []; 14 | br.push(''); 15 | return br; 16 | } 17 | 18 | if (cr > er || cc > ec) { 19 | //= ===========NEGATIVE BASE CASE=========== 20 | const br = []; 21 | return br; 22 | } 23 | 24 | const myResult = []; 25 | 26 | const recResultH = getMazePath(cr, cc + 1, er, ec); 27 | recResultH.forEach((rrh) => { 28 | myResult.push(`H${rrh}`); 29 | }); 30 | 31 | const recResultV = getMazePath(cr + 1, cc, er, ec); 32 | recResultV.forEach((rrv) => { 33 | myResult.push(`V${rrv}`); 34 | }); 35 | 36 | return myResult; 37 | }; 38 | 39 | module.exports = { getMazePath }; 40 | -------------------------------------------------------------------------------- /src/_Problems_/get-smallest-common-number/get-smallest-common-number.test.js: -------------------------------------------------------------------------------- 1 | const { getSmallestCommonNumber } = require('.'); 2 | 3 | describe('Get common smallest number between two integer arrays', () => { 4 | it('Should return -1 when both has empty array', () => { 5 | const arr1 = []; 6 | const arr2 = []; 7 | 8 | expect(getSmallestCommonNumber(arr1, arr2)).toEqual(-1); 9 | }); 10 | 11 | it('Should return -1 when no common between two integer arrays', () => { 12 | const arr1 = [1, 3, 5]; 13 | const arr2 = [2, 4, 6]; 14 | 15 | expect(getSmallestCommonNumber(arr1, arr2)).toEqual(-1); 16 | }); 17 | 18 | it('Should return common smallest number between unsorted two integer arrays', () => { 19 | const arr1 = [-10, 3]; 20 | const arr2 = [2, -10, 7]; 21 | 22 | expect(getSmallestCommonNumber(arr1, arr2)).toEqual(-10); 23 | }); 24 | 25 | it('Should return common smallest number between unsorted two integer arrays', () => { 26 | const arr1 = [-10, 3, -11]; 27 | const arr2 = [-11, 2, -10, 7]; 28 | 29 | expect(getSmallestCommonNumber(arr1, arr2)).toEqual(-11); 30 | }); 31 | 32 | it('Should return common smallest number between sorted two integer arrays', () => { 33 | const arr1 = [2, 3]; 34 | const arr2 = [2, 5, 7]; 35 | 36 | expect(getSmallestCommonNumber(arr1, arr2)).toEqual(2); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/_Problems_/get-smallest-common-number/index.js: -------------------------------------------------------------------------------- 1 | // Get the common smallest number between two integer arrays 2 | 3 | const getSmallestCommonNumber = (a1, a2) => { 4 | const map = {}; 5 | let i = 0; 6 | let min; 7 | 8 | while (a1.length > i || a2.length > i) { 9 | if (i < a1.length) { 10 | map[`${a1[i]}a`] = true; 11 | if (map[`${a1[i]}b`] && (min > a1[i] || !min)) { 12 | min = a1[i]; 13 | } 14 | } 15 | 16 | if (i < a2.length) { 17 | map[`${a2[i]}b`] = true; 18 | if (map[`${a2[i]}a`] && (min > a2[i] || !min)) { 19 | min = a2[i]; 20 | } 21 | } 22 | 23 | i += 1; 24 | } 25 | 26 | return min || -1; 27 | }; 28 | 29 | module.exports = { getSmallestCommonNumber }; 30 | -------------------------------------------------------------------------------- /src/_Problems_/get-string-permutations/get-string-permutations.test.js: -------------------------------------------------------------------------------- 1 | const { getPermutations } = require('.'); 2 | 3 | describe('Get permutations of a string', () => { 4 | it('returns permutations of a short string', () => { 5 | const shortString = 'ab'; 6 | const expectedPermutations = ['ab', 'ba']; 7 | 8 | expect(getPermutations(shortString)).toEqual(expectedPermutations); 9 | }); 10 | 11 | it('returns permutations of a long string', () => { 12 | const shortString = 'XUNDA'; 13 | const expectedPermutations = [ 14 | 'XUNDA', 15 | 'UXNDA', 16 | 'NXUDA', 17 | 'XNUDA', 18 | 'UNXDA', 19 | 'NUXDA', 20 | 'DUXNA', 21 | 'UDXNA', 22 | 'XDUNA', 23 | 'DXUNA', 24 | 'UXDNA', 25 | 'XUDNA', 26 | 'XNDUA', 27 | 'NXDUA', 28 | 'DXNUA', 29 | 'XDNUA', 30 | 'NDXUA', 31 | 'DNXUA', 32 | 'DNUXA', 33 | 'NDUXA', 34 | 'UDNXA', 35 | 'DUNXA', 36 | 'NUDXA', 37 | 'UNDXA', 38 | 'ANDXU', 39 | 'NADXU', 40 | 'DANXU', 41 | 'ADNXU', 42 | 'NDAXU', 43 | 'DNAXU', 44 | 'XNADU', 45 | 'NXADU', 46 | 'AXNDU', 47 | 'XANDU', 48 | 'NAXDU', 49 | 'ANXDU', 50 | 'ADXNU', 51 | 'DAXNU', 52 | 'XADNU', 53 | 'AXDNU', 54 | 'DXANU', 55 | 'XDANU', 56 | 'XDNAU', 57 | 'DXNAU', 58 | 'NXDAU', 59 | 'XNDAU', 60 | 'DNXAU', 61 | 'NDXAU', 62 | 'UDXAN', 63 | 'DUXAN', 64 | 'XUDAN', 65 | 'UXDAN', 66 | 'DXUAN', 67 | 'XDUAN', 68 | 'ADUXN', 69 | 'DAUXN', 70 | 'UADXN', 71 | 'AUDXN', 72 | 'DUAXN', 73 | 'UDAXN', 74 | 'UXADN', 75 | 'XUADN', 76 | 'AUXDN', 77 | 'UAXDN', 78 | 'XAUDN', 79 | 'AXUDN', 80 | 'AXDUN', 81 | 'XADUN', 82 | 'DAXUN', 83 | 'ADXUN', 84 | 'XDAUN', 85 | 'DXAUN', 86 | 'NXAUD', 87 | 'XNAUD', 88 | 'ANXUD', 89 | 'NAXUD', 90 | 'XANUD', 91 | 'AXNUD', 92 | 'UXNAD', 93 | 'XUNAD', 94 | 'NUXAD', 95 | 'UNXAD', 96 | 'XNUAD', 97 | 'NXUAD', 98 | 'NAUXD', 99 | 'ANUXD', 100 | 'UNAXD', 101 | 'NUAXD', 102 | 'AUNXD', 103 | 'UANXD', 104 | 'UAXND', 105 | 'AUXND', 106 | 'XUAND', 107 | 'UXAND', 108 | 'AXUND', 109 | 'XAUND', 110 | 'DAUNX', 111 | 'ADUNX', 112 | 'UDANX', 113 | 'DUANX', 114 | 'AUDNX', 115 | 'UADNX', 116 | 'NADUX', 117 | 'ANDUX', 118 | 'DNAUX', 119 | 'NDAUX', 120 | 'ADNUX', 121 | 'DANUX', 122 | 'DUNAX', 123 | 'UDNAX', 124 | 'NDUAX', 125 | 'DNUAX', 126 | 'UNDAX', 127 | 'NUDAX', 128 | 'NUADX', 129 | 'UNADX', 130 | 'ANUDX', 131 | 'NAUDX', 132 | 'UANDX', 133 | 'AUNDX', 134 | ]; 135 | 136 | expect(getPermutations(shortString).sort()).toEqual(expectedPermutations.sort()); 137 | }); 138 | 139 | it('returns the same string if the string is one character long', () => { 140 | const shortString = 'a'; 141 | const expectedPermutations = ['a']; 142 | 143 | expect(getPermutations(shortString)).toEqual(expectedPermutations); 144 | }); 145 | 146 | it('returns an empty array for an empty string', () => { 147 | const shortString = ''; 148 | const expectedPermutations = []; 149 | 150 | expect(getPermutations(shortString)).toEqual(expectedPermutations); 151 | }); 152 | 153 | it('is case sensitive', () => { 154 | const shortString = 'aB'; 155 | 156 | expect(getPermutations(shortString)).not.toEqual(['ab', 'ba']); 157 | expect(getPermutations(shortString)).toEqual(['aB', 'Ba']); 158 | }); 159 | }); 160 | -------------------------------------------------------------------------------- /src/_Problems_/get-string-permutations/index.js: -------------------------------------------------------------------------------- 1 | // GET PERMUTATION OF A GIVEN STRING 2 | 3 | const getPermutations = (str) => { 4 | const result = []; 5 | 6 | if (str.length === 0) { 7 | return result; 8 | } 9 | 10 | if (str.length === 1) { 11 | result.push(str); 12 | return result; 13 | } 14 | 15 | const currentCharacter = str.charAt(0); 16 | const restOfString = str.substring(1); 17 | const returnResult = getPermutations(restOfString); 18 | 19 | for (let j = 0; j < returnResult.length; j += 1) { 20 | for (let i = 0; i <= returnResult[j].length; i += 1) { 21 | const value = returnResult[j].substring(0, i) + currentCharacter + returnResult[j].substring(i); 22 | result.push(value); 23 | } 24 | } 25 | 26 | return result; 27 | }; 28 | 29 | module.exports = { getPermutations }; 30 | -------------------------------------------------------------------------------- /src/_Problems_/get-subsequence/index.js: -------------------------------------------------------------------------------- 1 | // FIND SUBSEQUENCE OF A GIVEN SUBSTRING 2 | // SUBSTRING OF 'abc' ---->>>> [ '', 'a', 'b', 'ab', 'c', 'ac', 'bc', 'abc' ] 3 | // SUBSTRING OF 'bc' ---->>>> ['', 'b', 'c', 'bc'] 4 | // SUBSTRING OF 'c' ---->>>> ['', 'c'] 5 | // A pattern can be noticed in above three substrings. Technique followed is recursion. 6 | // Time complexity : O(2^n) n is the length of the string provided. 7 | 8 | function getSubesequence(str) { 9 | if (str.length === 0) { 10 | const array = ['']; 11 | return array; 12 | } 13 | 14 | const currentChar = str.charAt(0); 15 | const restOfString = str.substring(1); 16 | 17 | const result = []; 18 | const returnResult = getSubesequence(restOfString); 19 | for (let i = 0; i < returnResult.length; i += 1) { 20 | result.push(returnResult[i]); 21 | result.push(currentChar + returnResult[i]); 22 | } 23 | return result; 24 | } 25 | 26 | module.exports = { 27 | getSubesequence, 28 | }; 29 | -------------------------------------------------------------------------------- /src/_Problems_/get-subsequence/subsequence.test.js: -------------------------------------------------------------------------------- 1 | const { getSubesequence } = require('.'); 2 | 3 | describe('GetSubesequence', () => { 4 | it('Sequence of abc', () => { 5 | expect(getSubesequence('abc').sort()).toEqual(['', 'a', 'ab', 'abc', 'ac', 'b', 'bc', 'c']); 6 | }); 7 | 8 | it('Sequence of bc', () => { 9 | expect(getSubesequence('bc').sort()).toEqual(['', 'b', 'bc', 'c']); 10 | }); 11 | 12 | it('Sequence of c', () => { 13 | expect(getSubesequence('c').sort()).toEqual(['', 'c']); 14 | }); 15 | 16 | it('Sequence of empty', () => { 17 | expect(getSubesequence('').sort()).toEqual(['']); 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /src/_Problems_/max-consecutive-1s/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Find the length of maximum consecutive 1s in an array of o & 1 3 | * Input: [1, 0, 1, 1, 0] O/P - 2 4 | * Input: [0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1] O/P - 5 5 | * 6 | * Solved in O(n) 7 | */ 8 | 9 | function findMaxConsecutive1s(arr) { 10 | let count = 0; 11 | let max = 0; 12 | const { length } = arr; 13 | 14 | for (let i = 0; i < length; i += 1) { 15 | if (arr[i] === 1) { 16 | count += 1; 17 | } else if (arr[i] === 0) { 18 | if (count > max) max = count; 19 | count = 0; 20 | } 21 | } 22 | if (count > max) max = count; 23 | return max; 24 | } 25 | 26 | module.exports = findMaxConsecutive1s; 27 | -------------------------------------------------------------------------------- /src/_Problems_/max-consecutive-1s/max-consecutive-1s.test.js: -------------------------------------------------------------------------------- 1 | const findMaxConsecutive1s = require('.'); 2 | 3 | describe('Find max consecutive 1s', () => { 4 | it('returns 0 for an empty array', () => { 5 | const inputArr = []; 6 | const expected = 0; 7 | 8 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 9 | }); 10 | 11 | it('returns 0 for an array containing a single 0', () => { 12 | const inputArr = [0]; 13 | const expected = 0; 14 | 15 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 16 | }); 17 | 18 | it('returns 1 for an array containing a single 1', () => { 19 | const inputArr = [1]; 20 | const expected = 1; 21 | 22 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 23 | }); 24 | 25 | it('returns 1 for an array containing a single 1 and 0', () => { 26 | const inputArr = [1, 0]; 27 | const expected = 1; 28 | 29 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 30 | }); 31 | 32 | it('returns 1 for an array containing a single 0 and 1', () => { 33 | const inputArr = [0, 1]; 34 | const expected = 1; 35 | 36 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 37 | }); 38 | 39 | it('returns 1 for a large alternating array of 1s and 0s', () => { 40 | const inputArr = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1]; 41 | const expected = 1; 42 | 43 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 44 | }); 45 | 46 | it('returns 5 for increasing groups of 1s (max 5)', () => { 47 | const inputArr = [1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1]; 48 | const expected = 5; 49 | 50 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 51 | }); 52 | 53 | it('returns 5 for decreasing groups of 1s (max 5)', () => { 54 | const inputArr = [1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1]; 55 | const expected = 5; 56 | 57 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 58 | }); 59 | 60 | it('returns 5 for an array of 5 1s', () => { 61 | const inputArr = [1, 1, 1, 1, 1]; 62 | const expected = 5; 63 | 64 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 65 | }); 66 | 67 | it('skips 1s that are Strings', () => { 68 | const inputArr = [1, 1, '1']; 69 | const expected = 2; 70 | 71 | expect(findMaxConsecutive1s(inputArr)).toEqual(expected); 72 | }); 73 | }); 74 | -------------------------------------------------------------------------------- /src/_Problems_/max-product-of-3-numbers/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Given an array of numbers where the length can atmost be 10^4 3 | * Find the maximum product of 3 numbers 4 | * Samples: 5 | * [1, 2, 3] - 6 6 | * [-10, 10, 2, 3] - 60 7 | * [-10, -10, 2, 3] - 300 8 | */ 9 | 10 | /** The following solution is O(nlogn) because of sorting */ 11 | function maxProductof3Numbers(arr) { 12 | if (!Array.isArray(arr) || arr.length < 3) { 13 | throw new Error('Invalid Argument'); 14 | } 15 | 16 | // sort the array 17 | arr.sort((a, b) => a - b); 18 | 19 | const n = arr.length; 20 | 21 | // when the numbers are all positive 22 | const p1 = arr[n - 1] * arr[n - 2] * arr[n - 3]; 23 | 24 | // considering -ve numbers, 2 -ve on multiplication becomes +ve 25 | const p2 = arr[0] * arr[1] * arr[n - 1]; 26 | 27 | // return the largest of two probablities 28 | return p1 > p2 ? p1 : p2; 29 | } 30 | 31 | /** The following solution is O(n) */ 32 | function maxProductof3NumbersII(arr) { 33 | if (!Array.isArray(arr) || arr.length < 3) { 34 | throw new Error('Invalid Argument'); 35 | } 36 | 37 | let firstMax = Number.MIN_SAFE_INTEGER; 38 | let secondMax = Number.MIN_SAFE_INTEGER; 39 | let thirdMax = Number.MIN_SAFE_INTEGER; 40 | 41 | let firstMin = Number.MAX_SAFE_INTEGER; 42 | let secondMin = Number.MAX_SAFE_INTEGER; 43 | 44 | for (let i = 0; i < arr.length; i += 1) { 45 | if (arr[i] > firstMax) { 46 | thirdMax = secondMax; 47 | secondMax = firstMax; 48 | firstMax = arr[i]; 49 | } else if (arr[i] > secondMax) { 50 | thirdMax = secondMax; 51 | secondMax = arr[i]; 52 | } else if (arr[i] > thirdMax) { 53 | thirdMax = arr[i]; 54 | } 55 | 56 | // check for mins 57 | if (arr[i] < firstMin) { 58 | secondMin = firstMin; 59 | firstMin = arr[i]; 60 | } else if (arr[i] < secondMin) { 61 | secondMin = arr[i]; 62 | } 63 | } 64 | 65 | const p1 = firstMax * secondMax * thirdMax; 66 | const p2 = firstMin * secondMin * firstMax; 67 | 68 | return p1 > p2 ? p1 : p2; 69 | } 70 | 71 | module.exports = { maxProductof3Numbers, maxProductof3NumbersII }; 72 | -------------------------------------------------------------------------------- /src/_Problems_/max-product-of-3-numbers/max-product-of-3-numbers.test.js: -------------------------------------------------------------------------------- 1 | const { maxProductof3Numbers, maxProductof3NumbersII } = require('.'); 2 | 3 | describe('Maximum Product of three numbers', () => { 4 | it('throws an error with no Array is passed', () => { 5 | expect(() => { 6 | maxProductof3Numbers('xunda'); 7 | }).toThrowError(); 8 | expect(() => { 9 | maxProductof3NumbersII('xunda'); 10 | }).toThrowError(); 11 | }); 12 | 13 | it('returns the product of an array with 3 numbers', () => { 14 | expect(maxProductof3Numbers([1, 2, 3])).toEqual(6); 15 | expect(maxProductof3NumbersII([1, 2, 3])).toEqual(6); 16 | }); 17 | 18 | it('returns the product of an array with positive and negative numbers', () => { 19 | expect(maxProductof3Numbers([-10, -10, 2, 3])).toEqual(300); 20 | expect(maxProductof3NumbersII([-10, -10, 2, 3])).toEqual(300); 21 | }); 22 | 23 | it('returns the product of an array with negative numbers', () => { 24 | expect(maxProductof3Numbers([-10, -1, -2, -10])).toEqual(-20); 25 | expect(maxProductof3NumbersII([-10, -1, -2, -10])).toEqual(-20); 26 | }); 27 | 28 | it('returns the proper calculation if the array is large', () => { 29 | const largeArray = [ 30 | 100, 100, 100, 12, 3, 45, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 12, 3, 45, 4, 3, 7, 8, 1, 3, 7, 8, 31 | 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 12, 3, 45, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 32 | 7, 8, 45, 4, 3, 7, 8, 1, 3, 7, 8, 3, 45, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 12, 3, 45, 4, 3, 7, 33 | 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 3, 7, 8, 1, 3, 7, 8, 1, 12, 3, 45, 4, 3, 7, 8, 1, 3, 7, 8, 1, 4, 34 | 3, 7, 8, 1, 3, 7, 8, 45, 4, 3, 7, 8, 1, 3, 7, 8, 35 | ]; 36 | expect(maxProductof3Numbers(largeArray)).toEqual(100 * 100 * 100); 37 | expect(maxProductof3NumbersII(largeArray)).toEqual(100 * 100 * 100); 38 | }); 39 | 40 | it('returns an error if there are less than 3 numbers', () => { 41 | expect(() => { 42 | maxProductof3Numbers([-10, -1]); 43 | }).toThrowError(); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/_Problems_/maxchar/index.js: -------------------------------------------------------------------------------- 1 | function findMaxchar(str) { 2 | if (typeof str !== 'string') { 3 | throw new Error('Invalid Argument'); 4 | } 5 | 6 | const charMap = {}; 7 | const lowerCasedString = str.toLowerCase(); 8 | 9 | lowerCasedString.split('').forEach((char) => { 10 | if (!charMap[char]) { 11 | charMap[char] = 1; 12 | } else { 13 | charMap[char] += 1; 14 | } 15 | }); 16 | 17 | // find the char with highest score 18 | let max = charMap[lowerCasedString[0]]; 19 | let char = lowerCasedString[0]; 20 | 21 | Object.keys(charMap).forEach((e) => { 22 | if (charMap[e] > max) { 23 | max = charMap[e]; 24 | char = e; 25 | } 26 | }); 27 | 28 | return char; 29 | } 30 | 31 | module.exports = { 32 | findMaxchar, 33 | }; 34 | -------------------------------------------------------------------------------- /src/_Problems_/maxchar/maxchar.test.js: -------------------------------------------------------------------------------- 1 | const { findMaxchar } = require('.'); 2 | 3 | describe('MaxChar', () => { 4 | it('Should throw error for invalid argument', () => { 5 | expect(() => findMaxchar(21)).toThrow('Invalid Argument'); 6 | }); 7 | 8 | it('Should return `l` for given string `Hello World`', () => { 9 | expect(findMaxchar('Hello World')).toEqual('l'); 10 | }); 11 | 12 | it('Should return `a` for given string `A wonderful day`', () => { 13 | expect(findMaxchar('A wonderful day')).toEqual('a'); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /src/_Problems_/merge-two-sorted-arrays/index.js: -------------------------------------------------------------------------------- 1 | // the running complexity O(n + m) where n and m are the length of 2 arrays 2 | function mergeTwoSortedArrays(arr1, arr2) { 3 | let i = 0; 4 | let j = 0; 5 | 6 | const merged = []; 7 | 8 | while (i < arr1.length && j < arr2.length) { 9 | if (arr1[i] < arr2[j]) { 10 | merged.push(arr1[i]); 11 | i += 1; 12 | } else { 13 | merged.push(arr2[j]); 14 | j += 1; 15 | } 16 | } 17 | 18 | // if still elments are left in arr1 19 | if (i < arr1.length) { 20 | while (i < arr1.length) { 21 | merged.push(arr1[i]); 22 | i += 1; 23 | } 24 | } 25 | 26 | // if still elments are left in arr2 27 | if (j < arr2.length) { 28 | while (j < arr2.length) { 29 | merged.push(arr2[j]); 30 | j += 1; 31 | } 32 | } 33 | 34 | return merged; 35 | } 36 | 37 | // since we are using sort here, the running time complexity will be O(nlogn) 38 | function mergeTwoSortedArrays2(arr1, arr2) { 39 | return [...arr1, ...arr2].sort((a, b) => a - b); 40 | } 41 | 42 | module.exports = { 43 | mergeTwoSortedArrays, 44 | mergeTwoSortedArrays2, 45 | }; 46 | -------------------------------------------------------------------------------- /src/_Problems_/merge-two-sorted-arrays/merge-two-sorted-arrays.test.js: -------------------------------------------------------------------------------- 1 | const { mergeTwoSortedArrays, mergeTwoSortedArrays2 } = require('.'); 2 | 3 | describe('Merge Two Sorted Array which are already in ascending order ', () => { 4 | let array1 = []; 5 | let array2 = []; 6 | 7 | describe('When array[i]-array[j] = 1 where i>j & j>=0', () => { 8 | beforeEach(() => { 9 | array1 = [1, 2, 3, 4, 5]; 10 | array2 = [6, 7, 8]; 11 | }); 12 | it('Merge two sort array with complexity O(n+m)', () => { 13 | expect(mergeTwoSortedArrays(array1, array2)).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); 14 | }); 15 | it('Merge two sort array with complexity O(nlogn)', () => { 16 | expect(mergeTwoSortedArrays2(array1, array2)).toEqual([1, 2, 3, 4, 5, 6, 7, 8]); 17 | }); 18 | }); 19 | 20 | describe('When array[i]-array[j] => 1 where i>j & j>=0', () => { 21 | beforeEach(() => { 22 | array1 = [1, 3, 5, 6, 98, 100]; 23 | array2 = [2, 4, 99]; 24 | }); 25 | it('Merge two sort array with complexity O(n+m)', () => { 26 | expect(mergeTwoSortedArrays(array1, array2)).toEqual([1, 2, 3, 4, 5, 6, 98, 99, 100]); 27 | }); 28 | it('Merge two sort array with complexity O(nlogn)', () => { 29 | expect(mergeTwoSortedArrays2(array1, array2)).toEqual([1, 2, 3, 4, 5, 6, 98, 99, 100]); 30 | }); 31 | }); 32 | 33 | describe('When array[i]-array[j] <= 1 where i>j & j>=0', () => { 34 | beforeEach(() => { 35 | array1 = [1, 1, 5, 6, 98, 100]; 36 | array2 = [4, 4, 99]; 37 | }); 38 | it('Merge two sort array with complexity O(n+m)', () => { 39 | expect(mergeTwoSortedArrays(array1, array2)).toEqual([1, 1, 4, 4, 5, 6, 98, 99, 100]); 40 | }); 41 | it('Merge two sort array with complexity O(nlogn)', () => { 42 | expect(mergeTwoSortedArrays2(array1, array2)).toEqual([1, 1, 4, 4, 5, 6, 98, 99, 100]); 43 | }); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/_Problems_/next-greater-element/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | You must implement the nextGreaterElement() function. 3 | For each element in an array, it finds the next greater element in that array. 4 | To keep it simple, the next greater element for the last or maximum value in the array is -1. 5 | 6 | Input: [4, 6, 3, 2, 8, 1] 7 | Output: [6, 8, 8, 8, -1, -1] 8 | */ 9 | 10 | const Stack = require('../../_DataStructures_/Stack'); 11 | 12 | function nextGreaterElement(arr) { 13 | if (!Array.isArray(arr)) { 14 | throw new Error('Invalid Argument'); 15 | } 16 | 17 | const s1 = new Stack(); 18 | const nextGreater = []; 19 | 20 | for (let i = arr.length - 1; i >= 0; i -= 1) { 21 | if (s1.peek()) { 22 | let top = s1.peek(); 23 | while (top && top <= arr[i]) { 24 | // pop the elements 25 | s1.pop(); 26 | // get the new top 27 | top = s1.peek(); 28 | } 29 | } 30 | 31 | if (s1.peek()) { 32 | nextGreater[i] = s1.peek(); 33 | } else { 34 | nextGreater[i] = -1; 35 | } 36 | 37 | // push the element into the stack 38 | s1.push(arr[i]); 39 | } 40 | return nextGreater; 41 | } 42 | 43 | module.exports = { nextGreaterElement }; 44 | -------------------------------------------------------------------------------- /src/_Problems_/next-greater-element/next-greater-element.test.js: -------------------------------------------------------------------------------- 1 | const { nextGreaterElement } = require('.'); 2 | 3 | describe('Next greater element', () => { 4 | it('Should returns next greater elements collection', () => { 5 | const input = [4, 11, 6, 3, 2, 8, 1]; 6 | const greaterElements = [11, -1, 8, 8, 8, -1, -1]; 7 | 8 | expect(nextGreaterElement(input)).toEqual(greaterElements); 9 | }); 10 | 11 | it('Should returns and empty collection for an empty array', () => { 12 | expect(nextGreaterElement([])).toEqual([]); 13 | }); 14 | 15 | it('Should returns an array with -1 if the input has only one element', () => { 16 | expect(nextGreaterElement([0])).toEqual([-1]); 17 | }); 18 | 19 | it('Should returns a collection of -1 if there is no greater element', () => { 20 | const input = [90, 40, 15, 7, -1, -10]; 21 | const greaterElements = [-1, -1, -1, -1, -1, -1]; 22 | 23 | expect(nextGreaterElement(input)).toEqual(greaterElements); 24 | }); 25 | 26 | it('Should uses -1 if the numbers are the same', () => { 27 | const input = [90, 90]; 28 | const greaterElements = [-1, -1]; 29 | 30 | expect(nextGreaterElement(input)).toEqual(greaterElements); 31 | }); 32 | 33 | it('Should throws an error if the input is not an array', () => { 34 | expect(() => { 35 | nextGreaterElement('xunda'); 36 | }).toThrowError('Invalid Argument'); 37 | }); 38 | }); 39 | -------------------------------------------------------------------------------- /src/_Problems_/palindrome/index.js: -------------------------------------------------------------------------------- 1 | function palindromeUsingLoop(str) { 2 | let isPalindrome = true; 3 | let j = str.length - 1; 4 | 5 | for (let i = 0; i < str.length / 2; i += 1) { 6 | if (str[i] !== str[j]) { 7 | isPalindrome = false; 8 | break; 9 | } 10 | j -= 1; 11 | } 12 | 13 | return isPalindrome; 14 | } 15 | 16 | function palindromeUsingEvery(str) { 17 | return str.split('').every((char, index) => char === str[str.length - 1 - index]); 18 | } 19 | 20 | module.exports = { 21 | palindromeUsingLoop, 22 | palindromeUsingEvery, 23 | }; 24 | -------------------------------------------------------------------------------- /src/_Problems_/palindrome/palindrome.test.js: -------------------------------------------------------------------------------- 1 | const { palindromeUsingLoop, palindromeUsingEvery } = require('.'); 2 | 3 | describe('Palindrome Strings', () => { 4 | let mirror; 5 | let madam; 6 | 7 | beforeEach(() => { 8 | mirror = 'mirror'; 9 | madam = 'madam'; 10 | }); 11 | 12 | describe('Using traditional loop', () => { 13 | it('Should return FALSE for `mirror`', () => { 14 | expect(palindromeUsingLoop(mirror)).toBe(false); 15 | }); 16 | 17 | it('Should return TRUE for `madam`', () => { 18 | expect(palindromeUsingLoop(madam)).toBe(true); 19 | }); 20 | }); 21 | 22 | describe('Using ES6 Array.every', () => { 23 | it('Should return FALSE for `mirror`', () => { 24 | expect(palindromeUsingEvery(mirror)).toBe(false); 25 | }); 26 | 27 | it('Should return TRUE for `madam`', () => { 28 | expect(palindromeUsingEvery(madam)).toBe(true); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /src/_Problems_/product-of-elements/index.js: -------------------------------------------------------------------------------- 1 | // An array such that each index has a product of all the numbers in the array except the number stored at that index. 2 | 3 | /** 4 | * Input - [1, 2, 3, 4] 5 | * Output - [24, 12, 8, 6] 6 | * 7 | * Input - [0, 1, 3, 5] 8 | * Output - [15, 0, 0, 0] 9 | */ 10 | 11 | function findProduct(arr) { 12 | let left = 1; 13 | const result = []; 14 | 15 | // multiply all the numbers to the left side 16 | for (const el of arr) { 17 | result.push(left); 18 | left *= el; 19 | } 20 | 21 | let right = 1; 22 | 23 | // multiply all the numbers to the right side 24 | for (let i = arr.length - 1; i >= 0; i -= 1) { 25 | result[i] *= right; 26 | right *= arr[i]; 27 | } 28 | return result; 29 | } 30 | 31 | function findProduct2(arr) { 32 | let countZeros = 0; 33 | let positionOfZero = -1; 34 | let productOffAllExpectZero = 1; 35 | let result = []; 36 | for (let i = 0; i < arr.length; i += 1) { 37 | if (arr[i] === 0) { 38 | countZeros += 1; 39 | positionOfZero = i; 40 | } else { 41 | productOffAllExpectZero *= arr[i]; 42 | } 43 | } 44 | 45 | if (countZeros === 0) { 46 | for (let i = 0; i < arr.length; i += 1) { 47 | result[i] = productOffAllExpectZero / arr[i]; 48 | } 49 | } else if (countZeros === 1) { 50 | result = Array(arr.length).fill(0); 51 | result[positionOfZero] = productOffAllExpectZero; 52 | } else if (countZeros >= 2) { 53 | result = Array(arr.length).fill(0); 54 | } 55 | return result; 56 | } 57 | 58 | module.exports = { 59 | findProduct, 60 | findProduct2, 61 | }; 62 | -------------------------------------------------------------------------------- /src/_Problems_/product-of-elements/product-of-elements.test.js: -------------------------------------------------------------------------------- 1 | const { findProduct, findProduct2 } = require('.'); 2 | 3 | describe('Productof elements', () => { 4 | let array = []; 5 | 6 | describe('When count of zero is 0', () => { 7 | beforeEach(() => { 8 | array = [1, 2, 3, 4]; 9 | }); 10 | it('With 1st Approach', () => { 11 | expect(findProduct(array)).toEqual([24, 12, 8, 6]); 12 | }); 13 | it('With 2st Approach', () => { 14 | expect(findProduct2(array)).toEqual([24, 12, 8, 6]); 15 | }); 16 | }); 17 | 18 | describe('When count of zero is 1', () => { 19 | beforeEach(() => { 20 | array = [2, 3, 0, 4]; 21 | }); 22 | it('With 1st Approach', () => { 23 | expect(findProduct(array)).toEqual([0, 0, 24, 0]); 24 | }); 25 | it('With 2st Approach', () => { 26 | expect(findProduct2(array)).toEqual([0, 0, 24, 0]); 27 | }); 28 | }); 29 | 30 | describe('When count of zero is 2', () => { 31 | beforeEach(() => { 32 | array = [0, 3, 0, 4]; 33 | }); 34 | it('With 1st Approach', () => { 35 | expect(findProduct(array)).toEqual([0, 0, 0, 0]); 36 | }); 37 | it('With 2st Approach', () => { 38 | expect(findProduct2(array)).toEqual([0, 0, 0, 0]); 39 | }); 40 | }); 41 | 42 | describe('When count of zero is greater than 2', () => { 43 | beforeEach(() => { 44 | array = [0, 3, 0, 4, 8, 0]; 45 | }); 46 | it('With 1st Approach', () => { 47 | expect(findProduct(array)).toEqual([0, 0, 0, 0, 0, 0]); 48 | }); 49 | it('With 2st Approach', () => { 50 | expect(findProduct2(array)).toEqual([0, 0, 0, 0, 0, 0]); 51 | }); 52 | }); 53 | }); 54 | -------------------------------------------------------------------------------- /src/_Problems_/remove-duplicates/index.js: -------------------------------------------------------------------------------- 1 | function removeDuplicatesUsingHashTable(str) { 2 | let result = ''; 3 | const charHash = {}; 4 | 5 | // eslint-disable-next-line no-restricted-syntax 6 | for (const char of str) { 7 | if (!charHash[char]) { 8 | charHash[char] = char; 9 | } 10 | } 11 | 12 | // eslint-disable-next-line array-callback-return 13 | Object.keys(charHash).map((char) => { 14 | result += char; 15 | }); 16 | return result; 17 | } 18 | 19 | function removeDuplicatesUsingSet(str) { 20 | return [...new Set(str)].join(''); 21 | } 22 | 23 | module.exports = { 24 | removeDuplicatesUsingHashTable, 25 | removeDuplicatesUsingSet, 26 | }; 27 | -------------------------------------------------------------------------------- /src/_Problems_/remove-duplicates/remove-duplicates.test.js: -------------------------------------------------------------------------------- 1 | const { removeDuplicatesUsingHashTable, removeDuplicatesUsingSet } = require('.'); 2 | 3 | describe('Remove Duplicates', () => { 4 | describe('Using Hash Table', () => { 5 | it('Should remove the duplicate chars from `apple`', () => { 6 | expect(removeDuplicatesUsingHashTable('apple')).toEqual('aple'); 7 | }); 8 | 9 | it('Should return `bye` from `bye`', () => { 10 | expect(removeDuplicatesUsingHashTable('bye')).toEqual('bye'); 11 | }); 12 | }); 13 | 14 | describe('Using ES6 Set', () => { 15 | it('Should remove the duplicate chars from `apple`', () => { 16 | expect(removeDuplicatesUsingSet('apple')).toEqual('aple'); 17 | }); 18 | 19 | it('Should return `bye` from `bye`', () => { 20 | expect(removeDuplicatesUsingSet('bye')).toEqual('bye'); 21 | }); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/_Problems_/reverse-number/index.js: -------------------------------------------------------------------------------- 1 | function reverseNumber(num) { 2 | if (typeof num !== 'number') { 3 | throw new Error('Invalid Argument'); 4 | } 5 | 6 | let absNum = Math.abs(num); 7 | let reverse = 0; 8 | 9 | while (absNum > 0) { 10 | const rem = absNum % 10; 11 | reverse = reverse * 10 + rem; 12 | absNum = parseInt(absNum / 10, 10); // important to get whole number 13 | } 14 | 15 | return reverse * Math.sign(num); 16 | } 17 | 18 | /** 19 | * 20 | * Given a 32-bit signed integer, reverse digits of an integer. 21 | 22 | Example 1: 23 | 24 | Input: 123 25 | Output: 321 26 | Example 2: 27 | 28 | Input: -123 29 | Output: -321 30 | Example 3: 31 | 32 | Input: 1534236469 33 | Output: 0 // overflows 34 | Note: 35 | Assume we are dealing with an environment which could only 36 | store integers within the 32-bit signed integer range: [−2^31, 2^31 − 1]. 37 | For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows. 38 | */ 39 | 40 | function reverse32BitInt(n) { 41 | let x = n; 42 | let isNegetive = 0; 43 | if (x < 0) { 44 | x *= -1; 45 | isNegetive = 1; 46 | } 47 | let reverse = 0; 48 | while (x >= 1) { 49 | const r = Math.floor(x % 10); 50 | reverse = reverse * 10 + r; 51 | x = Math.floor(x / 10); 52 | } 53 | if (reverse > 0x7fffffff) { 54 | return 0; 55 | } 56 | return isNegetive ? reverse * -1 : reverse; 57 | } 58 | 59 | module.exports = { 60 | reverseNumber, 61 | reverse32BitInt, 62 | }; 63 | -------------------------------------------------------------------------------- /src/_Problems_/reverse-number/reverse-number.test.js: -------------------------------------------------------------------------------- 1 | const { reverseNumber, reverse32BitInt } = require('.'); 2 | 3 | describe('Reverse Numbers', () => { 4 | describe('Normal Reverse', () => { 5 | it('Should return a number', () => { 6 | expect(typeof reverseNumber(1) === 'number'); 7 | }); 8 | 9 | it('Should reverse 45 to 54', () => { 10 | expect(reverseNumber(45)).toEqual(54); 11 | }); 12 | 13 | it('Should reverse -2 to -2', () => { 14 | expect(reverseNumber(-2)).toEqual(-2); 15 | }); 16 | 17 | it('Should reverse -1234567 to -7654321', () => { 18 | expect(reverseNumber(-1234567)).toEqual(-7654321); 19 | }); 20 | 21 | it('Should throw error for invalid argument', () => { 22 | expect(() => reverseNumber('hello')).toThrow('Invalid Argument'); 23 | }); 24 | }); 25 | 26 | describe('32-bit signed integer reversal', () => { 27 | it('Should return a number', () => { 28 | expect(typeof reverse32BitInt(1) === 'number'); 29 | }); 30 | 31 | it('Should reverse 123 to 321', () => { 32 | expect(reverse32BitInt(123)).toEqual(321); 33 | }); 34 | 35 | it('Should reverse -871 to -178', () => { 36 | expect(reverse32BitInt(-871)).toEqual(-178); 37 | }); 38 | 39 | it('Should return 0 for 1534236469 because of overflow when reversed', () => { 40 | expect(reverse32BitInt(1534236469)).toEqual(0); 41 | }); 42 | }); 43 | }); 44 | -------------------------------------------------------------------------------- /src/_Problems_/reverse_string/index.js: -------------------------------------------------------------------------------- 1 | function usingInbuiltReverse(str) { 2 | return str.split('').reverse().join(''); 3 | } 4 | 5 | function usingLoopToReverse(str) { 6 | let reverse = ''; 7 | // eslint-disable-next-line no-restricted-syntax 8 | for (const char of str) { 9 | reverse = char + reverse; 10 | } 11 | return reverse; 12 | } 13 | 14 | function usingReduceToReverse(str) { 15 | return str.split('').reduce((reverse, char) => char + reverse); 16 | } 17 | 18 | module.exports = { 19 | usingInbuiltReverse, 20 | usingLoopToReverse, 21 | usingReduceToReverse, 22 | }; 23 | -------------------------------------------------------------------------------- /src/_Problems_/reverse_string/string-reverse.test.js: -------------------------------------------------------------------------------- 1 | const { usingInbuiltReverse, usingLoopToReverse, usingReduceToReverse } = require('.'); 2 | 3 | describe('Reverse a given string', () => { 4 | describe('Using Array.reverse() to reverse string', () => { 5 | it('Should reverse the given string', () => { 6 | expect(usingInbuiltReverse('apple')).toEqual('elppa'); 7 | }); 8 | }); 9 | 10 | describe('Using loop from end of the string to reverse string', () => { 11 | it('Should reverse the given string', () => { 12 | expect(usingLoopToReverse('apple')).toEqual('elppa'); 13 | }); 14 | }); 15 | 16 | describe('Using ES6 reduce() to reverse string', () => { 17 | it('Should reverse the given string', () => { 18 | expect(usingReduceToReverse('apple')).toEqual('elppa'); 19 | }); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /src/_Problems_/rotate-image/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | Rotate Image (LeetCode #48) 3 | 4 | - You are given an n x n 2D matrix representing an image, rotate the 5 | image by 90 degrees (clockwise). 6 | - You have to rotate the image in-place, which means you have to modify 7 | the input 2D matrix directly. DO NOT allocate another 2D matrix and do 8 | the rotation. 9 | 10 | Example 1: 11 | 12 | 1 2 3 7 4 1 13 | 4 5 6 ---> 8 5 2 14 | 7 8 9 9 6 3 15 | 16 | - Input: matrix = [[1,2,3],[4,5,6],[7,8,9]] 17 | - Output: [[7,4,1],[8,5,2],[9,6,3]] 18 | 19 | Example 2: 20 | 21 | 5 1 9 11 15 13 2 5 22 | 2 4 8 10 ---> 14 3 4 1 23 | 13 3 6 7 12 6 8 9 24 | 15 14 12 16 16 7 10 11 25 | 26 | - Input: matrix = [[5,1,9,11],[2,4,8,10],[13,3,6,7],[15,14,12,16]] 27 | - Output: [[15,13,2,5],[14,3,4,1],[12,6,8,9],[16,7,10,11]] 28 | 29 | Constraints: 30 | - n == matrix.length == matrix[i].length 31 | - 1 <= n <= 20 32 | - -1000 <= matrix[i][j] <= 1000 33 | */ 34 | 35 | /* 36 | Solution: 37 | 38 | 1. First, take the transpose of the matrix 39 | - Example: 40 | 1 2 3 1 4 7 41 | 4 5 6 ---> 2 5 8 42 | 7 8 9 3 6 9 43 | 44 | 2. Second, flip the matrix horizontally 45 | - Example: 46 | 1 4 7 7 4 1 47 | 2 5 8 ---> 8 5 2 48 | 3 6 9 9 6 3 49 | 50 | 3. Problem solved! 51 | 52 | - Solution is O(n) where n is the size (total number of entries) of the 53 | input matrix 54 | */ 55 | 56 | function rotateImage(m) { 57 | const matrix = m; 58 | const n = matrix.length; 59 | 60 | // take transpose 61 | for (let i = 0; i < n; i += 1) { 62 | for (let j = i; j < n; j += 1) { 63 | const temp = matrix[i][j]; 64 | matrix[i][j] = matrix[j][i]; 65 | matrix[j][i] = temp; 66 | } 67 | } 68 | 69 | // flip horizontally 70 | for (let i = 0; i < n; i += 1) { 71 | let left = 0; 72 | let right = n - 1; 73 | 74 | while (left < right) { 75 | const temp = matrix[i][left]; 76 | matrix[i][left] = matrix[i][right]; 77 | matrix[i][right] = temp; 78 | left += 1; 79 | right -= 1; 80 | } 81 | } 82 | } 83 | 84 | module.exports = { rotateImage }; 85 | -------------------------------------------------------------------------------- /src/_Problems_/rotate-image/rotate-image.test.js: -------------------------------------------------------------------------------- 1 | const { rotateImage } = require('.'); 2 | 3 | describe('Rotate Image', () => { 4 | it('Should rotate 3x3 matrix elements by 90 degrees', () => { 5 | const inputMatrix = [ 6 | [1, 2, 3], 7 | [4, 5, 6], 8 | [7, 8, 9], 9 | ]; 10 | 11 | const expectedMatrix = [ 12 | [7, 4, 1], 13 | [8, 5, 2], 14 | [9, 6, 3], 15 | ]; 16 | 17 | rotateImage(inputMatrix); 18 | expect(inputMatrix).toEqual(expectedMatrix); 19 | }); 20 | it('Should rotate 4x4 matrix elements by 90 degrees', () => { 21 | const inputMatrix = [ 22 | [5, 1, 9, 11], 23 | [2, 4, 8, 10], 24 | [13, 3, 6, 7], 25 | [15, 14, 12, 16], 26 | ]; 27 | 28 | const expectedMatrix = [ 29 | [15, 13, 2, 5], 30 | [14, 3, 4, 1], 31 | [12, 6, 8, 9], 32 | [16, 7, 10, 11], 33 | ]; 34 | 35 | rotateImage(inputMatrix); 36 | expect(inputMatrix).toEqual(expectedMatrix); 37 | }); 38 | it('Should rotate 5x5 matrix elements by 90 degrees', () => { 39 | const inputMatrix = [ 40 | [1, 2, 3, 4, 5], 41 | [6, 7, 8, 9, 10], 42 | [11, 12, 13, 14, 15], 43 | [16, 17, 18, 19, 20], 44 | [21, 22, 23, 24, 25], 45 | ]; 46 | 47 | const expectedMatrix = [ 48 | [21, 16, 11, 6, 1], 49 | [22, 17, 12, 7, 2], 50 | [23, 18, 13, 8, 3], 51 | [24, 19, 14, 9, 4], 52 | [25, 20, 15, 10, 5], 53 | ]; 54 | 55 | rotateImage(inputMatrix); 56 | expect(inputMatrix).toEqual(expectedMatrix); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /src/_Searching_/BinarySearch/BinarySearch.test.js: -------------------------------------------------------------------------------- 1 | const { binarySearch, binarySearchRecursive } = require('.'); 2 | 3 | describe('Binary Search', () => { 4 | const array = [1, 2, 3, 4, 5, 6, 7, 8]; 5 | const low = 0; 6 | const high = array.length - 1; 7 | 8 | describe('When element to find is at 1st position ', () => { 9 | it('Binary search with Loop', () => { 10 | expect(binarySearch(array, 1)).toEqual(0); 11 | }); 12 | it('Binary serach with recursion', () => { 13 | expect(binarySearchRecursive(array, low, high, 1)).toEqual(0); 14 | }); 15 | }); 16 | describe('When element to find is at last position ', () => { 17 | it('Binary search with Loop', () => { 18 | expect(binarySearch(array, 7)).toEqual(6); 19 | }); 20 | it('Binary serach with recursion', () => { 21 | expect(binarySearchRecursive(array, low, high, 7)).toEqual(6); 22 | }); 23 | }); 24 | describe('When element to find is at random position ', () => { 25 | it('Binary search with Loop', () => { 26 | expect(binarySearch(array, 3)).toEqual(2); 27 | }); 28 | it('Binary serach with recursion', () => { 29 | expect(binarySearchRecursive(array, low, high, 3)).toEqual(2); 30 | }); 31 | }); 32 | describe('When element to find is no present in array ', () => { 33 | it('Binary search with Loop', () => { 34 | expect(binarySearch(array, 10)).toEqual(null); 35 | }); 36 | it('Binary serach with recursion', () => { 37 | expect(binarySearchRecursive(array, low, high, 10)).toEqual(null); 38 | }); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /src/_Searching_/BinarySearch/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Note: Array must be sorted for binary search 3 | */ 4 | function binarySearch(arr, key) { 5 | let low = 0; 6 | let high = arr.length - 1; 7 | 8 | while (low <= high) { 9 | const mid = Math.floor((low + high) / 2); 10 | 11 | if (key < arr[mid]) { 12 | high = mid - 1; 13 | } else if (key > arr[mid]) { 14 | low = mid + 1; 15 | } else { 16 | // return the key 17 | return mid; 18 | } 19 | } 20 | return null; 21 | } 22 | 23 | function binarySearchRecursive(arr, low, high, key) { 24 | const mid = Math.floor((high - low) / 2 + low); 25 | 26 | if (high <= low && arr[mid] !== key) { 27 | return null; 28 | } 29 | if (key === arr[mid]) { 30 | return mid; 31 | } 32 | if (key < arr[mid]) { 33 | return binarySearchRecursive(arr, low, mid - 1, key); 34 | } 35 | if (key > arr[mid]) { 36 | return binarySearchRecursive(arr, mid + 1, high, key); 37 | } 38 | return null; 39 | } 40 | 41 | module.exports = { 42 | binarySearch, 43 | binarySearchRecursive, 44 | }; 45 | -------------------------------------------------------------------------------- /src/_Searching_/JumpSearch/JumpSearch.test.js: -------------------------------------------------------------------------------- 1 | const { jumpSearch } = require('.'); 2 | 3 | describe('Jump Search', () => { 4 | const array = [1, 2, 3, 4, 5, 6, 7, 8, 20]; 5 | describe('When element to find is at 1st position ', () => { 6 | it('Jump search', () => { 7 | expect(jumpSearch(array, 1)).toEqual(0); 8 | }); 9 | }); 10 | describe('When element to find is at last position ', () => { 11 | it('Jump search', () => { 12 | expect(jumpSearch(array, 20)).toEqual(8); 13 | }); 14 | }); 15 | describe('When element to find is at random position ', () => { 16 | it('Jump search', () => { 17 | expect(jumpSearch(array, 3)).toEqual(2); 18 | expect(jumpSearch(array, 5)).toEqual(4); 19 | expect(jumpSearch(array, 6)).toEqual(5); 20 | expect(jumpSearch(array, 8)).toEqual(7); 21 | }); 22 | }); 23 | describe('When element is not in array ', () => { 24 | it('Jump search', () => { 25 | expect(jumpSearch(array, 15)).toEqual(-1); 26 | expect(jumpSearch(array, 25)).toEqual(-1); 27 | expect(jumpSearch(array, 9)).toEqual(-1); 28 | }); 29 | }); 30 | }); 31 | -------------------------------------------------------------------------------- /src/_Searching_/JumpSearch/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Note: Array must be sorted for jump search 3 | * Complexity: 4 | * Worst case time complexity: O(√N) 5 | * Average case time complexity: O(√N) 6 | * Best case time complexity: O(1) 7 | * Space complexity: O(1) 8 | */ 9 | function jumpSearch(arr, key) { 10 | const n = arr.length; 11 | const jump = Math.floor(Math.sqrt(n)); 12 | let step = jump; 13 | let prev = 0; 14 | 15 | while (arr[Math.min(step, n) - 1] < key) { 16 | prev = step; 17 | step += jump; 18 | if (prev >= n) { 19 | return -1; 20 | } 21 | } 22 | 23 | while (arr[prev] < key && prev < Math.min(step, n)) { 24 | prev += 1; 25 | } 26 | 27 | if (arr[prev] === key) { 28 | return prev; 29 | } 30 | 31 | return -1; 32 | } 33 | 34 | module.exports = { 35 | jumpSearch, 36 | }; 37 | -------------------------------------------------------------------------------- /src/_Searching_/TernarySearch/TernarySearch.test.js: -------------------------------------------------------------------------------- 1 | const { ternarySearch, ternarySearchRecursive } = require('.'); 2 | 3 | describe('Ternary Search', () => { 4 | const array = [1, 2, 3, 4, 5, 6, 7, 8]; 5 | const low = 0; 6 | const high = array.length - 1; 7 | 8 | describe('When element to find is at 1st position ', () => { 9 | it('Ternary search with Loop', () => { 10 | expect(ternarySearch(array, 1)).toEqual(0); 11 | }); 12 | it('Ternary serach with recursion', () => { 13 | expect(ternarySearchRecursive(array, low, high, 1)).toEqual(0); 14 | }); 15 | }); 16 | describe('When element to find is at last position ', () => { 17 | it('Ternary search with Loop', () => { 18 | expect(ternarySearch(array, 8)).toEqual(7); 19 | }); 20 | it('Ternary serach with recursion', () => { 21 | expect(ternarySearchRecursive(array, low, high, 8)).toEqual(7); 22 | }); 23 | }); 24 | describe('When element to find is at random position ', () => { 25 | it('Ternary search with Loop', () => { 26 | expect(ternarySearch(array, 3)).toEqual(2); 27 | expect(ternarySearch(array, 5)).toEqual(4); 28 | }); 29 | it('Ternary serach with recursion', () => { 30 | expect(ternarySearchRecursive(array, low, high, 4)).toEqual(3); 31 | }); 32 | it('Ternary serach with recursion', () => { 33 | expect(ternarySearchRecursive(array, low, high, 5)).toEqual(4); 34 | }); 35 | }); 36 | describe('When element to find is no present in array ', () => { 37 | it('Ternary search with Loop', () => { 38 | expect(ternarySearch(array, 10)).toEqual(null); 39 | }); 40 | it('Ternary serach with recursion', () => { 41 | expect(ternarySearchRecursive(array, low, high, 10)).toEqual(null); 42 | }); 43 | }); 44 | }); 45 | -------------------------------------------------------------------------------- /src/_Searching_/TernarySearch/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Note: Array must be sorted for ternary search 3 | * Complexity: 4 | * Worst case time complexity: O(log N) 5 | * Average case time complexity: O(log N) 6 | * Best case time complexity: O(1) 7 | * Space complexity: O(1) 8 | */ 9 | function ternarySearch(arr, key) { 10 | let low = 0; 11 | let high = arr.length - 1; 12 | while (low <= high) { 13 | const lowMiddle = low + Math.floor((high - low) / 3); 14 | const highMiddle = low + 2 * Math.floor((high - low) / 3); 15 | if (key === arr[low]) { 16 | return low; 17 | } 18 | if (key === arr[high]) { 19 | return high; 20 | } 21 | if (key <= arr[lowMiddle]) { 22 | high = lowMiddle; 23 | } else if (key > arr[lowMiddle] && key <= arr[highMiddle]) { 24 | low = lowMiddle + 1; 25 | high = highMiddle; 26 | } else { 27 | low = highMiddle + 1; 28 | } 29 | } 30 | return null; 31 | } 32 | 33 | function ternarySearchRecursive(arr, low, high, key) { 34 | if (high >= low) { 35 | const highMiddle = low + 2 * Math.floor((high - low) / 3); 36 | const lowMiddle = low + Math.floor((high - low) / 3); 37 | if (key === arr[lowMiddle]) { 38 | return lowMiddle; 39 | } 40 | if (key === arr[highMiddle]) { 41 | return highMiddle; 42 | } 43 | if (key < arr[lowMiddle]) { 44 | return ternarySearchRecursive(arr, low, lowMiddle - 1, key); 45 | } 46 | if (key > arr[lowMiddle] && key < arr[highMiddle]) { 47 | return ternarySearchRecursive(arr, lowMiddle + 1, highMiddle - 1, key); 48 | } 49 | return ternarySearchRecursive(arr, highMiddle + 1, high, key); 50 | } 51 | return null; 52 | } 53 | 54 | module.exports = { 55 | ternarySearch, 56 | ternarySearchRecursive, 57 | }; 58 | --------------------------------------------------------------------------------