mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 15:39:42 +08:00
Merge branch 'master' into improve_pr_template
This commit is contained in:
41
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
41
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
name: Bug report
|
||||
description: 'Create a report to help us improve'
|
||||
title: '[BUG]: '
|
||||
labels: ['bug']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: '### Before you open an issue, please verify if a similar one already exists or has been closed before. More details about the process of contributing can be found in [CONTRIBUTING.md](https://github.com/TheAlgorithms/JavaScript/blob/master/CONTRIBUTING.md).'
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Description
|
||||
description: Explain what the problem is.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: expectedbhv
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: Describe what was the expected behavior.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: actualbhv
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: Describe what actually happens.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: steps
|
||||
attributes:
|
||||
label: Steps to reproduce (if applicable)
|
||||
description: List steps to reproduce the behavior.
|
||||
placeholder: |
|
||||
1.
|
||||
2.
|
||||
3.
|
||||
4.
|
||||
validations:
|
||||
required: false
|
31
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
31
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
name: Feature request
|
||||
description: 'Suggest features, propose improvements, discuss new ideas'
|
||||
title: '[FEATURE]: '
|
||||
labels: ['enhancement']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## This issue template is not for requesting new algorithms. For new algorithms, PRs should be opened directly.
|
||||
## Make sure your issue isn't a duplicate and you follow our [contributing guidelines](https://github.com/TheAlgorithms/JavaScript/blob/master/CONTRIBUTING.md)
|
||||
- type: textarea
|
||||
id: description
|
||||
attributes:
|
||||
label: Motivation
|
||||
description: Describe what is the motivation behind this feature.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: examples
|
||||
attributes:
|
||||
label: Examples
|
||||
description: If possible, provide examples of how this feature can be used.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
id: workarounds
|
||||
attributes:
|
||||
label: Possible workarounds
|
||||
description: If possible, describes possible workarounds to this feature.
|
||||
validations:
|
||||
required: false
|
10
.github/workflows/Ci.yml
vendored
10
.github/workflows/Ci.yml
vendored
@ -12,6 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
@ -20,8 +21,13 @@ jobs:
|
||||
- name: 📦 Install dependencies
|
||||
run: npm ci
|
||||
|
||||
- name: 🧪 Run tests
|
||||
run: npm test
|
||||
- name: 🧪 Run all tests
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: npm run test
|
||||
|
||||
- name: 🧪 Run tests for changed files only
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: npm run test-changed
|
||||
|
||||
- name: 💄 Code style
|
||||
run: npm run style
|
||||
|
10
.gitpod.yml
10
.gitpod.yml
@ -1,3 +1,11 @@
|
||||
github:
|
||||
prebuilds:
|
||||
addBadge: true
|
||||
addComment: false
|
||||
addCheck: false
|
||||
master: true
|
||||
branches: true
|
||||
pullRequestsFromForks: true
|
||||
|
||||
tasks:
|
||||
- init: npm install
|
||||
|
||||
|
@ -2,4 +2,4 @@
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npm run style
|
||||
npm run test
|
||||
npm run test-changed
|
||||
|
@ -1,14 +1,33 @@
|
||||
import { factorial } from '../../Recursive/Factorial'
|
||||
import { permutations } from '../GeneratePermutations'
|
||||
|
||||
describe('Permutations', () => {
|
||||
it('Permutations of [a]', () => {
|
||||
const perms = permutations(['a'])
|
||||
expect(perms).toHaveLength(factorial(1))
|
||||
expect(perms).toContainEqual(['a'])
|
||||
})
|
||||
|
||||
it('Permutations of [true, false]', () => {
|
||||
const perms = permutations([true, false])
|
||||
expect(perms).toHaveLength(factorial(2))
|
||||
expect(perms).toContainEqual([true, false])
|
||||
expect(perms).toContainEqual([false, true])
|
||||
})
|
||||
|
||||
it('Permutations of [1, 2, 3]', () => {
|
||||
expect(permutations([1, 2, 3])).toEqual([
|
||||
[1, 2, 3],
|
||||
[1, 3, 2],
|
||||
[2, 1, 3],
|
||||
[2, 3, 1],
|
||||
[3, 1, 2],
|
||||
[3, 2, 1]
|
||||
])
|
||||
const perms = permutations([1, 2, 3])
|
||||
expect(perms).toHaveLength(factorial(3))
|
||||
expect(perms).toContainEqual([1, 2, 3])
|
||||
expect(perms).toContainEqual([1, 3, 2])
|
||||
expect(perms).toContainEqual([2, 1, 3])
|
||||
expect(perms).toContainEqual([2, 3, 1])
|
||||
expect(perms).toContainEqual([3, 1, 2])
|
||||
expect(perms).toContainEqual([3, 2, 1])
|
||||
})
|
||||
|
||||
it('Permutation counts across larger input arrays', () => {
|
||||
expect(permutations([1, 2, 3, 4, 5, 6, 7, 8])).toHaveLength(factorial(8))
|
||||
expect(permutations([1, 2, 3, 4, 5, 6, 7, 8, 9])).toHaveLength(factorial(9))
|
||||
})
|
||||
})
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
## Before contributing
|
||||
|
||||
Welcome to [TheAlgorithms/Javascript](https://github.com/TheAlgorithms/Javascript)! Before sending your pull requests,
|
||||
make sure that you **read the whole guidelines**. If you have any doubt on the contributing guide, please feel free to
|
||||
[state it clearly in an issue](https://github.com/TheAlgorithms/Javascript/issues/new).
|
||||
Welcome to [TheAlgorithms/JavaScript](https://github.com/TheAlgorithms/JavaScript)! Before sending your pull requests,
|
||||
make sure that you **read the whole guidelines**. If you have any doubts about the contributing guide, please feel free to
|
||||
[state them clearly in an issue](https://github.com/TheAlgorithms/JavaScript/issues/new) or by joining our [Discord community](https://the-algorithms.com/discord).
|
||||
|
||||
## Contributing
|
||||
|
||||
@ -15,11 +15,11 @@ referenced and used by learners from around the globe. Being one of our contribu
|
||||
|
||||
- You did your work - plagiarism is not allowed.
|
||||
- Any plagiarized work will not be merged.
|
||||
- Your work will be distributed under [GNU License](LICENSE) once your pull request is merged.
|
||||
- Your work will be distributed under the [GNU GPLv3.0](https://github.com/TheAlgorithms/JavaScript/blob/master/LICENSE) once your pull request is merged.
|
||||
- Your submitted work must fulfill our styles and standards.
|
||||
|
||||
**New implementation** is welcome! For example, new solutions to a problem, different representations of a graph data
|
||||
structure or algorithm designs with different complexity.
|
||||
**New implementations** are welcome! For example, new solutions to a problem, different representations of a graph data
|
||||
structure, or algorithm designs with different complexity.
|
||||
|
||||
**Improving comments** and **writing proper tests** are also highly welcome.
|
||||
|
||||
@ -52,18 +52,39 @@ Algorithms should:
|
||||
Algorithms in this repo should not be how-to examples for existing JavaScript packages. Instead, they should perform
|
||||
internal calculations or manipulations to convert input values into different output values. Those calculations or
|
||||
manipulations can use data types, classes, or functions of existing JavaScript packages but each algorithm in this repo
|
||||
should add unique value.
|
||||
should add a unique value.
|
||||
|
||||
#### Commit guidelines
|
||||
|
||||
- Follow [**Conventional Commits**](https://www.conventionalcommits.org/en/v1.0.0/) guidelines at all times.
|
||||
- Use one of the following prefixes (there might be other miscellaneous prefixes, though).
|
||||
- fix: A bug fix in an algorithm, workflow, configuration/settings, etc..
|
||||
- feat: A new feature, such as new algorithms, new workflows, etc..
|
||||
- docs: Documentation changes or fixes, like improving the contributing guidelines, fixing a typo, etc..
|
||||
- test: Correct existing tests or add new ones.
|
||||
- chore: Miscellaneous changes that do not match any of the above.
|
||||
|
||||
Examples of best commit messages.
|
||||
|
||||
```txt
|
||||
fix: fixed error in XYZ algorithm
|
||||
feat: re-work the CI workflow
|
||||
docs: improve the contributing guidelines
|
||||
test: add self-tests for XYZ algorithm
|
||||
chore: update readme badges
|
||||
```
|
||||
|
||||
#### File Naming Convention
|
||||
|
||||
- filenames should use the UpperCamelCase (PascalCase) style.
|
||||
- Filenames should use the UpperCamelCase (PascalCase) style.
|
||||
- There should be no spaces in filenames.
|
||||
- **Example:** `UserProfile.js` is allowed but `userprofile.js`,`Userprofile.js`,`user-Profile.js`,`userProfile.js` are
|
||||
not.
|
||||
|
||||
#### Module System
|
||||
|
||||
We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which bring an official, standardized module system to JavaScript.
|
||||
We use the [ES Module](https://hacks.mozilla.org/2018/03/es-modules-a-cartoon-deep-dive/) system, which brings an
|
||||
official, standardized module system to JavaScript.
|
||||
|
||||
It roughly means you will need to use `export` and `import` statements instead of `module.exports` and `require()`.
|
||||
|
||||
@ -71,7 +92,7 @@ It roughly means you will need to use `export` and `import` statements instead o
|
||||
|
||||
Be confident that your code works. When was the last time you committed a code change, your build failed, and half of
|
||||
your app stopped working? Mine was last week. Writing tests for our Algorithms will help us ensure the implementations
|
||||
are air tight even after multiple fixes and code changes.
|
||||
are airtight even after multiple fixes and code changes.
|
||||
|
||||
We use [Jest](https://jestjs.io/) to run unit tests on our algorithms. It provides a very readable and expressive way to
|
||||
structure your test code.
|
||||
@ -82,31 +103,37 @@ and inspect the outcome. Example: [RatInAMaze.test.js](Backtracking/tests/RatInA
|
||||
|
||||
Please refrain from using `console` in your implementation AND test code.
|
||||
|
||||
First you should install all dependencies using:
|
||||
First, you should install all dependencies using:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
npm install
|
||||
```
|
||||
|
||||
You can (and should!) run all tests locally before committing your changes:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
npm test
|
||||
```
|
||||
|
||||
If you want save some time and just run a specific test:
|
||||
If you want to save some time and just run a specific test:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
# This will run any test file where the filename contains "koch" (no need to specify folder path)
|
||||
npm test -- koch
|
||||
```
|
||||
|
||||
You can also start Jest in "watch" mode:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
npm test -- --watchAll
|
||||
```
|
||||
|
||||
We also prepared a helper script that runs tests only for changed files:
|
||||
|
||||
```bash
|
||||
npm run test-changed
|
||||
```
|
||||
|
||||
This will run all tests and watch source and test files for changes. When a change is made, the tests will run again.
|
||||
|
||||
#### Coding Style
|
||||
@ -114,9 +141,9 @@ This will run all tests and watch source and test files for changes. When a chan
|
||||
To maximize the readability and correctness of our code, we require that new submissions follow the
|
||||
[JavaScript Standard Style](https://standardjs.com/).
|
||||
|
||||
Before committing, please run
|
||||
Before committing, please run:
|
||||
|
||||
```shell
|
||||
```bash
|
||||
npm run style
|
||||
```
|
||||
|
||||
@ -127,7 +154,7 @@ A few (but not all) of the things to keep in mind:
|
||||
|
||||
- Use camelCase with the leading character as lowercase for identifier names (variables and functions).
|
||||
- Names start with a letter.
|
||||
- Follow code indentation: Always use 2 spaces for indentation of code blocks.
|
||||
- Follow code indentation: Always use 2 spaces for code-block indentation.
|
||||
|
||||
```js
|
||||
function sumOfArray(arrayOfNumbers) {
|
||||
@ -151,4 +178,4 @@ function sumOfArray(arrayOfNumbers) {
|
||||
- **Be consistent in the use of these guidelines when submitting.**
|
||||
- Happy coding!
|
||||
|
||||
Writer [@itsvinayak](https://github.com/itsvinayak), May 2020.
|
||||
Writer [@itsvinayak](https://github.com/itsvinayak) and contributors, May 2020.
|
||||
|
11
Conversions/LitersToImperialGallons.js
Normal file
11
Conversions/LitersToImperialGallons.js
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This function converts liters to imperial gallons
|
||||
* @constructor
|
||||
* @param {number} liters - Amount of liters to convert to gallons
|
||||
* @see https://en.wikipedia.org/wiki/Gallon
|
||||
*/
|
||||
const litersToImperialGallons = (liters) => {
|
||||
return liters / 4.54609
|
||||
}
|
||||
|
||||
export default litersToImperialGallons
|
11
Conversions/LitersToUSGallons.js
Normal file
11
Conversions/LitersToUSGallons.js
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This function converts liters to US gallons
|
||||
* https://en.wikipedia.org/wiki/Gallon
|
||||
* @constructor
|
||||
* @param {number} liters - Amount of liters to convert to gallons
|
||||
*/
|
||||
const litersToUSGallons = (liters) => {
|
||||
return liters / 3.785411784
|
||||
}
|
||||
|
||||
export default litersToUSGallons
|
11
Conversions/OuncesToKilograms.js
Normal file
11
Conversions/OuncesToKilograms.js
Normal file
@ -0,0 +1,11 @@
|
||||
/**
|
||||
* This function converts ounces to kilograms
|
||||
* https://en.wikipedia.org/wiki/Ounce
|
||||
* @constructor
|
||||
* @param {number} oz - Amount of ounces to convert to kilograms
|
||||
*/
|
||||
const ouncesToKilograms = (oz) => {
|
||||
return oz * 28.3498 / 1000
|
||||
}
|
||||
|
||||
export default ouncesToKilograms
|
5
Conversions/test/LitersToImperialGallons.test.js
Normal file
5
Conversions/test/LitersToImperialGallons.test.js
Normal file
@ -0,0 +1,5 @@
|
||||
import litersToImperialGallons from '../LitersToImperialGallons'
|
||||
|
||||
test('Convert 25 liters to imperial gallons', () => {
|
||||
expect(parseFloat(litersToImperialGallons(25).toFixed(2))).toBe(5.5)
|
||||
})
|
5
Conversions/test/LitersToUSGallons.test.js
Normal file
5
Conversions/test/LitersToUSGallons.test.js
Normal file
@ -0,0 +1,5 @@
|
||||
import litersToUSGallons from '../LitersToUSGallons'
|
||||
|
||||
test('Convert 50 liters to US gallons', () => {
|
||||
expect(parseFloat(litersToUSGallons(50).toFixed(2))).toBe(13.21)
|
||||
})
|
5
Conversions/test/OuncesToKilogram.test.js
Normal file
5
Conversions/test/OuncesToKilogram.test.js
Normal file
@ -0,0 +1,5 @@
|
||||
import ouncesToKilograms from '../OuncesToKilograms'
|
||||
|
||||
test('Convert 60 ounces to kilograms', () => {
|
||||
expect(parseFloat(ouncesToKilograms(60).toFixed(3))).toBe(1.701)
|
||||
})
|
28
DIRECTORY.md
28
DIRECTORY.md
@ -42,9 +42,12 @@
|
||||
* [HexToBinary](Conversions/HexToBinary.js)
|
||||
* [HexToDecimal](Conversions/HexToDecimal.js)
|
||||
* [HexToRGB](Conversions/HexToRGB.js)
|
||||
* [LitersToImperialGallons](Conversions/LitersToImperialGallons.js)
|
||||
* [LitersToUSGallons](Conversions/LitersToUSGallons.js)
|
||||
* [LowerCaseConversion](Conversions/LowerCaseConversion.js)
|
||||
* [MeterToFeetConversion](Conversions/MeterToFeetConversion.js)
|
||||
* [OctToDecimal](Conversions/OctToDecimal.js)
|
||||
* [OuncesToKilograms](Conversions/OuncesToKilograms.js)
|
||||
* [RailwayTimeConversion](Conversions/RailwayTimeConversion.js)
|
||||
* [RgbHsvConversion](Conversions/RgbHsvConversion.js)
|
||||
* [RGBToHex](Conversions/RGBToHex.js)
|
||||
@ -57,6 +60,7 @@
|
||||
* [LocalMaximomPoint](Data-Structures/Array/LocalMaximomPoint.js)
|
||||
* [NumberOfLocalMaximumPoints](Data-Structures/Array/NumberOfLocalMaximumPoints.js)
|
||||
* [QuickSelect](Data-Structures/Array/QuickSelect.js)
|
||||
* [Reverse](Data-Structures/Array/Reverse.js)
|
||||
* **Graph**
|
||||
* [Graph](Data-Structures/Graph/Graph.js)
|
||||
* [Graph2](Data-Structures/Graph/Graph2.js)
|
||||
@ -69,6 +73,7 @@
|
||||
* [AddTwoNumbers](Data-Structures/Linked-List/AddTwoNumbers.js)
|
||||
* [CycleDetection](Data-Structures/Linked-List/CycleDetection.js)
|
||||
* [DoublyLinkedList](Data-Structures/Linked-List/DoublyLinkedList.js)
|
||||
* [ReverseSinglyLinkedList](Data-Structures/Linked-List/ReverseSinglyLinkedList.js)
|
||||
* [SinglyCircularLinkedList](Data-Structures/Linked-List/SinglyCircularLinkedList.js)
|
||||
* [SinglyLinkedList](Data-Structures/Linked-List/SinglyLinkedList.js)
|
||||
* **Queue**
|
||||
@ -81,6 +86,7 @@
|
||||
* **Tree**
|
||||
* [AVLTree](Data-Structures/Tree/AVLTree.js)
|
||||
* [BinarySearchTree](Data-Structures/Tree/BinarySearchTree.js)
|
||||
* [SegmentTree](Data-Structures/Tree/SegmentTree.js)
|
||||
* [Trie](Data-Structures/Tree/Trie.js)
|
||||
* **Vectors**
|
||||
* [Vector2](Data-Structures/Vectors/Vector2.js)
|
||||
@ -111,11 +117,18 @@
|
||||
* [SudokuSolver](Dynamic-Programming/SudokuSolver.js)
|
||||
* [TrappingRainWater](Dynamic-Programming/TrappingRainWater.js)
|
||||
* [TribonacciNumber](Dynamic-Programming/TribonacciNumber.js)
|
||||
* [UniquePaths](Dynamic-Programming/UniquePaths.js)
|
||||
* [UniquePaths2](Dynamic-Programming/UniquePaths2.js)
|
||||
* [ZeroOneKnapsack](Dynamic-Programming/ZeroOneKnapsack.js)
|
||||
* **Geometry**
|
||||
* [Circle](Geometry/Circle.js)
|
||||
* [Cone](Geometry/Cone.js)
|
||||
* [ConvexHullGraham](Geometry/ConvexHullGraham.js)
|
||||
* [Pyramid](Geometry/Pyramid.js)
|
||||
* [Sphere](Geometry/Sphere.js)
|
||||
* **Graphs**
|
||||
* [BellmanFord](Graphs/BellmanFord.js)
|
||||
* [BinaryLifting](Graphs/BinaryLifting.js)
|
||||
* [BreadthFirstSearch](Graphs/BreadthFirstSearch.js)
|
||||
* [BreadthFirstShortestPath](Graphs/BreadthFirstShortestPath.js)
|
||||
* [ConnectedComponents](Graphs/ConnectedComponents.js)
|
||||
@ -125,7 +138,9 @@
|
||||
* [Dijkstra](Graphs/Dijkstra.js)
|
||||
* [DijkstraSmallestPath](Graphs/DijkstraSmallestPath.js)
|
||||
* [FloydWarshall](Graphs/FloydWarshall.js)
|
||||
* [Kosaraju](Graphs/Kosaraju.js)
|
||||
* [KruskalMST](Graphs/KruskalMST.js)
|
||||
* [LCABinaryLifting](Graphs/LCABinaryLifting.js)
|
||||
* [NodeNeighbors](Graphs/NodeNeighbors.js)
|
||||
* [NumberOfIslands](Graphs/NumberOfIslands.js)
|
||||
* [PrimMST](Graphs/PrimMST.js)
|
||||
@ -157,6 +172,7 @@
|
||||
* [EulerMethod](Maths/EulerMethod.js)
|
||||
* [EulersTotient](Maths/EulersTotient.js)
|
||||
* [EulersTotientFunction](Maths/EulersTotientFunction.js)
|
||||
* [ExponentialFunction](Maths/ExponentialFunction.js)
|
||||
* [ExtendedEuclideanGCD](Maths/ExtendedEuclideanGCD.js)
|
||||
* [Factorial](Maths/Factorial.js)
|
||||
* [Factors](Maths/Factors.js)
|
||||
@ -169,8 +185,11 @@
|
||||
* [FindMaxRecursion](Maths/FindMaxRecursion.js)
|
||||
* [FindMin](Maths/FindMin.js)
|
||||
* [FindMinIterator](Maths/FindMinIterator.js)
|
||||
* [FriendlyNumbers](Maths/FriendlyNumbers.js)
|
||||
* [GetEuclidGCD](Maths/GetEuclidGCD.js)
|
||||
* [GridGet](Maths/GridGet.js)
|
||||
* [HexagonalNumber](Maths/HexagonalNumber.js)
|
||||
* [IntToBase](Maths/IntToBase.js)
|
||||
* [IsDivisible](Maths/IsDivisible.js)
|
||||
* [IsEven](Maths/IsEven.js)
|
||||
* [IsOdd](Maths/IsOdd.js)
|
||||
@ -189,6 +208,7 @@
|
||||
* [MeanSquareError](Maths/MeanSquareError.js)
|
||||
* [MidpointIntegration](Maths/MidpointIntegration.js)
|
||||
* [MobiusFunction](Maths/MobiusFunction.js)
|
||||
* [ModularArithmetic](Maths/ModularArithmetic.js)
|
||||
* [ModularBinaryExponentiationRecursive](Maths/ModularBinaryExponentiationRecursive.js)
|
||||
* [NumberOfDigits](Maths/NumberOfDigits.js)
|
||||
* [Palindrome](Maths/Palindrome.js)
|
||||
@ -208,9 +228,12 @@
|
||||
* [ReversePolishNotation](Maths/ReversePolishNotation.js)
|
||||
* [ShorsAlgorithm](Maths/ShorsAlgorithm.js)
|
||||
* [SieveOfEratosthenes](Maths/SieveOfEratosthenes.js)
|
||||
* [SieveOfEratosthenesIntArray](Maths/SieveOfEratosthenesIntArray.js)
|
||||
* [Signum](Maths/Signum.js)
|
||||
* [SimpsonIntegration](Maths/SimpsonIntegration.js)
|
||||
* [Softmax](Maths/Softmax.js)
|
||||
* [SquareRoot](Maths/SquareRoot.js)
|
||||
* [SquareRootLogarithmic](Maths/SquareRootLogarithmic.js)
|
||||
* [SumOfDigits](Maths/SumOfDigits.js)
|
||||
* [SumOfGeometricProgression](Maths/SumOfGeometricProgression.js)
|
||||
* [TwinPrime](Maths/TwinPrime.js)
|
||||
@ -242,6 +265,7 @@
|
||||
* [Problem023](Project-Euler/Problem023.js)
|
||||
* [Problem025](Project-Euler/Problem025.js)
|
||||
* [Problem028](Project-Euler/Problem028.js)
|
||||
* [Problem035](Project-Euler/Problem035.js)
|
||||
* [Problem044](Project-Euler/Problem044.js)
|
||||
* **Recursive**
|
||||
* [BinaryEquivalent](Recursive/BinaryEquivalent.js)
|
||||
@ -251,6 +275,7 @@
|
||||
* [FibonacciNumberRecursive](Recursive/FibonacciNumberRecursive.js)
|
||||
* [FloodFill](Recursive/FloodFill.js)
|
||||
* [KochSnowflake](Recursive/KochSnowflake.js)
|
||||
* [LetterCombination](Recursive/LetterCombination.js)
|
||||
* [Palindrome](Recursive/Palindrome.js)
|
||||
* [SubsequenceRecursive](Recursive/SubsequenceRecursive.js)
|
||||
* [TowerOfHanoi](Recursive/TowerOfHanoi.js)
|
||||
@ -315,6 +340,7 @@
|
||||
* [CheckRearrangePalindrome](String/CheckRearrangePalindrome.js)
|
||||
* [CheckSnakeCase](String/CheckSnakeCase.js)
|
||||
* [CheckWordOccurrence](String/CheckWordOccurrence.js)
|
||||
* [CountLetters](String/CountLetters.js)
|
||||
* [CountSubstrings](String/CountSubstrings.js)
|
||||
* [CountVowels](String/CountVowels.js)
|
||||
* [CreatePermutations](String/CreatePermutations.js)
|
||||
@ -330,6 +356,7 @@
|
||||
* [MaxCharacter](String/MaxCharacter.js)
|
||||
* [MaxWord](String/MaxWord.js)
|
||||
* [PatternMatching](String/PatternMatching.js)
|
||||
* [PercentageOfLetters](String/PercentageOfLetters.js)
|
||||
* [PermutateString](String/PermutateString.js)
|
||||
* [ReverseString](String/ReverseString.js)
|
||||
* [ReverseWords](String/ReverseWords.js)
|
||||
@ -338,6 +365,7 @@
|
||||
* [ValidateCreditCard](String/ValidateCreditCard.js)
|
||||
* [ValidateEmail](String/ValidateEmail.js)
|
||||
* [ValidateUrl](String/ValidateUrl.js)
|
||||
* [ZFunction](String/ZFunction.js)
|
||||
* **Timing-Functions**
|
||||
* [GetMonthDays](Timing-Functions/GetMonthDays.js)
|
||||
* [IntervalTimer](Timing-Functions/IntervalTimer.js)
|
||||
|
17
Data-Structures/Array/Reverse.js
Normal file
17
Data-Structures/Array/Reverse.js
Normal file
@ -0,0 +1,17 @@
|
||||
/** https://www.geeksforgeeks.org/write-a-program-to-Reverse-an-array-or-string/
|
||||
* This function will accept an array and
|
||||
* Reverse its elements and returns the inverted array
|
||||
* @param {Array} arr array with elements of any data type
|
||||
* @returns {Array} array with inverted elements
|
||||
*/
|
||||
|
||||
const Reverse = (arr) => {
|
||||
// limit specifies the amount of Reverse actions
|
||||
for (let i = 0, j = arr.length - 1; i < arr.length / 2; i++, j--) {
|
||||
const temp = arr[i]
|
||||
arr[i] = arr[j]
|
||||
arr[j] = temp
|
||||
}
|
||||
return arr
|
||||
}
|
||||
export { Reverse }
|
13
Data-Structures/Array/test/Reverse.test.js
Normal file
13
Data-Structures/Array/test/Reverse.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { Reverse } from '../Reverse.js'
|
||||
import each from 'jest-each'
|
||||
|
||||
describe('reverse elements in an array', () => {
|
||||
each`
|
||||
array | expected
|
||||
${[]} | ${[]}
|
||||
${[1]} | ${[1]}
|
||||
${[1, 2, 3, 4]} | ${[4, 3, 2, 1]}
|
||||
`.test('returns $expected when given $array', ({ array, expected }) => {
|
||||
expect(Reverse(array)).toEqual(expected)
|
||||
})
|
||||
})
|
16
Data-Structures/Linked-List/ReverseSinglyLinkedList.js
Normal file
16
Data-Structures/Linked-List/ReverseSinglyLinkedList.js
Normal file
@ -0,0 +1,16 @@
|
||||
/** A LinkedList based solution to reverse a number
|
||||
Problem Statement: Given a number such that each of its digit is stored in a singly linked list. Reverse the linked list and return the head of the linked list Link for the Problem: https://leetcode.com/problems/reverse-linked-list/ */
|
||||
class ReverseSinglyLinkedList {
|
||||
solution (head) {
|
||||
let prev = null
|
||||
let next = null
|
||||
while (head) {
|
||||
next = head.next
|
||||
head.next = prev
|
||||
prev = head
|
||||
head = next
|
||||
}
|
||||
return prev
|
||||
};
|
||||
}
|
||||
export { ReverseSinglyLinkedList }
|
@ -274,6 +274,20 @@ class LinkedList {
|
||||
log () {
|
||||
console.log(JSON.stringify(this.headNode, null, 2))
|
||||
}
|
||||
|
||||
// Method to reverse the LinkedList
|
||||
reverse () {
|
||||
let head = this.headNode
|
||||
let prev = null
|
||||
let next = null
|
||||
while (head) {
|
||||
next = head.next
|
||||
head.next = prev
|
||||
prev = head
|
||||
head = next
|
||||
}
|
||||
this.headNode = prev
|
||||
};
|
||||
}
|
||||
|
||||
export { Node, LinkedList }
|
||||
|
@ -0,0 +1,14 @@
|
||||
import { ReverseSinglyLinkedList } from '../ReverseSinglyLinkedList'
|
||||
import { Node } from '../SinglyLinkedList'
|
||||
describe('ReverseSinglyLinkedList', () => {
|
||||
it('Reverse a Number Represented as Linked List', () => {
|
||||
const headNode = new Node(3)
|
||||
headNode.next = new Node(4)
|
||||
headNode.next.next = new Node(1)
|
||||
const expected = new Node(1)
|
||||
expected.next = new Node(4)
|
||||
expected.next.next = new Node(3)
|
||||
const reverseSinglyLinkedList = new ReverseSinglyLinkedList()
|
||||
expect(reverseSinglyLinkedList.solution(headNode)).toEqual(expected)
|
||||
})
|
||||
})
|
@ -247,4 +247,10 @@ describe('SinglyLinkedList', () => {
|
||||
headNode.rotateListRight(5)
|
||||
expect(headNode.get()).toEqual([20, 30, 40, 50, 10])
|
||||
})
|
||||
|
||||
it('Reverse a Linked List', () => {
|
||||
const list = new LinkedList([4, 3, 1])
|
||||
list.reverse()
|
||||
expect(list.get()).toEqual([1, 3, 4])
|
||||
})
|
||||
})
|
||||
|
97
Data-Structures/Tree/SegmentTree.js
Normal file
97
Data-Structures/Tree/SegmentTree.js
Normal file
@ -0,0 +1,97 @@
|
||||
/**
|
||||
* Segment Tree
|
||||
* concept : [Wikipedia](https://en.wikipedia.org/wiki/Segment_tree)
|
||||
* inspired by : https://www.geeksforgeeks.org/segment-tree-efficient-implementation/
|
||||
*
|
||||
* time complexity
|
||||
* - init : O(N)
|
||||
* - update : O(log(N))
|
||||
* - query : O(log(N))
|
||||
*
|
||||
* space complexity : O(N)
|
||||
*/
|
||||
class SegmentTree {
|
||||
size
|
||||
tree
|
||||
constructor (arr) {
|
||||
// we define tree like this
|
||||
// tree[1] : root node of tree
|
||||
// tree[i] : i'th node
|
||||
// tree[i * 2] : i'th left child
|
||||
// tree[i * 2 + 1] : i'th right child
|
||||
// and we use bit, shift operation for index
|
||||
this.size = arr.length
|
||||
this.tree = new Array(2 * arr.length)
|
||||
this.tree.fill(0)
|
||||
|
||||
this.build(arr)
|
||||
}
|
||||
|
||||
// function to build the tree
|
||||
build (arr) {
|
||||
const { size, tree } = this
|
||||
// insert leaf nodes in tree
|
||||
// leaf nodes will start from index N
|
||||
// in this array and will go up to index (2 * N – 1)
|
||||
for (let i = 0; i < size; i++) {
|
||||
tree[size + i] = arr[i]
|
||||
}
|
||||
|
||||
// build the tree by calculating parents
|
||||
// tree's root node will contain all leaf node's sum
|
||||
for (let i = size - 1; i > 0; --i) {
|
||||
// current node's value is the sum of left child, right child
|
||||
tree[i] = tree[i * 2] + tree[i * 2 + 1]
|
||||
}
|
||||
}
|
||||
|
||||
update (index, value) {
|
||||
const { size, tree } = this
|
||||
|
||||
// only update values in the parents of the given node being changed.
|
||||
// to get the parent move to parent node (index / 2)
|
||||
|
||||
// set value at position index
|
||||
index += size
|
||||
// tree[index] is leaf node and index's value of array
|
||||
tree[index] = value
|
||||
|
||||
// move upward and update parents
|
||||
for (let i = index; i > 1; i >>= 1) {
|
||||
// i ^ 1 turns (2 * i) to (2 * i + 1)
|
||||
// i ^ 1 is second child
|
||||
tree[i >> 1] = tree[i] + tree[i ^ 1]
|
||||
}
|
||||
}
|
||||
|
||||
// interval [L,R) with left index(L) included and right (R) excluded.
|
||||
query (left, right) {
|
||||
const { size, tree } = this
|
||||
// cause R is excluded, increase right for convenient
|
||||
right++
|
||||
let res = 0
|
||||
|
||||
// loop to find the sum in the range
|
||||
for (left += size, right += size; left < right; left >>= 1, right >>= 1) {
|
||||
// L is the left border of an query interval
|
||||
|
||||
// if L is odd it means that it is the right child of its parent and our interval includes only L and not the parent.
|
||||
// So we will simply include this node to sum and move to the parent of its next node by doing L = (L + 1) / 2.
|
||||
|
||||
// if L is even it is the left child of its parent
|
||||
// and the interval includes its parent also unless the right borders interfere.
|
||||
if ((left & 1) > 0) {
|
||||
res += tree[left++]
|
||||
}
|
||||
|
||||
// same in R (the right border of an query interval)
|
||||
if ((right & 1) > 0) {
|
||||
res += tree[--right]
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
||||
|
||||
export { SegmentTree }
|
16
Data-Structures/Tree/test/SegmentTree.test.js
Normal file
16
Data-Structures/Tree/test/SegmentTree.test.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { SegmentTree } from '../SegmentTree'
|
||||
|
||||
describe('SegmentTree sum test', () => {
|
||||
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||
|
||||
const segment = new SegmentTree(a)
|
||||
|
||||
it('init sum check', () => {
|
||||
expect(segment.query(0, 2)).toBe(6)
|
||||
})
|
||||
|
||||
it('init sum check', () => {
|
||||
segment.update(2, 1)
|
||||
expect(segment.query(0, 2)).toBe(4)
|
||||
})
|
||||
})
|
41
Dynamic-Programming/UniquePaths.js
Normal file
41
Dynamic-Programming/UniquePaths.js
Normal file
@ -0,0 +1,41 @@
|
||||
|
||||
/*
|
||||
*
|
||||
* Unique Paths
|
||||
*
|
||||
* There is a robot on an `m x n` grid.
|
||||
* The robot is initially located at the top-left corner.
|
||||
* The robot tries to move to the bottom-right corner.
|
||||
* The robot can only move either down or right at any point in time.
|
||||
*
|
||||
* Given the two integers `m` and `n`,
|
||||
* return the number of possible unique paths that the robot can take to reach the bottom-right corner.
|
||||
* More info: https://leetcode.com/problems/unique-paths/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @param {number} m
|
||||
* @param {number} n
|
||||
* @return {number}
|
||||
*/
|
||||
|
||||
const uniquePaths = (m, n) => {
|
||||
// only one way to reach end
|
||||
if (m === 1 || n === 1) return 1
|
||||
|
||||
// build a linear grid of size m
|
||||
// base case, position 1 has only 1 move
|
||||
const paths = new Array(m).fill(1)
|
||||
|
||||
for (let i = 1; i < n; i++) {
|
||||
for (let j = 1; j < m; j++) {
|
||||
// paths[j] in RHS represents the cell value stored above the current cell
|
||||
// paths[j-1] in RHS represents the cell value stored to the left of the current cell
|
||||
// paths [j] on the LHS represents the number of distinct pathways to the cell (i,j)
|
||||
paths[j] = paths[j - 1] + paths[j]
|
||||
}
|
||||
}
|
||||
return paths[m - 1]
|
||||
}
|
||||
|
||||
export { uniquePaths }
|
76
Dynamic-Programming/UniquePaths2.js
Normal file
76
Dynamic-Programming/UniquePaths2.js
Normal file
@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Unique Paths 2
|
||||
*
|
||||
* There is a robot on an `m x n` grid.
|
||||
* The robot is initially located at the top-left corner
|
||||
* The robot tries to move to the bottom-right corner.
|
||||
* The robot can only move either down or right at any point in time.
|
||||
*
|
||||
* Given grid with obstacles
|
||||
* An obstacle and space are marked as 1 or 0 respectively in grid.
|
||||
* A path that the robot takes cannot include any square that is an obstacle.
|
||||
* Return the number of possible unique paths that the robot can take to reach the bottom-right corner.
|
||||
*
|
||||
* More info: https://leetcode.com/problems/unique-paths-ii/
|
||||
*/
|
||||
|
||||
/**
|
||||
* @description Return 'rows x columns' grid with cells filled by 'filler'
|
||||
* @param {Number} rows Number of rows in the grid
|
||||
* @param {Number} columns Number of columns in the grid
|
||||
* @param {String | Number | Boolean} filler The value to fill cells
|
||||
* @returns {Array [][]}
|
||||
*/
|
||||
const generateMatrix = (rows, columns, filler = 0) => {
|
||||
const matrix = []
|
||||
for (let i = 0; i < rows; i++) {
|
||||
const submatrix = []
|
||||
for (let k = 0; k < columns; k++) {
|
||||
submatrix[k] = filler
|
||||
}
|
||||
matrix[i] = submatrix
|
||||
}
|
||||
return matrix
|
||||
}
|
||||
|
||||
/**
|
||||
* @description Return number of unique paths
|
||||
* @param {Array [][]} obstacles Obstacles grid
|
||||
* @returns {Number}
|
||||
*/
|
||||
const uniquePaths2 = (obstacles) => {
|
||||
if (!Array.isArray(obstacles)) {
|
||||
throw new Error('Input data must be type of Array')
|
||||
}
|
||||
// Create grid for calculating number of unique ways
|
||||
const rows = obstacles.length
|
||||
const columns = obstacles[0].length
|
||||
const grid = generateMatrix(rows, columns)
|
||||
// Fill the outermost cell with 1 b/c it has
|
||||
// the only way to reach neighbor
|
||||
for (let i = 0; i < rows; i++) {
|
||||
// If robot encounters an obstacle in these cells,
|
||||
// he cannot continue moving in that direction
|
||||
if (obstacles[i][0]) {
|
||||
break
|
||||
}
|
||||
grid[i][0] = 1
|
||||
}
|
||||
for (let j = 0; j < columns; j++) {
|
||||
if (obstacles[0][j]) {
|
||||
break
|
||||
}
|
||||
grid[0][j] = 1
|
||||
}
|
||||
// Fill the rest of grid by dynamic programming
|
||||
// using following reccurent formula:
|
||||
// K[i][j] = K[i - 1][j] + K[i][j - 1]
|
||||
for (let i = 1; i < rows; i++) {
|
||||
for (let j = 1; j < columns; j++) {
|
||||
grid[i][j] = obstacles[i][j] ? 0 : grid[i - 1][j] + grid[i][j - 1]
|
||||
}
|
||||
}
|
||||
return grid[rows - 1][columns - 1]
|
||||
}
|
||||
|
||||
export { uniquePaths2 }
|
11
Dynamic-Programming/tests/UniquePaths.test.js
Normal file
11
Dynamic-Programming/tests/UniquePaths.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import { uniquePaths } from '../UniquePaths'
|
||||
|
||||
describe('Unique Paths', () => {
|
||||
it('should return 28 when m is 3 and n is 7', () => {
|
||||
expect(uniquePaths(3, 7)).toBe(28)
|
||||
})
|
||||
|
||||
it('should return 48620 when m is 10 and n is 10', () => {
|
||||
expect(uniquePaths(10, 10)).toBe(48620)
|
||||
})
|
||||
})
|
19
Dynamic-Programming/tests/UniquePaths2.test.js
Normal file
19
Dynamic-Programming/tests/UniquePaths2.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { uniquePaths2 } from '../UniquePaths2'
|
||||
|
||||
describe('Unique Paths2', () => {
|
||||
// Should return number of ways, taken into account the obstacles
|
||||
test('There are obstacles in the way', () => {
|
||||
expect(uniquePaths2([[0, 0, 0], [0, 1, 0], [0, 0, 0]])).toEqual(2)
|
||||
expect(uniquePaths2([[0, 0, 0], [0, 1, 0], [0, 0, 0], [1, 0, 0]])).toEqual(3)
|
||||
})
|
||||
// Should return number of all possible ways to reach right-bottom corner
|
||||
test('There are no obstacles in the way', () => {
|
||||
expect(uniquePaths2([[0, 0, 0], [0, 0, 0], [0, 0, 0]])).toEqual(6)
|
||||
expect(uniquePaths2([[0, 0, 0], [0, 0, 0]])).toEqual(3)
|
||||
})
|
||||
// Should throw an exception b/c input data has wrong type
|
||||
test('There are wrong type of input data', () => {
|
||||
expect(() => uniquePaths2('wrong input')).toThrow()
|
||||
expect(() => uniquePaths2(100)).toThrow()
|
||||
})
|
||||
})
|
19
Geometry/Circle.js
Normal file
19
Geometry/Circle.js
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This class represents a circle and can calculate it's perimeter and area
|
||||
* https://en.wikipedia.org/wiki/Circle
|
||||
* @constructor
|
||||
* @param {number} radius - The radius of the circule.
|
||||
*/
|
||||
export default class Circle {
|
||||
constructor (radius) {
|
||||
this.radius = radius
|
||||
}
|
||||
|
||||
perimeter = () => {
|
||||
return this.radius * 2 * Math.PI
|
||||
}
|
||||
|
||||
area = () => {
|
||||
return Math.pow(this.radius, 2) * Math.PI
|
||||
}
|
||||
}
|
25
Geometry/Cone.js
Normal file
25
Geometry/Cone.js
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* This class represents a circular cone and can calculate its volume and surface area
|
||||
* https://en.wikipedia.org/wiki/Cone
|
||||
* @constructor
|
||||
* @param {number} baseRadius - The radius of the base of the cone.
|
||||
* @param {number} height - The height of the cone
|
||||
*/
|
||||
export default class Cone {
|
||||
constructor (baseRadius, height) {
|
||||
this.baseRadius = baseRadius
|
||||
this.height = height
|
||||
}
|
||||
|
||||
baseArea = () => {
|
||||
return Math.pow(this.baseRadius, 2) * Math.PI
|
||||
}
|
||||
|
||||
volume = () => {
|
||||
return this.baseArea() * this.height * 1 / 3
|
||||
}
|
||||
|
||||
surfaceArea = () => {
|
||||
return this.baseArea() + Math.PI * this.baseRadius * Math.sqrt(Math.pow(this.baseRadius, 2) + Math.pow(this.height, 2))
|
||||
}
|
||||
}
|
25
Geometry/Pyramid.js
Normal file
25
Geometry/Pyramid.js
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* This class represents a regular pyramid and can calculate its volume and surface area
|
||||
* https://en.wikipedia.org/wiki/Pyramid_(geometry)
|
||||
* @constructor
|
||||
* @param {number} bsl - The side length of the base of the pyramid.
|
||||
* @param {number} height - The height of the pyramid
|
||||
*/
|
||||
export default class Pyramid {
|
||||
constructor (bsl, height) {
|
||||
this.bsl = bsl
|
||||
this.height = height
|
||||
}
|
||||
|
||||
baseArea = () => {
|
||||
return Math.pow(this.bsl, 2)
|
||||
}
|
||||
|
||||
volume = () => {
|
||||
return this.baseArea() * this.height / 3
|
||||
}
|
||||
|
||||
surfaceArea = () => {
|
||||
return this.baseArea() + this.bsl * 4 / 2 * Math.sqrt(Math.pow(this.bsl / 2, 2) + Math.pow(this.height, 2))
|
||||
}
|
||||
}
|
19
Geometry/Sphere.js
Normal file
19
Geometry/Sphere.js
Normal file
@ -0,0 +1,19 @@
|
||||
/**
|
||||
* This class represents a sphere and can calculate its volume and surface area
|
||||
* @constructor
|
||||
* @param {number} radius - The radius of the sphere
|
||||
* @see https://en.wikipedia.org/wiki/Sphere
|
||||
*/
|
||||
export default class Sphere {
|
||||
constructor (radius) {
|
||||
this.radius = radius
|
||||
}
|
||||
|
||||
volume = () => {
|
||||
return Math.pow(this.radius, 3) * Math.PI * 4 / 3
|
||||
}
|
||||
|
||||
surfaceArea = () => {
|
||||
return Math.pow(this.radius, 2) * Math.PI * 4
|
||||
}
|
||||
}
|
11
Geometry/Test/Circle.test.js
Normal file
11
Geometry/Test/Circle.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Circle from '../Circle'
|
||||
|
||||
const circle = new Circle(3)
|
||||
|
||||
test('The area of a circle with radius equal to 3', () => {
|
||||
expect(parseFloat(circle.area().toFixed(2))).toEqual(28.27)
|
||||
})
|
||||
|
||||
test('The perimeter of a circle with radius equal to 3', () => {
|
||||
expect(parseFloat(circle.perimeter().toFixed(2))).toEqual(18.85)
|
||||
})
|
11
Geometry/Test/Cone.test.js
Normal file
11
Geometry/Test/Cone.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Cone from '../Cone'
|
||||
|
||||
const cone = new Cone(3, 5)
|
||||
|
||||
test('The Volume of a cone with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(cone.volume().toFixed(2))).toEqual(47.12)
|
||||
})
|
||||
|
||||
test('The Surface Area of a cone with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(cone.surfaceArea().toFixed(2))).toEqual(83.23)
|
||||
})
|
11
Geometry/Test/Pyramid.test.js
Normal file
11
Geometry/Test/Pyramid.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Pyramid from '../Pyramid'
|
||||
|
||||
const pyramid = new Pyramid(3, 5)
|
||||
|
||||
test('The Volume of a cone with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(pyramid.volume().toFixed(2))).toEqual(15)
|
||||
})
|
||||
|
||||
test('The Surface Area of a cone with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(pyramid.surfaceArea().toFixed(2))).toEqual(40.32)
|
||||
})
|
11
Geometry/Test/Sphere.test.js
Normal file
11
Geometry/Test/Sphere.test.js
Normal file
@ -0,0 +1,11 @@
|
||||
import Sphere from '../Sphere'
|
||||
|
||||
const sphere = new Sphere(3)
|
||||
|
||||
test('The Volume of a sphere with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(sphere.volume().toFixed(2))).toEqual(113.1)
|
||||
})
|
||||
|
||||
test('The Surface Area of a sphere with base radius equal to 3 and height equal to 5', () => {
|
||||
expect(parseFloat(sphere.surfaceArea().toFixed(2))).toEqual(113.1)
|
||||
})
|
82
Graphs/BinaryLifting.js
Normal file
82
Graphs/BinaryLifting.js
Normal file
@ -0,0 +1,82 @@
|
||||
/**
|
||||
* Author: Adrito Mukherjee
|
||||
* Binary Lifting implementation in Javascript
|
||||
* Binary Lifting is a technique that is used to find the kth ancestor of a node in a rooted tree with N nodes
|
||||
* The technique requires preprocessing the tree in O(N log N) using dynamic programming
|
||||
* The techniqe can answer Q queries about kth ancestor of any node in O(Q log N)
|
||||
* It is faster than the naive algorithm that answers Q queries with complexity O(Q K)
|
||||
* It can be used to find Lowest Common Ancestor of two nodes in O(log N)
|
||||
* Tutorial on Binary Lifting: https://codeforces.com/blog/entry/100826
|
||||
*/
|
||||
|
||||
export class BinaryLifting {
|
||||
constructor (root, tree) {
|
||||
this.root = root
|
||||
this.connections = new Map()
|
||||
this.up = new Map() // up[node][i] stores the 2^i-th parent of node
|
||||
for (const [i, j] of tree) {
|
||||
this.addEdge(i, j)
|
||||
}
|
||||
this.log = Math.ceil(Math.log2(this.connections.size))
|
||||
this.dfs(root, root)
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
// Function to add a node to the tree (connection represented by set)
|
||||
this.connections.set(node, new Set())
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the tree)
|
||||
if (!this.connections.has(node1)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!this.connections.has(node2)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections.get(node1).add(node2)
|
||||
this.connections.get(node2).add(node1)
|
||||
}
|
||||
|
||||
dfs (node, parent) {
|
||||
// The dfs function calculates 2^i-th ancestor of all nodes for i ranging from 0 to this.log
|
||||
// We make use of the fact the two consecutive jumps of length 2^(i-1) make the total jump length 2^i
|
||||
this.up.set(node, new Map())
|
||||
this.up.get(node).set(0, parent)
|
||||
for (let i = 1; i < this.log; i++) {
|
||||
this.up
|
||||
.get(node)
|
||||
.set(i, this.up.get(this.up.get(node).get(i - 1)).get(i - 1))
|
||||
}
|
||||
for (const child of this.connections.get(node)) {
|
||||
if (child !== parent) this.dfs(child, node)
|
||||
}
|
||||
}
|
||||
|
||||
kthAncestor (node, k) {
|
||||
// if value of k is more than or equal to the number of total nodes, we return the root of the graph
|
||||
if (k >= this.connections.size) {
|
||||
return this.root
|
||||
}
|
||||
// if i-th bit is set in the binary representation of k, we jump from a node to its 2^i-th ancestor
|
||||
// so after checking all bits of k, we will have made jumps of total length k, in just log k steps
|
||||
for (let i = 0; i < this.log; i++) {
|
||||
if (k & (1 << i)) {
|
||||
node = this.up.get(node).get(i)
|
||||
}
|
||||
}
|
||||
return node
|
||||
}
|
||||
}
|
||||
|
||||
function binaryLifting (root, tree, queries) {
|
||||
const graphObject = new BinaryLifting(root, tree)
|
||||
const ancestors = []
|
||||
for (const [node, k] of queries) {
|
||||
const ancestor = graphObject.kthAncestor(node, k)
|
||||
ancestors.push(ancestor)
|
||||
}
|
||||
return ancestors
|
||||
}
|
||||
|
||||
export default binaryLifting
|
@ -1,3 +1,5 @@
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
|
||||
/**
|
||||
* Breadth-first search is an algorithm for traversing a graph.
|
||||
*
|
||||
@ -12,11 +14,12 @@ export function breadthFirstSearch (graph, startingNode) {
|
||||
const visited = new Set()
|
||||
|
||||
// queue contains the nodes to be explored in the future
|
||||
const queue = [startingNode]
|
||||
const queue = new Queue()
|
||||
queue.enqueue(startingNode)
|
||||
|
||||
while (queue.length > 0) {
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first node
|
||||
const node = queue.shift()
|
||||
const node = queue.dequeue()
|
||||
|
||||
if (!visited.has(node)) {
|
||||
// mark the node as visited
|
||||
@ -25,7 +28,7 @@ export function breadthFirstSearch (graph, startingNode) {
|
||||
|
||||
// put all its neighbors into the queue
|
||||
for (let i = 0; i < neighbors.length; i++) {
|
||||
queue.push(neighbors[i])
|
||||
queue.enqueue(neighbors[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
import Queue from '../Data-Structures/Queue/Queue'
|
||||
/**
|
||||
* Breadth-first approach can be applied to determine the shortest path between two nodes in an equi-weighted graph.
|
||||
*
|
||||
@ -18,11 +19,12 @@ export function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
|
||||
// queue contains the paths to be explored in the future
|
||||
const initialPath = [startNode]
|
||||
const queue = [initialPath]
|
||||
const queue = new Queue()
|
||||
queue.enqueue(initialPath)
|
||||
|
||||
while (queue.length > 0) {
|
||||
while (!queue.isEmpty()) {
|
||||
// start with the queue's first path
|
||||
const path = queue.shift()
|
||||
const path = queue.dequeue()
|
||||
const node = path[path.length - 1]
|
||||
|
||||
// explore this node if it hasn't been visited yet
|
||||
@ -42,7 +44,7 @@ export function breadthFirstShortestPath (graph, startNode, targetNode) {
|
||||
}
|
||||
|
||||
// queue the new path
|
||||
queue.push(newPath)
|
||||
queue.enqueue(newPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
100
Graphs/Kosaraju.js
Normal file
100
Graphs/Kosaraju.js
Normal file
@ -0,0 +1,100 @@
|
||||
/**
|
||||
* Author: Adrito Mukherjee
|
||||
* Kosaraju's Algorithm implementation in Javascript
|
||||
* Kosaraju's Algorithm finds all the connected components in a Directed Acyclic Graph (DAG)
|
||||
* It uses Stack data structure to store the Topological Sorted Order of vertices and also Graph data structure
|
||||
*
|
||||
* Wikipedia: https://en.wikipedia.org/wiki/Kosaraju%27s_algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
class Kosaraju {
|
||||
constructor (graph) {
|
||||
this.connections = {}
|
||||
this.reverseConnections = {}
|
||||
this.stronglyConnectedComponents = []
|
||||
for (const [i, j] of graph) {
|
||||
this.addEdge(i, j)
|
||||
}
|
||||
this.topoSort()
|
||||
return this.kosaraju()
|
||||
}
|
||||
|
||||
addNode (node) {
|
||||
// Function to add a node to the graph (connection represented by set)
|
||||
this.connections[node] = new Set()
|
||||
this.reverseConnections[node] = new Set()
|
||||
this.topoSorted = []
|
||||
}
|
||||
|
||||
addEdge (node1, node2) {
|
||||
// Function to add an edge (adds the node too if they are not present in the graph)
|
||||
if (!(node1 in this.connections) || !(node1 in this.reverseConnections)) {
|
||||
this.addNode(node1)
|
||||
}
|
||||
if (!(node2 in this.connections) || !(node2 in this.reverseConnections)) {
|
||||
this.addNode(node2)
|
||||
}
|
||||
this.connections[node1].add(node2)
|
||||
this.reverseConnections[node2].add(node1)
|
||||
}
|
||||
|
||||
dfsTopoSort (node, visited) {
|
||||
visited.add(node)
|
||||
for (const child of this.connections[node]) {
|
||||
if (!visited.has(child)) this.dfsTopoSort(child, visited)
|
||||
}
|
||||
this.topoSorted.push(node)
|
||||
}
|
||||
|
||||
topoSort () {
|
||||
// Function to perform topological sorting
|
||||
const visited = new Set()
|
||||
const nodes = Object.keys(this.connections).map((key) => Number(key))
|
||||
for (const node of nodes) {
|
||||
if (!visited.has(node)) this.dfsTopoSort(node, visited)
|
||||
}
|
||||
}
|
||||
|
||||
dfsKosaraju (node, visited) {
|
||||
visited.add(node)
|
||||
this.stronglyConnectedComponents[
|
||||
this.stronglyConnectedComponents.length - 1
|
||||
].push(node)
|
||||
for (const child of this.reverseConnections[node]) {
|
||||
if (!visited.has(child)) this.dfsKosaraju(child, visited)
|
||||
}
|
||||
}
|
||||
|
||||
kosaraju () {
|
||||
// Function to perform Kosaraju Algorithm
|
||||
const visited = new Set()
|
||||
while (this.topoSorted.length > 0) {
|
||||
const node = this.topoSorted.pop()
|
||||
if (!visited.has(node)) {
|
||||
this.stronglyConnectedComponents.push([])
|
||||
this.dfsKosaraju(node, visited)
|
||||
}
|
||||
}
|
||||
return this.stronglyConnectedComponents
|
||||
}
|
||||
}
|
||||
|
||||
function kosaraju (graph) {
|
||||
const stronglyConnectedComponents = new Kosaraju(graph)
|
||||
return stronglyConnectedComponents
|
||||
}
|
||||
|
||||
export { kosaraju }
|
||||
|
||||
// kosaraju([
|
||||
// [1, 2],
|
||||
// [2, 3],
|
||||
// [3, 1],
|
||||
// [2, 4],
|
||||
// [4, 5],
|
||||
// [5, 6],
|
||||
// [6, 4],
|
||||
// ])
|
||||
|
||||
// [ [ 1, 3, 2 ], [ 4, 6, 5 ] ]
|
61
Graphs/LCABinaryLifting.js
Normal file
61
Graphs/LCABinaryLifting.js
Normal file
@ -0,0 +1,61 @@
|
||||
/**
|
||||
* Author: Adrito Mukherjee
|
||||
* Findind Lowest Common Ancestor By Binary Lifting implementation in JavaScript
|
||||
* The technique requires preprocessing the tree in O(N log N) using dynamic programming)
|
||||
* It can be used to find Lowest Common Ancestor of two nodes in O(log N)
|
||||
* Tutorial on Lowest Common Ancestor: https://www.geeksforgeeks.org/lca-in-a-tree-using-binary-lifting-technique
|
||||
*/
|
||||
|
||||
import { BinaryLifting } from './BinaryLifting'
|
||||
|
||||
class LCABinaryLifting extends BinaryLifting {
|
||||
constructor (root, tree) {
|
||||
super(root, tree)
|
||||
this.depth = new Map() // depth[node] stores the depth of node from root
|
||||
this.depth.set(root, 1)
|
||||
this.dfsDepth(root, root)
|
||||
}
|
||||
|
||||
dfsDepth (node, parent) {
|
||||
// DFS to find depth of every node in the tree
|
||||
for (const child of this.connections.get(node)) {
|
||||
if (child !== parent) {
|
||||
this.depth.set(child, this.depth.get(node) + 1)
|
||||
this.dfsDepth(child, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getLCA (node1, node2) {
|
||||
// We make sure that node1 is the deeper node among node1 and node2
|
||||
if (this.depth.get(node1) < this.depth.get(node2)) {
|
||||
[node1, node2] = [node2, node1]
|
||||
}
|
||||
// We check if node1 is the ancestor of node2, and if so, then return node1
|
||||
const k = this.depth.get(node1) - this.depth.get(node2)
|
||||
node1 = this.kthAncestor(node1, k)
|
||||
if (node1 === node2) {
|
||||
return node1
|
||||
}
|
||||
|
||||
for (let i = this.log - 1; i >= 0; i--) {
|
||||
if (this.up.get(node1).get(i) !== this.up.get(node2).get(i)) {
|
||||
node1 = this.up.get(node1).get(i)
|
||||
node2 = this.up.get(node2).get(i)
|
||||
}
|
||||
}
|
||||
return this.up.get(node1).get(0)
|
||||
}
|
||||
}
|
||||
|
||||
function lcaBinaryLifting (root, tree, queries) {
|
||||
const graphObject = new LCABinaryLifting(root, tree)
|
||||
const lowestCommonAncestors = []
|
||||
for (const [node1, node2] of queries) {
|
||||
const lca = graphObject.getLCA(node1, node2)
|
||||
lowestCommonAncestors.push(lca)
|
||||
}
|
||||
return lowestCommonAncestors
|
||||
}
|
||||
|
||||
export default lcaBinaryLifting
|
82
Graphs/test/BinaryLifting.test.js
Normal file
82
Graphs/test/BinaryLifting.test.js
Normal file
@ -0,0 +1,82 @@
|
||||
import binaryLifting from '../BinaryLifting'
|
||||
|
||||
// The graph for Test Case 1 looks like this:
|
||||
//
|
||||
// 0
|
||||
// /|\
|
||||
// / | \
|
||||
// 1 3 5
|
||||
// / \ \
|
||||
// 2 4 6
|
||||
// \
|
||||
// 7
|
||||
// / \
|
||||
// 11 8
|
||||
// \
|
||||
// 9
|
||||
// \
|
||||
// 10
|
||||
|
||||
test('Test case 1', () => {
|
||||
const root = 0
|
||||
const graph = [
|
||||
[0, 1],
|
||||
[0, 3],
|
||||
[0, 5],
|
||||
[5, 6],
|
||||
[1, 2],
|
||||
[1, 4],
|
||||
[4, 7],
|
||||
[7, 11],
|
||||
[7, 8],
|
||||
[8, 9],
|
||||
[9, 10]
|
||||
]
|
||||
const queries = [
|
||||
[2, 1],
|
||||
[6, 1],
|
||||
[7, 2],
|
||||
[8, 2],
|
||||
[10, 2],
|
||||
[10, 3],
|
||||
[10, 5],
|
||||
[11, 3]
|
||||
]
|
||||
const kthAncestors = binaryLifting(root, graph, queries)
|
||||
expect(kthAncestors).toEqual([1, 5, 1, 4, 8, 7, 1, 1])
|
||||
})
|
||||
|
||||
// The graph for Test Case 2 looks like this:
|
||||
//
|
||||
// 0
|
||||
// / \
|
||||
// 1 2
|
||||
// / \ \
|
||||
// 3 4 5
|
||||
// / / \
|
||||
// 6 7 8
|
||||
|
||||
test('Test case 2', () => {
|
||||
const root = 0
|
||||
const graph = [
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[1, 3],
|
||||
[1, 4],
|
||||
[2, 5],
|
||||
[3, 6],
|
||||
[5, 7],
|
||||
[5, 8]
|
||||
]
|
||||
const queries = [
|
||||
[2, 1],
|
||||
[3, 1],
|
||||
[3, 2],
|
||||
[6, 2],
|
||||
[7, 3],
|
||||
[8, 2],
|
||||
[8, 3]
|
||||
]
|
||||
const kthAncestors = binaryLifting(root, graph, queries)
|
||||
expect(kthAncestors).toEqual([0, 1, 0, 1, 0, 2, 0])
|
||||
})
|
30
Graphs/test/Kosaraju.test.js
Normal file
30
Graphs/test/Kosaraju.test.js
Normal file
@ -0,0 +1,30 @@
|
||||
import { kosaraju } from '../Kosaraju.js'
|
||||
|
||||
test('Test Case 1', () => {
|
||||
const graph = [
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 1],
|
||||
[2, 4],
|
||||
[4, 5],
|
||||
[5, 6],
|
||||
[6, 4]
|
||||
]
|
||||
const stronglyConnectedComponents = kosaraju(graph)
|
||||
expect(stronglyConnectedComponents).toStrictEqual([
|
||||
[1, 3, 2],
|
||||
[4, 6, 5]
|
||||
])
|
||||
})
|
||||
|
||||
test('Test Case 2', () => {
|
||||
const graph = [
|
||||
[1, 2],
|
||||
[2, 3],
|
||||
[3, 1],
|
||||
[2, 4],
|
||||
[4, 5]
|
||||
]
|
||||
const stronglyConnectedComponents = kosaraju(graph)
|
||||
expect(stronglyConnectedComponents).toStrictEqual([[1, 3, 2], [4], [5]])
|
||||
})
|
80
Graphs/test/LCABinaryLifting.test.js
Normal file
80
Graphs/test/LCABinaryLifting.test.js
Normal file
@ -0,0 +1,80 @@
|
||||
import lcaBinaryLifting from '../LCABinaryLifting'
|
||||
|
||||
// The graph for Test Case 1 looks like this:
|
||||
//
|
||||
// 0
|
||||
// /|\
|
||||
// / | \
|
||||
// 1 3 5
|
||||
// / \ \
|
||||
// 2 4 6
|
||||
// \
|
||||
// 7
|
||||
// / \
|
||||
// 11 8
|
||||
// \
|
||||
// 9
|
||||
// \
|
||||
// 10
|
||||
|
||||
test('Test case 1', () => {
|
||||
const root = 0
|
||||
const graph = [
|
||||
[0, 1],
|
||||
[0, 3],
|
||||
[0, 5],
|
||||
[5, 6],
|
||||
[1, 2],
|
||||
[1, 4],
|
||||
[4, 7],
|
||||
[7, 11],
|
||||
[7, 8],
|
||||
[8, 9],
|
||||
[9, 10]
|
||||
]
|
||||
const queries = [
|
||||
[1, 3],
|
||||
[6, 5],
|
||||
[3, 6],
|
||||
[7, 10],
|
||||
[8, 10],
|
||||
[11, 2],
|
||||
[11, 10]
|
||||
]
|
||||
const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries)
|
||||
expect(lowestCommonAncestors).toEqual([0, 5, 0, 7, 8, 1, 7])
|
||||
})
|
||||
|
||||
// The graph for Test Case 2 looks like this:
|
||||
//
|
||||
// 0
|
||||
// / \
|
||||
// 1 2
|
||||
// / \ \
|
||||
// 3 4 5
|
||||
// / / \
|
||||
// 6 7 8
|
||||
|
||||
test('Test case 2', () => {
|
||||
const root = 0
|
||||
const graph = [
|
||||
[0, 1],
|
||||
[0, 2],
|
||||
[1, 3],
|
||||
[1, 4],
|
||||
[2, 5],
|
||||
[3, 6],
|
||||
[5, 7],
|
||||
[5, 8]
|
||||
]
|
||||
const queries = [
|
||||
[1, 2],
|
||||
[3, 4],
|
||||
[5, 4],
|
||||
[6, 7],
|
||||
[6, 8],
|
||||
[7, 8]
|
||||
]
|
||||
const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries)
|
||||
expect(lowestCommonAncestors).toEqual([0, 1, 0, 0, 0, 5])
|
||||
})
|
25
Maths/ExponentialFunction.js
Normal file
25
Maths/ExponentialFunction.js
Normal file
@ -0,0 +1,25 @@
|
||||
/**
|
||||
* @function exponentialFunction
|
||||
* @description Calculates the n+1 th order Taylor series approximation of exponential function e^x given n
|
||||
* @param {Integer} power
|
||||
* @param {Integer} order - 1
|
||||
* @returns exponentialFunction(2,20) = 7.3890560989301735
|
||||
* @url https://en.wikipedia.org/wiki/Exponential_function
|
||||
*/
|
||||
function exponentialFunction (power, n) {
|
||||
let output = 0
|
||||
let fac = 1
|
||||
if (isNaN(power) || isNaN(n) || n < 0) {
|
||||
throw new TypeError('Invalid Input')
|
||||
}
|
||||
if (n === 0) { return 1 }
|
||||
for (let i = 0; i < n; i++) {
|
||||
output += (power ** i) / fac
|
||||
fac *= (i + 1)
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
export {
|
||||
exponentialFunction
|
||||
}
|
@ -11,16 +11,18 @@
|
||||
|
||||
'use strict'
|
||||
|
||||
import { findHCF } from './FindHcf'
|
||||
|
||||
// Find the LCM of two numbers.
|
||||
const findLcm = (num1, num2) => {
|
||||
// If the input numbers are less than 1 return an error message.
|
||||
if (num1 < 1 || num2 < 1) {
|
||||
return 'Please enter values greater than zero.'
|
||||
throw Error('Numbers must be positive.')
|
||||
}
|
||||
|
||||
// If the input numbers are not integers return an error message.
|
||||
if (num1 !== Math.round(num1) || num2 !== Math.round(num2)) {
|
||||
return 'Please enter whole numbers.'
|
||||
throw Error('Numbers must be whole.')
|
||||
}
|
||||
|
||||
// Get the larger number between the two
|
||||
@ -33,4 +35,19 @@ const findLcm = (num1, num2) => {
|
||||
}
|
||||
}
|
||||
|
||||
export { findLcm }
|
||||
// Typically, but not always, more efficient
|
||||
const findLcmWithHcf = (num1, num2) => {
|
||||
// If the input numbers are less than 1 return an error message.
|
||||
if (num1 < 1 || num2 < 1) {
|
||||
throw Error('Numbers must be positive.')
|
||||
}
|
||||
|
||||
// If the input numbers are not integers return an error message.
|
||||
if (num1 !== Math.round(num1) || num2 !== Math.round(num2)) {
|
||||
throw Error('Numbers must be whole.')
|
||||
}
|
||||
|
||||
return num1 * num2 / findHCF(num1, num2)
|
||||
}
|
||||
|
||||
export { findLcm, findLcmWithHcf }
|
||||
|
32
Maths/FriendlyNumbers.js
Normal file
32
Maths/FriendlyNumbers.js
Normal file
@ -0,0 +1,32 @@
|
||||
/*
|
||||
'In number theory, friendly numbers are two or more natural numbers with a common abundancy index, the
|
||||
ratio between the sum of divisors of a number and the number itself.'
|
||||
Source: https://en.wikipedia.org/wiki/Friendly_number
|
||||
See also: https://mathworld.wolfram.com/FriendlyNumber.html#:~:text=The%20numbers%20known%20to%20be,numbers%20have%20a%20positive%20density.
|
||||
*/
|
||||
|
||||
export const FriendlyNumbers = (firstNumber, secondNumber) => {
|
||||
// input: two integers
|
||||
// output: true if the two integers are friendly numbers, false if they are not friendly numbers
|
||||
|
||||
// First, check that the parameters are valid
|
||||
if (!Number.isInteger(firstNumber) || !Number.isInteger(secondNumber) || firstNumber === 0 || secondNumber === 0 || firstNumber === secondNumber) {
|
||||
throw new Error('The two parameters must be distinct, non-null integers')
|
||||
}
|
||||
|
||||
return abundancyIndex(firstNumber) === abundancyIndex(secondNumber)
|
||||
}
|
||||
|
||||
function abundancyIndex (number) {
|
||||
return sumDivisors(number) / number
|
||||
}
|
||||
|
||||
function sumDivisors (number) {
|
||||
let runningSumDivisors = number
|
||||
for (let i = 0; i < number / 2; i++) {
|
||||
if (Number.isInteger(number / i)) {
|
||||
runningSumDivisors += i
|
||||
}
|
||||
}
|
||||
return runningSumDivisors
|
||||
}
|
21
Maths/HexagonalNumber.js
Normal file
21
Maths/HexagonalNumber.js
Normal file
@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Author: Akshay Dubey (https://github.com/itsAkshayDubey)
|
||||
* Hexagonal Number: https://en.wikipedia.org/wiki/Hexagonal_number
|
||||
* The nth hexagonal number hn is the number of distinct dots in a pattern of dots
|
||||
* consisting of the outlines of regular hexagons with sides up to n dots, when the
|
||||
* hexagons are overlaid so that they share one vertex.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @function hexagonalNumber
|
||||
* @description -> returns nth hexagonal number
|
||||
* @param {Integer} number
|
||||
* @returns {Integer} nth hexagonal number
|
||||
*/
|
||||
|
||||
export const hexagonalNumber = (number) => {
|
||||
if (number <= 0) {
|
||||
throw new Error('Number must be greater than zero.')
|
||||
}
|
||||
return number * (2 * number - 1)
|
||||
}
|
40
Maths/IntToBase.js
Normal file
40
Maths/IntToBase.js
Normal file
@ -0,0 +1,40 @@
|
||||
/**
|
||||
* @function intToBase
|
||||
* @description Convert a number from decimal system to another (till decimal)
|
||||
* @param {Number} number Number to be converted
|
||||
* @param {Number} base Base of new number system
|
||||
* @returns {String} Converted Number
|
||||
* @see [HornerMethod](https://en.wikipedia.org/wiki/Horner%27s_method)
|
||||
* @example
|
||||
* const num1 = 125 // Needs to be converted to the binary number system
|
||||
* gornerScheme(num, 2); // ===> 1111101
|
||||
* @example
|
||||
* const num2 = 125 // Needs to be converted to the octal number system
|
||||
* gornerScheme(num, 8); // ===> 175
|
||||
*/
|
||||
const intToBase = (number, base) => {
|
||||
if (typeof number !== 'number' || typeof base !== 'number') {
|
||||
throw new Error('Input data must be numbers')
|
||||
}
|
||||
// Zero in any number system is zero
|
||||
if (number === 0) {
|
||||
return '0'
|
||||
}
|
||||
let absoluteValue = Math.abs(number)
|
||||
let convertedNumber = ''
|
||||
while (absoluteValue > 0) {
|
||||
// Every iteration last digit is taken away
|
||||
// and added to the previous one
|
||||
const lastDigit = absoluteValue % base
|
||||
convertedNumber = lastDigit + convertedNumber
|
||||
absoluteValue = Math.trunc(absoluteValue / base)
|
||||
}
|
||||
// Result is whether negative or positive,
|
||||
// depending on the original value
|
||||
if (number < 0) {
|
||||
convertedNumber = '-' + convertedNumber
|
||||
}
|
||||
return convertedNumber
|
||||
}
|
||||
|
||||
export { intToBase }
|
56
Maths/ModularArithmetic.js
Normal file
56
Maths/ModularArithmetic.js
Normal file
@ -0,0 +1,56 @@
|
||||
import { extendedEuclideanGCD } from './ExtendedEuclideanGCD'
|
||||
|
||||
/**
|
||||
* https://brilliant.org/wiki/modular-arithmetic/
|
||||
* @param {Number} arg1 first argument
|
||||
* @param {Number} arg2 second argument
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
export class ModRing {
|
||||
constructor (MOD) {
|
||||
this.MOD = MOD
|
||||
}
|
||||
|
||||
isInputValid = (arg1, arg2) => {
|
||||
if (!this.MOD) {
|
||||
throw new Error('Modulus must be initialized in the object constructor')
|
||||
}
|
||||
if (typeof arg1 !== 'number' || typeof arg2 !== 'number') {
|
||||
throw new TypeError('Input must be Numbers')
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Modulus is Distributive property,
|
||||
* As a result, we separate it into numbers in order to keep it within MOD's range
|
||||
*/
|
||||
|
||||
add = (arg1, arg2) => {
|
||||
this.isInputValid(arg1, arg2)
|
||||
return ((arg1 % this.MOD) + (arg2 % this.MOD)) % this.MOD
|
||||
}
|
||||
|
||||
subtract = (arg1, arg2) => {
|
||||
this.isInputValid(arg1, arg2)
|
||||
// An extra MOD is added to check negative results
|
||||
return ((arg1 % this.MOD) - (arg2 % this.MOD) + this.MOD) % this.MOD
|
||||
}
|
||||
|
||||
multiply = (arg1, arg2) => {
|
||||
this.isInputValid(arg1, arg2)
|
||||
return ((arg1 % this.MOD) * (arg2 % this.MOD)) % this.MOD
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* It is not Possible to find Division directly like the above methods,
|
||||
* So we have to use the Extended Euclidean Theorem for finding Multiplicative Inverse
|
||||
* https://github.com/TheAlgorithms/JavaScript/blob/master/Maths/ExtendedEuclideanGCD.js
|
||||
*/
|
||||
|
||||
divide = (arg1, arg2) => {
|
||||
// 1st Index contains the required result
|
||||
// The theorem may have return Negative value, we need to add MOD to make it Positive
|
||||
return (extendedEuclideanGCD(arg1, arg2)[1] + this.MOD) % this.MOD
|
||||
}
|
||||
}
|
22
Maths/SieveOfEratosthenesIntArray.js
Normal file
22
Maths/SieveOfEratosthenesIntArray.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
* Function to get all prime numbers below a given number
|
||||
* This function returns an array of prime numbers
|
||||
* @see {@link https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes}
|
||||
*/
|
||||
|
||||
function sieveOfEratosthenes (max) {
|
||||
const sieve = []
|
||||
const primes = []
|
||||
|
||||
for (let i = 2; i <= max; ++i) {
|
||||
if (!sieve[i]) { // If i has not been marked then it is prime
|
||||
primes.push(i)
|
||||
for (let j = i << 1; j <= max; j += i) { // Mark all multiples of i as non-prime
|
||||
sieve[j] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return primes
|
||||
}
|
||||
|
||||
export { sieveOfEratosthenes }
|
25
Maths/Signum.js
Normal file
25
Maths/Signum.js
Normal file
@ -0,0 +1,25 @@
|
||||
/*
|
||||
A program to demonstrate the implementation of the signum function,
|
||||
also known as the sign function, in JavaScript.
|
||||
|
||||
The signum function is an odd mathematical function, which returns the
|
||||
sign of the provided real number.
|
||||
It can return 3 values: 1 for values greater than zero, 0 for zero itself,
|
||||
and -1 for values less than zero
|
||||
|
||||
Wikipedia: https://en.wikipedia.org/wiki/Sign_function
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {Number} input
|
||||
* @returns {-1 | 0 | 1 | NaN} sign of input (and NaN if the input is not a number)
|
||||
*/
|
||||
function signum (input) {
|
||||
if (input === 0) return 0
|
||||
if (input > 0) return 1
|
||||
if (input < 0) return -1
|
||||
|
||||
return NaN
|
||||
}
|
||||
|
||||
export { signum }
|
41
Maths/SquareRootLogarithmic.js
Normal file
41
Maths/SquareRootLogarithmic.js
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @function squareRootLogarithmic
|
||||
* @description
|
||||
* Return the square root of 'num' rounded down
|
||||
* to the nearest integer.
|
||||
* More info: https://leetcode.com/problems/sqrtx/
|
||||
* @param {Number} num Number whose square of root is to be found
|
||||
* @returns {Number} Square root
|
||||
* @see [BinarySearch](https://en.wikipedia.org/wiki/Binary_search_algorithm)
|
||||
* @example
|
||||
* const num1 = 4
|
||||
* logarithmicSquareRoot(num1) // ====> 2
|
||||
* @example
|
||||
* const num2 = 8
|
||||
* logarithmicSquareRoot(num1) // ====> 2
|
||||
*
|
||||
*/
|
||||
const squareRootLogarithmic = (num) => {
|
||||
if (typeof num !== 'number') {
|
||||
throw new Error('Input data must be numbers')
|
||||
}
|
||||
let answer = 0
|
||||
let sqrt = 0
|
||||
let edge = num
|
||||
|
||||
while (sqrt <= edge) {
|
||||
const mid = Math.trunc((sqrt + edge) / 2)
|
||||
if (mid * mid === num) {
|
||||
return mid
|
||||
} else if (mid * mid < num) {
|
||||
sqrt = mid + 1
|
||||
answer = mid
|
||||
} else {
|
||||
edge = mid - 1
|
||||
}
|
||||
}
|
||||
|
||||
return answer
|
||||
}
|
||||
|
||||
export { squareRootLogarithmic }
|
16
Maths/test/ExponentialFunction.test.js
Normal file
16
Maths/test/ExponentialFunction.test.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { exponentialFunction } from '../ExponentialFunction'
|
||||
|
||||
describe('Tests for exponential function', () => {
|
||||
it('should be a function', () => {
|
||||
expect(typeof exponentialFunction).toEqual('function')
|
||||
})
|
||||
|
||||
it('should throw error for invalid input', () => {
|
||||
expect(() => exponentialFunction(2, -34)).toThrow()
|
||||
})
|
||||
|
||||
it('should return the exponential function of power of 5 and order of 21', () => {
|
||||
const ex = exponentialFunction(5, 20)
|
||||
expect(ex).toBe(148.4131078683383)
|
||||
})
|
||||
})
|
@ -1,20 +1,39 @@
|
||||
import { findLcm } from '../FindLcm'
|
||||
import { findLcm, findLcmWithHcf } from '../FindLcm'
|
||||
|
||||
describe('findLcm', () => {
|
||||
it('should throw a statement for values less than 1', () => {
|
||||
expect(findLcm(0, 0)).toBe('Please enter values greater than zero.')
|
||||
expect(() => { findLcm(0, 0) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should throw a statement for one value less than 1', () => {
|
||||
expect(findLcm(1, 0)).toBe('Please enter values greater than zero.')
|
||||
expect(findLcm(0, 1)).toBe('Please enter values greater than zero.')
|
||||
expect(() => { findLcm(1, 0) }).toThrow(Error)
|
||||
expect(() => { findLcm(0, 1) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should return an error for values non-integer values', () => {
|
||||
expect(findLcm(4.564, 7.39)).toBe('Please enter whole numbers.')
|
||||
expect(() => { findLcm(4.564, 7.39) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should return the LCM of two given integers', () => {
|
||||
expect(findLcm(27, 36)).toBe(108)
|
||||
})
|
||||
})
|
||||
|
||||
describe('findLcmWithHcf', () => {
|
||||
it('should throw a statement for values less than 1', () => {
|
||||
expect(() => { findLcmWithHcf(0, 0) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should throw a statement for one value less than 1', () => {
|
||||
expect(() => { findLcmWithHcf(1, 0) }).toThrow(Error)
|
||||
expect(() => { findLcmWithHcf(0, 1) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should return an error for values non-integer values', () => {
|
||||
expect(() => { findLcmWithHcf(4.564, 7.39) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should return the LCM of two given integers', () => {
|
||||
expect(findLcmWithHcf(27, 36)).toBe(108)
|
||||
})
|
||||
})
|
||||
|
19
Maths/test/HexagonalNumber.test.js
Normal file
19
Maths/test/HexagonalNumber.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { hexagonalNumber } from '../HexagonalNumber'
|
||||
|
||||
const expectedValuesArray = [1, 6, 15, 28, 45, 66, 91, 120, 153, 190, 231, 276, 325, 378, 435, 496, 561, 630, 703, 780, 861, 946]
|
||||
|
||||
describe('Testing hexagonalNumber', () => {
|
||||
for (let i = 1; i <= 22; i++) {
|
||||
it('Testing for number = ' + i + ', should return ' + expectedValuesArray[i], () => {
|
||||
expect(hexagonalNumber(i)).toBe(expectedValuesArray[i - 1])
|
||||
})
|
||||
}
|
||||
|
||||
it('should throw error when supplied negative numbers', () => {
|
||||
expect(() => { hexagonalNumber(-1) }).toThrow(Error)
|
||||
})
|
||||
|
||||
it('should throw error when supplied zero', () => {
|
||||
expect(() => { hexagonalNumber(0) }).toThrow(Error)
|
||||
})
|
||||
})
|
25
Maths/test/IntToBase.test.js
Normal file
25
Maths/test/IntToBase.test.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { intToBase } from '../IntToBase'
|
||||
|
||||
describe('Int to Base', () => {
|
||||
test('Conversion to the binary system', () => {
|
||||
expect(intToBase(210, 2)).toEqual('11010010')
|
||||
expect(intToBase(-210, 2)).toEqual('-11010010')
|
||||
})
|
||||
test('Conversion to the system with base 5', () => {
|
||||
expect(intToBase(210, 5)).toEqual('1320')
|
||||
expect(intToBase(-210, 5)).toEqual('-1320')
|
||||
})
|
||||
test('Conversion to the octal system', () => {
|
||||
expect(intToBase(210, 8)).toEqual('322')
|
||||
expect(intToBase(-210, 8)).toEqual('-322')
|
||||
})
|
||||
test('Output is 0', () => {
|
||||
expect(intToBase(0, 8)).toEqual('0')
|
||||
expect(intToBase(0, 8)).toEqual('0')
|
||||
})
|
||||
test('Throwing an exception', () => {
|
||||
expect(() => intToBase('string', 2)).toThrow()
|
||||
expect(() => intToBase(10, 'base')).toThrow()
|
||||
expect(() => intToBase(true, false)).toThrow()
|
||||
})
|
||||
})
|
45
Maths/test/ModularArithmetic.test.js
Normal file
45
Maths/test/ModularArithmetic.test.js
Normal file
@ -0,0 +1,45 @@
|
||||
import { ModRing } from '../ModularArithmetic'
|
||||
|
||||
describe('Modular Arithmetic', () => {
|
||||
const MOD = 10000007
|
||||
let ring
|
||||
beforeEach(() => {
|
||||
ring = new ModRing(MOD)
|
||||
})
|
||||
|
||||
describe('add', () => {
|
||||
it('Should return 9999993 for 10000000 and 10000000', () => {
|
||||
expect(ring.add(10000000, 10000000)).toBe(9999993)
|
||||
})
|
||||
it('Should return 9999986 for 10000000 and 20000000', () => {
|
||||
expect(ring.add(10000000, 20000000)).toBe(9999986)
|
||||
})
|
||||
})
|
||||
|
||||
describe('subtract', () => {
|
||||
it('Should return 1000000 for 10000000 and 9000000', () => {
|
||||
expect(ring.subtract(10000000, 9000000)).toBe(1000000)
|
||||
})
|
||||
it('Should return 7 for 10000000 and 20000000', () => {
|
||||
expect(ring.subtract(10000000, 20000000)).toBe(7)
|
||||
})
|
||||
})
|
||||
|
||||
describe('multiply', () => {
|
||||
it('Should return 1000000 for 100000 and 10000', () => {
|
||||
expect(ring.multiply(100000, 10000)).toBe(9999307)
|
||||
})
|
||||
it('Should return 7 for 100000 and 10000100', () => {
|
||||
expect(ring.multiply(10000000, 20000000)).toBe(98)
|
||||
})
|
||||
})
|
||||
|
||||
describe('divide', () => {
|
||||
it('Should return 4 for 3 and 11', () => {
|
||||
expect(ring.divide(3, 11)).toBe(4)
|
||||
})
|
||||
it('Should return 2 for 18 and 7', () => {
|
||||
expect(ring.divide(18, 7)).toBe(2)
|
||||
})
|
||||
})
|
||||
})
|
12
Maths/test/SieveOfEratosthenesIntArray.test.js
Normal file
12
Maths/test/SieveOfEratosthenesIntArray.test.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { sieveOfEratosthenes } from '../SieveOfEratosthenesIntArray'
|
||||
import { PrimeCheck } from '../PrimeCheck'
|
||||
|
||||
describe('should return an array of prime numbers', () => {
|
||||
it('should have each element in the array as a prime numbers', () => {
|
||||
const n = 100
|
||||
const primes = sieveOfEratosthenes(n)
|
||||
primes.forEach(prime => {
|
||||
expect(PrimeCheck(prime)).toBeTruthy()
|
||||
})
|
||||
})
|
||||
})
|
19
Maths/test/Signum.test.js
Normal file
19
Maths/test/Signum.test.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { signum } from '../Signum'
|
||||
|
||||
describe('The sign of a number', () => {
|
||||
it('Sign of 10', () => {
|
||||
expect(signum(10)).toBe(1)
|
||||
})
|
||||
|
||||
it('Sign of 0', () => {
|
||||
expect(signum(0)).toBe(0)
|
||||
})
|
||||
|
||||
it('Sign of -420', () => {
|
||||
expect(signum(-420)).toBe(-1)
|
||||
})
|
||||
|
||||
it('Sign of NaN', () => {
|
||||
expect(signum(NaN)).toBe(NaN)
|
||||
})
|
||||
})
|
13
Maths/test/SquareRootLogarithmic.test.js
Normal file
13
Maths/test/SquareRootLogarithmic.test.js
Normal file
@ -0,0 +1,13 @@
|
||||
import { squareRootLogarithmic } from '../SquareRootLogarithmic'
|
||||
|
||||
describe('SquareRootLogarithmic', () => {
|
||||
test('Finding the square root of a positive integer', () => {
|
||||
expect(squareRootLogarithmic(4)).toEqual(2)
|
||||
expect(squareRootLogarithmic(16)).toEqual(4)
|
||||
expect(squareRootLogarithmic(8)).toEqual(2)
|
||||
})
|
||||
test('Throwing an exception', () => {
|
||||
expect(() => squareRootLogarithmic('not a number')).toThrow()
|
||||
expect(() => squareRootLogarithmic(true)).toThrow()
|
||||
})
|
||||
})
|
35
Project-Euler/Problem035.js
Normal file
35
Project-Euler/Problem035.js
Normal file
@ -0,0 +1,35 @@
|
||||
/**
|
||||
* Problem 35 - Circular primes
|
||||
*
|
||||
* @see {@link https://projecteuler.net/problem=35}
|
||||
*
|
||||
* The number, 197, is called a circular prime because all rotations of the digits: 197, 971, and 719, are themselves prime.
|
||||
* There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, and 97.
|
||||
* How many circular primes are there below one million?
|
||||
*
|
||||
* @author ddaniel27
|
||||
*/
|
||||
import { sieveOfEratosthenes } from '../Maths/SieveOfEratosthenesIntArray'
|
||||
|
||||
function problem35 (n) {
|
||||
if (n < 2) {
|
||||
throw new Error('Invalid input')
|
||||
}
|
||||
// Get a list of primes without 0, 2, 4, 5, 6, 8; this discards the circular primes 2 & 5
|
||||
const list = sieveOfEratosthenes(n).filter(prime => !prime.toString().match(/[024568]/))
|
||||
|
||||
const result = list.filter((number, _idx, arr) => {
|
||||
const str = String(number)
|
||||
for (let i = 0; i < str.length; i++) { // Get all rotations of the number
|
||||
const rotation = str.slice(i) + str.slice(0, i)
|
||||
if (!arr.includes(Number(rotation))) { // Check if the rotation is prime
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true // If all rotations are prime, then the number is circular prime
|
||||
})
|
||||
|
||||
return result.length + 2 // Add 2 to the result because the circular primes 2 & 5 were discarded
|
||||
}
|
||||
|
||||
export { problem35 }
|
12
Project-Euler/test/Problem003.test.js
Normal file
12
Project-Euler/test/Problem003.test.js
Normal file
@ -0,0 +1,12 @@
|
||||
import { largestPrime } from '../Problem003.js'
|
||||
|
||||
describe('Largest prime factor', () => {
|
||||
test('if the number is 13195', () => {
|
||||
expect(largestPrime(13195)).toBe(29)
|
||||
})
|
||||
// Project Euler Condition Check
|
||||
test('if the number is 600851475143', () => {
|
||||
// Default value is same as the tested value
|
||||
expect(largestPrime()).toBe(6857)
|
||||
})
|
||||
})
|
18
Project-Euler/test/Problem035.test.js
Normal file
18
Project-Euler/test/Problem035.test.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { problem35 } from '../Problem035.js'
|
||||
|
||||
describe('checking circular primes', () => {
|
||||
it('should be invalid input if number is negative', () => {
|
||||
expect(() => problem35(-3)).toThrowError('Invalid input')
|
||||
})
|
||||
it('should be invalid input if number is 0', () => {
|
||||
expect(() => problem35(0)).toThrowError('Invalid input')
|
||||
})
|
||||
// Project Euler Condition Check
|
||||
test('if the number is equal to 100 result should be 13', () => {
|
||||
expect(problem35(100)).toBe(13)
|
||||
})
|
||||
// Project Euler Challenge Check
|
||||
test('if the number is equal to one million result should be 55', () => {
|
||||
expect(problem35(1000000)).toBe(55)
|
||||
})
|
||||
})
|
@ -1,10 +1,10 @@
|
||||
import { problem44 } from '../Problem044.js'
|
||||
|
||||
describe('checking nth prime number', () => {
|
||||
it('should be invalid input if number is negative', () => {
|
||||
test('should be invalid input if number is negative', () => {
|
||||
expect(() => problem44(-3)).toThrowError('Invalid Input')
|
||||
})
|
||||
it('should be invalid input if number is 0', () => {
|
||||
test('should be invalid input if number is 0', () => {
|
||||
expect(() => problem44(0)).toThrowError('Invalid Input')
|
||||
})
|
||||
// Project Euler Condition Check
|
||||
@ -12,6 +12,7 @@ describe('checking nth prime number', () => {
|
||||
expect(problem44(1)).toBe(5482660)
|
||||
})
|
||||
// Project Euler Second Value for Condition Check
|
||||
// FIXME skip this test for now because it runs very long and clogs up the CI & pre-commit hook
|
||||
test('if the number is greater or equal to 2167', () => {
|
||||
expect(problem44(2167)).toBe(8476206790)
|
||||
})
|
||||
|
@ -7,7 +7,6 @@ JavaScript Repository of TheAlgorithms, which implements various algorithms and
|
||||
|
||||
[![JavaScript Banner][banner]](DIRECTORY.md)
|
||||
|
||||
[![Language grade: JavaScript][grade]][lgtm]
|
||||
[![Checks][checks]][actions]
|
||||
[![Contributions Welcome][welcome]](CONTRIBUTING.md)
|
||||
[![standard.js][standard-logo]][standard-js]
|
||||
@ -32,7 +31,7 @@ JavaScript Repository of TheAlgorithms, which implements various algorithms and
|
||||
Before contributing to this repository, make sure to read our [Contribution Guidelines](CONTRIBUTING.md). You can look
|
||||
at other [TheAlgorithms Repositories][repositories] or the [issues with a "help wanted" label][help-wanted] for
|
||||
inspiration regarding what to implement. Our maintainers will guide you through how to make your contribution properly
|
||||
if you make any mistakes. The names of the maintainers of this repository is listed in the
|
||||
if you make any mistakes. The names of the maintainers of this repository are listed in the
|
||||
[CODEOWNERS file](.github/CODEOWNERS).
|
||||
|
||||
You can find a list of the algorithms currently in the repository in the [directory](DIRECTORY.md). Explanations of
|
||||
@ -47,8 +46,8 @@ many of the algorithms can be found in the [wiki][explanation].
|
||||
[standard-logo]: https://img.shields.io/badge/code%20style-standardjs-%23f3df49
|
||||
[chat]: https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA
|
||||
[welcome]: https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3
|
||||
[checks]: https://img.shields.io/github/workflow/status/TheAlgorithms/JavaScript/Node%20CI?label=checks
|
||||
[grade]: https://img.shields.io/lgtm/grade/javascript/g/TheAlgorithms/Javascript.svg?logo=lgtm&logoWidth=18
|
||||
[checks]: https://img.shields.io/github/actions/workflow/status/TheAlgorithms/JavaScript/Ci.yml?branch=master&label=checks
|
||||
[grade]: https://img.shields.io/lgtm/grade/javascript/g/TheAlgorithms/JavaScript.svg?logo=lgtm&logoWidth=18
|
||||
|
||||
<!-- External Links -->
|
||||
[standard-js]: https://standardjs.com/
|
||||
@ -56,6 +55,4 @@ many of the algorithms can be found in the [wiki][explanation].
|
||||
[actions]: https://github.com/TheAlgorithms/JavaScript/actions
|
||||
[explanation]: https://github.com/TheAlgorithms/JavaScript/wiki
|
||||
[repositories]: https://github.com/orgs/TheAlgorithms/repositories
|
||||
[lgtm]: https://lgtm.com/projects/g/TheAlgorithms/Javascript/context:javascript
|
||||
[help-wanted]: https://github.com/TheAlgorithms/JavaScript/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
|
||||
|
||||
|
@ -9,6 +9,14 @@
|
||||
*/
|
||||
|
||||
const factorial = (n) => {
|
||||
if (!Number.isInteger(n)) {
|
||||
throw new RangeError('Not a Whole Number')
|
||||
}
|
||||
|
||||
if (n < 0) {
|
||||
throw new RangeError('Not a Positive Number')
|
||||
}
|
||||
|
||||
if (n === 0) {
|
||||
return 1
|
||||
}
|
||||
|
53
Recursive/LetterCombination.js
Normal file
53
Recursive/LetterCombination.js
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
*
|
||||
* Letter Combinations of a Phone Number
|
||||
*
|
||||
* Given a string containing digits from 2-9 inclusive,
|
||||
* return all possible letter combinations that the number could represent.
|
||||
* Return the answer in any order.
|
||||
|
||||
* A mapping of digits to letters (just like on the telephone buttons) is given below.
|
||||
* Note that 1 does not map to any letters.
|
||||
* More info: https://leetcode.com/problems/letter-combinations-of-a-phone-number/
|
||||
*/
|
||||
|
||||
/*
|
||||
* @param {string} digits
|
||||
* @returns {string[]} all the possible combinations
|
||||
*/
|
||||
|
||||
const letterCombinations = (digits) => {
|
||||
const length = digits?.length
|
||||
const result = []
|
||||
if (!length) {
|
||||
return result
|
||||
}
|
||||
const digitMap = {
|
||||
2: 'abc',
|
||||
3: 'def',
|
||||
4: 'ghi',
|
||||
5: 'jkl',
|
||||
6: 'mno',
|
||||
7: 'pqrs',
|
||||
8: 'tuv',
|
||||
9: 'wxyz'
|
||||
}
|
||||
|
||||
const combinations = (index, combination) => {
|
||||
let letter
|
||||
let letterIndex
|
||||
if (index >= length) {
|
||||
result.push(combination)
|
||||
return
|
||||
}
|
||||
const digit = digitMap[digits[index]]
|
||||
letterIndex = 0
|
||||
while ((letter = digit[letterIndex++])) {
|
||||
combinations(index + 1, combination + letter)
|
||||
}
|
||||
}
|
||||
combinations(0, '')
|
||||
return result
|
||||
}
|
||||
|
||||
export { letterCombinations }
|
@ -8,4 +8,12 @@ describe('Factorial', () => {
|
||||
it('should return factorial 120 for value "5"', () => {
|
||||
expect(factorial(5)).toBe(120)
|
||||
})
|
||||
|
||||
it('Throw Error for Invalid Input', () => {
|
||||
expect(() => factorial('-')).toThrow('Not a Whole Number')
|
||||
expect(() => factorial(null)).toThrow('Not a Whole Number')
|
||||
expect(() => factorial(undefined)).toThrow('Not a Whole Number')
|
||||
expect(() => factorial(3.142)).toThrow('Not a Whole Number')
|
||||
expect(() => factorial(-1)).toThrow('Not a Positive Number')
|
||||
})
|
||||
})
|
||||
|
48
Recursive/test/LetterCombination.test.js
Normal file
48
Recursive/test/LetterCombination.test.js
Normal file
@ -0,0 +1,48 @@
|
||||
import { letterCombinations } from '../LetterCombination'
|
||||
|
||||
describe('Letter Combinations', () => {
|
||||
it('should return empty array if provided string is not valid', () => {
|
||||
const result = letterCombinations('')
|
||||
expect(Array.isArray(result)).toBe(true)
|
||||
expect(result.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should return empty array if provided string is empty', () => {
|
||||
const result = letterCombinations(null)
|
||||
expect(Array.isArray(result)).toBe(true)
|
||||
expect(result.length).toBe(0)
|
||||
})
|
||||
|
||||
it('should return letter combination of 234', () => {
|
||||
const result = letterCombinations('234')
|
||||
expect(result).toEqual([
|
||||
'adg',
|
||||
'adh',
|
||||
'adi',
|
||||
'aeg',
|
||||
'aeh',
|
||||
'aei',
|
||||
'afg',
|
||||
'afh',
|
||||
'afi',
|
||||
'bdg',
|
||||
'bdh',
|
||||
'bdi',
|
||||
'beg',
|
||||
'beh',
|
||||
'bei',
|
||||
'bfg',
|
||||
'bfh',
|
||||
'bfi',
|
||||
'cdg',
|
||||
'cdh',
|
||||
'cdi',
|
||||
'ceg',
|
||||
'ceh',
|
||||
'cei',
|
||||
'cfg',
|
||||
'cfh',
|
||||
'cfi'
|
||||
])
|
||||
})
|
||||
})
|
33
String/CountLetters.js
Normal file
33
String/CountLetters.js
Normal file
@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @function countLetters
|
||||
* @description Given a string, count the number of each letter.
|
||||
* @param {String} str - The input string
|
||||
* @return {Object} - Object with letters and number of times
|
||||
* @example countLetters("hello") => {h: 1, e: 1, l: 2, o: 1}
|
||||
*/
|
||||
|
||||
const countLetters = (str) => {
|
||||
const specialChars = /\W/g
|
||||
|
||||
if (typeof str !== 'string') {
|
||||
throw new TypeError('Input should be a string')
|
||||
}
|
||||
|
||||
if (specialChars.test(str)) {
|
||||
throw new TypeError('Input must not contain special characters')
|
||||
}
|
||||
|
||||
if (/\d/.test(str)) {
|
||||
throw new TypeError('Input must not contain numbers')
|
||||
}
|
||||
|
||||
const obj = {}
|
||||
for (let i = 0; i < str.toLowerCase().length; i++) {
|
||||
const char = str.toLowerCase().charAt(i)
|
||||
obj[char] = (obj[char] || 0) + 1
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
export { countLetters }
|
27
String/PercentageOfLetters.js
Normal file
27
String/PercentageOfLetters.js
Normal file
@ -0,0 +1,27 @@
|
||||
/**
|
||||
* @function percentageOfLetter
|
||||
* @description Return the percentage of characters in 'str'
|
||||
* that equal 'letter' rounded down to the nearest whole percent.
|
||||
* More info: https://leetcode.com/problems/percentage-of-letter-in-string/
|
||||
* @param {String} str
|
||||
* @param {String} letter
|
||||
* @returns {Number}
|
||||
* @example
|
||||
* const str = 'foobar', const letter = 'o'
|
||||
* percentageOfLetter(str, letter) // ===> 33
|
||||
*/
|
||||
const percentageOfLetter = (str, letter) => {
|
||||
if (typeof str !== 'string' || typeof letter !== 'string') {
|
||||
throw new Error('Input data must be strings')
|
||||
}
|
||||
let letterCount = 0
|
||||
// Iterate through the whole given text
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
// Count how often the letter appears in the word
|
||||
letterCount += str[i].toLowerCase() === letter.toLowerCase() ? 1 : 0
|
||||
}
|
||||
const percentage = Math.floor((100 * letterCount) / str.length)
|
||||
return percentage
|
||||
}
|
||||
|
||||
export { percentageOfLetter }
|
41
String/ZFunction.js
Normal file
41
String/ZFunction.js
Normal file
@ -0,0 +1,41 @@
|
||||
/**
|
||||
* @author: Adrito Mukherjee
|
||||
* Implementation of ZFunction in JavaScript
|
||||
* ZFunction at an index i gives the length of the longest substring starting at i, that is also a prefix of the whole string
|
||||
* ZFunction for all indices in a string can be calculated in O(N)
|
||||
* @see https://cp-algorithms.com/string/z-function.html
|
||||
* @param {String} text The string whose Z Function is to be calculated
|
||||
* @return {Array} Returns an array whose i-th index is the value of Z Function for text at index i
|
||||
*/
|
||||
|
||||
function zFunction (text) {
|
||||
const length = text.length
|
||||
const zArray = Array(length).fill(0)
|
||||
// Initializing left and right variable to zero
|
||||
let left = 0
|
||||
let right = 0
|
||||
for (let index = 0; index < length; index++) {
|
||||
// If index is less than or equal to right, we reuse the values of zFunction at index right-index+1
|
||||
// It is made sure that value of zFunction at index is not greater than maximum possible value at index
|
||||
if (index <= right) {
|
||||
zArray[index] = Math.min(right - index + 1, zArray[index - left])
|
||||
}
|
||||
|
||||
// After zArray[index] is initialized, we see if we can increase its value by trivially comparing character by character
|
||||
while (
|
||||
index + zArray[index] < length &&
|
||||
text[zArray[index]] === text[index + zArray[index]]
|
||||
) {
|
||||
zArray[index]++
|
||||
}
|
||||
|
||||
// If index + zArray[index] - 1 is greater than right, we update values of variables left and right
|
||||
if (index + zArray[index] - 1 > right) {
|
||||
left = index
|
||||
right = index + zArray[index] - 1
|
||||
}
|
||||
}
|
||||
return zArray
|
||||
}
|
||||
|
||||
export default zFunction
|
33
String/test/CountLetters.test.js
Normal file
33
String/test/CountLetters.test.js
Normal file
@ -0,0 +1,33 @@
|
||||
import { countLetters } from '../CountLetters'
|
||||
|
||||
describe('CountLetters', () => {
|
||||
it('expect throws on use wrong param', () => {
|
||||
expect(() => countLetters(0)).toThrow()
|
||||
})
|
||||
|
||||
it('expect throws when using a number in the string', () => {
|
||||
expect(() => countLetters('h3llo')).toThrow()
|
||||
})
|
||||
|
||||
it('expect throws when using a special characters in the string', () => {
|
||||
expect(() => countLetters('hello!')).toThrow()
|
||||
})
|
||||
|
||||
it('count the letters in a string. Allows lower case', () => {
|
||||
const value = 'hello'
|
||||
const count = countLetters(value)
|
||||
expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 })
|
||||
})
|
||||
|
||||
it('count the letters in a string. Allows upper case', () => {
|
||||
const value = 'HELLO'
|
||||
const count = countLetters(value)
|
||||
expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 })
|
||||
})
|
||||
|
||||
it('count the letters in a string. Allows upper and lower case', () => {
|
||||
const value = 'HelLo'
|
||||
const count = countLetters(value)
|
||||
expect(count).toEqual({ h: 1, e: 1, l: 2, o: 1 })
|
||||
})
|
||||
})
|
16
String/test/PercentageOfLetters.test.js
Normal file
16
String/test/PercentageOfLetters.test.js
Normal file
@ -0,0 +1,16 @@
|
||||
import { percentageOfLetter } from '../PercentageOfLetters'
|
||||
|
||||
describe('Percentage of Letters in a String', () => {
|
||||
test('Calculate percent for lower case', () => {
|
||||
expect(percentageOfLetter('foobar', 'o')).toEqual(33)
|
||||
expect(percentageOfLetter('aaabcd', 'a')).toEqual(50)
|
||||
})
|
||||
test('Calculate percent for upper case', () => {
|
||||
expect(percentageOfLetter('foobar', 'o')).toEqual(33)
|
||||
expect(percentageOfLetter('aAabcd', 'a')).toEqual(50)
|
||||
})
|
||||
test('Throwing an exception', () => {
|
||||
expect(() => percentageOfLetter(100, 'string')).toThrow()
|
||||
expect(() => percentageOfLetter('string', true)).toThrow()
|
||||
})
|
||||
})
|
8
String/test/ZFunction.test.js
Normal file
8
String/test/ZFunction.test.js
Normal file
@ -0,0 +1,8 @@
|
||||
import zFunction from '../ZFunction'
|
||||
|
||||
test('Testing zFunction', () => {
|
||||
expect(zFunction('aabxaayaab')).toEqual([10, 1, 0, 0, 2, 1, 0, 3, 1, 0])
|
||||
expect(zFunction('aabxaabxcaabxaabxay')).toEqual([
|
||||
19, 1, 0, 0, 4, 1, 0, 0, 0, 8, 1, 0, 0, 5, 1, 0, 0, 1, 0
|
||||
])
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
/*
|
||||
* Author: Mohit Kumar
|
||||
* Fedwick Tree Implementation in JavaScript
|
||||
* Fedwick Tree Implementation for finding prefix sum.
|
||||
* Fenwick Tree Implementation in JavaScript
|
||||
* Fenwick Tree Implementation for finding prefix sum.
|
||||
*/
|
||||
|
||||
class FenwickTree {
|
||||
|
2
package-lock.json
generated
2
package-lock.json
generated
@ -23,7 +23,7 @@
|
||||
"standard": "^17.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
"node": ">=16.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
|
@ -4,7 +4,8 @@
|
||||
"type": "module",
|
||||
"description": "A repository for All algorithms implemented in Javascript (for educational purposes only)",
|
||||
"scripts": {
|
||||
"test": "jest --no-cache",
|
||||
"test": "jest",
|
||||
"test-changed": "jest --onlyChanged",
|
||||
"style": "standard",
|
||||
"prepare": "husky install"
|
||||
},
|
||||
|
Reference in New Issue
Block a user