├── .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 | [](https://travis-ci.org/knaxus/problem-solving-javascript)
6 | [](https://coveralls.io/github/knaxus/problem-solving-javascript?branch=master)
7 | 
8 | 
9 | [](https://github.com/knaxus/problem-solving-javascript/issues)
10 | 
11 |
12 | Collection of interview questions with Unit Tests. Problems include Data Structures, Logical and few Classical problems.
13 |
14 | 
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) | | [Ashok Dey](https://linkedin.com/in/ashokdey) | [https://ashokdey.in](https://ashokdey.in) |
32 | | [Ashu Deshwal](https://github.com/TheSTL) | | [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 |
--------------------------------------------------------------------------------