├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug_report.yml
│ └── feature_request.yml
└── workflows
│ ├── ci.yml
│ ├── directory_formatter.yml
│ └── upload_coverage_report.yml
├── .gitignore
├── .gitpod.yml
├── .prettierignore
├── .prettierrc
├── CONTRIBUTING.md
├── DIRECTORY.md
├── LICENSE
├── README.md
├── babel.config.js
├── backtracking
├── all_combinations_of_size_k.ts
├── generateparentheses.ts
└── test
│ ├── all_combinations_of_size_k.test.ts
│ └── generateparentheses.test.ts
├── bit_manipulation
├── add_binary.ts
├── is_power_of_2.ts
├── is_power_of_4.ts
├── log_two.ts
└── test
│ ├── add_binary.test.ts
│ ├── is_power_of_2.test.ts
│ ├── is_power_of_4.test.ts
│ └── log_two.test.ts
├── ciphers
├── test
│ └── xor_cipher.test.ts
└── xor_cipher.ts
├── data_structures
├── disjoint_set
│ ├── disjoint_set.ts
│ └── test
│ │ └── disjoint_set.test.ts
├── heap
│ ├── heap.ts
│ └── test
│ │ └── heap.test.ts
├── list
│ ├── doubly_linked_list.ts
│ ├── linked_list.ts
│ ├── singly_linked_list.ts
│ └── test
│ │ ├── doubly_linked_list.test.ts
│ │ ├── linked_list.ts
│ │ └── singly_linked_list.test.ts
├── map
│ ├── hash_map.ts
│ ├── map.ts
│ └── test
│ │ └── hash_map.test.ts
├── queue
│ ├── array_queue.ts
│ ├── circular_queue.ts
│ ├── linked_queue.ts
│ ├── queue.ts
│ ├── stack_queue.ts
│ └── test
│ │ ├── array_queue.test.ts
│ │ ├── circular_queue.test.ts
│ │ ├── linked_queue.test.ts
│ │ ├── queue.ts
│ │ └── stack_queue.test.ts
├── set
│ ├── hash_map_set.ts
│ ├── map_set.ts
│ └── set.ts
├── stack
│ ├── linked_list_stack.ts
│ ├── stack.ts
│ └── test
│ │ ├── linked_list_stack.test.ts
│ │ └── stack.test.ts
├── tree
│ ├── binary_search_tree.ts
│ └── test
│ │ └── binary_search_tree.test.ts
└── tries
│ ├── test
│ └── tries.test.ts
│ └── tries.ts
├── dynamic_programming
├── coin_change.ts
├── knapsack.ts
├── lcs.ts
└── test
│ ├── coin_change.test.ts
│ ├── knapsack.test.ts
│ └── lcs.test.ts
├── graph
├── bellman_ford.ts
├── bipartite_graph.ts
├── dijkstra.ts
├── edmonds_karp.ts
├── floyd_warshall.ts
├── johnson.ts
├── kosajaru.ts
├── kruskal.ts
├── prim.ts
├── tarjan.ts
└── test
│ ├── bellman_ford.test.ts
│ ├── bipartite_graph.test.ts
│ ├── dijkstra.test.ts
│ ├── edmonds_karp.test.ts
│ ├── floyd_warshall.test.ts
│ ├── johnson.test.ts
│ ├── kosajaru.test.ts
│ ├── kruskal.test.ts
│ ├── prim.test.ts
│ └── tarjan.test.ts
├── jest.config.ts
├── maths
├── absolute_value.ts
├── aliquot_sum.ts
├── armstrong_number.ts
├── binary_convert.ts
├── binomial_coefficient.ts
├── calculate_mean.ts
├── calculate_median.ts
├── degrees_to_radians.ts
├── digit_sum.ts
├── double_factorial_iterative.ts
├── euler_totient.ts
├── factorial.ts
├── factors.ts
├── fibonacci.ts
├── find_min.ts
├── gaussian_elimination.ts
├── greatest_common_factor.ts
├── hamming_distance.ts
├── is_divisible.ts
├── is_even.ts
├── is_leap_year.ts
├── is_odd.ts
├── is_palindrome.ts
├── is_square_free.ts
├── juggler_sequence.ts
├── lowest_common_multiple.ts
├── matrix_multiplication.ts
├── number_of_digits.ts
├── pascals_triangle.ts
├── perfect_cube.ts
├── perfect_number.ts
├── perfect_square.ts
├── prime_factorization.ts
├── primes.ts
├── pronic_number.ts
├── radians_to_degrees.ts
├── series
│ ├── hexagonal_numbers.ts
│ └── test
│ │ └── hexagonal_numbers.test.ts
├── sieve_of_eratosthenes.ts
├── signum.ts
├── square_root.ts
├── test
│ ├── absolute_value.test.ts
│ ├── aliquot_sum.test.ts
│ ├── armstrong_number.test.ts
│ ├── binary_convert.test.ts
│ ├── binomial_coefficient.test.ts
│ ├── calculate_mean.test.ts
│ ├── calculate_median.test.ts
│ ├── degrees_to_radians.test.ts
│ ├── digit_sum.test.ts
│ ├── double_factorial_iterative.test.ts
│ ├── euler_totient.test.ts
│ ├── factorial.test.ts
│ ├── factors.test.ts
│ ├── fibonacci.test.ts
│ ├── find_min.test.ts
│ ├── gaussian_elimination.test.ts
│ ├── greatest_common_factor.test.ts
│ ├── hamming_distance.test.ts
│ ├── is_divisible.test.ts
│ ├── is_even.test.ts
│ ├── is_leap_year.test.ts
│ ├── is_odd.test.ts
│ ├── is_palindrome.test.ts
│ ├── is_square_free.test.ts
│ ├── juggler_sequence.test.ts
│ ├── lowest_common_multiple.test.ts
│ ├── matrix_multiplication.test.ts
│ ├── number_of_digits.test.ts
│ ├── pascals_triangle.test.ts
│ ├── perfect_cube.test.ts
│ ├── perfect_numbers.test.ts
│ ├── perfect_square.test.ts
│ ├── prime_factorization.test.ts
│ ├── primes.test.ts
│ ├── pronic_number.test.ts
│ ├── radians_to_degrees.test.ts
│ ├── sieve_of_eratosthenes.test.ts
│ ├── signum.test.ts
│ ├── square_root.test.ts
│ ├── ugly_numbers.test.ts
│ └── zellers_congruence.test.ts
├── ugly_numbers.ts
└── zellers_congruence.ts
├── other
├── is_sorted_array.ts
├── parse_nested_brackets.ts
├── shuffle_array.ts
└── test
│ ├── is_sorted_array.test.ts
│ ├── parse_nested_brackets.test.ts
│ └── shuffle_array.test.ts
├── package-lock.json
├── package.json
├── search
├── binary_search.ts
├── exponential_search.ts
├── fibonacci_search.ts
├── interpolation_search.ts
├── jump_search.ts
├── linear_search.ts
├── sentinel_search.ts
└── test
│ ├── binary_search.test.ts
│ ├── exponential_search.test.ts
│ ├── fibonacci_search.test.ts
│ ├── interpolation_search.test.ts
│ ├── jump_search.test.ts
│ ├── linear_search.test.ts
│ └── sentinel_search.test.ts
├── sorts
├── bogo_sort.ts
├── bubble_sort.ts
├── counting_sort.ts
├── cycle_sort.ts
├── gnome_sort.ts
├── heap_sort.ts
├── insertion_sort.ts
├── merge_sort.ts
├── quick_select.ts
├── quick_sort.ts
├── selection_sort.ts
├── shell_sort.ts
├── swap_sort.ts
├── test
│ ├── bogo_sort.test.ts
│ ├── bubble_sort.test.ts
│ ├── counting_sort.test.ts
│ ├── cycle_sort.test.ts
│ ├── gnome_sort.test.ts
│ ├── heap_sort.test.ts
│ ├── insertion_sort.test.ts
│ ├── merge_sort.test.ts
│ ├── quick_select.test.ts
│ ├── quick_sort.test.ts
│ ├── selection_sort.test.ts
│ ├── shell_sort.test.ts
│ ├── swap_sort.test.ts
│ └── tree_sort.test.ts
└── tree_sort.ts
└── tsconfig.json
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @raklaptudirm @appgurueu
2 | *.md @Panquesito7
3 | /.github @Panquesito7
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug report
2 | description: "Create a report to help us improve"
3 | title: "[BUG]: "
4 | labels: ["bug"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | 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/TypeScript/blob/master/CONTRIBUTING.md)."
9 | - type: textarea
10 | id: description
11 | attributes:
12 | label: Description
13 | description: Explain what the problem is.
14 | validations:
15 | required: true
16 | - type: textarea
17 | id: expectedbhv
18 | attributes:
19 | label: Expected Behavior
20 | description: Describe what was the expected behavior.
21 | validations:
22 | required: true
23 | - type: textarea
24 | id: actualbhv
25 | attributes:
26 | label: Actual Behavior
27 | description: Describe what actually happens.
28 | validations:
29 | required: true
30 | - type: textarea
31 | id: steps
32 | attributes:
33 | label: Steps to reproduce (if applicable)
34 | description: List steps to reproduce the behavior.
35 | placeholder: |
36 | 1.
37 | 2.
38 | 3.
39 | 4.
40 | validations:
41 | required: false
42 | - type: textarea
43 | id: specifications
44 | attributes:
45 | label: Specifications
46 | description: Specify version, platform, and web browser (if applicable).
47 | placeholder: |
48 | 1. Version:
49 | 2. Platform:
50 | 3. Browser:
51 | validations:
52 | required: false
53 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature request
2 | description: "Suggest features, propose improvements, discuss new ideas"
3 | title: "[FEATURE]: "
4 | labels: ["enhancement"]
5 | body:
6 | - type: markdown
7 | attributes:
8 | value: |
9 | ## This issue template is not for requesting new algorithms. For new algorithms, PRs should be opened directly.
10 | ## Make sure your issue isn't a duplicate and you follow our [contributing guidelines](https://github.com/TheAlgorithms/TypeScript/blob/master/CONTRIBUTING.md)
11 | - type: textarea
12 | id: description
13 | attributes:
14 | label: Motivation
15 | description: Describe what is the motivation behind this feature.
16 | validations:
17 | required: true
18 | - type: textarea
19 | id: examples
20 | attributes:
21 | label: Examples
22 | description: If possible, provide examples of how this feature can be used.
23 | validations:
24 | required: false
25 | - type: textarea
26 | id: workarounds
27 | attributes:
28 | label: Possible workarounds
29 | description: If possible, describes possible workarounds to this feature.
30 | validations:
31 | required: false
32 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches: [master]
6 | pull_request:
7 | branches: [master]
8 |
9 | jobs:
10 | test:
11 | runs-on: ubuntu-22.04
12 | steps:
13 | - uses: actions/checkout@v3
14 | - uses: actions/setup-node@v3
15 | with:
16 | node-version: "18.x"
17 | - run: npm ci
18 | - run: npm test
19 | - run: npm run check-style
20 |
--------------------------------------------------------------------------------
/.github/workflows/directory_formatter.yml:
--------------------------------------------------------------------------------
1 | name: Directory/Filename Formatter workflow
2 | on: [push, pull_request]
3 |
4 | jobs:
5 | main:
6 | name: (Directory) Formatter
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@main
10 | - name: Setup Git configuration
11 | run: |
12 | git config --global user.name 'autoprettier'
13 | git config --global user.email 'actions@github.com'
14 | git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
15 | - name: Filename Formatter
16 | run: |
17 | IFS=$'\n'
18 | for fname in `find . -type f -name '*.ts' -o -name '*.ts'`
19 | do
20 | echo "${fname}"
21 | new_fname=`echo ${fname} | tr ' ' '_'`
22 | echo " ${new_fname}"
23 | new_fname=`echo ${new_fname} | tr 'A-Z' 'a-z'`
24 | echo " ${new_fname}"
25 | new_fname=`echo ${new_fname} | tr '-' '_'`
26 | echo " ${new_fname}"
27 | if [ ${fname} != ${new_fname} ]
28 | then
29 | echo " ${fname} --> ${new_fname}"
30 | git "mv" "${fname}" ${new_fname}
31 | fi
32 | done
33 | git commit -am "Formatting filenames ${GITHUB_SHA::8}" || true
34 | - name: Update DIRECTORY.md
35 | run: |
36 | wget https://raw.githubusercontent.com/TheAlgorithms/scripts/main/build_directory_md.py
37 | python3 build_directory_md.py TypeScript . .ts jest.config.ts,sorts/test,search/test,maths/test,dynamic_programming/test,data_structures/test,ciphers/test > DIRECTORY.md
38 |
39 | git diff
40 | git commit -m "Update DIRECTORY.md" DIRECTORY.md || true
41 | git push --force origin HEAD:$GITHUB_REF || true
42 |
--------------------------------------------------------------------------------
/.github/workflows/upload_coverage_report.yml:
--------------------------------------------------------------------------------
1 | ---
2 | name: upload_coverage_report
3 |
4 | 'on':
5 | workflow_dispatch:
6 | push:
7 | branches:
8 | - master
9 | pull_request:
10 |
11 | jobs:
12 | upload_coverage_report:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 |
17 | - uses: actions/setup-node@v4
18 | with:
19 | node-version: "18.x"
20 |
21 | - name: Install dependencies
22 | run: npm ci
23 |
24 | - name: Generate coverage report
25 | run: npm test -- --coverage
26 |
27 | - name: Upload coverage to codecov
28 | uses: codecov/codecov-action@v3
29 | with:
30 | files: "coverage/coverage-final.json"
31 | fail_ci_if_error: true
32 | ...
33 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # dependencies
2 | /node_modules
3 |
4 | # misc
5 | .DS_Store
6 | .env.local
7 | .env.development.local
8 | .env.test.local
9 | .env.production.local
10 |
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 |
15 | # intelliJ workspace folder
16 | .idea
17 |
18 | /coverage
19 |
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | tasks:
2 | - init: |
3 | npm install && npm test
4 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | .github
2 | *.md
3 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "arrowParens": "always",
3 | "bracketSpacing": true,
4 | "endOfLine": "lf",
5 | "insertPragma": false,
6 | "printWidth": 80,
7 | "proseWrap": "preserve",
8 | "quoteProps": "as-needed",
9 | "requirePragma": false,
10 | "semi": false,
11 | "singleQuote": true,
12 | "tabWidth": 2,
13 | "trailingComma": "none",
14 | "useTabs": false
15 | }
16 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 The Algorithms
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # The Algorithms - TypeScript
2 |
3 |
4 | TypeScript Repository of TheAlgorithms, which implements various algorithms and data structures in TypeScript.
5 |
6 |
7 |
8 | [![TypeScript Banner][banner]](DIRECTORY.md)
9 |
10 | [](https://codecov.io/gh/TheAlgorithms/TypeScript)
11 | [![Contributions Welcome][welcome]](CONTRIBUTING.md)
12 | [![Discord chat][chat]][discord-server]
13 |
14 |
15 |
16 |
17 |
18 | ---
19 |
20 |
21 |
22 |
23 | These implementations are for demonstrative/educational purposes only. Dedicated implementations of these algorithms and data
24 | structures are much better for performance and security reasons. We also do not provide any guarantee for API stability.
25 |
26 |
27 | ---
28 |
29 |
30 |
31 | Before contributing to this repository, make sure to read our [Contribution Guidelines](CONTRIBUTING.md). You can look
32 | at other [TheAlgorithms Repositories][repositories] or the [issues with a "help wanted" label][help-wanted] for
33 | inspiration regarding what to implement. Our maintainers will guide you through how to make your contribution properly
34 | if you make any mistakes. The names of the maintainers of this repository are listed in the
35 | [CODEOWNERS file](.github/CODEOWNERS).
36 |
37 | ---
38 |
39 |
40 | [banner]: https://user-images.githubusercontent.com/68542775/188368881-da2b9676-417a-4bbe-b8b7-20a497246f30.jpg
41 |
42 |
43 | [chat]: https://img.shields.io/discord/808045925556682782.svg?logo=discord&colorB=7289DA
44 | [welcome]: https://img.shields.io/static/v1.svg?label=Contributions&message=Welcome&color=0059b3
45 |
46 |
47 | [discord-server]: https://the-algorithms.com/discord/
48 | [actions]: https://github.com/TheAlgorithms/TypeScript/actions
49 | [repositories]: https://github.com/orgs/TheAlgorithms/repositories
50 | [help-wanted]: https://github.com/TheAlgorithms/TypeScript/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22
51 |
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | ['@babel/preset-env', { targets: { node: 'current' } }],
4 | '@babel/preset-typescript'
5 | ]
6 | }
7 |
--------------------------------------------------------------------------------
/backtracking/all_combinations_of_size_k.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This generates an array of unique sub"sets" (represented by ascendingly sorted subarrays)
3 | * of size k out of n+1 numbers from 1 to n.
4 | *
5 | * By using a backtracking algorithm we can incrementally build sub"sets" while dropping candidates
6 | * that cannot contribute anymore to a valid solution.
7 | * Steps:
8 | * - From the starting number (i.e. "1") generate all combinations of k numbers.
9 | * - Once we got all combinations for the given number we can discard it (“backtracks”)
10 | * and repeat the same process for the next number.
11 | */
12 | export function generateCombinations(n: number, k: number): number[][] {
13 | const combinationsAcc: number[][] = []
14 | const currentCombination: number[] = []
15 |
16 | function generateAllCombos(
17 | n: number,
18 | k: number,
19 | startCursor: number
20 | ): number[][] {
21 | if (k === 0) {
22 | if (currentCombination.length > 0) {
23 | combinationsAcc.push(currentCombination.slice())
24 | }
25 | return combinationsAcc
26 | }
27 |
28 | const endCursor = n - k + 2
29 | for (let i = startCursor; i < endCursor; i++) {
30 | currentCombination.push(i)
31 | generateAllCombos(n, k - 1, i + 1)
32 | currentCombination.pop()
33 | }
34 | return combinationsAcc
35 | }
36 |
37 | return generateAllCombos(n, k, 1)
38 | }
39 |
--------------------------------------------------------------------------------
/backtracking/generateparentheses.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Given a number n pairs of parentheses, generate all combinations of valid parentheses
3 | * @param {number} n: Number of given parentheses
4 | * @return {string[]} result: Array that contains all valid parentheses
5 | * @see https://leetcode.com/problems/generate-parentheses/
6 | */
7 |
8 | const generateParentheses = (n: number): string[] => {
9 | const result: string[] = []
10 |
11 | const solve = (
12 | chars: string,
13 | openParentheses: number,
14 | closedParentheses: number
15 | ) => {
16 | if (openParentheses === n && closedParentheses === n) {
17 | result.push(chars)
18 | return
19 | }
20 |
21 | if (openParentheses <= n) {
22 | solve(chars + '(', openParentheses + 1, closedParentheses)
23 | }
24 |
25 | if (closedParentheses < openParentheses) {
26 | solve(chars + ')', openParentheses, closedParentheses + 1)
27 | }
28 | }
29 |
30 | solve('', 0, 0)
31 |
32 | return result
33 | }
34 |
35 | export { generateParentheses }
36 |
--------------------------------------------------------------------------------
/backtracking/test/all_combinations_of_size_k.test.ts:
--------------------------------------------------------------------------------
1 | import { generateCombinations } from '../all_combinations_of_size_k'
2 |
3 | const cases = [
4 | [
5 | 3,
6 | 2,
7 | [
8 | [1, 2],
9 | [1, 3],
10 | [2, 3]
11 | ]
12 | ],
13 | [
14 | 4,
15 | 2,
16 | [
17 | [1, 2],
18 | [1, 3],
19 | [1, 4],
20 | [2, 3],
21 | [2, 4],
22 | [3, 4]
23 | ]
24 | ],
25 | [0, 0, []],
26 | [2, 3, []]
27 | ] as const
28 |
29 | describe('AllCombinationsOfSizeK', () => {
30 | it.each(cases)(
31 | 'create all combinations given n=%p and k=%p',
32 | (n, k, expectedCombos) => {
33 | const combinations = generateCombinations(n, k)
34 | expect(combinations).toEqual(expectedCombos)
35 | }
36 | )
37 | })
38 |
--------------------------------------------------------------------------------
/backtracking/test/generateparentheses.test.ts:
--------------------------------------------------------------------------------
1 | import { generateParentheses } from '../generateparentheses'
2 |
3 | const cases: [number, string[]][] = [
4 | [0, ['']],
5 | [1, ['()']],
6 | [2, ['(())', '()()']],
7 | [3, ['((()))', '(()())', '(())()', '()(())', '()()()']],
8 | [
9 | 4,
10 | [
11 | '(((())))',
12 | '((()()))',
13 | '((())())',
14 | '((()))()',
15 | '(()(()))',
16 | '(()()())',
17 | '(()())()',
18 | '(())(())',
19 | '(())()()',
20 | '()((()))',
21 | '()(()())',
22 | '()(())()',
23 | '()()(())',
24 | '()()()()'
25 | ]
26 | ],
27 | [
28 | 5,
29 | [
30 | '((((()))))',
31 | '(((()())))',
32 | '(((())()))',
33 | '(((()))())',
34 | '(((())))()',
35 | '((()(())))',
36 | '((()()()))',
37 | '((()())())',
38 | '((()()))()',
39 | '((())(()))',
40 | '((())()())',
41 | '((())())()',
42 | '((()))(())',
43 | '((()))()()',
44 | '(()((())))',
45 | '(()(()()))',
46 | '(()(())())',
47 | '(()(()))()',
48 | '(()()(()))',
49 | '(()()()())',
50 | '(()()())()',
51 | '(()())(())',
52 | '(()())()()',
53 | '(())((()))',
54 | '(())(()())',
55 | '(())(())()',
56 | '(())()(())',
57 | '(())()()()',
58 | '()(((())))',
59 | '()((()()))',
60 | '()((())())',
61 | '()((()))()',
62 | '()(()(()))',
63 | '()(()()())',
64 | '()(()())()',
65 | '()(())(())',
66 | '()(())()()',
67 | '()()((()))',
68 | '()()(()())',
69 | '()()(())()',
70 | '()()()(())',
71 | '()()()()()'
72 | ]
73 | ]
74 | ]
75 |
76 | describe('Generate Parentheses', () => {
77 | test.each(cases)(
78 | 'generate all valid parentheses of input %n',
79 | (n: number, expected: string[]) => {
80 | expect(generateParentheses(n)).toStrictEqual(expected)
81 | }
82 | )
83 | })
84 |
--------------------------------------------------------------------------------
/bit_manipulation/add_binary.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Adds two binary strings and returns the result as a binary string.
3 | *
4 | * @param firstBinaryNo - The first binary string.
5 | * @param secondBinaryNo - The second binary string.
6 | * @returns The binary sum of the input strings.
7 | */
8 | export function addBinary(
9 | firstBinaryNo: string,
10 | secondBinaryNo: string
11 | ): string {
12 | let lengthOfFirstNumber: number = firstBinaryNo.length - 1
13 | let lengthOfSecondNumber: number = secondBinaryNo.length - 1
14 | const solution: string[] = []
15 | let carry: number = 0
16 |
17 | while (lengthOfFirstNumber >= 0 || lengthOfSecondNumber >= 0) {
18 | let sum: number = carry
19 | if (lengthOfFirstNumber >= 0)
20 | sum += parseInt(firstBinaryNo.charAt(lengthOfFirstNumber))
21 | if (lengthOfSecondNumber >= 0)
22 | sum += parseInt(secondBinaryNo.charAt(lengthOfSecondNumber))
23 | solution.push((sum % 2).toString())
24 | carry = Math.floor(sum / 2)
25 | lengthOfFirstNumber--
26 | lengthOfSecondNumber--
27 | }
28 |
29 | if (carry !== 0) solution.push(carry.toString())
30 |
31 | return solution.reverse().join('')
32 | }
33 |
--------------------------------------------------------------------------------
/bit_manipulation/is_power_of_2.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This code will check whether the given number is a power of two or not.
3 | * @author dev-madhurendra
4 | * @explanation
5 |
6 | A number will be a power of two if only one bit is set and rest are unset.
7 | This is true for all the cases except 01 because (2^0 = 1) which is not a power of 2.
8 | For eg: 10 (2^1 = 2), 100 (2^2 = 4), 10000 (2^4 = 16)
9 |
10 | @see: https://www.hackerearth.com/practice/notes/round-a-number-to-the-next-power-of-2/
11 |
12 | If we will subtract 1 from a number that is a power of 2 we will get it's
13 | 1's complement.And we know that 1's complement is just opp. of that number.
14 | So, (n & (n-1)) will be 0.
15 |
16 | For eg: (1000 & (1000-1))
17 | 1 0 0 0 // Original Number (8)
18 | 0 1 1 1 // After Subtracting 1 (8-1 = 7)
19 | _______
20 | 0 0 0 0 // will become 0
21 | * @param {number}
22 | * @returns {boolean}
23 | */
24 |
25 | export const isPowerOfTwo = (n: number): boolean => n > 0 && (n & (n - 1)) === 0
26 |
--------------------------------------------------------------------------------
/bit_manipulation/is_power_of_4.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author : dev-madhurendra
3 | * Checks whether the given number is a power of four or not.
4 | *
5 | * A number is considered a power of four if and only if there is a single '1' bit in its binary representation,
6 | * and that '1' bit is at the first position, followed by an even number of '0' bits.
7 | *
8 | * @param {number} n - The input number to check.
9 | * @returns {boolean} True if the number is a power of four, false otherwise.
10 | *
11 | * @example
12 | * const result = isPowerOfFour(16); // Returns true (16 is 4^2)
13 | * const result2 = isPowerOfFour(5); // Returns false (5 is not a power of four)
14 | */
15 | export const isPowerOfFour = (n: number): boolean =>
16 | n > 0 && (n & (n - 1)) === 0 && n % 3 === 1
17 |
--------------------------------------------------------------------------------
/bit_manipulation/log_two.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dev-madhurendra
3 | * @see https://handwiki.org/wiki/Binary_logarithm
4 | * Approximate log2 using bitwise operators
5 | * @param {number} n
6 | * @returns {number} Log2 approximation equal to floor(log2(n))
7 | */
8 | export const logTwo = (n: number): number => {
9 | let result = 0
10 | while (n >> 1) {
11 | n >>= 1
12 | result++
13 | }
14 | return result
15 | }
16 |
--------------------------------------------------------------------------------
/bit_manipulation/test/add_binary.test.ts:
--------------------------------------------------------------------------------
1 | import { addBinary } from '../add_binary'
2 |
3 | describe('Add Binary Number', () => {
4 | it('should add two binary numbers with no carry', () => {
5 | const result = addBinary('1101', '1011')
6 | expect(result).toBe('11000')
7 | })
8 |
9 | it('should add two binary numbers with carry', () => {
10 | const result = addBinary('1111', '1111')
11 | expect(result).toBe('11110')
12 | })
13 |
14 | it('should add two different-length binary numbers', () => {
15 | const result = addBinary('1101', '111')
16 | expect(result).toBe('10100')
17 | })
18 |
19 | it('should add two empty binary numbers', () => {
20 | const result = addBinary('', '')
21 | expect(result).toBe('')
22 | })
23 |
24 | it('should add one empty binary number to a non-empty number', () => {
25 | const result = addBinary('1010', '')
26 | expect(result).toBe('1010')
27 | })
28 |
29 | it('should add one non-empty binary number to an empty number', () => {
30 | const result = addBinary('', '1101')
31 | expect(result).toBe('1101')
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/bit_manipulation/test/is_power_of_2.test.ts:
--------------------------------------------------------------------------------
1 | import { isPowerOfTwo } from '../is_power_of_2'
2 |
3 | describe('IsPowerOfTwo', () => {
4 | it.each([
5 | [0, false],
6 | [1, true],
7 | [4, true],
8 | [1024, true],
9 | [1025, false]
10 | ])('Check if %i is a power of 2 or not', (number, expected) => {
11 | expect(isPowerOfTwo(number)).toBe(expected)
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/bit_manipulation/test/is_power_of_4.test.ts:
--------------------------------------------------------------------------------
1 | import { isPowerOfFour } from '../is_power_of_4'
2 |
3 | describe('IsPowerOfFour', () => {
4 | it.each([
5 | [0, false],
6 | [4, true],
7 | [16, true],
8 | [12, false],
9 | [64, true],
10 | [-64, false]
11 | ])('should return the number %i is power of four or not', (n, expected) => {
12 | expect(isPowerOfFour(n)).toBe(expected)
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/bit_manipulation/test/log_two.test.ts:
--------------------------------------------------------------------------------
1 | import { logTwo } from '../log_two'
2 |
3 | describe('LogTwoTests', () => {
4 | test.each([...Array(100).keys()].map((i) => [i + 1]))('log2(%i)', (input) => {
5 | expect(logTwo(input)).toBe(Math.floor(Math.log2(input)))
6 | })
7 | })
8 |
--------------------------------------------------------------------------------
/ciphers/test/xor_cipher.test.ts:
--------------------------------------------------------------------------------
1 | import { XORCipher } from '../xor_cipher'
2 |
3 | describe('Testing XORCipher function', () => {
4 | it('passing a string & number as an argument', () => {
5 | expect(XORCipher('test', 32)).toBe('TEST')
6 | expect(XORCipher('TEST', 32)).toBe('test')
7 | })
8 | })
9 |
--------------------------------------------------------------------------------
/ciphers/xor_cipher.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function XORCipher
3 | * @description - Encrypt using an XOR cipher
4 | * The XOR cipher is a type of additive cipher.
5 | * Each character is bitwise XORed with the key.
6 | * We loop through the input string, XORing each
7 | * character with the key.
8 | * @param {string} str - string to be encrypted
9 | * @param {number} key - key for encryption
10 | * @return {string} encrypted string
11 | */
12 | export const XORCipher = (str: string, key: number): string =>
13 | str.replace(/./g, (char: string) =>
14 | String.fromCharCode(char.charCodeAt(0) ^ key)
15 | )
16 |
--------------------------------------------------------------------------------
/data_structures/disjoint_set/disjoint_set.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A Disjoint Set is a data structure that keeps track of a set of elements
3 | * partitioned into a number of disjoint (non-overlapping) subsets.
4 | * Elements are uniquely represented by an index (0-based).
5 | *
6 | * The find operation uses path compression.
7 | * This allows the time complexity of the find operation be O(alpha(n)).
8 | * alpha(n) being the inverse Ackermann function.
9 | *
10 | * The join operation uses union by size: The smaller set is joined to the bigger one.
11 | *
12 | * You can perform the following operations on the disjoint set:
13 | * - find: Determine which subset a particular element is in - O(alpha(n))
14 | * - join: Join two subsets into a single subset - O(1)
15 | * - isSame: Check if two elements are in the same subset - O(1)
16 | */
17 | export class DisjointSet {
18 | /** Direct parent for an element */
19 | private head: number[]
20 |
21 | /** Size of the subtree above an element */
22 | private size: number[]
23 |
24 | constructor(n: number) {
25 | // Initially each set has its own id element
26 | this.head = Array.from({ length: n }, (_, index) => index)
27 | this.size = Array(n).fill(1)
28 | }
29 |
30 | /**
31 | * Find the representative index for an element
32 | */
33 | find(index: number): number {
34 | if (this.head[index] != index) {
35 | // Use path compression (set an edge between the element and its head)
36 | this.head[index] = this.find(this.head[index])
37 | }
38 | return this.head[index]
39 | }
40 |
41 | /**
42 | * Join two sets
43 | */
44 | join(first: number, second: number): void {
45 | // Get the root of each set to join
46 | let firstHead = this.find(first)
47 | let secondHead = this.find(second)
48 |
49 | // If they're the same (same set)
50 | if (firstHead === secondHead) return
51 |
52 | // Keep the bigger set in firstHead
53 | if (this.size[firstHead] < this.size[secondHead]) {
54 | ;[firstHead, secondHead] = [secondHead, firstHead]
55 | }
56 |
57 | // Join the smallest set with the bigger one
58 | this.head[secondHead] = firstHead
59 |
60 | // Update size of the bigger set after join
61 | this.size[firstHead] += this.size[secondHead]
62 | }
63 |
64 | /**
65 | * Check whether two elements are in the same set
66 | */
67 | isSame(first: number, second: number): boolean {
68 | return this.find(first) === this.find(second)
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/data_structures/disjoint_set/test/disjoint_set.test.ts:
--------------------------------------------------------------------------------
1 | import { DisjointSet } from '../disjoint_set'
2 |
3 | describe('DisjointSet', () => {
4 | let ds: DisjointSet
5 |
6 | beforeEach(() => {
7 | // Ensure create a new DisjoinSet instance on every test
8 | ds = new DisjointSet(10)
9 | })
10 |
11 | it('should show proper head element after join', () => {
12 | expect(ds.find(0)).toEqual(0)
13 |
14 | ds.join(1, 4)
15 | ds.join(2, 3)
16 | expect(ds.isSame(1, 4)).toEqual(true)
17 | expect(ds.isSame(2, 3)).toEqual(true)
18 | expect(ds.isSame(1, 3)).toEqual(false)
19 |
20 | ds.join(4, 3)
21 | expect(ds.isSame(1, 3)).toEqual(true)
22 | expect(ds.isSame(2, 9)).toEqual(false)
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/data_structures/list/linked_list.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * An interface for linked lists, which shares the common methods.
3 | */
4 | export interface LinkedList {
5 | isEmpty(): boolean
6 | get(index: number): T | null | undefined
7 | push(data: T): void
8 | pop(): T | undefined
9 | append(data: T): void
10 | removeTail(): T | undefined
11 | insertAt(index: number, data: T): void
12 | removeAt(index: number): T | undefined
13 | clear(): void
14 | toArray(): (T | undefined)[]
15 | getLength(): number
16 | }
17 |
--------------------------------------------------------------------------------
/data_structures/list/test/doubly_linked_list.test.ts:
--------------------------------------------------------------------------------
1 | import { DoublyLinkedList } from '../doubly_linked_list'
2 | import { testLinkedList } from './linked_list'
3 |
4 | describe('DoublyLinkedList', () => {
5 | testLinkedList(DoublyLinkedList)
6 |
7 | it('should reverse the list', () => {
8 | const list: DoublyLinkedList = new DoublyLinkedList()
9 |
10 | list.append(1)
11 | list.append(2)
12 | list.append(3)
13 | list.reverse()
14 |
15 | expect(list.get(0)).toBe(3)
16 | expect(list.get(1)).toBe(2)
17 | })
18 |
19 | it('should return null for reverse when list is empty', () => {
20 | const list: DoublyLinkedList = new DoublyLinkedList()
21 |
22 | expect(list.reverse()).toBeNull()
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/data_structures/list/test/singly_linked_list.test.ts:
--------------------------------------------------------------------------------
1 | import { SinglyLinkedList } from '../singly_linked_list'
2 | import { testLinkedList } from './linked_list'
3 |
4 | describe('Singly linked list', () => testLinkedList(SinglyLinkedList))
5 |
--------------------------------------------------------------------------------
/data_structures/map/map.ts:
--------------------------------------------------------------------------------
1 | import { MapEntry } from './hash_map'
2 |
3 | /**
4 | * This interface is a representation of the Map data structure.
5 | */
6 | export interface Map {
7 | getSize(): number
8 | set(key: K, value: V): void
9 | get(key: K): V | null
10 | delete(key: K): void
11 | has(key: K): boolean
12 | clear(): void
13 | keys(): K[]
14 | values(): V[]
15 | entries(): MapEntry[]
16 | }
17 |
--------------------------------------------------------------------------------
/data_structures/queue/array_queue.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This is an array-based implementation of a Queue.
3 | * A Queue is a data structure that follows the FIFO (First In First Out) principle.
4 | * It means that the first element that was added to the queue will be the first one to be removed.
5 | * The time complexity of the operations is O(n).
6 | */
7 | import { Queue } from './queue'
8 | export class ArrayQueue implements Queue {
9 | private queue: T[] = []
10 |
11 | /**
12 | * Returns the number of items in the queue.
13 | *
14 | * @returns {number} The number of items in the queue.
15 | */
16 | length(): number {
17 | return this.queue.length
18 | }
19 |
20 | /**
21 | * Checks if the queue is empty.
22 | *
23 | * @returns {boolean} Whether the queue is empty or not.
24 | */
25 | isEmpty(): boolean {
26 | return this.queue.length === 0
27 | }
28 |
29 | /**
30 | * Adds an item to the queue.
31 | *
32 | * @param item The item being added to the queue.
33 | */
34 | enqueue(item: T): void {
35 | this.queue.push(item)
36 | }
37 |
38 | /**
39 | * Removes an item from the queue and returns it.
40 | *
41 | * @throws Queue Underflow if the queue is empty.
42 | * @returns The item that was removed from the queue.
43 | */
44 | dequeue(): T {
45 | if (this.isEmpty()) {
46 | throw new Error('Queue Underflow')
47 | }
48 |
49 | return this.queue.shift() as T
50 | }
51 |
52 | /**
53 | * Returns the item at the front of the queue.
54 | *
55 | * @returns The item at the front of the queue or null if the queue is empty.
56 | */
57 | peek(): T | null {
58 | if (this.isEmpty()) {
59 | return null
60 | }
61 |
62 | return this.queue[0]
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/data_structures/queue/linked_queue.ts:
--------------------------------------------------------------------------------
1 | import { Queue } from './queue'
2 |
3 | type Node = {
4 | value: T
5 | next?: Node
6 | }
7 |
8 | /**
9 | * This is a LinkedList-like implementation of a Queue,
10 | * allowing the operations to be implemented in constant time.
11 | * A Queue is a data structure that follows the FIFO (First-In First-Out) principle:
12 | * The first element that was added to the queue will be the first one to be removed.
13 | */
14 | export class LinkedQueue implements Queue {
15 | public size: number
16 | public head?: Node
17 | private tail?: Node
18 |
19 | constructor() {
20 | this.head = this.tail = undefined
21 | this.size = 0
22 | }
23 |
24 | /**
25 | * Adds an item to the queue.
26 | *
27 | * @param item The item being added to the queue.
28 | */
29 | enqueue(item: T): void {
30 | const node = { value: item } as Node // Creates a new node
31 | this.size++ // Increase the length of the Queue
32 |
33 | if (!this.tail) {
34 | this.tail = this.head = node
35 | return
36 | }
37 | this.tail.next = node // Updates the next tail to the node created
38 | this.tail = node // The tail of the Queue then becomes the node created!!
39 | }
40 |
41 | /**
42 | * Removes an item from the queue and returns it.
43 | *
44 | * @throws Queue Underflow if the queue is empty.
45 | * @returns The item that was removed from the queue.
46 | */
47 | dequeue(): T | undefined {
48 | if (!this.head) {
49 | throw new Error('Queue Underflow')
50 | }
51 |
52 | this.size--
53 | const head = this.head // We store the head in order not to lose track of it
54 | this.head = this.head.next // Update the the head to the next node
55 | return head.value // Return the value of the head
56 | }
57 |
58 | /**
59 | * Returns the item at the front of the queue.
60 | *
61 | * @returns The item at the front of the queue or null if the queue is empty.
62 | */
63 | peek(): T | undefined | null {
64 | if (this.isEmpty()) {
65 | return null
66 | }
67 | return this.head?.value
68 | }
69 |
70 | /**
71 | * Checks if the queue is empty.
72 | *
73 | * @returns {boolean} Whether the queue is empty or not.
74 | */
75 | isEmpty(): boolean {
76 | return this.size === 0
77 | }
78 |
79 | /**
80 | * Returns the number of items in the queue.
81 | *
82 | * @returns {number} The number of items in the queue.
83 | */
84 | length(): number {
85 | return this.size
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/data_structures/queue/queue.ts:
--------------------------------------------------------------------------------
1 | export interface Queue {
2 | enqueue(item: T): void
3 | dequeue(): T | undefined
4 | peek(): T | undefined | null
5 | isEmpty(): boolean
6 | length(): number
7 | }
8 |
--------------------------------------------------------------------------------
/data_structures/queue/test/array_queue.test.ts:
--------------------------------------------------------------------------------
1 | import { ArrayQueue } from '../array_queue'
2 | import { testQueue } from './queue'
3 |
4 | describe('Array Queue', () => testQueue(ArrayQueue))
5 |
--------------------------------------------------------------------------------
/data_structures/queue/test/circular_queue.test.ts:
--------------------------------------------------------------------------------
1 | import { CircularQueue } from '../circular_queue'
2 |
3 | describe('Circular Queue', () => {
4 | let queue: CircularQueue
5 |
6 | beforeEach(() => {
7 | queue = new CircularQueue(5)
8 | })
9 |
10 | it('should enqueue an element', () => {
11 | queue.enqueue(1)
12 |
13 | expect(queue.peek()).toBe(1)
14 | })
15 |
16 | it('should throw an error on enqueue when queue is full', () => {
17 | queue.enqueue(1)
18 | queue.enqueue(2)
19 | queue.enqueue(3)
20 | queue.enqueue(4)
21 | queue.enqueue(5)
22 |
23 | expect(() => queue.enqueue(6)).toThrowError('Queue is full')
24 | })
25 |
26 | it('should dequeue an element', () => {
27 | queue.enqueue(1)
28 | queue.enqueue(2)
29 |
30 | expect(queue.dequeue()).toBe(1)
31 | })
32 |
33 | it('should throw an error on dequeue when queue is empty', () => {
34 | expect(() => queue.dequeue()).toThrowError('Queue is empty')
35 | })
36 |
37 | it('should peek an element', () => {
38 | queue.enqueue(1)
39 | queue.enqueue(2)
40 |
41 | expect(queue.peek()).toBe(1)
42 | })
43 |
44 | it('should return null on peek when queue is empty', () => {
45 | expect(queue.peek()).toBeNull()
46 | })
47 |
48 | it('should return true on isEmpty when queue is empty', () => {
49 | expect(queue.isEmpty()).toBeTruthy()
50 | })
51 |
52 | it('should return false on isEmpty when queue is not empty', () => {
53 | queue.enqueue(1)
54 |
55 | expect(queue.isEmpty()).toBeFalsy()
56 | })
57 |
58 | it('should return the correct length', () => {
59 | queue.enqueue(1)
60 | queue.enqueue(2)
61 | queue.enqueue(3)
62 |
63 | expect(queue.length()).toBe(3)
64 | })
65 | })
66 |
--------------------------------------------------------------------------------
/data_structures/queue/test/linked_queue.test.ts:
--------------------------------------------------------------------------------
1 | import { testQueue } from './queue'
2 | import { LinkedQueue } from '../linked_queue'
3 |
4 | describe('Linked Queue', () => testQueue(LinkedQueue))
5 |
--------------------------------------------------------------------------------
/data_structures/queue/test/queue.ts:
--------------------------------------------------------------------------------
1 | import { Queue } from '../queue'
2 |
3 | type QueueConstructor = new () => Queue
4 | export function testQueue(Queue: QueueConstructor) {
5 | it('enqueue should add a new element to the queue', () => {
6 | const queue = new Queue()
7 | queue.enqueue(1)
8 | expect(queue.length()).toBe(1)
9 | })
10 |
11 | it('isEmpty should return true on empty queue', () => {
12 | const queue = new Queue()
13 | expect(queue.isEmpty()).toBeTruthy()
14 | })
15 |
16 | it('isEmpty should return false on not empty queue', () => {
17 | const queue = new Queue()
18 | queue.enqueue(1)
19 | expect(queue.isEmpty()).toBeFalsy()
20 | })
21 |
22 | it('front should return the first value', () => {
23 | const queue = new Queue()
24 | queue.enqueue(1)
25 | expect(queue.peek()).toBe(1)
26 | })
27 |
28 | it('front should return null when the queue is empty', () => {
29 | const queue = new Queue()
30 | expect(queue.peek()).toBe(null)
31 | })
32 |
33 | it('length should return the number of elements in the queue', () => {
34 | const queue = new Queue()
35 | queue.enqueue(1)
36 | queue.enqueue(1)
37 | queue.enqueue(1)
38 | expect(queue.length()).toBe(3)
39 | })
40 |
41 | it('dequeue should remove the first element', () => {
42 | const queue = new Queue()
43 | queue.enqueue(1)
44 | queue.enqueue(2)
45 | queue.enqueue(3)
46 | queue.dequeue()
47 | expect(queue.length()).toBe(2)
48 | })
49 |
50 | it('dequeue should throw error on empty queue', () => {
51 | const queue = new Queue()
52 | expect(() => queue.dequeue()).toThrow('Queue Underflow')
53 | })
54 | }
55 |
--------------------------------------------------------------------------------
/data_structures/queue/test/stack_queue.test.ts:
--------------------------------------------------------------------------------
1 | import { testQueue } from './queue'
2 | import { StackQueue } from '../stack_queue'
3 |
4 | describe('Stack Based Queue', () => testQueue(StackQueue))
5 |
--------------------------------------------------------------------------------
/data_structures/set/hash_map_set.ts:
--------------------------------------------------------------------------------
1 | import { Map } from '../map/map'
2 | import { HashMap } from '../map/hash_map'
3 | import { MapSet } from './map_set'
4 |
5 | /**
6 | * This class is a representation of the Set data structure based on a hash map.
7 | *
8 | * @template K The value type.
9 | * @extends MapSet
10 | */
11 | export class HashMapSet extends MapSet {
12 | constructor() {
13 | super()
14 | }
15 |
16 | /**
17 | * Initializes the map used to store the set.
18 | *
19 | * @returns {Map} The map used to store the set.
20 | */
21 | protected initMap(): Map {
22 | return new HashMap()
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/data_structures/set/map_set.ts:
--------------------------------------------------------------------------------
1 | import { Map } from './map'
2 | import { Set } from './set'
3 |
4 | /**
5 | * This class is a representation of the Set data structure based on a hash map.
6 | *
7 | * @template K The value type.
8 | * @implements Set
9 | * @property {Map} map The map used to store the set.
10 | */
11 | export abstract class MapSet implements Set {
12 | private map: Map
13 |
14 | constructor() {
15 | this.map = this.initMap()
16 | }
17 |
18 | /**
19 | * Initializes the map used to store the set.
20 | */
21 | protected abstract initMap(): Map
22 |
23 | /**
24 | * Adds a new element to the set.
25 | *
26 | * @param value The value to add to the set.
27 | */
28 | add(value: K): void {
29 | this.map.set(value, null)
30 | }
31 |
32 | /**
33 | * Removes an element from the set.
34 | *
35 | * @param value The value to remove from the set.
36 | */
37 | delete(value: K): void {
38 | this.map.delete(value)
39 | }
40 |
41 | /**
42 | * Checks if the set contains a given value.
43 | *
44 | * @param value The value to check for.
45 | * @returns Whether the set contains the value.
46 | */
47 | has(value: K): boolean {
48 | return this.map.has(value)
49 | }
50 |
51 | /**
52 | * Removes all elements from the set.
53 | */
54 | clear(): void {
55 | this.map.clear()
56 | }
57 |
58 | /**
59 | * Returns an array of all the values in the set.
60 | *
61 | * @returns An array of all the values in the set.
62 | */
63 | values(): K[] {
64 | return this.map.keys()
65 | }
66 |
67 | /**
68 | * Returns the number of elements in the set.
69 | *
70 | * @returns The number of elements in the set.
71 | */
72 | getSize(): number {
73 | return this.map.getSize()
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/data_structures/set/set.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * This interface is a representation of the Set data structure.
3 | */
4 | export interface Set {
5 | getSize(): number
6 | add(value: K): void
7 | delete(value: K): void
8 | has(value: K): boolean
9 | clear(): void
10 | values(): K[]
11 | }
12 |
--------------------------------------------------------------------------------
/data_structures/stack/linked_list_stack.ts:
--------------------------------------------------------------------------------
1 | import { SinglyLinkedList } from '../list/singly_linked_list'
2 |
3 | /**
4 | * This is an implementation of a stack, based on a linked list.
5 | * A stack is a linear data structure that works with the LIFO (Last-In-First-Out) principle.
6 | * A linked list is a linear data structure that works with the FIFO (First-In-First-Out) principle and uses references
7 | * to determine which element is next in the list.
8 | */
9 | export class LinkedListStack {
10 | private list: SinglyLinkedList
11 | private limit: number
12 |
13 | /**
14 | * Creates a new stack object.
15 | */
16 | constructor(limit: number = Number.MAX_VALUE) {
17 | this.list = new SinglyLinkedList()
18 | this.limit = limit
19 | }
20 |
21 | /**
22 | * Gets the top element of the stack.
23 | * Time complexity: constant (O(1))
24 | *
25 | * @returns The top element of the stack.
26 | */
27 | top(): T | null {
28 | if (this.list.isEmpty()) {
29 | return null
30 | }
31 |
32 | return this.list.get(0)!
33 | }
34 |
35 | /**
36 | * Inserts a new element on the top of the stack.
37 | * Time complexity: constant (O(1))
38 | *
39 | * @param data The data of the element to insert.
40 | * @throws Stack overflow, if the new element does not fit in the limit.
41 | */
42 | push(data: T): void {
43 | if (this.list.getLength() + 1 > this.limit) {
44 | throw new Error('Stack overflow')
45 | }
46 |
47 | this.list.push(data)
48 | }
49 |
50 | /**
51 | * Removes the top element from the stack.
52 | * Time complexity: constant (O(1))
53 | *
54 | * @returns The previous top element.
55 | * @throws Stack underflow, if the stack has no elements to pop.
56 | */
57 | pop(): T {
58 | if (this.list.isEmpty()) {
59 | throw new Error('Stack underflow')
60 | }
61 |
62 | return this.list.pop()
63 | }
64 |
65 | /**
66 | * Gets the amount of elements in the stack.
67 | *
68 | * @returns The amount of elements in the stack.
69 | */
70 | length(): number {
71 | return this.list.getLength()
72 | }
73 |
74 | /**
75 | * Gets whether the stack is empty or not.
76 | *
77 | * @returns Whether the stack is empty or not.
78 | */
79 | isEmpty(): boolean {
80 | return this.list.isEmpty()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/data_structures/stack/stack.ts:
--------------------------------------------------------------------------------
1 | /* Stack data-structure. It's work is based on the LIFO method (last-IN-first-OUT).
2 | * It means that elements added to the stack are placed on the top and only the
3 | * last element (from the top) can be reached. After we get access to the last
4 | * element, it pops from the stack.
5 | * This is a class-based implementation of a Stack.
6 | */
7 | export class Stack {
8 | private stack: T[] = []
9 | private limit: number
10 |
11 | /**
12 | * constructor of the stack, can set a limit, if not provided there is no limit to the stack.
13 | * @param {number} [limit=Number.MAX_VALUE] the limit of the stack
14 | */
15 | constructor(limit: number = Number.MAX_VALUE) {
16 | this.limit = limit
17 | }
18 |
19 | /**
20 | * @function push
21 | * @description - adds a new element to the stack
22 | * @param {T} value - the new value to add
23 | */
24 | push(value: T) {
25 | if (this.length() + 1 > this.limit) {
26 | throw new Error('Stack Overflow')
27 | }
28 |
29 | this.stack.push(value)
30 | }
31 |
32 | /**
33 | * @function pop
34 | * @description - remove an element from the top
35 | * @throws will throw an error if the stack is empty
36 | * @return {T} removed element
37 | */
38 | pop(): T {
39 | if (this.length() !== 0) {
40 | return this.stack.pop() as T
41 | }
42 |
43 | throw new Error('Stack Underflow')
44 | }
45 |
46 | /**
47 | * @function length
48 | * @description - number of elements in the stack
49 | * @return {number} the number of elements in the stack
50 | */
51 | length(): number {
52 | return this.stack.length
53 | }
54 |
55 | /**
56 | * @function isEmpty
57 | * @description - check if the stack is empty
58 | * @return {boolean} returns true if the stack is empty, otherwise false
59 | */
60 | isEmpty(): boolean {
61 | return this.length() === 0
62 | }
63 |
64 | /**
65 | * @function top
66 | * @description - return the last element in the stack without removing it
67 | * @return {T | null} return the last element or null if the stack is empty
68 | */
69 | top(): T | null {
70 | if (this.length() !== 0) {
71 | return this.stack[this.length() - 1]
72 | }
73 |
74 | return null
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/data_structures/stack/test/linked_list_stack.test.ts:
--------------------------------------------------------------------------------
1 | import { LinkedListStack } from '../linked_list_stack'
2 |
3 | describe('Linked List Stack', () => {
4 | const stack: LinkedListStack = new LinkedListStack(4)
5 |
6 | stack.push(1)
7 | stack.push(2)
8 | stack.push(3)
9 |
10 | it('should get the top element from the stack', () => {
11 | expect(stack.top()).toBe(3)
12 | })
13 |
14 | it('should remove the top element from the stack and give the new top element', () => {
15 | expect(stack.pop()).toBe(3)
16 | expect(stack.top()).toBe(2)
17 | })
18 |
19 | it('should add a new element on top', () => {
20 | expect(stack.push(4))
21 | })
22 |
23 | it('should fail to add the second element on top, because of a stack overflow', () => {
24 | stack.push(4)
25 | expect(() => stack.push(5)).toThrowError('Stack overflow')
26 | })
27 |
28 | it('should fail to pop the top element on an empty stack', () => {
29 | const s: LinkedListStack = new LinkedListStack()
30 | expect(() => s.pop()).toThrowError('Stack underflow')
31 | })
32 | })
33 |
--------------------------------------------------------------------------------
/data_structures/stack/test/stack.test.ts:
--------------------------------------------------------------------------------
1 | import { Stack } from '../stack'
2 |
3 | describe('Testing Stack data structure', () => {
4 | it('push should add a new element to the stack', () => {
5 | const stack = new Stack()
6 | stack.push(2)
7 |
8 | expect(stack.length()).toBe(1)
9 | })
10 |
11 | it('push should throw error on reach limit', () => {
12 | const stack = new Stack(2)
13 | stack.push(2)
14 | stack.push(3)
15 |
16 | expect(() => stack.push(4)).toThrow('Stack Overflow')
17 | })
18 |
19 | it('isEmpty should return true on empty stack', () => {
20 | const stack = new Stack()
21 | expect(stack.isEmpty()).toBeTruthy()
22 | })
23 |
24 | it('isEmpty should return false on not empty stack', () => {
25 | const stack = new Stack()
26 | stack.push(2)
27 |
28 | expect(stack.isEmpty()).toBeFalsy()
29 | })
30 |
31 | it('top should return the last value', () => {
32 | const stack = new Stack()
33 | stack.push(2)
34 |
35 | expect(stack.top()).toBe(2)
36 | })
37 |
38 | it('top should return null when the stack is empty', () => {
39 | const stack = new Stack()
40 |
41 | expect(stack.top()).toBe(null)
42 | })
43 |
44 | it('length should return the number of elements in the stack', () => {
45 | const stack = new Stack()
46 | stack.push(2)
47 | stack.push(2)
48 | stack.push(2)
49 |
50 | expect(stack.length()).toBe(3)
51 | })
52 |
53 | it('pop should remove the last element and return it', () => {
54 | const stack = new Stack()
55 | stack.push(1)
56 | stack.push(2)
57 | stack.push(3)
58 |
59 | expect(stack.pop()).toBe(3)
60 | expect(stack.length()).toBe(2)
61 | })
62 |
63 | it('pop should throw an exception if the stack is empty', () => {
64 | const stack = new Stack()
65 |
66 | expect(() => stack.pop()).toThrow('Stack Underflow')
67 | })
68 | })
69 |
--------------------------------------------------------------------------------
/data_structures/tree/test/binary_search_tree.test.ts:
--------------------------------------------------------------------------------
1 | import { BinarySearchTree } from '../binary_search_tree'
2 |
3 | describe('BinarySearchTree', () => {
4 | describe('with filled binary search tree (insert)', () => {
5 | let binarySearchTree: BinarySearchTree
6 |
7 | beforeEach(() => {
8 | binarySearchTree = new BinarySearchTree()
9 | binarySearchTree.insert(25)
10 | binarySearchTree.insert(80)
11 | binarySearchTree.insert(12)
12 | binarySearchTree.insert(5)
13 | binarySearchTree.insert(64)
14 | })
15 |
16 | it('should return false for isEmpty when binary search tree is not empty', () => {
17 | expect(binarySearchTree.isEmpty()).toBeFalsy()
18 | })
19 |
20 | it('should return correct root node for search', () => {
21 | expect(binarySearchTree.rootNode?.data).toBe(25)
22 | })
23 |
24 | it('should return whether an element is in the set', () => {
25 | expect(binarySearchTree.has(5)).toBe(true)
26 | expect(binarySearchTree.has(42)).toBe(false)
27 | })
28 |
29 | it('should traverse in in-order through the tree', () => {
30 | expect(binarySearchTree.inOrderTraversal()).toStrictEqual([
31 | 5, 12, 25, 64, 80
32 | ])
33 | })
34 |
35 | it('should traverse in pre-order through the tree', () => {
36 | console.log(binarySearchTree.preOrderTraversal())
37 |
38 | expect(binarySearchTree.preOrderTraversal()).toStrictEqual([
39 | 25, 12, 5, 80, 64
40 | ])
41 | })
42 |
43 | it('should traverse in post-order through the tree', () => {
44 | expect(binarySearchTree.postOrderTraversal()).toStrictEqual([
45 | 5, 12, 64, 80, 25
46 | ])
47 | })
48 |
49 | it('should return the minimum value of the binary search tree', () => {
50 | expect(binarySearchTree.findMin()).toBe(5)
51 | })
52 |
53 | it('should return the maximum value of the binary search tree', () => {
54 | expect(binarySearchTree.findMax()).toBe(80)
55 | })
56 | })
57 | })
58 |
--------------------------------------------------------------------------------
/data_structures/tries/test/tries.test.ts:
--------------------------------------------------------------------------------
1 | import { Trie } from '../tries'
2 |
3 | describe('Trie', () => {
4 | let trie: Trie
5 |
6 | beforeEach(() => {
7 | trie = new Trie()
8 | })
9 |
10 | it('should add and find a word', () => {
11 | trie.add('apple')
12 | expect(trie.find('apple')).toBe(true)
13 | })
14 |
15 | it('should not find a word that was not added', () => {
16 | trie.add('apple')
17 | expect(trie.find('banana')).toBe(false)
18 | })
19 |
20 | it('should not find a partial word', () => {
21 | trie.add('apple')
22 | expect(trie.find('app')).toBe(false)
23 | })
24 |
25 | it('should add and find multiple words', () => {
26 | trie.add('apple')
27 | trie.add('banana')
28 | trie.add('cherry')
29 | expect(trie.find('apple')).toBe(true)
30 | expect(trie.find('banana')).toBe(true)
31 | expect(trie.find('cherry')).toBe(true)
32 | })
33 |
34 | it('should find words with a common prefix', () => {
35 | trie.add('apple')
36 | trie.add('appetizer')
37 | expect(trie.find('app', true)).toBe(true)
38 | expect(trie.find('app', false)).toBe(false)
39 | })
40 | })
41 |
--------------------------------------------------------------------------------
/data_structures/tries/tries.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Represents a node in a Trie data structure.
3 | */
4 | class TrieNode {
5 | /**
6 | * An object that stores child nodes for each character in the alphabet.
7 | */
8 | children: Record = {}
9 |
10 | /**
11 | * Indicates whether the node represents the end of a word.
12 | */
13 | isWord = false
14 | }
15 |
16 | /**
17 | * Trie Data structure for storing and searching words.
18 | */
19 | export class Trie {
20 | /**
21 | * The root node of the Trie.
22 | */
23 | root: TrieNode = new TrieNode()
24 |
25 | /**
26 | * Inserts a word into the Trie.
27 | *
28 | * @param word - The word to insert into the Trie.
29 | */
30 | private insertNode(node: TrieNode, word: string): void {
31 | for (const char of word) {
32 | if (!node.children[char]) {
33 | node.children[char] = new TrieNode()
34 | }
35 | node = node.children[char]
36 | }
37 | node.isWord = true
38 | }
39 |
40 | /**
41 | * Searches for a word in the Trie.
42 | *
43 | * @param word - The word to search for.
44 | * @param isPrefixMatch - Indicates whether to perform a prefix match (default: false).
45 | * If true, the method returns true if the Trie contains words with the specified prefix.
46 | * If false, the method returns true only if an exact match is found.
47 | * @returns True if the word (or prefix) is found in the Trie; otherwise, false.
48 | */
49 | public find(word: string, isPrefixMatch = false): boolean {
50 | return this.searchNode(this.root, word, isPrefixMatch)
51 | }
52 |
53 | /**
54 | * Adds a word to the Trie.
55 | *
56 | * @param word - The word to add to the Trie.
57 | * @returns The Trie instance, allowing for method chaining.
58 | */
59 | public add(word: string): this {
60 | this.insertNode(this.root, word)
61 | return this
62 | }
63 |
64 | /**
65 | * Searches for a word in the Trie.
66 | *
67 | * @param node - The current Trie node being examined.
68 | * @param word - The word to search for.
69 | * @param prefixMatch - Indicates whether to perform a prefix match.
70 | * @returns True if the word (or prefix) is found in the Trie; otherwise, false.
71 | * @private
72 | */
73 | private searchNode(
74 | node: TrieNode,
75 | word: string,
76 | prefixMatch: boolean
77 | ): boolean {
78 | for (const char of word) {
79 | if (!node.children[char]) {
80 | return false
81 | }
82 | node = node.children[char]
83 | }
84 | return prefixMatch || node.isWord
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/dynamic_programming/coin_change.ts:
--------------------------------------------------------------------------------
1 | export interface CoinChange {
2 | minCoins: number
3 | coins: number[]
4 | }
5 |
6 | /**
7 | * Given a set of categories of coins C and an amount of money S, the goal is:
8 | * to give change for S but to use a minimum number of coins. Suppose each category of coin has an infinite number of pieces.
9 | * @param money - amon of money.
10 | * @param coins - The coins that are available.
11 | * @returns CoinChange, the minimum number of coins, and which coins are selected
12 | */
13 | export const coinChange = (money: number, coins: number[]): CoinChange => {
14 | const minCoins: number[] = Array(money + 1).fill(Infinity)
15 | const lastCoin: number[] = Array(money + 1).fill(-1)
16 |
17 | minCoins[0] = 0
18 |
19 | // Fill in the DP table
20 | for (const coin of coins) {
21 | for (let j = 0; j <= money; j++) {
22 | if (j >= coin) {
23 | if (minCoins[j] > 1 + minCoins[j - coin]) {
24 | minCoins[j] = 1 + minCoins[j - coin]
25 | lastCoin[j] = coin
26 | }
27 | }
28 | }
29 | }
30 |
31 | const res: CoinChange = {
32 | minCoins: minCoins[money],
33 | coins: []
34 | }
35 |
36 | let total: number = money
37 | while (total > 0) {
38 | res.coins.push(lastCoin[total])
39 | total -= lastCoin[total]
40 | }
41 |
42 | return res
43 | }
44 |
--------------------------------------------------------------------------------
/dynamic_programming/knapsack.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Solves the 0-1 Knapsack Problem.
3 | * @param capacity Knapsack capacity
4 | * @param weights Array of item weights
5 | * @param values Array of item values
6 | * @returns Maximum value subset such that sum of the weights of this subset is smaller than or equal to capacity
7 | * @throws If weights and values arrays have different lengths
8 | * @see [Knapsack](https://www.geeksforgeeks.org/0-1-knapsack-problem-dp-10/)
9 | * @example knapsack(3, [3, 4, 5], [30, 50, 60]) // Output: 90
10 | */
11 |
12 | export const knapsack = (
13 | capacity: number,
14 | weights: number[],
15 | values: number[]
16 | ): number => {
17 | if (weights.length !== values.length) {
18 | throw new Error(
19 | 'Weights and values arrays should have the same number of elements'
20 | )
21 | }
22 |
23 | const numberOfItems: number = weights.length
24 |
25 | // Initializing a 2D array to store calculated states/values
26 | const dp: number[][] = new Array(numberOfItems + 1)
27 | .fill(0)
28 | .map(() => new Array(capacity + 1).fill(0))
29 |
30 | // Loop traversing each state of dp
31 | for (let itemIndex = 1; itemIndex <= numberOfItems; itemIndex++) {
32 | const weight = weights[itemIndex - 1]
33 | const value = values[itemIndex - 1]
34 | for (
35 | let currentCapacity = 1;
36 | currentCapacity <= capacity;
37 | currentCapacity++
38 | ) {
39 | if (weight <= currentCapacity) {
40 | // Select the maximum value of including the current item or excluding it
41 | dp[itemIndex][currentCapacity] = Math.max(
42 | value + dp[itemIndex - 1][currentCapacity - weight],
43 | dp[itemIndex - 1][currentCapacity]
44 | )
45 | } else {
46 | // If the current item's weight exceeds the current capacity, exclude it
47 | dp[itemIndex][currentCapacity] = dp[itemIndex - 1][currentCapacity]
48 | }
49 | }
50 | }
51 |
52 | // Return the final maximized value at the last position of the dp matrix
53 | return dp[numberOfItems][capacity]
54 | }
55 |
--------------------------------------------------------------------------------
/dynamic_programming/lcs.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Find the Longest Common Subsequence (LCS) of two strings.
3 | * @param text1 - The first input string.
4 | * @param text2 - The second input string.
5 | * @returns The longest common subsequence as a string.
6 | */
7 |
8 | export const longestCommonSubsequence = (
9 | text1: string,
10 | text2: string
11 | ): string => {
12 | const m = text1.length
13 | const n = text2.length
14 |
15 | // Create a 2D array to store the lengths of LCS
16 | const dp: number[][] = Array.from({ length: m + 1 }, () =>
17 | Array(n + 1).fill(0)
18 | )
19 |
20 | // Fill in the DP table
21 | for (let i = 1; i <= m; i++) {
22 | for (let j = 1; j <= n; j++) {
23 | if (text1[i - 1] === text2[j - 1]) {
24 | dp[i][j] = dp[i - 1][j - 1] + 1
25 | } else {
26 | dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1])
27 | }
28 | }
29 | }
30 |
31 | // Reconstruct the LCS from the DP table
32 | let i = m
33 | let j = n
34 | const lcs: string[] = []
35 | while (i > 0 && j > 0) {
36 | if (text1[i - 1] === text2[j - 1]) {
37 | lcs.unshift(text1[i - 1])
38 | i--
39 | j--
40 | } else if (dp[i - 1][j] > dp[i][j - 1]) {
41 | i--
42 | } else {
43 | j--
44 | }
45 | }
46 |
47 | return lcs.join('')
48 | }
49 |
--------------------------------------------------------------------------------
/dynamic_programming/test/coin_change.test.ts:
--------------------------------------------------------------------------------
1 | import { CoinChange, coinChange } from '../coin_change'
2 |
3 | interface TestCase {
4 | money: number
5 | coins: number[]
6 | expected: CoinChange
7 | }
8 |
9 | const cases: TestCase[] = [
10 | {
11 | money: 13,
12 | coins: [7, 2, 3, 6],
13 | expected: {
14 | minCoins: 2,
15 | coins: [6, 7]
16 | }
17 | },
18 | {
19 | money: 10,
20 | coins: [1, 5],
21 | expected: {
22 | minCoins: 2,
23 | coins: [5, 5]
24 | }
25 | }
26 | ]
27 |
28 | describe('Coin Change Algorithm Test', () => {
29 | test.each(cases)(
30 | 'given money: $money, and coins: $coins the minimum coin change should return $expected',
31 | ({ money, coins, expected }) => {
32 | const result = coinChange(money, coins)
33 | expect(result).toEqual(expected)
34 | }
35 | )
36 | })
37 |
--------------------------------------------------------------------------------
/dynamic_programming/test/knapsack.test.ts:
--------------------------------------------------------------------------------
1 | import { knapsack } from '../knapsack'
2 |
3 | const cases: [number, number[], number[], number][] = [
4 | [15, [6, 5, 6, 6, 3, 7], [5, 6, 4, 6, 5, 2], 17],
5 | [50, [10, 20, 30], [60, 100, 120], 220],
6 | [8, [3, 4, 5], [30, 50, 60], 90],
7 | [
8 | 5,
9 | [1, 1, 1, 1, 1],
10 | [1000000000, 1000000000, 1000000000, 1000000000, 1000000000],
11 | 5000000000
12 | ]
13 | ]
14 |
15 | describe('Knapsack Algorithm Test', () => {
16 | test.each(cases)(
17 | 'given %p capacity available, with weights %p and values %p, knapsack should return %p',
18 | (capacity, weights, values, expectedResult) => {
19 | const result = knapsack(capacity, weights, values)
20 | expect(result).toBe(expectedResult)
21 | }
22 | )
23 | })
24 |
--------------------------------------------------------------------------------
/dynamic_programming/test/lcs.test.ts:
--------------------------------------------------------------------------------
1 | import { longestCommonSubsequence } from '../lcs'
2 |
3 | describe('longestCommonSubsequence', () => {
4 | it('should return the longest common subsequence', () => {
5 | expect(longestCommonSubsequence('ABCD', 'ACDF')).toBe('ACD')
6 |
7 | expect(longestCommonSubsequence('AGGTAB', 'GXTXAYB')).toBe('GTAB')
8 |
9 | expect(longestCommonSubsequence('abcdef', 'xyz')).toBe('')
10 |
11 | expect(longestCommonSubsequence('', '')).toBe('')
12 | })
13 |
14 | it('should handle cases with spaces and special characters', () => {
15 | expect(longestCommonSubsequence('A B C D', 'A C D E')).toBe('A C D')
16 |
17 | expect(longestCommonSubsequence('1234$%^', '!@#$%^')).toBe('$%^')
18 | })
19 |
20 | it('should handle cases with longer input strings', () => {
21 | expect(
22 | longestCommonSubsequence(
23 | 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.',
24 | 'Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.'
25 | )
26 | ).toBe('e iumoor it t oeetr ag li.')
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/graph/bellman_ford.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function bellmanFord
3 | * @description Compute the shortest path from a source node to all other nodes. If there is negative weight cycle, returns undefined. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'th item is the edge weight.
4 | * @Complexity_Analysis
5 | * Time complexity: O(E*V)
6 | * Space Complexity: O(V)
7 | * @param {[number, number][][]} graph - The graph in adjacency list form
8 | * @param {number} start - The source node
9 | * @return {number[] | undefined} - The shortest path to each node, undefined if there is negative weight cycle
10 | * @see https://en.wikipedia.org/wiki/Bellman%E2%80%93Ford_algorithm
11 | */
12 | export const bellmanFord = (
13 | graph: [number, number][][],
14 | start: number
15 | ): number[] | undefined => {
16 | // We save the shortest distance to each node in `distances`. If a node is
17 | // unreachable from the start node, its distance is Infinity.
18 | const distances = Array(graph.length).fill(Infinity)
19 | distances[start] = 0
20 |
21 | // On the i'th iteration, we compute all shortest paths that consists of i+1
22 | // nodes. If we compute this V-1 times, we will have computed all simple
23 | // shortest paths in the graph because a shortest path has at most V nodes.
24 | for (let i = 0; i < graph.length - 1; ++i) {
25 | for (let node = 0; node < graph.length; ++node) {
26 | for (const [child, weight] of graph[node]) {
27 | const new_distance = distances[node] + weight
28 | if (new_distance < distances[child]) {
29 | distances[child] = new_distance
30 | }
31 | }
32 | }
33 | }
34 |
35 | // Look through all edges. If the shortest path to a destination node d is
36 | // larger than the distance to source node s and weight(s->d), then the path
37 | // to s must have a negative weight cycle.
38 | for (let node = 0; node < graph.length; ++node) {
39 | for (const [child, weight] of graph[node]) {
40 | if (distances[child] > distances[node] + weight) {
41 | return undefined
42 | }
43 | }
44 | }
45 |
46 | return distances
47 | }
48 |
--------------------------------------------------------------------------------
/graph/bipartite_graph.ts:
--------------------------------------------------------------------------------
1 | const dfs = (
2 | graph: number[][],
3 | colors: number[],
4 | node: number,
5 | color: number
6 | ): boolean => {
7 | if (colors[node] !== 0) {
8 | return colors[node] === color
9 | }
10 |
11 | colors[node] = color
12 |
13 | for (const neighbor of graph[node]) {
14 | if (!dfs(graph, colors, neighbor, -color)) {
15 | return false
16 | }
17 | }
18 |
19 | return true
20 | }
21 |
22 | /**
23 | * Determines if a given graph is bipartite.
24 | *
25 | * A Bipartite Graph is a graph whose vertices can be divided into two independent sets,
26 | * U and V such that every edge (u, v) either connects a vertex from U to V or a vertex from
27 | * V to U
28 | *
29 | * @param {number[][]} graph - The graph represented as an adjacency list.
30 | * @returns {boolean} - `true` if the graph is bipartite, `false` otherwise.
31 | */
32 |
33 | export const isBipartite = (graph: number[][]): boolean => {
34 | const n: number = graph.length
35 | const colors: number[] = new Array(n).fill(0)
36 |
37 | for (let i = 0; i < n; i++) {
38 | if (colors[i] === 0 && !dfs(graph, colors, i, 1)) {
39 | return false
40 | }
41 | }
42 |
43 | return true
44 | }
45 |
--------------------------------------------------------------------------------
/graph/dijkstra.ts:
--------------------------------------------------------------------------------
1 | import { PriorityQueue } from '../data_structures/heap/heap'
2 | /**
3 | * @function dijkstra
4 | * @description Compute the shortest path from a source node to all other nodes. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'th item is the edge weight.
5 | * @Complexity_Analysis
6 | * Time complexity: O((V+E)*log(V)). For fully connected graphs, it is O(E*log(V)).
7 | * Space Complexity: O(V)
8 | * @param {[number, number][][]} graph - The graph in adjacency list form
9 | * @param {number} start - The source node
10 | * @return {number[]} - The shortest path to each node
11 | * @see https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
12 | */
13 | export const dijkstra = (
14 | graph: [number, number][][],
15 | start: number
16 | ): number[] => {
17 | // We use a priority queue to make sure we always visit the closest node. The
18 | // queue makes comparisons based on path weights.
19 | const priorityQueue = new PriorityQueue(
20 | (a: [number, number]) => {
21 | return a[0]
22 | },
23 | graph.length,
24 | (a: [number, number], b: [number, number]) => {
25 | return a[1] < b[1]
26 | }
27 | )
28 | priorityQueue.insert([start, 0])
29 | // We save the shortest distance to each node in `distances`. If a node is
30 | // unreachable from the start node, its distance is Infinity.
31 | const distances = Array(graph.length).fill(Infinity)
32 | distances[start] = 0
33 |
34 | while (priorityQueue.size() > 0) {
35 | const node = priorityQueue.extract()[0]
36 | graph[node].forEach(([child, weight]) => {
37 | const new_distance = distances[node] + weight
38 | if (new_distance < distances[child]) {
39 | // Found a new shortest path to child node. Record its distance and add child to the queue.
40 | // If the child already exists in the queue, the priority will be updated. This will make sure the queue will be at most size V (number of vertices).
41 | priorityQueue.increasePriority(child, [child, weight])
42 | distances[child] = new_distance
43 | }
44 | })
45 | }
46 |
47 | return distances
48 | }
49 |
--------------------------------------------------------------------------------
/graph/floyd_warshall.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function floydWarshall
3 | * @description Compute the shortest path for all pairs of nodes for a graph without negative weight edges. The input graph is a adjacency matrix, where graph[i][j] holds the weight of edges a->b. If the edge does not exist, the value in the matrix is Infinity.
4 | * @Complexity_Analysis
5 | * Time complexity: O(V^3)
6 | * Space Complexity: O(V^2). This space is required to hold the result
7 | * @param {number[][]} graph - The graph in adjacency matrix form
8 | * @return {number[][]} - A matrix holding the shortest path for each pair of nodes. matrix[i][j] holds the distance of the shortest path (i -> j).
9 | * @see https://en.wikipedia.org/wiki/Floyd%E2%80%93Warshall_algorithm
10 | */
11 | export const floydWarshall = (graph: number[][]): number[][] => {
12 | let distances = structuredClone(graph)
13 | const N = graph.length
14 |
15 | // We begin by setting the weighted adjacency matrix as the shortest paths.
16 | // For the k'th iteration, we try to relax the shortest paths by including node k in the path.
17 | for (let k = 0; k < N; ++k) {
18 | const newDistances = []
19 | for (let i = 0; i < N; ++i) {
20 | newDistances.push(Array(N).fill(Infinity))
21 | }
22 |
23 | for (let i = 0; i < N; ++i) {
24 | for (let j = 0; j < N; ++j) {
25 | // The shortest path from node i to j is the minimum of:
26 | // 1. the shortest path (i -> j) without node k
27 | // 2. the sum of the shortest path (i -> k) and (k -> j)
28 | newDistances[i][j] = Math.min(
29 | distances[i][j],
30 | distances[i][k] + distances[k][j]
31 | )
32 | }
33 | }
34 | distances = newDistances
35 | }
36 |
37 | return distances
38 | }
39 |
--------------------------------------------------------------------------------
/graph/johnson.ts:
--------------------------------------------------------------------------------
1 | import { bellmanFord } from './bellman_ford'
2 | import { dijkstra } from './dijkstra'
3 |
4 | /**
5 | * @function johnson
6 | * @description Compute the shortest path for all pairs of nodes. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'th item is the edge weight. Returned undefined if the graph has negative weighted cycles.
7 | * @Complexity_Analysis
8 | * Time complexity: O(VElog(V))
9 | * Space Complexity: O(V^2) to hold the result
10 | * @param {[number, number][][]} graph - The graph in adjacency list form
11 | * @return {number[][]} - A matrix holding the shortest path for each pair of nodes. matrix[i][j] holds the distance of the shortest path (i -> j).
12 | * @see https://en.wikipedia.org/wiki/Johnson%27s_algorithm
13 | */
14 | export const johnson = (
15 | graph: [number, number][][]
16 | ): number[][] | undefined => {
17 | const N = graph.length
18 |
19 | // Add a new node and 0 weighted edges from the new node to all existing nodes.
20 | const newNodeGraph = structuredClone(graph)
21 | const newNode: [number, number][] = []
22 | for (let i = 0; i < N; ++i) {
23 | newNode.push([i, 0])
24 | }
25 | newNodeGraph.push(newNode)
26 |
27 | // Compute distances from the new node to existing nodes using the Bellman-Ford algorithm.
28 | const adjustedGraph = bellmanFord(newNodeGraph, N)
29 | if (adjustedGraph === undefined) {
30 | // Found a negative weight cycle.
31 | return undefined
32 | }
33 |
34 | for (let i = 0; i < N; ++i) {
35 | for (const edge of graph[i]) {
36 | // Adjust edge weights using the Bellman Ford output weights. This ensure that:
37 | // 1. Each weight is non-negative. This is required for the Dijkstra algorithm.
38 | // 2. The shortest path from node i to node j consists of the same nodes with or without adjustment.
39 | edge[1] += adjustedGraph[i] - adjustedGraph[edge[0]]
40 | }
41 | }
42 |
43 | const shortestPaths: number[][] = []
44 | for (let i = 0; i < N; ++i) {
45 | // Compute Dijkstra weights for each node and re-adjust weights to their original values.
46 | const dijkstraShorestPaths = dijkstra(graph, i)
47 | for (let j = 0; j < N; ++j) {
48 | dijkstraShorestPaths[j] += adjustedGraph[j] - adjustedGraph[i]
49 | }
50 | shortestPaths.push(dijkstraShorestPaths)
51 | }
52 | return shortestPaths
53 | }
54 |
--------------------------------------------------------------------------------
/graph/kruskal.ts:
--------------------------------------------------------------------------------
1 | import { DisjointSet } from '../data_structures/disjoint_set/disjoint_set'
2 |
3 | /**
4 | * @function kruskal
5 | * @description Compute a minimum spanning forest of a weighted undirected graph
6 | * @Complexity_Analysis
7 | * Time complexity: O(Elog(V))
8 | * Space Complexity: O(V)
9 | * @param {Edge[]} edges - The edges of the graph
10 | * @param {number} num_vertices - The number of vertices in the graph
11 | * @return {Edge[], number} - [The edges of the minimum spanning tree, the sum of the weights of the edges in the tree]
12 | * @see https://en.wikipedia.org/wiki/Kruskal%27s_algorithm
13 | */
14 | export const kruskal = (
15 | edges: Edge[],
16 | num_vertices: number
17 | ): [Edge[], number] => {
18 | let cost = 0
19 | const minimum_spanning_tree = []
20 |
21 | // Use a disjoint set to quickly join sets and find if vertices live in different sets
22 | const sets = new DisjointSet(num_vertices)
23 |
24 | // Sort the edges in ascending order by weight so that we can greedily add cheaper edges to the tree
25 | edges.sort((a, b) => a.weight - b.weight)
26 |
27 | for (const edge of edges) {
28 | if (sets.find(edge.a) !== sets.find(edge.b)) {
29 | // Node A and B live in different sets. Add edge(a, b) to the tree and join the nodes' sets together.
30 | minimum_spanning_tree.push(edge)
31 | cost += edge.weight
32 | sets.join(edge.a, edge.b)
33 | }
34 | }
35 |
36 | return [minimum_spanning_tree, cost]
37 | }
38 |
39 | export class Edge {
40 | a: number = 0
41 | b: number = 0
42 | weight: number = 0
43 | constructor(a: number, b: number, weight: number) {
44 | this.a = a
45 | this.b = b
46 | this.weight = weight
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/graph/prim.ts:
--------------------------------------------------------------------------------
1 | import { PriorityQueue } from '../data_structures/heap/heap'
2 | /**
3 | * @function prim
4 | * @description Compute a minimum spanning tree(MST) of a fully connected weighted undirected graph. The input graph is in adjacency list form. It is a multidimensional array of edges. graph[i] holds the edges for the i'th node. Each edge is a 2-tuple where the 0'th item is the destination node, and the 1'th item is the edge weight.
5 | * @Complexity_Analysis
6 | * Time complexity: O(Elog(V))
7 | * Space Complexity: O(V)
8 | * @param {[number, number][][]} graph - The graph in adjacency list form
9 | * @return {Edge[], number} - [The edges of the minimum spanning tree, the sum of the weights of the edges in the tree]
10 | * @see https://en.wikipedia.org/wiki/Prim%27s_algorithm
11 | */
12 | export const prim = (graph: [number, number][][]): [Edge[], number] => {
13 | if (graph.length == 0) {
14 | return [[], 0]
15 | }
16 | const minimum_spanning_tree: Edge[] = []
17 | let total_weight = 0
18 |
19 | const priorityQueue = new PriorityQueue(
20 | (e: Edge) => {
21 | return e.b
22 | },
23 | graph.length,
24 | (a: Edge, b: Edge) => {
25 | return a.weight < b.weight
26 | }
27 | )
28 | const visited = new Set()
29 |
30 | // Start from the 0'th node. For fully connected graphs, we can start from any node and still produce the MST.
31 | visited.add(0)
32 | add_children(graph, priorityQueue, 0)
33 |
34 | while (!priorityQueue.isEmpty()) {
35 | // We have already visited vertex `edge.a`. If we have not visited `edge.b` yet, we add its outgoing edges to the PriorityQueue.
36 | const edge = priorityQueue.extract()
37 | if (visited.has(edge.b)) {
38 | continue
39 | }
40 | minimum_spanning_tree.push(edge)
41 | total_weight += edge.weight
42 | visited.add(edge.b)
43 | add_children(graph, priorityQueue, edge.b)
44 | }
45 |
46 | return [minimum_spanning_tree, total_weight]
47 | }
48 |
49 | const add_children = (
50 | graph: [number, number][][],
51 | priorityQueue: PriorityQueue,
52 | node: number
53 | ) => {
54 | for (const out_edge of graph[node]) {
55 | // By increasing the priority, we ensure we only add each vertex to the queue one time, and the queue will be at most size V.
56 | priorityQueue.increasePriority(
57 | out_edge[0],
58 | new Edge(node, out_edge[0], out_edge[1])
59 | )
60 | }
61 | }
62 |
63 | export class Edge {
64 | a: number = 0
65 | b: number = 0
66 | weight: number = 0
67 | constructor(a: number, b: number, weight: number) {
68 | this.a = a
69 | this.b = b
70 | this.weight = weight
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/graph/test/bipartite_graph.test.ts:
--------------------------------------------------------------------------------
1 | import { isBipartite } from '../bipartite_graph'
2 |
3 | describe('isBipartite', () => {
4 | it('should return true for a bipartite graph', () => {
5 | const graph = [
6 | [1, 3],
7 | [0, 2],
8 | [1, 3],
9 | [0, 2]
10 | ]
11 | const result = isBipartite(graph)
12 | expect(result).toBe(true)
13 | })
14 |
15 | it('should return true for an empty graph', () => {
16 | const graph: number[][] = []
17 | const result = isBipartite(graph)
18 | expect(result).toBe(true)
19 | })
20 |
21 | it('should return true for a single node graph', () => {
22 | const graph = [[]]
23 | const result = isBipartite(graph)
24 | expect(result).toBe(true)
25 | })
26 |
27 | it('should return false for a non-bipartite graph', () => {
28 | const graph = [
29 | [1, 2, 3],
30 | [0, 2],
31 | [0, 1, 3],
32 | [0, 2]
33 | ]
34 | const result = isBipartite(graph)
35 | expect(result).toBe(false)
36 | })
37 |
38 | it('should return true for a disconnected bipartite graph', () => {
39 | const graph = [[1, 2], [0], [0], [4], [3]]
40 | const result = isBipartite(graph)
41 | expect(result).toBe(true)
42 | })
43 | })
44 |
--------------------------------------------------------------------------------
/graph/test/dijkstra.test.ts:
--------------------------------------------------------------------------------
1 | import { dijkstra } from '../dijkstra'
2 |
3 | describe('dijkstra', () => {
4 | const init_graph = (N: number): [number, number][][] => {
5 | const graph = Array(N)
6 | for (let i = 0; i < N; ++i) {
7 | graph[i] = []
8 | }
9 | return graph
10 | }
11 |
12 | const add_edge = (
13 | graph: [number, number][][],
14 | a: number,
15 | b: number,
16 | weight: number
17 | ) => {
18 | graph[a].push([b, weight])
19 | graph[b].push([a, weight])
20 | }
21 |
22 | it('should return the correct value', () => {
23 | const graph = init_graph(9)
24 | add_edge(graph, 0, 1, 4)
25 | add_edge(graph, 0, 7, 8)
26 | add_edge(graph, 1, 2, 8)
27 | add_edge(graph, 1, 7, 11)
28 | add_edge(graph, 2, 3, 7)
29 | add_edge(graph, 2, 5, 4)
30 | add_edge(graph, 2, 8, 2)
31 | add_edge(graph, 3, 4, 9)
32 | add_edge(graph, 3, 5, 14)
33 | add_edge(graph, 4, 5, 10)
34 | add_edge(graph, 5, 6, 2)
35 | add_edge(graph, 6, 7, 1)
36 | add_edge(graph, 6, 8, 6)
37 | add_edge(graph, 7, 8, 7)
38 | expect(dijkstra(graph, 0)).toStrictEqual([0, 4, 12, 19, 21, 11, 9, 8, 14])
39 | })
40 |
41 | it('should return the correct value for single element graph', () => {
42 | expect(dijkstra([[]], 0)).toStrictEqual([0])
43 | })
44 |
45 | const linear_graph = init_graph(4)
46 | add_edge(linear_graph, 0, 1, 1)
47 | add_edge(linear_graph, 1, 2, 2)
48 | add_edge(linear_graph, 2, 3, 3)
49 | test.each([
50 | [0, [0, 1, 3, 6]],
51 | [1, [1, 0, 2, 5]],
52 | [2, [3, 2, 0, 3]],
53 | [3, [6, 5, 3, 0]]
54 | ])(
55 | 'correct result for linear graph with source node %i',
56 | (source, result) => {
57 | expect(dijkstra(linear_graph, source)).toStrictEqual(result)
58 | }
59 | )
60 |
61 | const unreachable_graph = init_graph(3)
62 | add_edge(unreachable_graph, 0, 1, 1)
63 | test.each([
64 | [0, [0, 1, Infinity]],
65 | [1, [1, 0, Infinity]],
66 | [2, [Infinity, Infinity, 0]]
67 | ])(
68 | 'correct result for graph with unreachable nodes with source node %i',
69 | (source, result) => {
70 | expect(dijkstra(unreachable_graph, source)).toStrictEqual(result)
71 | }
72 | )
73 | })
74 |
--------------------------------------------------------------------------------
/graph/test/floyd_warshall.test.ts:
--------------------------------------------------------------------------------
1 | import { floydWarshall } from '../floyd_warshall'
2 |
3 | describe('floydWarshall', () => {
4 | it('should return the correct value for zero element graph', () => {
5 | expect(floydWarshall([])).toEqual([])
6 | })
7 |
8 | it('should return the correct value for one element graph', () => {
9 | expect(floydWarshall([[1]])).toStrictEqual([[1]])
10 | })
11 |
12 | it('should return the correct value for two element graph', () => {
13 | expect(
14 | floydWarshall([
15 | [10, 4],
16 | [3, 6]
17 | ])
18 | ).toStrictEqual([
19 | [7, 4],
20 | [3, 6]
21 | ])
22 | })
23 |
24 | it('should return the correct value', () => {
25 | const graph = []
26 | for (let i = 1; i <= 5; ++i) {
27 | const arr = []
28 | for (let j = 1; j <= 5; ++j) {
29 | arr.push(i * j)
30 | }
31 | graph.push(arr)
32 | }
33 |
34 | const expected = [
35 | [1, 2, 3, 4, 5],
36 | [2, 4, 5, 6, 7],
37 | [3, 5, 6, 7, 8],
38 | [4, 6, 7, 8, 9],
39 | [5, 7, 8, 9, 10]
40 | ]
41 | expect(floydWarshall(graph)).toStrictEqual(expected)
42 | })
43 |
44 | it('should return the correct value', () => {
45 | const graph = [
46 | [0, 4, Infinity, Infinity, Infinity, Infinity, Infinity, 8, Infinity],
47 | [4, 0, 8, Infinity, Infinity, Infinity, Infinity, 11, Infinity],
48 | [Infinity, 8, 0, 7, Infinity, 4, Infinity, Infinity, 2],
49 | [Infinity, Infinity, 7, 0, 9, 14, Infinity, Infinity, Infinity],
50 | [Infinity, Infinity, Infinity, 9, 0, 10, Infinity, Infinity, Infinity],
51 | [Infinity, Infinity, 4, 14, 10, 0, 2, Infinity, Infinity],
52 | [Infinity, Infinity, Infinity, Infinity, Infinity, 2, 0, 1, 6],
53 | [8, 11, Infinity, Infinity, Infinity, Infinity, 1, 0, 7],
54 | [Infinity, Infinity, 2, Infinity, Infinity, Infinity, 6, 7, 0]
55 | ]
56 |
57 | const expected = [
58 | [0, 4, 12, 19, 21, 11, 9, 8, 14],
59 | [4, 0, 8, 15, 22, 12, 12, 11, 10],
60 | [12, 8, 0, 7, 14, 4, 6, 7, 2],
61 | [19, 15, 7, 0, 9, 11, 13, 14, 9],
62 | [21, 22, 14, 9, 0, 10, 12, 13, 16],
63 | [11, 12, 4, 11, 10, 0, 2, 3, 6],
64 | [9, 12, 6, 13, 12, 2, 0, 1, 6],
65 | [8, 11, 7, 14, 13, 3, 1, 0, 7],
66 | [14, 10, 2, 9, 16, 6, 6, 7, 0]
67 | ]
68 |
69 | expect(floydWarshall(graph)).toStrictEqual(expected)
70 | })
71 | })
72 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | import type { Config } from '@jest/types'
2 | // Sync object
3 | const config: Config.InitialOptions = {
4 | verbose: true,
5 | transform: {
6 | '^.+\\.tsx?$': 'ts-jest'
7 | }
8 | }
9 | export default config
10 |
--------------------------------------------------------------------------------
/maths/absolute_value.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function absoluteValue
3 | * @description Calculate the absolute value of an input number.
4 | * @param {number} number - a numeric input value
5 | * @return {number} - Absolute number of input number
6 | * @see https://en.wikipedia.org/wiki/Absolute_value
7 | * @example absoluteValue(-10) = 10
8 | * @example absoluteValue(50) = 50
9 | * @example absoluteValue(0) = 0
10 | */
11 |
12 | export const absoluteValue = (number: number): number => {
13 | // if input number is less than 0, convert it to positive via double negation
14 | // e.g. if n = -2, then return -(-2) = 2
15 | return number < 0 ? -number : number
16 | }
17 |
--------------------------------------------------------------------------------
/maths/aliquot_sum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function aliquotSum
3 | * @description Returns the aliquot sum of the provided number
4 | * @summary The aliquot sum of a number n is the sum of all the proper divisors
5 | * of n apart from n itself.
6 | * So, for example, the number 6 has three proper divisors, 1, 2, 3
7 | * Hence its aliquot sum is 1 + 2 + 3 = 6
8 | * For all prime numbers, the aliquot sum is 1, and for 1, the aliquot sum is 0
9 | * @param {number} num The input number
10 | * @return {number} The aliquot sum of the number
11 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Aliquot_sum)
12 | * @example aliquotSum(18) = 21
13 | * @example aliquotSum(15) = 9
14 | */
15 | export const aliquotSum = (num: number): number => {
16 | if (typeof num !== 'number') throw new TypeError('Input needs to be a number')
17 | if (num < 0) throw new TypeError('Input cannot be negative')
18 | if (!Number.isInteger(num)) throw new TypeError('Input cannot be a decimal')
19 |
20 | let sum = 0
21 |
22 | for (let i = 1; i <= num / 2; i++) {
23 | if (num % i === 0) sum += i
24 | }
25 |
26 | return sum
27 | }
28 |
--------------------------------------------------------------------------------
/maths/armstrong_number.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function armstrongNumber
3 | * @description Check if the provided number is an Armstrong number or not.
4 | * @summary Armstrong numbers are numbers, the sum of whose digits each raised
5 | * to the power of the number of digits is equal to the number itself.
6 | * For example:
7 | * 370 is an Armstrong number since 3^3 + 7^3 + 0^3 = 370
8 | * (These numbers are also known as Narcissistic numbers, and Pluperfect numbers)
9 | * @param {number} num The number you want to check for
10 | * @return {boolean} Whether the input number is an Armstrong number
11 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Armstrong_number)
12 | * @see [OEIS](https://oeis.org/A005188)
13 | * @example armstrongNumber(370) = true
14 | * @example armstrongNumber(10) = false
15 | */
16 | export const armstrongNumber = (num: number): boolean => {
17 | if (typeof num !== 'number' || num <= 0) return false
18 |
19 | let compNum = 0
20 | let cloneNum = num
21 | const numOfDigits = Math.floor(1 + Math.log10(num))
22 |
23 | while (cloneNum > 0) {
24 | compNum += Math.pow(cloneNum % 10, numOfDigits)
25 | cloneNum = Math.floor(cloneNum / 10)
26 | }
27 |
28 | return compNum === num
29 | }
30 |
--------------------------------------------------------------------------------
/maths/binary_convert.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function binaryConvert
3 | * @description Convert the decimal to binary.
4 | * @param {number} num - The input integer
5 | * @return {string} - Binary of num.
6 | * @see [BinaryConvert](https://www.programiz.com/javascript/examples/decimal-binary)
7 | * @example binaryConvert(12) = 1100
8 | * @example binaryConvert(12 + 2) = 1110
9 | */
10 |
11 | export const binaryConvert = (num: number): string => {
12 | let binary = ''
13 |
14 | while (num !== 0) {
15 | binary = (num % 2) + binary
16 | num = Math.floor(num / 2)
17 | }
18 |
19 | return binary
20 | }
21 |
--------------------------------------------------------------------------------
/maths/binomial_coefficient.ts:
--------------------------------------------------------------------------------
1 | import { factorial } from './factorial'
2 | /**
3 | * @function binomialCoefficient
4 | * @description Calculate the binomial coefficient (n choose k) of two input numbers.
5 | * @param {number} n - the total number of items
6 | * @param {number} k - the number of items to be chosen
7 | * @return {number} - Binomial coefficient (n choose k)
8 | * @see https://en.wikipedia.org/wiki/Binomial_coefficient
9 | * @example binomialCoefficient(5, 2) = 10
10 | * @example binomialCoefficient(10, 3) = 120
11 | * @example binomialCoefficient(6, 0) = 1
12 | */
13 |
14 | export const binomialCoefficient = (n: number, k: number): number => {
15 | // Check if k is larger than n or negative
16 | if (k > n || k < 0) {
17 | return 0
18 | }
19 |
20 | // Calculate the binomial coefficient using the implemented factorial
21 | const numerator = factorial(n)
22 | const denominator = factorial(k) * factorial(n - k)
23 | return numerator / denominator
24 | }
25 |
--------------------------------------------------------------------------------
/maths/calculate_mean.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function calculateMean
3 | * @description This script will find the mean value of a array of numbers.
4 | * @param {number[]} numbers - Array of numeric values
5 | * @return {number} - mean of input numbers
6 | * @see [Mean](https://en.wikipedia.org/wiki/Mean)
7 | * @example calculateMean([1, 2, 4, 5]) = 3
8 | * @example calculateMean([10, 40, 100, 20]) = 42.5
9 | */
10 |
11 | export const calculateMean = (numbers: number[]): number => {
12 | if (numbers.length < 1) {
13 | throw new TypeError('Invalid Input')
14 | }
15 |
16 | // This loop sums all values in the 'numbers' array using an array reducer
17 | const sum = numbers.reduce((sum, current) => sum + current, 0)
18 |
19 | // Divide sum by the length of the 'numbers' array.
20 | return sum / numbers.length
21 | }
22 |
--------------------------------------------------------------------------------
/maths/calculate_median.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function calculateMedian
3 | * @description This function will find the median value of an array of numbers.
4 | * @param {number[]} numbers Sorted array of numeric values.
5 | * @return {number} The median of input numbers.
6 | * @see https://en.wikipedia.org/wiki/Median
7 | * @example calculateMedian([1, 2, 4, 5, 8]) = 4
8 | * @example calculateMedian([1, 2, 4, 5]) = 3
9 | */
10 |
11 | export const calculateMedian = (numbers: number[]): number => {
12 | if (numbers.length < 1) {
13 | throw new TypeError('Input array must contain at least one number.')
14 | }
15 |
16 | const totalNumbers = numbers.length
17 |
18 | if (totalNumbers % 2 === 0) {
19 | const index = totalNumbers / 2
20 | return (numbers[index - 1] + numbers[index]) / 2
21 | } else {
22 | const index = (totalNumbers + 1) / 2
23 | return numbers[index - 1]
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/maths/degrees_to_radians.ts:
--------------------------------------------------------------------------------
1 | /** * A function to get radians from the degrees
2 | * @param {number} degree - The input integer
3 | * @return {number} radians of degrees
4 | * @example degreesToRadians(45) => 0.7853 | degreesToRadians(90) => 1.5708
5 | * @see https://en.m.wikipedia.org/wiki/Radian
6 | * @author MohdFaisalBidda
7 | */
8 |
9 | export const degreesToRadians = (degrees: number): number => {
10 | return (degrees * Math.PI) / 180
11 | }
12 |
--------------------------------------------------------------------------------
/maths/digit_sum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function digitSum
3 | * @description Calculate the sum of all digits of a natural number (number base 10).
4 | * @param {number} num - A natural number.
5 | * @return {number} - Sum of all digits of given natural number.
6 | * @see https://en.wikipedia.org/wiki/Digit_sum
7 | * @example digitSum(12) = 3
8 | * @example digitSum(9045) = 18
9 | */
10 |
11 | export const digitSum = (num: number): number => {
12 | if (num < 0 || !Number.isInteger(num)) {
13 | throw new Error('only natural numbers are supported')
14 | }
15 |
16 | let sum = 0
17 | while (num != 0) {
18 | sum += num % 10
19 | num = Math.floor(num / 10)
20 | }
21 |
22 | return sum
23 | }
24 |
--------------------------------------------------------------------------------
/maths/double_factorial_iterative.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function DoubleFactorialIterative
3 | * @description Calculate the double factorial of a number (iterative implementation)
4 | * @summary In mathematics, double factorial of a number n is denoted by n!!.
5 | * It is not to be confused with (n!)!, which is the factorial function iterated twice.
6 | * The double factorial is the product of all positive integers upto n that have the same parity (odd or even)
7 | * as n.
8 | * Therefore,
9 | * 9!! = 9 . 7 . 5 . 3 . 1
10 | * 10!! = 10 . 8 . 6 . 4 . 2
11 | *
12 | * Please note that for factorials of even numbers, the series ends at 2.
13 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Double_factorial)
14 | * @see [Mathworld](https://mathworld.wolfram.com/DoubleFactorial.html)
15 | * @see [GeeksForGeeks](https://www.geeksforgeeks.org/double-factorial/)
16 | * @example DoubleFactorialIterative(4) = 8
17 | * @example DoubleFactorialIterative(5) = 15
18 | */
19 | const DoubleFactorialIterative = (n: number) => {
20 | if (n < 0) throw new RangeError('The number needs to be non-negative')
21 | let doubleFactorial = 1
22 |
23 | for (let i = n; i > 0; i -= 2) doubleFactorial *= i
24 |
25 | return doubleFactorial
26 | }
27 |
28 | export { DoubleFactorialIterative }
29 |
--------------------------------------------------------------------------------
/maths/euler_totient.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description counts the positive integers up to a given integer n that are relatively prime to n.
3 | * @param {number} n - A natural number.
4 | * @return {number} - euler's totient.
5 | * @see https://en.wikipedia.org/wiki/Euler%27s_totient_function
6 | * @example phi(4) = 2
7 | * @example phi(5) = 4
8 | */
9 | export const phi = (n: number): number => {
10 | let result: number = n
11 | for (let i = 2; i * i <= n; i++) {
12 | if (n % i == 0) {
13 | while (n % i == 0) n = n / i
14 | result -= Math.floor(result / i)
15 | }
16 | }
17 | if (n > 1) result -= Math.floor(result / n)
18 |
19 | return result
20 | }
21 |
--------------------------------------------------------------------------------
/maths/factorial.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function factorial
3 | * @description Calculate the factorial of a natural number.
4 | * @param {number} num - A natural number.
5 | * @return {number} - The factorial.
6 | * @see https://en.wikipedia.org/wiki/factorial
7 | * @example factorial(0) = 1
8 | * @example factorial(3) = 6
9 | */
10 | export const factorial = (num: number): number => {
11 | if (num < 0 || !Number.isInteger(num)) {
12 | throw new Error('only natural numbers are supported')
13 | }
14 |
15 | return num === 0 ? 1 : num * factorial(num - 1)
16 | }
17 |
--------------------------------------------------------------------------------
/maths/factors.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function findFactors
3 | * @description Find all the factors of a natural number.
4 | * @param {number} num - A natural number.
5 | * @return {Set} - A set of all the factors of given natural number.
6 | * @see https://en.wikipedia.org/wiki/Divisor
7 | * @example findFactors(1) = [1]
8 | * @example findFactors(4) = [1,2,4]
9 | * @example findFactors(16) = [1,3,5,15]
10 | */
11 | export const findFactors = (num: number): Set => {
12 | if (num <= 0 || !Number.isInteger(num)) {
13 | throw new Error('Only natural numbers are supported.')
14 | }
15 |
16 | const res: Set = new Set()
17 | // Iterates from 1 to square root of num & pushes factors into the res set.
18 | for (let i = 1; i * i <= num; i++) {
19 | if (num % i === 0) {
20 | res.add(i)
21 |
22 | const sqrtFactor = Math.floor(num / i)
23 | res.add(sqrtFactor)
24 | }
25 | }
26 |
27 | return res
28 | }
29 |
--------------------------------------------------------------------------------
/maths/fibonacci.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A function to get nth Fibonacci number.
3 | *
4 | * Time Complexity: linear (O(n))
5 | *
6 | * @param number The index of the number in the Fibonacci sequence.
7 | * @return The Fibonacci number on the nth index in the sequence.
8 | *
9 | * @example nthFibonacci(4) => 3 | nthFibonacci(6) => 8
10 | * @see https://en.m.wikipedia.org/wiki/Fibonacci_number
11 | * @author MohdFaisalBidda
12 | */
13 | function* generateFibonacci(): Generator {
14 | let a = 0
15 | let b = 1
16 | while (true) {
17 | yield a
18 | const c = a + b
19 | a = b
20 | b = c
21 | }
22 | }
23 |
24 | export const nthFibonacci = (number: number): number => {
25 | if (isNaN(number)) throw new Error('The input needs to be a number')
26 | if (!Number.isInteger(number) || number < 0)
27 | throw new Error('The input needs to be a non-negative integer')
28 |
29 | if (number === 0) {
30 | return 0
31 | }
32 |
33 | const fibonacciGenerator = generateFibonacci()
34 | let result = 0
35 | for (let i = 0; i <= number; ++i) {
36 | result = fibonacciGenerator.next().value
37 | }
38 | return result
39 | }
40 |
41 | /**
42 | * A function to get nth Fibonacci number recursively. **Note: This recursive approach increases the time complexity**
43 | *
44 | * Time Complexity: exponential (O(ϕ^n))
45 | *
46 | * @param number The index of the number in the Fibonacci sequence.
47 | * @return The Fibonacci number on the nth index in the sequence.
48 | *
49 | * @example nthFibonacci(4) => 3 | nthFibonacci(6) => 8
50 | * @see https://en.m.wikipedia.org/wiki/Fibonacci_number
51 | * @author zFlxw
52 | */
53 | export const nthFibonacciRecursively = (number: number): number => {
54 | if (number === 0) {
55 | return 0
56 | }
57 |
58 | if (number <= 2) {
59 | return 1
60 | }
61 |
62 | return (
63 | nthFibonacciRecursively(number - 1) + nthFibonacciRecursively(number - 2)
64 | )
65 | }
66 |
67 | /**
68 | * @param number The index of the number in the Fibonacci sequence.
69 | * @return The Fibonacci number on the nth index in the sequence.
70 | * @example nthFibonacci(4) => 3 | nthFibonacci(6) => 8
71 | * @see : https://math.hmc.edu/funfacts/fibonacci-number-formula/
72 | * @author : dev-madhurendra
73 | */
74 |
75 | const sqrt5 = Math.sqrt(5)
76 | const phi = (1 + sqrt5) / 2
77 | const psi = (1 - sqrt5) / 2
78 |
79 | export const nthFibonacciUsingFormula = (n: number) =>
80 | Math.round((phi ** n - psi ** n) / sqrt5)
81 |
--------------------------------------------------------------------------------
/maths/find_min.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function findMin
3 | * @description Find the minimum in an array of numbers.
4 | * @param {Number[]} nums - An array of numbers.
5 | * @return {Number} - The minimum.
6 | * @see https://infinitbility.com/how-to-find-minimum-value-in-array-in-typescript/
7 | * @example findMin([1,2,3,4,5]) = 1
8 | * @example findMin([87,6,13,999]) = 6
9 | * @example findMin([0.8,0.2,0.3,0.5]) = 0.2
10 | * @example findMin([1,0.1,-1]) = -1
11 | */
12 | export const findMin = (nums: number[]): number => {
13 | if (nums.length === 0) {
14 | throw new Error('array must have length of 1 or greater')
15 | }
16 |
17 | let minimumSeen: number = nums[0]
18 | for (const num of nums) {
19 | if (num < minimumSeen) {
20 | minimumSeen = num
21 | }
22 | }
23 |
24 | return minimumSeen
25 | }
26 |
--------------------------------------------------------------------------------
/maths/gaussian_elimination.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Solves a system of linear equations using Gaussian Elimination with partial pivoting.
3 | *
4 | * @param {number[][]} matrix - The augmented matrix representing the system of equations.
5 | * @returns {number[]} An array representing the solutions to the equations.
6 | */
7 | export function gaussianElimination(matrix: number[][]): number[] {
8 | const result: number[] = new Array(matrix.length)
9 |
10 | function partialPivot(): void {
11 | for (let row = 0; row < matrix.length; row++) {
12 | let pivotRow = row
13 |
14 | for (let column = row + 1; column < matrix.length; column++) {
15 | if (Math.abs(matrix[column][row]) > Math.abs(matrix[pivotRow][row])) {
16 | pivotRow = column
17 | }
18 | }
19 |
20 | if (pivotRow !== row) {
21 | for (let column = row; column <= matrix.length; column++) {
22 | ;[matrix[row][column], matrix[pivotRow][column]] = [
23 | matrix[pivotRow][column],
24 | matrix[row][column]
25 | ]
26 | }
27 | }
28 |
29 | for (let column = row + 1; column < matrix.length; column++) {
30 | const factor = matrix[column][row] / matrix[row][row]
31 | for (let k = row; k <= matrix.length; k++) {
32 | matrix[column][k] -= factor * matrix[row][k]
33 | }
34 | }
35 | }
36 | }
37 |
38 | function backSubstitute(): void {
39 | for (let row = matrix.length - 1; row >= 0; row--) {
40 | let sum = 0
41 | for (let column = row + 1; column < matrix.length; column++) {
42 | sum += matrix[row][column] * result[column]
43 | }
44 | result[row] = (matrix[row][matrix.length] - sum) / matrix[row][row]
45 | }
46 | }
47 |
48 | partialPivot()
49 | backSubstitute()
50 |
51 | return result
52 | }
53 |
--------------------------------------------------------------------------------
/maths/greatest_common_factor.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function greatestCommonFactor
3 | * @description Determine the greatest common factor of a group of numbers.
4 | * @param {Number[]} nums - An array of numbers.
5 | * @return {Number} - The greatest common factor.
6 | * @see https://www.mathsisfun.com/greatest-common-factor.html
7 | * @example GreatestCommonFactor(12, 8) = 4
8 | * @example GreatestCommonFactor(3, 6) = 3
9 | * @example GreatestCommonFactor(32, 16, 12) = 4
10 | */
11 |
12 | export const binaryGCF = (a: number, b: number): number => {
13 | if (!Number.isInteger(a) || !Number.isInteger(b) || a < 0 || b < 0) {
14 | throw new Error('numbers must be natural to determine factors')
15 | }
16 |
17 | while (b) {
18 | ;[a, b] = [b, a % b]
19 | }
20 | return a
21 | }
22 |
23 | export const greatestCommonFactor = (nums: number[]): number => {
24 | if (nums.length === 0) {
25 | throw new Error('at least one number must be passed in')
26 | }
27 |
28 | return nums.reduce(binaryGCF)
29 | }
30 |
--------------------------------------------------------------------------------
/maths/hamming_distance.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function hammingDistance
3 | * @description Returns the Hamming distance between two strings of equal length
4 | * @summary The Hamming distance between two strings of equal length is the
5 | * number of positions at which the corresponding symbols are different. In other words,
6 | * it measures the minimum number of substitutions required, to change one string to the other
7 | * It is one of the several sring metrics, used fror measuring the edit distance between two sequences
8 | * and is named after the American mathematician Richard Hamming
9 | * @param str1 One of the strings to compare to the other
10 | * @param str2 One of the strings to compare to the other
11 | * @returns {number}
12 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Hamming_distance)
13 | * @example hammingDistance('happy', 'homie')
14 | */
15 | const hammingDistance = (str1: string, str2: string) => {
16 | if (str1.length !== str2.length)
17 | throw new Error('Strings must of the same length.')
18 |
19 | let dist = 0
20 |
21 | for (let i = 0; i < str1.length; i++) if (str1[i] !== str2[i]) dist++
22 |
23 | return dist
24 | }
25 |
26 | export { hammingDistance }
27 |
--------------------------------------------------------------------------------
/maths/is_divisible.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isDivisible
3 | * @description Checks is number is divisible by another number without remainder.
4 | * @param {number} num1 - first number, a dividend.
5 | * @param {number} num2 - second number, a divisor.
6 | * @return {boolean} - true if first number can be divided by second number without a remainder.
7 | * @example isDivisible(10, 2) = true
8 | * @example isDivisible(11, 3) = false
9 | */
10 |
11 | export const isDivisible = (num1: number, num2: number): boolean => {
12 | if (num2 === 0) {
13 | throw new Error('Cannot divide by 0')
14 | }
15 | return num1 % num2 === 0
16 | }
17 |
--------------------------------------------------------------------------------
/maths/is_even.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isEven
3 | * @description Determine whether a number is even.
4 | * @param {Number} num - A number.
5 | * @return {Boolean} - Whether the given number is even.
6 | * @see https://en.wikipedia.org/wiki/Parity_(mathematics)
7 | * @example isEven(1) = false
8 | * @example isEven(2) = true
9 | */
10 | export const isEven = (num: number): boolean => {
11 | if (!Number.isInteger(num)) {
12 | throw new Error('only integers can be even or odd')
13 | }
14 |
15 | return num % 2 === 0
16 | }
17 |
--------------------------------------------------------------------------------
/maths/is_leap_year.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isLeapYear
3 | * @description Checks if a year is a leap year (Gregorian calendar).
4 | * A year is a leap year if it is divisible by 4 but not by 400 or if it is divisible by 400.
5 | * @param {number} year - A year, natural number > 0.
6 | * @return {boolean} - True if given year is a leap year.
7 | * @see https://en.wikipedia.org/wiki/Leap_year#Gregorian_calendar
8 | * @example isLeapYear(2000) = true
9 | * @example isLeapYear(2001) = false
10 | */
11 |
12 | export const isLeapYear = (year: number): boolean => {
13 | if (year <= 0 || !Number.isInteger(year)) {
14 | throw new Error('year must be a natural number > 0')
15 | }
16 |
17 | return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
18 | }
19 |
--------------------------------------------------------------------------------
/maths/is_odd.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isOdd
3 | * @description Determine whether a number is odd.
4 | * @param {Number} num - A number.
5 | * @return {Boolean} - Whether the given number is odd.
6 | * @see https://en.wikipedia.org/wiki/Parity_(mathematics)
7 | * @example isOdd(1) = true
8 | * @example isOdd(2) = false
9 | */
10 | export const isOdd = (num: number): boolean => {
11 | if (!Number.isInteger(num)) {
12 | throw new Error('only integers can be even or odd')
13 | }
14 |
15 | return num % 2 !== 0
16 | }
17 |
--------------------------------------------------------------------------------
/maths/is_palindrome.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A function to see if a number is a palindrome.
3 | * Note that if the reversed number is larger than MAX_SAFE_INTEGER, rounding errors may occur and the result may be incorrect.
4 | * Time Complexity: O(log(n))
5 | *
6 | * @param number The input number.
7 | * @return {boolean} Wether the number is a Palindrome or not.
8 | */
9 | export const isPalindrome = (number: number): boolean => {
10 | if (number < 0 || (number % 10 === 0 && number !== 0)) {
11 | return false
12 | }
13 |
14 | let reversed: number = 0
15 | while (number > reversed) {
16 | reversed = reversed * 10 + (number % 10)
17 | number = Math.floor(number / 10)
18 | }
19 |
20 | return number === reversed || number === Math.floor(reversed / 10)
21 | }
22 |
--------------------------------------------------------------------------------
/maths/is_square_free.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isSquareFree
3 | * @description A number is said to be square-free if no prime factor divides it more than once, i.e., the largest power of a prime factor that divides n is one.
4 | * @param {number} n - A number.
5 | * @return {boolean} - True if given number is a square free.
6 | * @see https://www.geeksforgeeks.org/square-free-number/
7 | * @example isSquareFree(10) = true
8 | * @example isSquareFree(20) = false
9 | */
10 |
11 | export const isSquareFree = (n: number): boolean => {
12 | if (n < 0) throw new Error('number must be a natural number > 0')
13 | if (n % 2 === 0) n = n / 2
14 | if (n % 2 === 0) return false
15 |
16 | for (let i: number = 3; i <= Math.sqrt(n); i = i + 2) {
17 | if (n % i === 0) {
18 | n = n / i
19 | if (n % i === 0) return false
20 | }
21 | }
22 | return true
23 | }
24 |
--------------------------------------------------------------------------------
/maths/juggler_sequence.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * The juggler sequence is a integer sequence that starts with an positive integer a and the subsequent terms are
3 | * described as following:
4 | * if a_k is even:
5 | * a_k+1 = floor(sqrt(a_k))
6 | * else:
7 | * a_k+1 = floor(sqrt(a_k^3))
8 | *
9 | * Time Complexity: linear (O(n))
10 | *
11 | * @param a The number to start with
12 | * @param n The index of the searched number in the sequence.
13 | * @returns The number at index n in the sequence.
14 | * @see https://en.wikipedia.org/wiki/Juggler_sequence
15 | */
16 | export const jugglerSequence = (a: number, n: number) => {
17 | let k: number = a
18 | for (let i: number = 0; i < n; i++) {
19 | k = Math.floor(Math.pow(k, (k % 2 === 0 ? 1 : 3) / 2))
20 | }
21 |
22 | return k
23 | }
24 |
--------------------------------------------------------------------------------
/maths/lowest_common_multiple.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function lowestCommonMultiple
3 | * @description Determine the lowest common multiple of a group of numbers.
4 | * @param {Number[]} nums - An array of numbers.
5 | * @return {Number} - The lowest common multiple.
6 | * @see https://www.mathsisfun.com/least-common-multiple.html
7 | * @example LowestCommonMultiple(3, 4) = 12
8 | * @example LowestCommonMultiple(8, 6) = 24
9 | * @example LowestCommonMultiple(5, 8, 3) = 120
10 | */
11 |
12 | import { greatestCommonFactor } from './greatest_common_factor'
13 |
14 | //A naive solution which requires no additional mathematical algorithm
15 |
16 | export const naiveLCM = (nums: number[]): number => {
17 | if (nums.some((num) => num < 0)) {
18 | throw new Error(
19 | 'numbers must be positive to determine lowest common multiple'
20 | )
21 | }
22 |
23 | if (nums.length === 0) {
24 | throw new Error('at least one number must be passed in')
25 | }
26 |
27 | const max_num = Math.max(...nums)
28 | let current_num = max_num
29 |
30 | while (true) {
31 | if (nums.every((num) => current_num % num === 0)) {
32 | return current_num
33 | } else {
34 | current_num += max_num
35 | }
36 | }
37 | }
38 |
39 | //A typically more efficient solution which requires prior knowledge of GCF
40 | //Note that due to utilizing GCF, which requires natural numbers, this method only accepts natural numbers.
41 |
42 | export const binaryLCM = (a: number, b: number): number => {
43 | return (a * b) / greatestCommonFactor([a, b])
44 | }
45 |
46 | export const lowestCommonMultiple = (nums: number[]): number => {
47 | if (nums.length === 0) {
48 | throw new Error('at least one number must be passed in')
49 | }
50 |
51 | return nums.reduce(binaryLCM)
52 | }
53 |
--------------------------------------------------------------------------------
/maths/matrix_multiplication.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function matrixMultiplication
3 | * @description Multiply a matrix with either another matrix, a vector or a scalar
4 | * @param {Number[][]} matA - An array of an array of numbers
5 | * @param {Number[][] | Number[] | Number} b - Either an array of an array of numbers, an array of numbers, or a number
6 | * @return {Number[][] | Number[]} - Either an array of an array of numbers, or an array of numbers
7 | * @example matrixMultiplication([[1, 2], [3, 4]], [[1, 2], [3, 4]]) = [[7, 10], [15, 22]]
8 | * @example GreatestCommonFactor([[1, 2], [3, 4]], 2) = [[2, 4], [6, 8]]
9 | * @example GreatestCommonFactor([[1, 2], [3, 4]], [1, 2]) = [5, 11]
10 | */
11 |
12 | function matrixMultiplication(matA: number[][], b: number[][]): number[][]
13 | function matrixMultiplication(matA: number[][], b: number): number[][]
14 | function matrixMultiplication(matA: number[][], b: number[]): number[]
15 |
16 | function matrixMultiplication(
17 | matA: number[][],
18 | b: any
19 | ): number[][] | number[] | null {
20 | let matC: any = null
21 |
22 | if (typeof b === 'number') {
23 | matC = matA.map((row) => row.map((colVal) => colVal * b))
24 | } else {
25 | if (matA[0].length !== b.length) {
26 | return null
27 | }
28 |
29 | if (typeof b[0] === 'number') {
30 | matC = matA.map((row) =>
31 | row.reduce((sum, colVal, i) => sum + colVal * b[i], 0)
32 | )
33 | } else {
34 | matC = new Array(matA.length)
35 | .fill(null)
36 | .map(() => new Array(b[0].length).fill(0))
37 | let i: number, j: number, k: number
38 |
39 | for (i = 0; i < matA.length; i++) {
40 | for (j = 0; j < b[0].length; j++) {
41 | for (k = 0; k < matA[0].length; k++) {
42 | matC[i][j] += matA[i][k] * b[k][j]
43 | }
44 | }
45 | }
46 | }
47 | }
48 | return matC
49 | }
50 |
51 | export { matrixMultiplication }
52 |
--------------------------------------------------------------------------------
/maths/number_of_digits.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function numberOfDigits
3 | * @description Calculate the number of digits of a natural number.
4 | * @param {number} num - A natural number.
5 | * @return {number} - Number of digits of given natural number.
6 | * @see https://math.stackexchange.com/a/231745/518862
7 | * @example numberOfDigits(18) = 2
8 | * @example numberOfDigits(294568) = 6
9 | * @example numberOfDigits(128798319794) = 12
10 | */
11 |
12 | export const numberOfDigits = (num: number): number => {
13 | if (num <= 0 || !Number.isInteger(num)) {
14 | throw new Error('only natural numbers are supported')
15 | }
16 |
17 | return Math.floor(Math.log10(num)) + 1
18 | }
19 |
--------------------------------------------------------------------------------
/maths/pascals_triangle.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Pascal's Triangle is an array of binomial coefficients. It can be used for unwrapping terms like
3 | * (a + b)^5.
4 | * To construct Pascal's Triangle you add the numbers above the child entry together. Here are the first five rows:
5 | * 1
6 | * 1 1
7 | * 1 2 1
8 | * 1 3 3 1
9 | * 1 4 6 4 1
10 | *
11 | * Time Complexity: quadratic (O(n^2)).
12 | *
13 | * @param n The exponent / The index of the searched row.
14 | * @returns The nth row of Pascal's Triangle
15 | * @see https://en.wikipedia.org/wiki/Pascal's_triangle
16 | */
17 | export const pascalsTriangle = (n: number): number[] => {
18 | const arr: number[][] = []
19 | for (let i: number = 0; i < n; i++) {
20 | if (i === 0) {
21 | arr.push([1])
22 | continue
23 | }
24 |
25 | const lastRow: number[] = arr[i - 1]
26 | const temp: number[] = []
27 | for (let j: number = 0; j < lastRow.length + 1; j++) {
28 | if (j === 0 || j === lastRow.length) {
29 | temp.push(1)
30 | continue
31 | }
32 |
33 | temp.push(lastRow[j - 1] + lastRow[j])
34 | }
35 |
36 | arr.push(temp)
37 | }
38 |
39 | return arr[arr.length - 1]
40 | }
41 |
--------------------------------------------------------------------------------
/maths/perfect_cube.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * A number is a perfect cube, if the cube root is an integer.
3 | *
4 | * @param n The number to check.
5 | */
6 |
7 | export const perfectCube = (n: number): boolean => {
8 | return Math.round(n ** (1 / 3)) ** 3 === n
9 | }
10 |
--------------------------------------------------------------------------------
/maths/perfect_number.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isPerfectNumber
3 | * @abstract A function to determine if a number is a perfect number
4 | * @param {number} n
5 | *
6 | * @example console.log(isPerfectNumber(6)) => true
7 | * @example console.log(isPerfectNumber(28)) => true
8 | * @example console.log(isPerfectNumber(12))=> false
9 | */
10 |
11 | export const isPerfectNumber = (n: number): boolean => {
12 | if (n <= 0 || !Number.isInteger(n)) {
13 | return false
14 | }
15 | let sum = 1
16 | const sqrt = Math.sqrt(n)
17 | for (let i = 2; i < sqrt; i++) {
18 | if (n % i === 0) {
19 | sum += i + n / i
20 | }
21 | }
22 | if (sqrt === Math.floor(sqrt)) {
23 | sum += sqrt
24 | }
25 |
26 | return sum === n
27 | }
28 |
--------------------------------------------------------------------------------
/maths/perfect_square.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function perfectSquare
3 | * @description the square root of a number should be an integer
4 | * @see [Perfect square](https://www.cuemath.com/algebra/perfect-squares/)
5 | * @example Perfect square - 9, 16, 25
6 | * @param {num} number
7 | */
8 |
9 | export const perfectSquare = (num: number) => {
10 | return Number.isInteger(Math.sqrt(num))
11 | }
12 |
--------------------------------------------------------------------------------
/maths/prime_factorization.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description Get exponenets of each prime number in factorization of a number n
3 | * @param {number} n - A natural number.
4 | * @return {Map} - factorization of number n.
5 | * @see https://en.wikipedia.org/wiki/Integer_factorization
6 | * @example factorize(4) = Map {2 => 2}
7 | * @example factorize(5) = Map {5 => 1}
8 | */
9 | export const factorize = (n: number): Map => {
10 | const result: Map = new Map()
11 |
12 | for (let i = 2; i * i <= n; i++) {
13 | while (n % i == 0) {
14 | let occurence = result.get(i)
15 | if (!occurence) occurence = 0
16 | result.set(i, occurence + 1)
17 | n = n / i
18 | }
19 | }
20 | if (n > 1) {
21 | let occurence = result.get(n)
22 | if (!occurence) occurence = 0
23 | result.set(n, occurence + 1)
24 | }
25 |
26 | return result
27 | }
28 |
--------------------------------------------------------------------------------
/maths/primes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Implementation of the Sieve of Eratosthenes algorithm.
3 | *
4 | * @param limit An integer _n_ > 1
5 | * @returns All prime numbers from 2 through {@link limit}
6 | *
7 | * @see https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes
8 | */
9 | export function sieveOfEratosthenes(limit: number): number[] {
10 | if (!Number.isInteger(limit) || limit <= 1) {
11 | throw new Error('limit should be an integer greater than 1')
12 | }
13 |
14 | const maybePrime: boolean[] = new Array(limit + 1).fill(true)
15 | for (let i = 2; i * i <= limit; i++) {
16 | if (!maybePrime[i]) continue
17 | for (let j = i * i; j <= limit; j += i) {
18 | maybePrime[j] = false
19 | }
20 | }
21 |
22 | const primes: number[] = []
23 | for (let i = 2; i < maybePrime.length; i++) {
24 | if (maybePrime[i]) {
25 | primes.push(i)
26 | }
27 | }
28 |
29 | return primes
30 | }
31 |
32 | /**
33 | * Generator that yields primes.
34 | *
35 | * Inspired by https://gist.github.com/e-nikolov/cd94db0de2a6b70da144124ae93a6458
36 | */
37 | export function* primeGenerator() {
38 | type NumberGen = Generator
39 |
40 | function* filter(input: NumberGen, prime: number): NumberGen {
41 | while (true) {
42 | const { done, value } = input.next()
43 | if (done) break
44 | if (value % prime !== 0) yield value
45 | }
46 | }
47 |
48 | let chain: NumberGen = (function* () {
49 | let i = 2
50 | while (true) yield i++
51 | })()
52 |
53 | while (true) {
54 | const { done, value } = chain.next()
55 | if (done) break
56 | yield value
57 | chain = filter(chain, value)
58 | }
59 | }
60 |
61 | /**
62 | * @function isPrime
63 | * @description Determine if given number is prime.
64 | * @param {number} num - A natural number.
65 | * @return {boolean} - Whether the given number is prime.
66 | * @see https://en.wikipedia.org/wiki/Prime_number
67 | * @example isPrime(2) = false
68 | * @example isPrime(3) = true
69 | */
70 | export const isPrime = (num: number): boolean => {
71 | // raise corresponding errors upon invalid inputs
72 | if (num <= 0 || !Number.isInteger(num)) {
73 | throw new Error('only natural numbers are supported')
74 | }
75 |
76 | // handle input being 1
77 | if (num === 1) return false
78 |
79 | // iterate from 2 to the square root of num to find a factor
80 | // return false upon finding a factor
81 | for (let i = 2; i <= Math.sqrt(num); i++) {
82 | if (num % i === 0) return false
83 | }
84 |
85 | // if the entire loop runs without finding a factor, return true
86 | return true
87 | }
88 |
--------------------------------------------------------------------------------
/maths/pronic_number.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function pronicNumber
3 | * @description Checks whether a given number is a pronic number or not
4 | * @summary Pronic numbers, or oblong numbers as they are often referred to as,
5 | * are numbers which are the product of two consecutive integers. That is,
6 | * they are numbers of the form n*(n+1)
7 | *
8 | * For example, 20 is a pronic number since 20 = 4 * 5
9 | * @param num The number to check for being pronic
10 | * @returns {boolean} Whether the number is pronic or not
11 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Pronic_number)
12 | * @example pronicNumber(20) = true
13 | * @example pronicNumber(30) = true
14 | * @example pronicNumber(49) = false
15 | */
16 | const pronicNumber = (n: number) => {
17 | if (isNaN(n)) throw new Error('The input needs to be a number')
18 | if (!Number.isInteger(n) || n < 0)
19 | throw new Error('The input needs to be a non-negative integer')
20 | if (n === 0) return true
21 |
22 | return (
23 | !Number.isInteger(Math.sqrt(n)) &&
24 | Math.floor(Math.sqrt(n)) * Math.ceil(Math.sqrt(n)) === n
25 | )
26 | }
27 |
28 | export { pronicNumber }
29 |
--------------------------------------------------------------------------------
/maths/radians_to_degrees.ts:
--------------------------------------------------------------------------------
1 | /** * A function to get degrees from the radians
2 | * @param {number} radians - The input integer
3 | * @return {number} degrees of radians
4 | * @example radiansToDegrees(0.7853) => 45 | radiansTiDegrees(1.5708) => 90
5 | * @see https://en.m.wikipedia.org/wiki/Radian
6 | * @author MohdFaisalBidda
7 | */
8 |
9 | export const radiansToDegrees = (radians: number): number => {
10 | return (radians * 180) / Math.PI
11 | }
12 |
--------------------------------------------------------------------------------
/maths/series/hexagonal_numbers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function HexagonalNumbers
3 | * @description To generate the requested number of hexagonal numbers
4 | * @summary A hexagonal number, hₙ, is a figurate number which represents the number
5 | * of distinct dots in a pattern of dots consisting of the outlines of regular
6 | * hexagons with sides upto 'n' dots, when the hexagons are overlaid so that they share a common vertex
7 | *
8 | * The nth hexagonal number, hₙ, is calculated by the formula:
9 | * hₙ = n * (2n - 1)
10 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Hexagonal_number)
11 | * @see [OEIS](https://oeis.org/A000384)
12 | * @param {number} n - The number of Hexagonal numbers to generate
13 | * @returns {number[]} - An array containing first 'n' hexagonal numbers
14 | * @example HexagonalNumbers(10) = [ 1, 6, 15, 28, 45, 66, 91, 120, 153, 190 ]
15 | * @example HexagonalNumbers(15) = [ 1, 6, 15, 28, 45, 66, 91, 120, 153, 190, 231, 276, 325, 378, 435 ]
16 | */
17 | export const HexagonalNumbers = (n: number): number[] => {
18 | if (isNaN(n)) throw new Error('The input needs to be a number')
19 | if (!Number.isInteger(n) || n < 0)
20 | throw new Error('The input needs to be a non-negative integer')
21 | const hexagonalNumbers = []
22 |
23 | for (let i = 1; i <= n; i++) {
24 | hexagonalNumbers.push(i * (2 * i - 1))
25 | }
26 |
27 | return hexagonalNumbers
28 | }
29 |
--------------------------------------------------------------------------------
/maths/series/test/hexagonal_numbers.test.ts:
--------------------------------------------------------------------------------
1 | import { HexagonalNumbers } from '../hexagonal_numbers'
2 |
3 | describe('HexagonalNumbers', () => {
4 | it('should return the first 10 hexagonal numbers', () => {
5 | expect(HexagonalNumbers(10)).toStrictEqual([
6 | 1, 6, 15, 28, 45, 66, 91, 120, 153, 190
7 | ])
8 | })
9 |
10 | it('should return the first 5 hexagonal numbers', () => {
11 | expect(HexagonalNumbers(5)).toStrictEqual([1, 6, 15, 28, 45])
12 | })
13 |
14 | it('should return zero hexagonal numbers', () => {
15 | expect(HexagonalNumbers(0)).toStrictEqual([])
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/maths/sieve_of_eratosthenes.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function sieveOfEratosthenes
3 | * @description Find the prime numbers between 2 and n
4 | * @param {number} n - numbers set the limit that the algorithm needs to look to find the primes
5 | * @return {number[]} - List of prime numbers
6 | * @see https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes\
7 | * @example sieveOfEratosthenes(5) = [2,3,5]
8 | * @example sieveOfEratosthenes(10) = [2,3,5,7]
9 | */
10 |
11 | export function sieveOfEratosthenes(n: number): number[] {
12 | if (n < 0 || !Number.isInteger(n)) {
13 | throw new Error('Only natural numbers are supported')
14 | }
15 | const numbers = new Array(n + 1).fill(true)
16 | const primeNumbers: number[] = []
17 | for (let i = 2; i <= n; i++) {
18 | if (numbers[i]) {
19 | primeNumbers.push(i)
20 | for (let j = i + i; j <= n; j += i) {
21 | numbers[j] = false
22 | }
23 | }
24 | }
25 | return primeNumbers
26 | }
27 |
--------------------------------------------------------------------------------
/maths/signum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function signum
3 | * @description Returns the sign of a number
4 | * @summary The signum function is an odd mathematical function, which returns the
5 | * sign of the provided real number.
6 | * It can return 3 values: 1 for values greater than zero, 0 for zero itself,
7 | * and -1 for values less than zero
8 | * @param {Number} input
9 | * @returns {-1 | 0 | 1 | NaN} sign of input (and NaN if the input is not a number)
10 | * @see [Wikipedia](https://en.wikipedia.org/wiki/Sign_function)
11 | * @example signum(10) = 1
12 | * @example signum(0) = 0
13 | * @example signum(-69) = -1
14 | * @example signum("hello world") = NaN
15 | */
16 | export const signum = (num: number) => {
17 | if (num === 0) return 0
18 | if (num > 0) return 1
19 | if (num < 0) return -1
20 |
21 | return NaN
22 | }
23 |
--------------------------------------------------------------------------------
/maths/square_root.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function squareRoot
3 | * @description Finding the square root of a number using Newton's method.
4 | * @param {number} num - A number.
5 | * @param {number} precision - Precision of square root, 1e-15 by default.
6 | * @returns {number} - Square root of the given number.
7 | * @see https://www.geeksforgeeks.org/find-root-of-a-number-using-newtons-method/
8 | * @example SquareRoot(36) = 6
9 | * @example SquareRoot(50) = 7.0710678118654755
10 | */
11 |
12 | export const squareRoot = (num: number, precision: number = 1e-15): number => {
13 | if (num < 0) throw new Error('number must be non-negative number')
14 | if (num === 0) return 0
15 |
16 | let sqrt: number = num
17 | let curr: number
18 |
19 | while (true) {
20 | curr = 0.5 * (sqrt + num / sqrt)
21 | if (Math.abs(curr - sqrt) < precision) {
22 | return sqrt
23 | }
24 | sqrt = curr
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/maths/test/absolute_value.test.ts:
--------------------------------------------------------------------------------
1 | import { absoluteValue } from '../absolute_value'
2 |
3 | describe('absoluteValue', () => {
4 | it('should return the absolute value of zero', () => {
5 | const absoluteValueOfZero = absoluteValue(0)
6 | expect(absoluteValueOfZero).toBe(0)
7 | })
8 |
9 | it('should return the absolute value of a negative integer', () => {
10 | const absoluteValueOfNegativeInteger = absoluteValue(-34)
11 | expect(absoluteValueOfNegativeInteger).toBe(34)
12 | })
13 |
14 | it('should return the absolute value of a positive integer', () => {
15 | const absoluteValueOfPositiveInteger = absoluteValue(50)
16 | expect(absoluteValueOfPositiveInteger).toBe(50)
17 | })
18 |
19 | it('should return the absolute value of a positive floating number', () => {
20 | const absoluteValueOfPositiveFloating = absoluteValue(20.2034)
21 | expect(absoluteValueOfPositiveFloating).toBe(20.2034)
22 | })
23 |
24 | it('should return the absolute value of a negative floating number', () => {
25 | const absoluteValueOfNegativeFloating = absoluteValue(-20.2034)
26 | expect(absoluteValueOfNegativeFloating).toBe(20.2034)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/maths/test/aliquot_sum.test.ts:
--------------------------------------------------------------------------------
1 | import { aliquotSum } from '../aliquot_sum'
2 |
3 | test.each([
4 | [15, 9],
5 | [18, 21],
6 | [28, 28],
7 | [100, 117],
8 | [169, 14],
9 | [1729, 511],
10 | [15625, 3906]
11 | ])('Aliquot Sum of %i is %i', (num, expected) => {
12 | expect(aliquotSum(num)).toBe(expected)
13 | })
14 |
--------------------------------------------------------------------------------
/maths/test/armstrong_number.test.ts:
--------------------------------------------------------------------------------
1 | import { armstrongNumber } from '../armstrong_number'
2 |
3 | test.each([
4 | [9, true],
5 | [-310, false],
6 | [0, false],
7 | [407, true],
8 | [420, false],
9 | [92727, true],
10 | [13579, false]
11 | ])('i is an Armstrong number or not', (num, expected) => {
12 | expect(armstrongNumber(num)).toBe(expected)
13 | })
14 |
--------------------------------------------------------------------------------
/maths/test/binary_convert.test.ts:
--------------------------------------------------------------------------------
1 | import { binaryConvert } from '../binary_convert'
2 |
3 | describe('binaryConvert', () => {
4 | it('should return the correct value', () => {
5 | expect(binaryConvert(4)).toBe('100')
6 | })
7 | it('should return the correct value', () => {
8 | expect(binaryConvert(12)).toBe('1100')
9 | })
10 | it('should return the correct value of the sum from two number', () => {
11 | expect(binaryConvert(12 + 2)).toBe('1110')
12 | })
13 | it('should return the correct value of the subtract from two number', () => {
14 | expect(binaryConvert(245 - 56)).toBe('10111101')
15 | })
16 | it('should return the correct value', () => {
17 | expect(binaryConvert(254)).toBe('11111110')
18 | })
19 | it('should return the correct value', () => {
20 | expect(binaryConvert(63483)).toBe('1111011111111011')
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/maths/test/binomial_coefficient.test.ts:
--------------------------------------------------------------------------------
1 | import { binomialCoefficient } from '../binomial_coefficient'
2 |
3 | describe('binomialCoefficient', () => {
4 | it('should calculate the correct binomial coefficient', () => {
5 | // Test cases with expected results
6 | const testCases: [number, number, number][] = [
7 | [5, 2, 10],
8 | [10, 3, 120],
9 | [6, 0, 1],
10 | [4, 4, 1],
11 | [7, 5, 21],
12 | [10, 10, 1]
13 | ]
14 |
15 | // Iterate through each test case and verify the result
16 | testCases.forEach(([n, k, expected]) => {
17 | const result = binomialCoefficient(n, k)
18 | expect(result).toEqual(expected)
19 | })
20 | })
21 |
22 | it('should return 0 if k is larger than n or negative', () => {
23 | const invalidCases: [number, number][] = [
24 | [5, 6], // k is larger than n
25 | [10, -3], // k is negative
26 | [5, 10] // k is larger than n
27 | ]
28 |
29 | invalidCases.forEach(([n, k]) => {
30 | const result = binomialCoefficient(n, k)
31 | expect(result).toEqual(0)
32 | })
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/maths/test/calculate_mean.test.ts:
--------------------------------------------------------------------------------
1 | import { calculateMean } from '../calculate_mean'
2 |
3 | describe('Tests for AverageMean', () => {
4 | it('should be a function', () => {
5 | expect(typeof calculateMean).toEqual('function')
6 | })
7 |
8 | it('should throw error for invalid input', () => {
9 | expect(() => calculateMean([])).toThrow()
10 | })
11 |
12 | it('should return the mean of an array of consecutive numbers', () => {
13 | const meanFunction = calculateMean([1, 2, 3, 4])
14 | expect(meanFunction).toBe(2.5)
15 | })
16 |
17 | it('should return the mean of an array of numbers', () => {
18 | const meanFunction = calculateMean([10, 40, 100, 20])
19 | expect(meanFunction).toBe(42.5)
20 | })
21 |
22 | it('should return the mean of an array of decimal numbers', () => {
23 | const meanFunction = calculateMean([1.3, 12.67, 99.14, 20])
24 | expect(meanFunction).toBe(33.2775)
25 | })
26 |
27 | it('should return the mean of an array of numbers, including negatives', () => {
28 | const meanFunction = calculateMean([10, -40, 100, -20])
29 | expect(meanFunction).toBe(12.5)
30 | })
31 | })
32 |
--------------------------------------------------------------------------------
/maths/test/calculate_median.test.ts:
--------------------------------------------------------------------------------
1 | import { calculateMedian } from '../calculate_median'
2 |
3 | describe('Tests for CalculateMedian', () => {
4 | it('should be a function', () => {
5 | expect(typeof calculateMedian).toEqual('function')
6 | })
7 |
8 | it('should throw error for invalid input', () => {
9 | expect(() => calculateMedian([])).toThrowError(
10 | 'Input array must contain at least one number.'
11 | )
12 | })
13 |
14 | it('should return the median of an array of numbers - even length', () => {
15 | const medianFunction = calculateMedian([1, 2, 3, 4])
16 | expect(medianFunction).toBe(2.5)
17 | })
18 |
19 | it('should return the median of an array of numbers - odd length', () => {
20 | const medianFunction = calculateMedian([1, 2, 3, 4, 6, 8, 9])
21 | expect(medianFunction).toBe(4)
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/maths/test/degrees_to_radians.test.ts:
--------------------------------------------------------------------------------
1 | import { degreesToRadians } from '../degrees_to_radians'
2 |
3 | test('DegreesToRadians', () => {
4 | expect(degreesToRadians(0)).toBe(0)
5 | expect(degreesToRadians(45)).toBe(0.7853981633974483)
6 | expect(degreesToRadians(90)).toBe(1.5707963267948966)
7 | })
8 |
--------------------------------------------------------------------------------
/maths/test/digit_sum.test.ts:
--------------------------------------------------------------------------------
1 | import { digitSum } from '../digit_sum'
2 |
3 | describe('digitSum', () => {
4 | test.each([-42, -0.1, -1, 0.2, 3.3, NaN, -Infinity, Infinity])(
5 | 'should throw an error for non natural number %d',
6 | (num) => {
7 | expect(() => digitSum(num)).toThrowError(
8 | 'only natural numbers are supported'
9 | )
10 | }
11 | )
12 |
13 | test.each([
14 | [0, 0],
15 | [1, 1],
16 | [12, 3],
17 | [123, 6],
18 | [9045, 18],
19 | [1234567890, 45]
20 | ])('of %i should be %i', (num, expected) => {
21 | expect(digitSum(num)).toBe(expected)
22 | })
23 | })
24 |
--------------------------------------------------------------------------------
/maths/test/double_factorial_iterative.test.ts:
--------------------------------------------------------------------------------
1 | import { DoubleFactorialIterative } from '../double_factorial_iterative'
2 |
3 | describe('Double Factorial', () => {
4 | test.each([
5 | [4, 8],
6 | [5, 15],
7 | [10, 3840]
8 | ])('%i!! = %i', (n, expected) => {
9 | expect(DoubleFactorialIterative(n)).toBe(expected)
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/maths/test/euler_totient.test.ts:
--------------------------------------------------------------------------------
1 | import { phi } from '../euler_totient'
2 |
3 | const cases: [number, number][] = [
4 | [4, 2],
5 | [5, 4],
6 | [7, 6],
7 | [10, 4],
8 | [999, 648],
9 | [1000, 400],
10 | [1000000, 400000],
11 | [999999, 466560],
12 | [999999999999878, 473684210526240]
13 | ]
14 |
15 | describe('phi', () => {
16 | test.each(cases)('phi of %i should be %i', (num, expected) => {
17 | expect(phi(num)).toBe(expected)
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/maths/test/factorial.test.ts:
--------------------------------------------------------------------------------
1 | import { factorial } from '../factorial'
2 |
3 | describe('factorial', () => {
4 | test.each([-0.1, -1, -2, -42, 0.01, 0.42, 0.5, 1.337])(
5 | 'should throw an error for non natural number %d',
6 | (num) => {
7 | expect(() => factorial(num)).toThrowError(
8 | 'only natural numbers are supported'
9 | )
10 | }
11 | )
12 |
13 | test.each([
14 | [1, 1],
15 | [3, 6],
16 | [5, 120],
17 | [10, 3628800]
18 | ])('of %i should be %i', (num, expected) => {
19 | expect(factorial(num)).toBe(expected)
20 | })
21 |
22 | test('of 1 should be 0 by definition', () => {
23 | expect(factorial(0)).toBe(1)
24 | })
25 | })
26 |
--------------------------------------------------------------------------------
/maths/test/factors.test.ts:
--------------------------------------------------------------------------------
1 | import { findFactors } from '../factors'
2 |
3 | describe('findFactors', () => {
4 | test.each([-890, -5.56, -7, 0, 0.73, 4.2, NaN, -Infinity, Infinity])(
5 | 'should throw an error for non natural number %d',
6 | (num) => {
7 | expect(() => findFactors(num)).toThrowError(
8 | 'Only natural numbers are supported.'
9 | )
10 | }
11 | )
12 |
13 | test.each([
14 | [1, new Set([1])],
15 | [2, new Set([1, 2])],
16 | [4, new Set([1, 2, 4])],
17 | [6, new Set([1, 2, 3, 6])],
18 | [16, new Set([1, 2, 4, 8, 16])]
19 | ])('of %i should return the correct set of its factors', (num, expected) => {
20 | expect(findFactors(num)).toStrictEqual(expected)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/maths/test/fibonacci.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | nthFibonacciUsingFormula,
3 | nthFibonacci,
4 | nthFibonacciRecursively
5 | } from '../fibonacci'
6 |
7 | const test = (func: (n: number) => number) =>
8 | it.each([
9 | [0, 0],
10 | [1, 1],
11 | [2, 1],
12 | [5, 5],
13 | [10, 55],
14 | [15, 610]
15 | ])('fib(%i) = %i', (n, expected) => expect(func(n)).toBe(expected))
16 | describe('Fibonacci iterative', () => test(nthFibonacci))
17 | describe('Fibonacci recursive', () => test(nthFibonacciRecursively))
18 | describe('Fibonacci Using formula', () => test(nthFibonacciUsingFormula))
19 |
--------------------------------------------------------------------------------
/maths/test/find_min.test.ts:
--------------------------------------------------------------------------------
1 | import { findMin } from '../find_min'
2 |
3 | describe('findMin', () => {
4 | test.each([
5 | [[1, 2, 3, 4, 5, 6], 1],
6 | [[87, 6, 13, 999], 6],
7 | [[0.8, 0.2, 0.3, 0.5], 0.2],
8 | [[1, 0.1, -1], -1]
9 | ])('of this array should be %i', (nums, expected) => {
10 | expect(findMin(nums)).toBe(expected)
11 | })
12 |
13 | test('of arrays with length 0 should error', () => {
14 | expect(() => findMin([])).toThrowError(
15 | 'array must have length of 1 or greater'
16 | )
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/maths/test/gaussian_elimination.test.ts:
--------------------------------------------------------------------------------
1 | import { gaussianElimination } from '../gaussian_elimination'
2 |
3 | describe('gaussianElimination', () => {
4 | it('should solve system of linear equations', () => {
5 | const A: number[][] = [
6 | [3.0, 2.0, -4.0, 3.0],
7 | [2.0, 3.0, 3.0, 15.0],
8 | [5.0, -3, 1.0, 14.0]
9 | ]
10 |
11 | let solution: number[] = gaussianElimination(A)
12 | solution = solution.map((x) => Math.round(x))
13 |
14 | expect(solution.map((x) => Math.round(x))).toEqual([3, 1, 2])
15 | })
16 |
17 | it('should solve a 2x2 system of linear equations', () => {
18 | const A: number[][] = [
19 | [2.0, 1.0, 5.0],
20 | [1.0, -3.0, 6.0]
21 | ]
22 |
23 | let solution: number[] = gaussianElimination(A)
24 | solution = solution.map((x) => Math.round(x))
25 |
26 | expect(solution.map((x) => Math.round(x))).toEqual([3, -1])
27 | })
28 |
29 | it('should handle a system with no solution', () => {
30 | const A: number[][] = [
31 | [1.0, 2.0, 3.0, 4.0],
32 | [2.0, 4.0, 6.0, 7.0],
33 | [3.0, 6.0, 9.0, 10.0]
34 | ]
35 |
36 | let solution: number[] = gaussianElimination(A)
37 | solution = solution.filter((value) => !isNaN(value))
38 |
39 | expect(solution).toEqual([])
40 | })
41 | })
42 |
--------------------------------------------------------------------------------
/maths/test/greatest_common_factor.test.ts:
--------------------------------------------------------------------------------
1 | import { binaryGCF, greatestCommonFactor } from '../greatest_common_factor'
2 |
3 | describe('binaryGCF', () => {
4 | test.each([
5 | [12, 8, 4],
6 | [1, 199, 1],
7 | [88, 40, 8],
8 | [288, 160, 32]
9 | ])('of given two numbers is correct', (numa, numb, expected) => {
10 | expect(binaryGCF(numa, numb)).toBe(expected)
11 | })
12 |
13 | test('only whole numbers should be accepted', () => {
14 | expect(() => binaryGCF(0.5, 0.8)).toThrowError(
15 | 'numbers must be natural to determine factors'
16 | )
17 | })
18 |
19 | test('only positive numbers should be accepted', () => {
20 | expect(() => binaryGCF(-2, 4)).toThrowError(
21 | 'numbers must be natural to determine factors'
22 | )
23 | })
24 | })
25 |
26 | describe('greatestCommonFactor', () => {
27 | test.each([
28 | [[7], 7],
29 | [[12, 8], 4],
30 | [[1, 199], 1],
31 | [[88, 40, 32], 8],
32 | [[288, 160, 64], 32]
33 | ])('of given list is correct', (nums, expected) => {
34 | expect(greatestCommonFactor(nums)).toBe(expected)
35 | })
36 |
37 | test('the list should consist of at least one number', () => {
38 | expect(() => greatestCommonFactor([])).toThrowError(
39 | 'at least one number must be passed in'
40 | )
41 | })
42 | })
43 |
--------------------------------------------------------------------------------
/maths/test/hamming_distance.test.ts:
--------------------------------------------------------------------------------
1 | import { hammingDistance } from '../hamming_distance'
2 |
3 | test.each([
4 | ['happy', 'homie', 4],
5 | ['hole', 'home', 1],
6 | ['cathrine', 'caroline', 3],
7 | ['happiness', 'dizziness', 4]
8 | ])('Hamming Distance', (str1, str2, result) => {
9 | expect(hammingDistance(str1, str2)).toBe(result)
10 | })
11 |
--------------------------------------------------------------------------------
/maths/test/is_divisible.test.ts:
--------------------------------------------------------------------------------
1 | import { isDivisible } from '../is_divisible'
2 |
3 | describe('isDivisible', () => {
4 | test.each([
5 | [1, 1],
6 | [6, 3],
7 | [101, 1],
8 | [5555, 5],
9 | [143, 13],
10 | [535, 107],
11 | [855144, 999],
12 | [100000, 10],
13 | [1.5, 0.5]
14 | ])('%f is divisible by %f', (num1, num2) => {
15 | expect(isDivisible(num1, num2)).toBe(true)
16 | })
17 |
18 | test.each([
19 | [1, 2],
20 | [61, 3],
21 | [120, 11],
22 | [5556, 5],
23 | [10, 9],
24 | [75623, 3],
25 | [45213, 11],
26 | [784, 24],
27 | [1.2, 0.35]
28 | ])('%f is not divisible by %f', (num1, num2) => {
29 | expect(isDivisible(num1, num2)).toBe(false)
30 | })
31 |
32 | test('should not divide by 0', () => {
33 | expect(() => isDivisible(10, 0)).toThrow()
34 | })
35 | })
36 |
--------------------------------------------------------------------------------
/maths/test/is_even.test.ts:
--------------------------------------------------------------------------------
1 | import { isEven } from '../is_even'
2 |
3 | describe('isEven', () => {
4 | test.each([
5 | [2, true],
6 | [1, false],
7 | [0, true],
8 | [-1, false],
9 | [-2, true]
10 | ])('correct output for for %i', (nums, expected) => {
11 | expect(isEven(nums)).toBe(expected)
12 | })
13 |
14 | test('only whole numbers should be accepted', () => {
15 | expect(() => isEven(0.5)).toThrowError('only integers can be even or odd')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/maths/test/is_leap_year.test.ts:
--------------------------------------------------------------------------------
1 | import { isLeapYear } from '../is_leap_year'
2 |
3 | describe('isLeapYear', () => {
4 | test.each([4, 8, 12, 2004])(
5 | 'a year is a leap year it is divisible by 4 but not by 400 like %i',
6 | (year) => {
7 | expect(year % 4 === 0).toBe(true)
8 | expect(year % 400 === 0).toBe(false)
9 | expect(isLeapYear(year)).toBe(true)
10 | }
11 | )
12 |
13 | test.each([400, 800, 1200, 1600, 2000, 2400, 40000])(
14 | 'a year is a leap year it is divisible by 400 like %i',
15 | (year) => {
16 | expect(year % 400 === 0).toBe(true)
17 | expect(isLeapYear(year)).toBe(true)
18 | }
19 | )
20 |
21 | test.each([1, 313, 1997, 2001, 2021, 13337])(
22 | 'a year is not a leap year if it is not divisible by 4 like %i',
23 | (year) => {
24 | expect(year % 4 === 0).toBe(false)
25 | expect(isLeapYear(year)).toBe(false)
26 | }
27 | )
28 |
29 | test.each([100, 200, 300, 700, 2100])(
30 | 'a year is not a leap year if it is divisible by 100 but not by 400 like %i',
31 | (year) => {
32 | expect(year % 100 === 0).toBe(true)
33 | expect(year % 400 === 0).toBe(false)
34 | expect(isLeapYear(year)).toBe(false)
35 | }
36 | )
37 |
38 | test.each([1, 2022, 3000000])(
39 | 'a year is supported if it is a natural number > 0 like %i',
40 | (year) => {
41 | expect(year > 0).toBe(true)
42 | expect(Number.isInteger(year)).toBe(true)
43 | expect(() => isLeapYear(year)).not.toThrow()
44 | }
45 | )
46 |
47 | test.each([-1, -10, -Infinity])(
48 | 'a year is not supported if it is negative like %i',
49 | (year) => {
50 | expect(year < 0).toBe(true)
51 | expect(() => isLeapYear(year)).toThrow(
52 | 'year must be a natural number > 0'
53 | )
54 | }
55 | )
56 |
57 | test.each([0.1, 1.2, 4.2])(
58 | 'a year is not supported if it is not an integer %d',
59 | (year) => {
60 | expect(Number.isInteger(year)).toBe(false)
61 | expect(() => isLeapYear(year)).toThrow(
62 | 'year must be a natural number > 0'
63 | )
64 | }
65 | )
66 |
67 | test('a year is not supported if it is 0', () => {
68 | expect(() => isLeapYear(0)).toThrow('year must be a natural number > 0')
69 | })
70 | })
71 |
--------------------------------------------------------------------------------
/maths/test/is_odd.test.ts:
--------------------------------------------------------------------------------
1 | import { isOdd } from '../is_odd'
2 |
3 | describe('isOdd', () => {
4 | test.each([
5 | [2, false],
6 | [1, true],
7 | [0, false],
8 | [-1, true],
9 | [-2, false]
10 | ])('correct output for for %i', (nums, expected) => {
11 | expect(isOdd(nums)).toBe(expected)
12 | })
13 |
14 | test('only whole numbers should be accepted', () => {
15 | expect(() => isOdd(0.5)).toThrowError('only integers can be even or odd')
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/maths/test/is_palindrome.test.ts:
--------------------------------------------------------------------------------
1 | import { isPalindrome } from '../is_palindrome'
2 |
3 | describe('isPalindrome', () => {
4 | test.each([
5 | [0, true],
6 | [1, true],
7 | [5, true],
8 | [1234, false],
9 | [12321, true],
10 | [31343, false],
11 | [-1, false],
12 | [-11, false],
13 | [10, false]
14 | ])('correct output for %i', (nums, expected) => {
15 | expect(isPalindrome(nums)).toBe(expected)
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/maths/test/is_square_free.test.ts:
--------------------------------------------------------------------------------
1 | import { isSquareFree } from '../is_square_free'
2 |
3 | describe('isSquareFree', () => {
4 | test.each([1, 2, 3, 5, 7, 10, 26, 2 * 3, 3 * 5 * 7, 11 * 13 * 17 * 19])(
5 | '%i is square free',
6 | (input) => {
7 | expect(isSquareFree(input)).toBe(true)
8 | }
9 | )
10 | test.each([
11 | 20,
12 | 48,
13 | 2 * 7 * 7,
14 | 2 * 3 * 3,
15 | 5 * 5 * 7,
16 | 2 * 3 * 13 * 13 * 17,
17 | 4 * 4 * 4,
18 | 2 * 2,
19 | 3 * 3,
20 | 5 * 5,
21 | 100,
22 | 0
23 | ])('%i is not square free', (input) => {
24 | expect(isSquareFree(input)).toBe(false)
25 | })
26 | })
27 |
--------------------------------------------------------------------------------
/maths/test/juggler_sequence.test.ts:
--------------------------------------------------------------------------------
1 | import { jugglerSequence } from '../juggler_sequence'
2 |
3 | describe('jugglerSequence', () => {
4 | it.each([
5 | [3, 3, 36],
6 | [3, 5, 2],
7 | [7, 3, 2],
8 | [5, 1, 11]
9 | ])('%i at index %i should equal %i', (a, n, k) => {
10 | expect(jugglerSequence(a, n)).toBe(k)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/maths/test/lowest_common_multiple.test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | binaryLCM,
3 | lowestCommonMultiple,
4 | naiveLCM
5 | } from '../lowest_common_multiple'
6 |
7 | describe('naiveLCM', () => {
8 | test.each([
9 | [[3, 4], 12],
10 | [[8, 6], 24],
11 | [[5, 8, 3], 120],
12 | [[0.8, 0.4], 0.8]
13 | ])('of given two numbers is correct', (nums, expected) => {
14 | expect(naiveLCM(nums)).toBe(expected)
15 | })
16 |
17 | test('only positive numbers should be accepted', () => {
18 | expect(() => naiveLCM([-2, -3])).toThrowError(
19 | 'numbers must be positive to determine lowest common multiple'
20 | )
21 | })
22 |
23 | test('at least one number must be passed in', () => {
24 | expect(() => naiveLCM([])).toThrowError(
25 | 'at least one number must be passed in'
26 | )
27 | })
28 | })
29 |
30 | describe('binaryLCM', () => {
31 | test.each([
32 | [3, 4, 12],
33 | [8, 6, 24],
34 | [8, 16, 16]
35 | ])('of given two numbers is correct', (numa, numb, expected) => {
36 | expect(binaryLCM(numa, numb)).toBe(expected)
37 | })
38 |
39 | test('only natural numbers should be accepted', () => {
40 | expect(() => binaryLCM(-2, -3)).toThrowError()
41 | expect(() => binaryLCM(2, -3)).toThrowError()
42 | expect(() => binaryLCM(-2, 3)).toThrowError()
43 | })
44 |
45 | test('should throw when any of the inputs is not an int', () => {
46 | expect(() => binaryLCM(1, 2.5)).toThrowError()
47 | expect(() => binaryLCM(1.5, 2)).toThrowError()
48 | })
49 | })
50 |
51 | describe('lowestCommonMultiple', () => {
52 | test.each([
53 | [[3, 4], 12],
54 | [[8, 6], 24],
55 | [[5, 8, 3], 120],
56 | [[8, 16], 16]
57 | ])('of given two numbers is correct', (nums, expected) => {
58 | expect(lowestCommonMultiple(nums)).toBe(expected)
59 | })
60 |
61 | test('only positive numbers should be accepted', () => {
62 | expect(() => lowestCommonMultiple([-2, -3])).toThrowError()
63 | })
64 |
65 | test('at least one number must be passed in', () => {
66 | expect(() => lowestCommonMultiple([])).toThrowError(
67 | 'at least one number must be passed in'
68 | )
69 | })
70 | })
71 |
--------------------------------------------------------------------------------
/maths/test/number_of_digits.test.ts:
--------------------------------------------------------------------------------
1 | import { numberOfDigits } from '../number_of_digits'
2 |
3 | describe('numberOfDigits', () => {
4 | test.each([-890, -5.56, -7, 0, 0.73, 4.2, NaN, -Infinity, Infinity])(
5 | 'should throw an error for non natural number %d',
6 | (num) => {
7 | expect(() => numberOfDigits(num)).toThrowError(
8 | 'only natural numbers are supported'
9 | )
10 | }
11 | )
12 |
13 | test.each([
14 | [1, 1],
15 | [18, 2],
16 | [549, 3],
17 | [7293, 4],
18 | [1234567890, 10]
19 | ])('of %i should be %i', (num, expected) => {
20 | expect(numberOfDigits(num)).toBe(expected)
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/maths/test/pascals_triangle.test.ts:
--------------------------------------------------------------------------------
1 | import { pascalsTriangle } from '../pascals_triangle'
2 |
3 | describe('pascalsTriangle', () => {
4 | it.each([
5 | [2, [1, 1]],
6 | [4, [1, 3, 3, 1]],
7 | [6, [1, 5, 10, 10, 5, 1]]
8 | ])('The %i th row should equal to %i', (n, expectation) => {
9 | expect(pascalsTriangle(n)).toEqual(expectation)
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/maths/test/perfect_cube.test.ts:
--------------------------------------------------------------------------------
1 | import { perfectCube } from '../perfect_cube'
2 |
3 | describe('perfect cube tests', () => {
4 | it.each([
5 | [27, true],
6 | [9, false],
7 | [8, true],
8 | [12, false],
9 | [64, true],
10 | [151, false],
11 | [125, true]
12 | ])('The return value of %i should be %s', (n, expectation) => {
13 | expect(perfectCube(n)).toBe(expectation)
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/maths/test/perfect_numbers.test.ts:
--------------------------------------------------------------------------------
1 | import { isPerfectNumber } from '../perfect_number'
2 |
3 | describe('perfect Numbers tests', () => {
4 | it.each([
5 | [6, true],
6 | [28, true],
7 | [496, true],
8 | [8128, true],
9 | [12, false],
10 | [42, false],
11 | [100, false],
12 | [0, false],
13 | [-1, false],
14 | [1.5, false]
15 | ])('The return value of %i should be %s', (n, expectation) => {
16 | expect(isPerfectNumber(n)).toBe(expectation)
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/maths/test/perfect_square.test.ts:
--------------------------------------------------------------------------------
1 | import { perfectSquare } from '../perfect_square'
2 |
3 | test('Check perfect square', () => {
4 | expect(perfectSquare(16)).toBe(true)
5 | expect(perfectSquare(12)).toBe(false)
6 | expect(perfectSquare(19)).toBe(false)
7 | expect(perfectSquare(25)).toBe(true)
8 | expect(perfectSquare(42)).toBe(false)
9 | })
10 |
--------------------------------------------------------------------------------
/maths/test/prime_factorization.test.ts:
--------------------------------------------------------------------------------
1 | import { factorize } from '../prime_factorization'
2 |
3 | interface TestCase {
4 | n: number
5 | expected: Map
6 | }
7 |
8 | const cases: TestCase[] = [
9 | { n: 4, expected: new Map([[2, 2]]) },
10 | { n: 5, expected: new Map([[5, 1]]) },
11 | { n: 7, expected: new Map([[7, 1]]) },
12 | {
13 | n: 10,
14 | expected: new Map([
15 | [2, 1],
16 | [5, 1]
17 | ])
18 | },
19 | {
20 | n: 999,
21 | expected: new Map([
22 | [3, 3],
23 | [37, 1]
24 | ])
25 | },
26 | {
27 | n: 999999999999878,
28 | expected: new Map([
29 | [2, 1],
30 | [19, 1],
31 | [26315789473681, 1]
32 | ])
33 | }
34 | ]
35 |
36 | describe('factorize', () => {
37 | test.each(cases)(
38 | 'prime factorization of $n should be $expected',
39 | ({ n, expected }) => {
40 | expect(factorize(n)).toEqual(expected)
41 | }
42 | )
43 | })
44 |
--------------------------------------------------------------------------------
/maths/test/primes.test.ts:
--------------------------------------------------------------------------------
1 | import { isPrime, primeGenerator, sieveOfEratosthenes } from '../primes'
2 |
3 | describe(sieveOfEratosthenes, () => {
4 | test.each([-1, 0, 1, 2.123, 1337.80085])(
5 | 'should throw an error when given an invalid limit=%d',
6 | (invalidLimit) => {
7 | expect(() => sieveOfEratosthenes(invalidLimit)).toThrow()
8 | }
9 | )
10 | test.each([
11 | [2, [2]],
12 | [3, [2, 3]],
13 | [4, [2, 3]],
14 | [5, [2, 3, 5]],
15 | [6, [2, 3, 5]],
16 | [7, [2, 3, 5, 7]],
17 | [8, [2, 3, 5, 7]],
18 | [9, [2, 3, 5, 7]]
19 | ])(
20 | 'should return the expected list of primes for limit=%i',
21 | (limit, expected) => {
22 | expect(sieveOfEratosthenes(limit)).toEqual(expected)
23 | }
24 | )
25 | })
26 |
27 | describe(primeGenerator, () => {
28 | it('should generate prime numbers', () => {
29 | const primeGen = primeGenerator()
30 | for (let i = 0; i < 100; i++) {
31 | const prime = primeGen.next().value
32 |
33 | if (prime === undefined) {
34 | throw new Error('prime generator returned undefined')
35 | }
36 |
37 | expect(isPrime(prime)).toBe(true)
38 | }
39 | })
40 | })
41 |
42 | describe('IsPrime', () => {
43 | test.each([
44 | [1, false],
45 | [2, true],
46 | [3, true],
47 | [3 * 3, false],
48 | [13, true],
49 | [24, false]
50 | ])('correct output for %i', (nums, expected) => {
51 | expect(isPrime(nums)).toBe(expected)
52 | })
53 |
54 | test.each([-890, -5.56, -7, 0.73, 4.2, NaN, -Infinity, Infinity])(
55 | 'should throw an error for non natural number %d',
56 | (num) => {
57 | expect(() => isPrime(num)).toThrowError(
58 | 'only natural numbers are supported'
59 | )
60 | }
61 | )
62 | })
63 |
--------------------------------------------------------------------------------
/maths/test/pronic_number.test.ts:
--------------------------------------------------------------------------------
1 | import { pronicNumber } from '../pronic_number'
2 |
3 | test.each([
4 | [0, true],
5 | [10, false],
6 | [30, true],
7 | [69, false],
8 | [420, true]
9 | ])('Pronic Number', (number, result) => {
10 | expect(pronicNumber(number)).toBe(result)
11 | })
12 |
--------------------------------------------------------------------------------
/maths/test/radians_to_degrees.test.ts:
--------------------------------------------------------------------------------
1 | import { radiansToDegrees } from '../radians_to_degrees'
2 |
3 | test('RadiansToDegrees', () => {
4 | expect(radiansToDegrees(0)).toBe(0)
5 | expect(radiansToDegrees(0.7853981633974483)).toBe(45)
6 | expect(radiansToDegrees(1.5707963267948966)).toBe(90)
7 | })
8 |
--------------------------------------------------------------------------------
/maths/test/sieve_of_eratosthenes.test.ts:
--------------------------------------------------------------------------------
1 | import { sieveOfEratosthenes } from '../sieve_of_eratosthenes'
2 |
3 | describe('Sieve of Eratosthenes', () => {
4 | test.each([-2, 0.1, -0.01, 2.2])(
5 | 'should throw a error for non natural number',
6 | (n) => {
7 | expect(() => sieveOfEratosthenes(n)).toThrow(
8 | 'Only natural numbers are supported'
9 | )
10 | }
11 | )
12 |
13 | test.each([
14 | [5, [2, 3, 5]],
15 | [11, [2, 3, 5, 7, 11]],
16 | [30, [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]]
17 | ])('of %i should be %o', (num, expected) => {
18 | expect(sieveOfEratosthenes(num)).toStrictEqual(expected)
19 | })
20 | })
21 |
--------------------------------------------------------------------------------
/maths/test/signum.test.ts:
--------------------------------------------------------------------------------
1 | import { signum } from '../signum'
2 |
3 | test.each([
4 | [10, 1],
5 | [0, 0],
6 | [-69, -1],
7 | [NaN, NaN]
8 | ])('The sign of %i is %i', (num, expected) => {
9 | expect(signum(num)).toBe(expected)
10 | })
11 |
--------------------------------------------------------------------------------
/maths/test/square_root.test.ts:
--------------------------------------------------------------------------------
1 | import { squareRoot } from '../square_root'
2 |
3 | describe('squareRoot', () => {
4 | test.each([-1, -10, -2.4])(
5 | 'should throw an error for negative numbers',
6 | (n: number) => {
7 | expect(() => squareRoot(n)).toThrow('number must be non-negative number')
8 | }
9 | )
10 |
11 | test.each([0, 1, 4, 9, 16, 25])(
12 | 'should return correct rational square root value for %i',
13 | (n: number) => {
14 | expect(squareRoot(n)).toBeCloseTo(Math.sqrt(n))
15 | }
16 | )
17 |
18 | test.each([2, 15, 20, 40, 99, 10032])(
19 | 'should return correct irrational square root value %i',
20 | (n: number) => {
21 | expect(squareRoot(n)).toBeCloseTo(Math.sqrt(n))
22 | }
23 | )
24 | })
25 |
--------------------------------------------------------------------------------
/maths/test/ugly_numbers.test.ts:
--------------------------------------------------------------------------------
1 | import { uglyNumbers } from '../ugly_numbers'
2 |
3 | test('Ugly Numbers', () => {
4 | const uglyNums = uglyNumbers()
5 | expect(
6 | Array(11)
7 | .fill(undefined)
8 | .map(() => uglyNums.next().value)
9 | ).toEqual([1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15])
10 | })
11 |
--------------------------------------------------------------------------------
/maths/ugly_numbers.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @generator
3 | * @description Generates ugly numbers
4 | * @summary Ugly numbers are natural numbers whose only prime factors are 2, 3 and 5.
5 | * They can be represented in the form 2^a * 3^b * 5*c. By convention, 1 is also considered to be
6 | * an ugly number.
7 | * The first few terms of the sequence are: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, 16, 18, 20...
8 | *
9 | * For the provided n, the nth ugly number shall be computed.
10 | * @see [GeeksForGeeks](https://www.geeksforgeeks.org/ugly-numbers/)
11 | */
12 | function* uglyNumbers(): Generator {
13 | yield 1
14 |
15 | let idx2 = 0,
16 | idx3 = 0,
17 | idx5 = 0
18 | const uglyNums = [1]
19 |
20 | let nextx2: number, nextx3: number, nextx5: number, nextUglyNum: number
21 |
22 | while (true) {
23 | nextx2 = uglyNums[idx2] * 2
24 | nextx3 = uglyNums[idx3] * 3
25 | nextx5 = uglyNums[idx5] * 5
26 |
27 | nextUglyNum = Math.min(nextx2, nextx3, nextx5)
28 | yield nextUglyNum
29 |
30 | if (nextx2 === nextUglyNum) idx2++
31 | if (nextx3 === nextUglyNum) idx3++
32 | if (nextx5 === nextUglyNum) idx5++
33 |
34 | uglyNums.push(nextUglyNum)
35 | }
36 | }
37 |
38 | export { uglyNumbers }
39 |
--------------------------------------------------------------------------------
/maths/zellers_congruence.ts:
--------------------------------------------------------------------------------
1 | export enum Calendar {
2 | Gregorian,
3 | Julian
4 | }
5 |
6 | /**
7 | * @function getWeekday
8 | * @description Calculate the day of the week for any Julian or Gregorian calendar date.
9 | * @param {number} year - Year with century.
10 | * @param {number} month - Month of the year (1-12).
11 | * @param {number} day - Day of the month (1-31).
12 | * @return {number} Day of the week, where 0 represents Sunday.
13 | * @see https://en.wikipedia.org/wiki/Zeller's_congruence
14 | * @example getWeekday(2000, 1, 1) = 6
15 | * @example getWeekday(1500, 1, 1, Calendar.Julian) = 3
16 | */
17 | export const getWeekday = (
18 | year: number,
19 | month: number,
20 | day: number,
21 | calendar: Calendar = Calendar.Gregorian
22 | ): number => {
23 | // Input validation
24 | if (!Number.isInteger(year) || year < 1) {
25 | throw new Error('Year must be an integer greater than 0')
26 | }
27 |
28 | if (!Number.isInteger(month) || month < 1 || month > 12) {
29 | throw new Error('Month must be an integer between 1 and 12')
30 | }
31 |
32 | if (!Number.isInteger(day) || day < 1 || day > 31) {
33 | throw new Error('Day must be an integer between 1 and 31')
34 | }
35 |
36 | // Move January and February to the end of the previous year
37 | if (month < 3) {
38 | month += 12
39 | year--
40 | }
41 |
42 | const century = Math.floor(year / 100)
43 | year %= 100
44 |
45 | let weekday: number | undefined = undefined
46 | if (calendar === Calendar.Gregorian) {
47 | weekday =
48 | (day +
49 | Math.floor(2.6 * (month + 1)) +
50 | year +
51 | Math.floor(year / 4) +
52 | Math.floor(century / 4) +
53 | 5 * century) %
54 | 7
55 | } else {
56 | weekday =
57 | (day +
58 | Math.floor(2.6 * (month + 1)) +
59 | year +
60 | Math.floor(year / 4) +
61 | 5 +
62 | 6 * century) %
63 | 7
64 | }
65 |
66 | // Convert to Sunday being 0
67 | return (weekday + 6) % 7
68 | }
69 |
--------------------------------------------------------------------------------
/other/is_sorted_array.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function isSortedArray
3 | * @description Checks if the array is sorted.
4 | * @param {number[]} arr - array to check if it is sorted
5 | * @returns {boolean} - true if the array is sorted and false if it's not sorted
6 | * @example isSortedArray([1,2,3]) => true
7 | * @example isSortedArray([9,2,3]) => false
8 | */
9 | export function isSortedArray(arr: number[]): boolean {
10 | for (let i = 0; i < arr.length - 1; i++) {
11 | if (arr[i] >= arr[i + 1]) {
12 | return false
13 | }
14 | }
15 | return true
16 | }
17 |
--------------------------------------------------------------------------------
/other/parse_nested_brackets.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function parseNestedBrackets
3 | * @description Parse nested brackets algorithm for a string.
4 | * @param {string} text - text to parse
5 | * @param {string} openBrackets - open brackets
6 | * @param {string} closingBrackets - closing brackets
7 | * @returns {string[]} - array of the tags
8 | * @example parseNestedBrackets(``) => [ '', '' ]
9 | * @example parseNestedBrackets(
10 | * `THIS IS SAMPLE TEXT(MAIN hoge 0.1 fuga(ITEM fuga hoge)hoge(ITEM2 nogami(ABBR)))`,
11 | * { openBrackets: '(', closingBrackets: ')' }) =>
12 | * [
13 | '(MAIN hoge 0.1 fuga(ITEM fuga hoge)hoge(ITEM2 nogami(ABBR)))',
14 | '(ITEM fuga hoge)',
15 | '(ITEM2 nogami(ABBR))',
16 | '(ABBR)'
17 | ]
18 | */
19 | export const parseNestedBrackets = (
20 | text: string,
21 | openBrackets = '<',
22 | closingBrackets = '>'
23 | ) => {
24 | let array: string[] = [] // The array of the tags in this present floor.
25 | let prFloor = 0 // The present floor.
26 | let begin = 0, // The begin index of the tag.
27 | end = 0 // The end index of the tag.
28 | for (let i = 0; i < text.length; i++) {
29 | if (text[i] === openBrackets) {
30 | prFloor++
31 | if (prFloor === 1) begin = i
32 | } else if (text[i] === closingBrackets) {
33 | if (prFloor === 1) {
34 | end = i
35 | const tag = text.slice(begin + 1, end)
36 | // push the tag in this present floor.
37 | array.push(`${openBrackets}${tag}${closingBrackets}`)
38 | // push the array of the tags in the next floor.
39 | array = array.concat(
40 | parseNestedBrackets(tag, openBrackets, closingBrackets)
41 | )
42 | }
43 | prFloor--
44 | }
45 | }
46 | return array
47 | }
48 |
--------------------------------------------------------------------------------
/other/shuffle_array.ts:
--------------------------------------------------------------------------------
1 | export function shuffleArray(arr: number[]) {
2 | for (let i = arr.length - 1; i > 0; i--) {
3 | const j = Math.floor(Math.random() * (i + 1))
4 | const temp = arr[i]
5 | arr[i] = arr[j]
6 | arr[j] = temp
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/other/test/is_sorted_array.test.ts:
--------------------------------------------------------------------------------
1 | import { isSortedArray } from '../is_sorted_array'
2 |
3 | describe('isSortedArray', () => {
4 | test.each([
5 | { arr: [100], expected: true },
6 | { arr: [9, 2, 3], expected: false },
7 | { arr: [1, 2, 3], expected: true }
8 | ])('The return value of ($arr) should be $expected', ({ arr, expected }) => {
9 | expect(isSortedArray(arr)).toEqual(expected)
10 | })
11 | })
12 |
--------------------------------------------------------------------------------
/other/test/parse_nested_brackets.test.ts:
--------------------------------------------------------------------------------
1 | import { parseNestedBrackets } from '../parse_nested_brackets'
2 |
3 | describe('parseNestedBrackets', () => {
4 | it('should return an array of the tags', () => {
5 | expect(parseNestedBrackets('')).toEqual([
6 | '',
7 | ''
8 | ])
9 | })
10 | it('should return an array of the tags (nested)', () => {
11 | expect(
12 | parseNestedBrackets(
13 | `THIS IS SAMPLE TEXT(MAIN hoge 0.1 fuga(ITEM fuga hoge)hoge(ITEM2 nogami(ABBR)))`,
14 | '(',
15 | ')'
16 | )
17 | ).toEqual([
18 | '(MAIN hoge 0.1 fuga(ITEM fuga hoge)hoge(ITEM2 nogami(ABBR)))',
19 | '(ITEM fuga hoge)',
20 | '(ITEM2 nogami(ABBR))',
21 | '(ABBR)'
22 | ])
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/other/test/shuffle_array.test.ts:
--------------------------------------------------------------------------------
1 | import { shuffleArray } from '../shuffle_array'
2 |
3 | describe('shuffleArray', () => {
4 | test.each([{ arr: [1, 2, 3] }, { arr: [1, 2, 3, 6, 78, 2] }])(
5 | "The length of the array $arr does'nt change after shuffling the array",
6 | ({ arr }) => {
7 | const originalLength = arr.length
8 | shuffleArray(arr)
9 | expect(arr.length).toEqual(originalLength)
10 | }
11 | )
12 |
13 | test.each([{ arr: [1, 2, 3] }, { arr: [1, 2, 3, 6, 78, 2] }])(
14 | 'The elements of the array $arr remain the same (possibly with different order) after shuffling the array',
15 | ({ arr }) => {
16 | const copyArray = Array.from(arr)
17 | shuffleArray(arr)
18 | expect(
19 | arr.every((elem) => {
20 | return copyArray.includes(elem)
21 | })
22 | ).toEqual(true)
23 | }
24 | )
25 | })
26 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "typescript",
3 | "version": "1.0.0",
4 | "type": "module",
5 | "description": "A repository for All algorithms implemented in Typescript (for educational purposes only)",
6 | "main": "",
7 | "devDependencies": {
8 | "@types/jest": "^29.0.3",
9 | "husky": "^8.0.1",
10 | "jest": "^29.0.3",
11 | "prettier": "^3.2.5",
12 | "ts-jest": "^29.0.2",
13 | "ts-node": "^10.9.1"
14 | },
15 | "scripts": {
16 | "test": "jest --no-cache",
17 | "style": "npx prettier . --write",
18 | "check-style": "npx prettier . --check",
19 | "prepare": "husky install"
20 | },
21 | "author": "TheAlgorithms",
22 | "license": "MIT"
23 | }
24 |
--------------------------------------------------------------------------------
/search/binary_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function binarySearch
3 | * @description binary search algorithm (iterative & recursive implementations) for a sorted array.
4 | *
5 | * The algorithm searches for a specific value in a sorted array in logarithmic time.
6 | * It repeatedly halves the portion of the list that could contain the item,
7 | * until you've narrowed down the possible indices to just one.
8 | *
9 | * @param {number[]} array - sorted list of numbers
10 | * @param {number} target - target number to search for
11 | * @return {number} - index of the target number in the list, or null if not found
12 | * @see [BinarySearch](https://www.geeksforgeeks.org/binary-search/)
13 | * @example binarySearch([1,2,3], 2) => 1
14 | * @example binarySearch([4,5,6], 2) => null
15 | */
16 |
17 | export const binarySearchIterative = (
18 | array: number[],
19 | target: number,
20 | start: number = 0,
21 | end: number = array.length - 1
22 | ): number | null => {
23 | if (array.length === 0) return null
24 |
25 | // ensure the target is within the bounds of the array
26 | if (target < array[start] || target > array[end]) return null
27 |
28 | // declare pointers for the middle index
29 | let middle = (start + end) >> 1
30 |
31 | while (array[middle] !== target && start <= end) {
32 | // if the target is less than the middle value, move the end pointer to be middle -1 to narrow the search space
33 | // otherwise, move the start pointer to be middle + 1
34 | if (target < array[middle]) end = middle - 1
35 | else start = middle + 1
36 | // redeclare the middle index when the search window changes
37 | middle = (start + end) >> 1
38 | }
39 | // return the middle index if it is equal to target
40 | return array[middle] === target ? middle : null
41 | }
42 |
43 | export const binarySearchRecursive = (
44 | array: number[],
45 | target: number,
46 | start = 0,
47 | end = array.length - 1
48 | ): number | null => {
49 | if (array.length === 0) return null
50 |
51 | // ensure the target is within the bounds of the array
52 | if (target < array[start] || target > array[end]) return null
53 |
54 | const middle = (start + end) >> 1
55 |
56 | if (array[middle] === target) return middle // target found
57 | if (start > end) return null // target not found
58 |
59 | // if the target is less than the middle value, move the end pointer to be middle -1 to narrow the search space
60 | // otherwise, move the start pointer to be middle + 1
61 | return target < array[middle]
62 | ? binarySearchRecursive(array, target, start, middle - 1)
63 | : binarySearchRecursive(array, target, middle + 1, end)
64 | }
65 |
--------------------------------------------------------------------------------
/search/exponential_search.ts:
--------------------------------------------------------------------------------
1 | import { binarySearchIterative } from './binary_search'
2 |
3 | /**
4 | * @description Exponential search algorithm for a sorted array.
5 | *
6 | * The algorithm searches for a specific value in a sorted array by first finding a range
7 | * where the value may be present and then performing a binary search within that range.
8 | *
9 | * Compared with binary search, exponential search can be more convenient and advantageous
10 | * in cases where the element to be searched is closer to the beginning of the array,
11 | * thus avoiding several comparisons that would make the search more verbose.
12 | *
13 | * @param {number[]} array - sorted list of numbers
14 | * @param {number} x - target number to search for
15 | * @return {number | null} - index of the target number in the list, or null if not found
16 | * @see [ExponentialSearch](https://www.geeksforgeeks.org/exponential-search/)
17 | * @example exponentialSearch([1, 2, 3, 4, 5], 3) => 2
18 | * @example exponentialSearch([10, 20, 30, 40, 50], 35) => null
19 | */
20 |
21 | export const exponentialSearch = (
22 | array: number[],
23 | x: number
24 | ): number | null => {
25 | const arrayLength = array.length
26 | if (arrayLength === 0) return null
27 |
28 | if (array[0] === x) return 0
29 |
30 | let i = 1
31 | while (i < arrayLength && array[i] <= x) {
32 | i = i * 2
33 | }
34 |
35 | const start = Math.floor(i / 2)
36 | const end = Math.min(i, arrayLength - 1)
37 | const result = binarySearchIterative(array, x, start, end)
38 |
39 | return result
40 | }
41 |
--------------------------------------------------------------------------------
/search/fibonacci_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @description Fibonacci search algorithm for a sorted array.
3 | *
4 | * The algorithm searches for a specific value in a sorted array using Fibonacci numbers
5 | * to divide the array into smaller subarrays. This algorithm is useful for large arrays where
6 | * the cost of accessing elements is high.
7 | *
8 | * @param {number[]} array - sorted list of numbers
9 | * @param {number} target - target number to search for
10 | * @return {number | null} - index of the target number in the list, or null if not found
11 | * @see [FibonacciSearch](https://www.geeksforgeeks.org/fibonacci-search/)
12 | * @example fibonacciSearch([1,2,3], 2) => 1
13 | * @example fibonacciSearch([4,5,6], 2) => null
14 | */
15 |
16 | export const fibonacciSearch = (
17 | array: number[],
18 | target: number
19 | ): number | null => {
20 | const arrayLength = array.length
21 | let a = 0 // (n-2)'th Fibonacci No.
22 | let b = 1 // (n-1)'th Fibonacci No.
23 | let c = a + b // n'th Fibonacci
24 |
25 | while (c < arrayLength) {
26 | a = b
27 | b = c
28 | c = a + b
29 | }
30 |
31 | let offset = -1
32 |
33 | while (c > 1) {
34 | let i = Math.min(offset + a, arrayLength - 1)
35 |
36 | if (array[i] < target) {
37 | c = b
38 | b = a
39 | a = c - b
40 | offset = i
41 | } else if (array[i] > target) {
42 | c = a
43 | b = b - a
44 | a = c - b
45 | } else {
46 | // Element found then return index
47 | return i
48 | }
49 | }
50 |
51 | if (b && array[offset + 1] === target) {
52 | return offset + 1
53 | }
54 |
55 | // Element not found then return null
56 | return null
57 | }
58 |
--------------------------------------------------------------------------------
/search/interpolation_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function interpolationSearch
3 | * @description Interpolation search is an algorithm for searching for a
4 | * key in an array that has been ordered by numerical values assigned
5 | * to the keys (key values)
6 | * @param {number[]} array - list of numbers
7 | * @param {number} target - target number to search for
8 | * @return {number} - index of the target number in the list, or -1 if not found
9 | * @see https://en.wikipedia.org/wiki/Interpolation_search
10 | * @example interpolationSearch([1, 3, 5, 7, 9, 11], 1) => 0
11 | */
12 | export const interpolationSearch = (
13 | array: number[],
14 | target: number
15 | ): number => {
16 | let lowIndex: number = 0
17 | let highIndex: number = array.length - 1
18 | let currentValue: number = array[lowIndex]
19 | let pos: number = 0
20 |
21 | while (lowIndex <= highIndex) {
22 | const lowValue: number = array[lowIndex]
23 | const highValue: number = array[highIndex]
24 |
25 | if (lowValue === highValue) {
26 | if (array[lowIndex] === target) {
27 | return lowIndex
28 | }
29 | break
30 | }
31 |
32 | pos = Math.round(
33 | lowIndex +
34 | ((target - lowValue) * (highIndex - lowIndex)) / (highValue - lowValue)
35 | )
36 |
37 | if (pos < 0 || pos >= array.length) {
38 | break
39 | }
40 |
41 | currentValue = array[pos]
42 |
43 | if (target === currentValue) {
44 | return pos
45 | }
46 |
47 | if (target > currentValue) {
48 | lowIndex = pos + 1
49 | } else {
50 | highIndex = pos - 1
51 | }
52 | }
53 |
54 | return -1
55 | }
56 |
--------------------------------------------------------------------------------
/search/jump_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function jumpSearch
3 | * @description Jump search algorithm for a sorted array.
4 | *
5 | * Jump search is a searching algorithm for sorted arrays that checks elements
6 | * by jumping ahead by fixed steps. The optimal step size is the square root of the array length.
7 | *
8 | * The algorithm works as follows:
9 | * 1.Start from the first element and jump by step size until finding an element that is greater than or equal to the target value.
10 | * 2.Go back one step and perform a linear search from there until finding the target value or reaching the end of the subarray.
11 | * 3.If the target value is found, return its index. Otherwise, return -1 to indicate that it is not in the array.
12 | *
13 | * @param {number[]} array - sorted list of numbers
14 | * @param {number} target - target number to search for
15 | * @return {number} - index of the target number in the list, or -1 if not found
16 | * @see [JumpSearch](https://www.geeksforgeeks.org/jump-search/)
17 | * @example jumpSearch([1,2,3], 2) => 1
18 | * @example jumpSearch([4,5,6], 2) => -1
19 | */
20 |
21 | export const jumpSearch = (array: number[], target: number): number => {
22 | if (array.length === 0) return -1
23 |
24 | // declare pointers for the current and next indexes and step size
25 | const stepSize: number = Math.floor(Math.sqrt(array.length))
26 | let currentIdx: number = 0,
27 | nextIdx: number = stepSize
28 |
29 | while (array[nextIdx - 1] < target) {
30 | currentIdx = nextIdx
31 | nextIdx += stepSize
32 |
33 | if (nextIdx > array.length) {
34 | nextIdx = array.length
35 | break
36 | }
37 | }
38 |
39 | for (let index = currentIdx; index < nextIdx; index++) {
40 | if (array[index] == target) {
41 | return index
42 | }
43 | }
44 |
45 | return -1
46 | }
47 |
--------------------------------------------------------------------------------
/search/linear_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function linearSearch
3 | * @description linear search is the simplest search possible in a array
4 | * it has a linear cost, if the value is present in the array, then the index of the first occurence will be returned
5 | * if it's not present, the return it will be -1
6 | * @param {number[]} array - list of numbers
7 | * @param {number} target - target number to search for
8 | * @return {number} - index of the target number in the list, or -1 if not found
9 | * @see https://en.wikipedia.org/wiki/Linear_search\
10 | * @example linearSearch([1,2,3,5], 3) => 2
11 | * @example linearSearch([1,5,6], 2) => -1
12 | */
13 | export const linearSearch = (array: any[], target: any): number => {
14 | for (let i = 0; i < array.length; i++) {
15 | if (array[i] === target) return i
16 | }
17 | return -1
18 | }
19 |
--------------------------------------------------------------------------------
/search/sentinel_search.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function sentinelSearch
3 | * @description Sentinel search algorithm for array.
4 | *
5 | * Sentinel linear search is a variation of the standard linear search algorithm used to
6 | * find a target value in an array or list. The basic idea behind this algorithm is to add a
7 | * sentinel value at the end of the array which is equal to the target value we are looking for.
8 | * This helps to avoid checking the array boundary condition during each iteration of the loop,
9 | * as the sentinel value acts as a stopper for the loop.
10 | *
11 | * @param {number[]} array - sorted list of numbers
12 | * @param {number} target - target number to search for
13 | * @return {number|null} - index of the target number in the list, or null if not found
14 | * @see [SentinelSearch](https://www.geeksforgeeks.org/sentinel-linear-search/)
15 | * @example sentinelSearch([1,2,3], 2) => 1
16 | * @example sentinelSearch([4,5,6], 2) => null
17 | * @complexity_analysis
18 | * Time Complexity :
19 | * Worst Case -> The time complexity of the Sentinel Linear Search algorithm is O(n) in the worst case.
20 | * Best Case -> In the best case, when the key is found in the first iteration, the time complexity will be O(1).
21 | * Average Case -> However, the average time complexity is still O(n).
22 | * Auxiliary Space: O(1)
23 | */
24 |
25 | export const sentinelSearch = (
26 | array: number[],
27 | target: number
28 | ): number | null => {
29 | const arrayLength = array.length
30 | if (arrayLength === 0) return null
31 |
32 | // Element to be searched is placed at the last index
33 | const last = array[arrayLength - 1]
34 | array[arrayLength - 1] = target
35 |
36 | let index: number = 0
37 | while (array[index] !== target) index += 1
38 |
39 | // Put the last element back
40 | array[arrayLength - 1] = last
41 |
42 | if (index < arrayLength - 1 || array[arrayLength - 1] === target) return index
43 | return null
44 | }
45 |
--------------------------------------------------------------------------------
/search/test/binary_search.test.ts:
--------------------------------------------------------------------------------
1 | import { binarySearchIterative, binarySearchRecursive } from '../binary_search'
2 |
3 | describe('BinarySearch', () => {
4 | const testArray: number[] = [1, 2, 3, 4]
5 | type FunctionsArray = { (array: number[], index: number): number | null }[]
6 | const functions: FunctionsArray = [
7 | binarySearchIterative,
8 | binarySearchRecursive
9 | ]
10 |
11 | for (const func of functions) {
12 | it('should be defined', () => {
13 | expect(func(testArray, 2)).toBeDefined()
14 | })
15 | it('should return a number or null', () => {
16 | expect(
17 | typeof func(testArray, 2) === 'number' || func(testArray, 2) === null
18 | ).toBe(true)
19 | })
20 | it('should return null if the target is not found in the array', () => {
21 | expect(func(testArray, 5)).toBe(null)
22 | })
23 | it('should return null if there are no elements in the array', () => {
24 | expect(func([], 5)).toBe(null)
25 | })
26 | it('should return the index of the target if it is found in the array', () => {
27 | expect(func(testArray, 2)).toBe(1)
28 | })
29 | it('should return a correct index of target when the array contains duplicate values', () => {
30 | expect(func([1, 2, 2, 3, 3, 3, 4], 2)).toBe(1)
31 | })
32 | it('should return the first index when the target is the first item in the array', () => {
33 | expect(func(testArray, 1)).toBe(0)
34 | })
35 | it('should return the last index when the target is the last item in the array', () => {
36 | expect(func(testArray, 4)).toBe(3)
37 | })
38 | }
39 | })
40 |
--------------------------------------------------------------------------------
/search/test/exponential_search.test.ts:
--------------------------------------------------------------------------------
1 | import { exponentialSearch } from '../exponential_search'
2 |
3 | describe('Exponential search', () => {
4 | test.each([
5 | [[1, 2, 3, 4, 5], 3, 2],
6 | [[10, 20, 30, 40, 50], 35, null],
7 | [[10, 20, 30, 40, 50], 10, 0],
8 | [[10, 20, 30, 40, 50], 50, 4],
9 | [[10, 20, 30, 40, 50], 60, null],
10 | [[], 10, null],
11 | [[1, 2, 3, 4, 5], 1, 0],
12 | [[1, 2, 3, 4, 5], 5, 4]
13 | ])(
14 | 'of %o, searching for %o, expected %i',
15 | (array: number[], target: number, expected: number | null) => {
16 | expect(exponentialSearch(array, target)).toBe(expected)
17 | }
18 | )
19 | })
20 |
--------------------------------------------------------------------------------
/search/test/fibonacci_search.test.ts:
--------------------------------------------------------------------------------
1 | import { fibonacciSearch } from '../fibonacci_search'
2 |
3 | describe('Fibonacci search', () => {
4 | test.each([
5 | [[1, 2, 3], 2, 1],
6 | [[4, 5, 6], 2, null],
7 | [[10, 22, 35, 40, 45, 50, 80, 82, 85, 90, 100], 85, 8],
8 | [[], 1, null],
9 | [[1], 1, 0],
10 | [[1, 3, 5, 7, 9, 11, 13], 11, 5],
11 | [[1, 3, 5, 7, 9, 11, 13], 8, null]
12 | ])(
13 | 'of %o, searching for %o, expected %i',
14 | (array: number[], target: number, expected: number | null) => {
15 | expect(fibonacciSearch(array, target)).toBe(expected)
16 | }
17 | )
18 | })
19 |
--------------------------------------------------------------------------------
/search/test/interpolation_search.test.ts:
--------------------------------------------------------------------------------
1 | import { interpolationSearch } from '../interpolation_search'
2 |
3 | describe('Interpolation search', () => {
4 | test.each([
5 | [[1, 3, 5, 7, 9, 11], 1, 0],
6 | [
7 | [
8 | 1, 3, 7, 10, 14, 15, 16, 18, 20, 21, 22, 23, 25, 33, 35, 42, 45, 47, 50,
9 | 52
10 | ],
11 | 33,
12 | 13
13 | ],
14 | [[0, 45, 67, 70, 89, 129, 150, 308], 308, 7],
15 | [[0, 45, 67, 70, 89, 129, 150, 308], 190, -1]
16 | ])(
17 | 'of %o, searching for %o, expected %i',
18 | (array: any[], target: any, index: number) => {
19 | expect(interpolationSearch(array, target)).toStrictEqual(index)
20 | }
21 | )
22 | })
23 |
--------------------------------------------------------------------------------
/search/test/jump_search.test.ts:
--------------------------------------------------------------------------------
1 | import { jumpSearch } from '../jump_search'
2 |
3 | describe('Jump search', () => {
4 | test.each([
5 | [[], 1, -1],
6 | [[1, 2, 3, 4, 5], 4, 3],
7 | [[1, 3, 5, 8, 9], 4, -1],
8 | [[1, 3, 5, 8], 8, 3],
9 | [[1, 3, 5, 8], 9, -1],
10 | [[1, 3, 5, 8], 7, -1],
11 | [[1, 3, 5, 8, 10], 10, 4],
12 | [[1, 3, 5, 8, 10], 11, -1],
13 | [[1, 3, 5, 8, 10], 9, -1],
14 | [[5], 5, 0],
15 | [[5], 100, -1],
16 | [[], 100, -1]
17 | ])(
18 | 'of %o , searching for %o, expected %i',
19 | (array: any[], target: any, index: number) => {
20 | expect(jumpSearch(array, target)).toStrictEqual(index)
21 | }
22 | )
23 | })
24 |
--------------------------------------------------------------------------------
/search/test/linear_search.test.ts:
--------------------------------------------------------------------------------
1 | import { linearSearch } from '../linear_search'
2 |
3 | describe('Linear search', () => {
4 | test.each([
5 | [['o', 'b', 'c'], 'c', 2],
6 | [[1, 2, 3, 4, 5], 4, 3],
7 | [['s', 't', 'r', 'i', 'n', 'g'], 'a', -1]
8 | ])(
9 | 'of %o , searching for %o, expected %i',
10 | (array: any[], target: any, index: number) => {
11 | expect(linearSearch(array, target)).toStrictEqual(index)
12 | }
13 | )
14 | })
15 |
--------------------------------------------------------------------------------
/search/test/sentinel_search.test.ts:
--------------------------------------------------------------------------------
1 | import { sentinelSearch } from '../sentinel_search'
2 |
3 | describe('Sentinel search', () => {
4 | test.each([
5 | [['o', 'b', 'c'], 'c', 2],
6 | [[1, 2, 3, 4, 5], 4, 3],
7 | [['s', 't', 'r', 'i', 'n', 'g'], 'a', null],
8 | [['1', '2', '3'], '1', 0],
9 | [['4', 'e', '6', '10'], 4, null]
10 | ])(
11 | 'of %o , searching for %o, expected %i',
12 | (array: any[], target: any, index: number | null) => {
13 | expect(sentinelSearch(array, target)).toStrictEqual(index)
14 | }
15 | )
16 | })
17 |
--------------------------------------------------------------------------------
/sorts/bogo_sort.ts:
--------------------------------------------------------------------------------
1 | import { isSortedArray } from '../other/is_sorted_array'
2 | import { shuffleArray } from '../other/shuffle_array'
3 |
4 | /**
5 | * @function bogoSort
6 | * @description bogo sort is very simple to understand, it randomly shuffeles the input array until it is sorted
7 | * @Complexity_Analysis
8 | * Space complexity - O(1)
9 | * Time complexity
10 | * Best case - O(n)
11 | * The best case occurs when the array is already sorted.
12 | * Worst case - unbounded
13 | * The worst case occurs when the shuffles never make the array sorted.
14 | * Average case - O(n!n)
15 | * The average case occurs when the shuffles sort the array after
16 | * n! iterations (every iteration has a probability of 1/n! to sort the array),
17 | * each iteration takes O(n) time.
18 | *
19 | * @param {number[]} arr - The input array
20 | * @return {number[]} - The sorted array.
21 | * @see [Bogo Sort](https://en.wikipedia.org/wiki/Bogosort)
22 | * @example bogoSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
23 | */
24 | export function bogoSort(arr: number[]): number[] {
25 | while (!isSortedArray(arr)) {
26 | shuffleArray(arr)
27 | }
28 | return arr
29 | }
30 |
--------------------------------------------------------------------------------
/sorts/bubble_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function bubbleSort
3 | * @description Bubble sort algorithm is simple and easy. In bubble sort every pair of adjacent value is compared and swap if the first value is greater than the second one. By this with every iteration the greatest value goes to the right side making it ascending order.This algorithm is not suitable for large data sets as its average and worst-case time complexity is quite high.
4 | * @Complexity_Analysis
5 | * Space complexity - O(1)
6 | * Time complexity
7 | * Best case - O(n^2)
8 | * The best case occurs when an array is already sorted.
9 | * Worst case - O(n^2)
10 | * The worst case occurs when an array is reverse sorted.
11 | * Average case - O(n^2)
12 | * The average case occurs when an array is reverse sorted.
13 | *
14 | * @param {number[]} arr - The input array
15 | * @return {number[]} - The sorted array.
16 | * @see [Bubble Sort](https://www.freecodecamp.org/news/bubble-sort)
17 | * @example bubbleSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
18 | */
19 |
20 | export const bubbleSort = (arr: number[]): number[] => {
21 | for (let i = 0; i < arr.length; i++) {
22 | for (let j = 0; j < arr.length - 1; j++) {
23 | //iterating till the 2nd last element of array
24 | if (arr[j] > arr[j + 1]) {
25 | //current indexed number > next indexed number
26 | const temp: number = arr[j] //swapping two numbers
27 | arr[j] = arr[j + 1]
28 | arr[j + 1] = temp
29 | }
30 | }
31 | }
32 | return arr
33 | }
34 |
--------------------------------------------------------------------------------
/sorts/counting_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author dev-madhurendra
3 | * Counting sort is an algorithm for sorting a collection
4 | * of objects according to keys that are small integers.
5 | * @see https://en.wikipedia.org/wiki/Counting_sort
6 | * @example
7 | * const array = [3, 0, 2, 5, 4, 1]
8 | * countingSort(array, 0, 5)
9 | */
10 |
11 | export const countingSort = (inputArr: number[], min: number, max: number) => {
12 | const sortedArr = []
13 |
14 | const count = new Array(max - min + 1).fill(0)
15 |
16 | for (const element of inputArr) count[element - min]++
17 |
18 | count[0] -= 1
19 |
20 | for (let i = 1; i < count.length; i++) count[i] += count[i - 1]
21 |
22 | for (let i = inputArr.length - 1; i >= 0; i--) {
23 | sortedArr[count[inputArr[i] - min]] = inputArr[i]
24 | count[inputArr[i] - min]--
25 | }
26 |
27 | return sortedArr
28 | }
29 |
--------------------------------------------------------------------------------
/sorts/cycle_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function cycleSort
3 | * @description Cycle sort is an in-place, unstable sorting algorithm, a comparison sort that is theoretically optimal in terms of the total number of writes to the original array, unlike any other in-place sorting algorithm. It is based on the idea that the permutation to be sorted can be factored into cycles, which can individually be rotated to give a sorted result.
4 | * @param {number[]}array - The input array
5 | * @return {number[]} - The sorted array.
6 | * @see [CycleSort] https://en.wikipedia.org/wiki/Cycle_sort
7 | * @example cycleSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
8 | */
9 |
10 | export const cycleSort = (array: number[]) => {
11 | for (let i: number = 0; i < array.length - 1; i++) {
12 | MoveCycle(array, i)
13 | }
14 | return array
15 | }
16 |
17 | function MoveCycle(array: number[], startIndex: number): void {
18 | let currentItem: number = array[startIndex]
19 | let nextChangeIndex: number =
20 | startIndex + CountSmallerItems(array, startIndex, currentItem)
21 | if (nextChangeIndex == startIndex) {
22 | return
23 | }
24 |
25 | nextChangeIndex = SkipDuplicates(array, nextChangeIndex, currentItem)
26 |
27 | let tmp: number = array[nextChangeIndex]
28 | array[nextChangeIndex] = currentItem
29 | currentItem = tmp
30 |
31 | while (nextChangeIndex != startIndex) {
32 | nextChangeIndex =
33 | startIndex + CountSmallerItems(array, startIndex, currentItem)
34 | nextChangeIndex = SkipDuplicates(array, nextChangeIndex, currentItem)
35 |
36 | tmp = array[nextChangeIndex]
37 | array[nextChangeIndex] = currentItem
38 | currentItem = tmp
39 | }
40 | }
41 |
42 | function CountSmallerItems(
43 | array: number[],
44 | startIndex: number,
45 | currentItem: number
46 | ): number {
47 | let elementsCount: number = 0
48 |
49 | for (let i: number = startIndex + 1; i < array.length; i++) {
50 | if (currentItem > array[i]) {
51 | elementsCount++
52 | }
53 | }
54 |
55 | return elementsCount
56 | }
57 |
58 | function SkipDuplicates(
59 | array: number[],
60 | currentPosition: number,
61 | currentItem: number
62 | ): number {
63 | while (array[currentPosition] == currentItem) {
64 | currentPosition++
65 | }
66 |
67 | return currentPosition
68 | }
69 |
--------------------------------------------------------------------------------
/sorts/gnome_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function GnomeSort
3 | * @description Gnome sort is a sort algorithm that moving an element to its proper place is accomplished by a series of swap
4 | * @param {number[]} arr - The input array
5 | * @return {number[]} - The sorted array.
6 | * @see [GnomeSort] https://en.wikipedia.org/wiki/Gnome_sort
7 | * @example gnomeSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
8 | */
9 |
10 | export const gnomeSort = (arr: number[]): number[] => {
11 | if (arr.length <= 1) {
12 | return arr
13 | }
14 |
15 | let i: number = 1
16 |
17 | while (i < arr.length) {
18 | if (arr[i - 1] <= arr[i]) {
19 | i++ //increment index if sub-array[0:i] already sorted
20 | } else {
21 | ;[arr[i], arr[i - 1]] = [arr[i - 1], arr[i]] //swapping two numbers
22 | i = Math.max(1, i - 1) //go back to the previous index to check the swapped number
23 | }
24 | }
25 | return arr
26 | }
27 |
--------------------------------------------------------------------------------
/sorts/heap_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function heapsort
3 | * @description is a comparison-based sorting algorithm that uses a binary heap data structure to repeatedly select and remove the maximum (for max-heap) or minimum (for min-heap) element and place it at the end of the sorted array.
4 | * @see [Heap Sort](https://www.geeksforgeeks.org/heap-sort/)
5 | * @example MergeSort([7, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 7]
6 | * @Complexity_Analysis
7 | * Space complexity - O(1)
8 | * Time complexity
9 | * Best case - O(nlogn)
10 | * Worst case - O(nlogn)
11 | * Average case - O(nlogn)
12 | */
13 |
14 | // Function to perform the Heap Sort
15 | export const HeapSort = (arr: number[]): number[] => {
16 | buildMaxHeap(arr)
17 |
18 | for (let i = arr.length - 1; i > 0; i--) {
19 | swap(arr, 0, i)
20 | heapify(arr, 0, i)
21 | }
22 |
23 | return arr
24 | }
25 |
26 | // Function to build a max-heap from an array
27 | function buildMaxHeap(arr: number[]): void {
28 | const n = arr.length
29 |
30 | for (let i = Math.floor(n / 2) - 1; i >= 0; i--) {
31 | heapify(arr, i, n)
32 | }
33 | }
34 |
35 | // Function to heapify a subtree rooted at a given index
36 | function heapify(arr: number[], index: number, size: number): void {
37 | let largest = index
38 | const left = 2 * index + 1
39 | const right = 2 * index + 2
40 |
41 | if (left < size && arr[left] > arr[largest]) {
42 | largest = left
43 | }
44 |
45 | if (right < size && arr[right] > arr[largest]) {
46 | largest = right
47 | }
48 |
49 | if (largest !== index) {
50 | swap(arr, index, largest)
51 | heapify(arr, largest, size)
52 | }
53 | }
54 |
55 | // Function to swap two elements in an array
56 | function swap(arr: number[], i: number, j: number): void {
57 | const temp = arr[i]
58 | arr[i] = arr[j]
59 | arr[j] = temp
60 | }
61 |
--------------------------------------------------------------------------------
/sorts/insertion_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function insertionSort
3 | * @description simple sortion algorithm for a small number of elements. It compares the `key` element with the previous elements. If the previous elements are greater than the `key` element, then you move the previous element to the next position.
4 | * @param {number[]} num - The input array
5 | * @return {number[]} - The sorted array.
6 | * @see [Insertion Sort](https://www.freecodecamp.org/news/sorting-algorithms-explained-with-examples-in-python-java-and-c#insertion-sort)
7 | * @example insertionSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
8 | */
9 |
10 | export const insertionSort = (arr: number[]): number[] => {
11 | for (let i = 1; i < arr.length; i++) {
12 | const temp = arr[i]
13 | let j = i - 1
14 | while (j >= 0 && arr[j] > temp) {
15 | arr[j + 1] = arr[j]
16 | j--
17 | }
18 | arr[j + 1] = temp
19 | }
20 |
21 | return arr
22 | }
23 |
--------------------------------------------------------------------------------
/sorts/merge_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function mergeSort
3 | * @description keeps on dividing the list into equal halves until it can no more be divided. By definition, if it is only one element in the list, it is sorted.
4 | * @see [Merge Sort](https://www.javatpoint.com/merge-sort)
5 | * @example MergeSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
6 | * @Complexity_Analysis
7 | * Space complexity - O(n)
8 | * Time complexity
9 | * Best case - O(nlogn)
10 | * Worst case - O(nlogn)
11 | * Average case - O(nlogn)
12 | *
13 | * Merge Sort is a recursive algorithm and time complexity can be expressed as following recurrence relation.
14 | * T(n) = 2T(n/2) + O(n)
15 | * The solution of the above recurrence is O(nLogn).
16 | */
17 |
18 | export function mergeSort(array: number[]): number[] {
19 | if (array.length <= 1) return array.slice()
20 |
21 | const midIndex = Math.floor(array.length / 2)
22 | const left = array.slice(0, midIndex)
23 | const right = array.slice(midIndex, array.length)
24 |
25 | return merge(mergeSort(left), mergeSort(right))
26 | }
27 |
28 | function merge(left: number[], right: number[]): number[] {
29 | const result = Array(left.length + right.length)
30 | let curIndex = 0
31 | let leftIndex = 0
32 | let rightIndex = 0
33 |
34 | while (leftIndex < left.length && rightIndex < right.length) {
35 | if (left[leftIndex] < right[rightIndex]) {
36 | result[curIndex++] = left[leftIndex++]
37 | } else {
38 | result[curIndex++] = right[rightIndex++]
39 | }
40 | }
41 | while (leftIndex < left.length) {
42 | result[curIndex++] = left[leftIndex++]
43 | }
44 | while (rightIndex < right.length) {
45 | result[curIndex++] = right[rightIndex++]
46 | }
47 |
48 | return result
49 | }
50 |
--------------------------------------------------------------------------------
/sorts/quick_select.ts:
--------------------------------------------------------------------------------
1 | import { partition } from './quick_sort'
2 | /**
3 | * @function QuickSelect
4 | * @description is an algorithm based on the QuickSort approach that selects the kth smallest element from an array
5 | * @param {number[]} array - The array from which to select the element
6 | * @param {number} k - The index representing the kth smallest element to find
7 | * @param {number} left - The left boundary of the array or subarray to consider (default: 0)
8 | * @param {number} right - The right boundary of the array or subarray to consider (default: array.length - 1)
9 | * @returns {number} - The kth smallest element from the array
10 | * @throws {Error} - If k is out of bounds (less than 0 or greater than or equal to array.length)
11 | */
12 |
13 | export const QuickSelect = (
14 | array: number[],
15 | k: number,
16 | left: number = 0,
17 | right: number = array.length - 1
18 | ): number => {
19 | if (k < 0 || k >= array.length) {
20 | throw new Error('k is out of bounds')
21 | }
22 | if (left === right) {
23 | // If the list contains only one element, return that element
24 | return array[left]
25 | }
26 |
27 | // Partition the array
28 | const pivotIndex = partition(array, left, right)
29 |
30 | // The pivot is in its final sorted position
31 | if (k === pivotIndex) {
32 | return array[k]
33 | } else if (k < pivotIndex) {
34 | // If k is less than the pivot index, look left
35 | return QuickSelect(array, k, left, pivotIndex - 1)
36 | } else {
37 | // If k is greater than the pivot index, look right
38 | return QuickSelect(array, k, pivotIndex + 1, right)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/sorts/quick_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function quickSort
3 | * @description is an algorithm based on divide and conquer approach in which an array is split into sub-arrays and these sub arrays are recursively sorted to get final array
4 | * @see [Quick Sort](https://www.javatpoint.com/quick-sort)
5 | * @example QuickSort([8, 3, 5, 1, 4, 2]) = [1, 2, 3, 4, 5, 8]
6 | */
7 |
8 | export const partition = (
9 | array: number[],
10 | left: number = 0,
11 | right: number = array.length - 1
12 | ) => {
13 | const pivotIndex = choosePivot(left, right)
14 | const pivot = array[pivotIndex]
15 | ;[array[pivotIndex], array[right]] = [array[right], array[pivotIndex]]
16 | let i = left - 1
17 | let j = right
18 |
19 | while (i < j) {
20 | while (array[++i] < pivot);
21 | while (array[--j] > pivot);
22 |
23 | if (i < j) {
24 | ;[array[i], array[j]] = [array[j], array[i]]
25 | }
26 | }
27 |
28 | ;[array[right], array[i]] = [array[i], array[right]]
29 | return i
30 | }
31 |
32 | /**
33 | * @function choosePivot
34 | * @description Chooses a pivot element randomly within the subarray.
35 | * @param {number} left - The left index of the subarray.
36 | * @param {number} right - The right index of the subarray.
37 | * @returns {number} - The index of the chosen pivot element.
38 | */
39 | const choosePivot = (left: number, right: number): number => {
40 | return Math.floor(Math.random() * (right - left + 1)) + left
41 | }
42 |
43 | /**
44 | * Quicksort implementation
45 | *
46 | * @param {number[]} array
47 | * @param {number} [left=0]
48 | * @param {number} [right=array.length - 1]
49 | * @returns {number[]}
50 | * @complexity_analysis
51 | * Space complexity - O(nlogn)
52 | * Time complexity
53 | * Best case - O(nlogn)
54 | * When pivot element lies in the middle of the list
55 | * Worst case - O(n^2)
56 | * When pivot element lies on the extreme ends
57 | * Average case - O(nlogn)
58 | * When the above two cases are not met
59 | */
60 |
61 | export const QuickSort = (
62 | array: number[],
63 | left: number = 0,
64 | right: number = array.length - 1
65 | ) => {
66 | if (array.length > 1) {
67 | const index = partition(array, left, right)
68 |
69 | if (left < index - 1) {
70 | QuickSort(array, left, index - 1)
71 | }
72 |
73 | if (index + 1 < right) {
74 | QuickSort(array, index + 1, right)
75 | }
76 | }
77 |
78 | return array
79 | }
80 |
--------------------------------------------------------------------------------
/sorts/selection_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function selectionSort
3 | * @description Selection sort algorithm is simple and easy. In selection sort the smallest value is selected from the unsorted part and placed at the beginning. This algorithm is not suitable for large data sets as its average and worst-case time complexity is quite high.
4 | * @Complexity_Analysis
5 | * Space complexity - O(1)
6 | * Time complexity
7 | * Best case - O(n^2)
8 | * The best case occurs when an array is already sorted.
9 | * Worst case - O(n^2)
10 | * The worst case occurs when an array is reverse sorted.
11 | * Average case - O(n^2)
12 | * The average case occurs when an array is reverse sorted.
13 | *
14 | * @param {number[]} items - The input array
15 | * @return {number[]} - The sorted array.
16 | * @see [Selection Sort](https://www.javatpoint.com/selection-sort)
17 | * @example selectionSort([12, 29, 25, 8, 32, 17, 40]) = [8, 12, 17, 25, 29, 32, 40]
18 | */
19 |
20 | export const selectionSort = (items: number[]) => {
21 | for (let i = 0; i < items.length; i++) {
22 | let min = i
23 | for (let j = i + 1; j < items.length; j++) {
24 | if (items[j] < items[min]) {
25 | min = j
26 | }
27 | }
28 | if (i !== min) {
29 | ;[items[i], items[min]] = [items[min], items[i]]
30 | }
31 | }
32 | return items
33 | }
34 |
--------------------------------------------------------------------------------
/sorts/shell_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @function shellSort
3 | * @description Shell sort algorithm is the optimization for insertion sort algorithm.
4 | * @Complexity_Analysis
5 | * Space complexity - O(1)
6 | * Time complexity
7 | * Best case - Ω(n log(n))
8 | * Worst case - O(n^2)
9 | * Average case - O(n log(n)^2)
10 | *
11 | * @param {T[]} arr - The input array
12 | * @return {T[]} - The sorted array.
13 | * @see [Shell Sort] (https://www.geeksforgeeks.org/shellsort/)
14 | * @example shellSort([4, 1, 8, 10, 3, 2, 5, 0, 7, 6, 9]) = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
15 | */
16 | export function shellSort(arr: T[]): T[] {
17 | // start with the biggest gap, reduce gap twice on each step
18 | for (let gap = arr.length >> 1; gap > 0; gap >>= 1) {
19 | for (let i = gap; i < arr.length; i++) {
20 | const temp = arr[i]
21 | let j = i // index for compared element on the left side
22 | // shift larger elements down
23 | while (j >= gap && arr[j - gap] > temp) {
24 | arr[j] = arr[j - gap]
25 | j -= gap
26 | }
27 | arr[j] = temp // place i-th element at appropriate position
28 | }
29 | }
30 | return arr
31 | }
32 |
--------------------------------------------------------------------------------
/sorts/swap_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author : dev-madhurendra
3 | * @description
4 | * Swap Sort is an algorithm to find the number of swaps required to sort an array.
5 | * @param {number[]} inputArr - Array of numbers
6 | * @return {number} - Number of swaps required to sort the array.
7 | * @see
8 | */
9 |
10 | export const minSwapsToSort = (inputArr: number[]): number => {
11 | const sortedArray = inputArr.slice()
12 |
13 | sortedArray.sort()
14 |
15 | const indexMap = new Map()
16 |
17 | for (let i = 0; i < inputArr.length; i++) indexMap.set(inputArr[i], i)
18 |
19 | let swaps = 0
20 | for (let i = 0; i < inputArr.length; i++) {
21 | if (inputArr[i] !== sortedArray[i]) {
22 | const temp = inputArr[i]
23 | inputArr[i] = inputArr[indexMap.get(sortedArray[i])]
24 | inputArr[indexMap.get(sortedArray[i])] = temp
25 | indexMap.set(temp, indexMap.get(sortedArray[i]))
26 | indexMap.set(sortedArray[i], 1)
27 | swaps++
28 | }
29 | }
30 |
31 | return swaps
32 | }
33 |
--------------------------------------------------------------------------------
/sorts/test/bogo_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { bogoSort } from '../bogo_sort'
2 |
3 | describe('BogoSort', () => {
4 | test.each([
5 | { arr: [1], expectedResult: [1] },
6 | { arr: [2, 1], expectedResult: [1, 2] },
7 | { arr: [3, 1, 2], expectedResult: [1, 2, 3] },
8 | { arr: [3, 4, 1, 2], expectedResult: [1, 2, 3, 4] }
9 | ])(
10 | 'The return value of $arr should be $expectedResult',
11 | ({ arr, expectedResult }) => {
12 | expect(bogoSort(arr)).toStrictEqual(expectedResult)
13 | }
14 | )
15 | })
16 |
--------------------------------------------------------------------------------
/sorts/test/bubble_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { bubbleSort } from '../bubble_sort'
2 |
3 | describe('BubbleSort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(bubbleSort([8, 3, 5, 1, 4, 2])).toStrictEqual([1, 2, 3, 4, 5, 8])
6 | })
7 |
8 | it('should return the correct value for worst case', () => {
9 | expect(bubbleSort([9, 8, 7, 6, 5, 4, 3, 2, 1])).toStrictEqual([
10 | 1, 2, 3, 4, 5, 6, 7, 8, 9
11 | ])
12 | })
13 |
14 | it('should return the correct value for best case', () => {
15 | expect(bubbleSort([1, 2, 3, 4, 5, 8])).toStrictEqual([1, 2, 3, 4, 5, 8])
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/sorts/test/counting_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { countingSort } from '../counting_sort'
2 |
3 | const testCases = [
4 | [
5 | [3, 0, 2, 5, 4, 1],
6 | [0, 1, 2, 3, 4, 5]
7 | ],
8 | [
9 | [6, 4, 2, 1, 3, 5],
10 | [1, 2, 3, 4, 5, 6]
11 | ],
12 | [
13 | [11, 14, 12, 15, 16, 13],
14 | [11, 12, 13, 14, 15, 16]
15 | ],
16 | [
17 | [13, 18, 2, 15, 43, 11],
18 | [2, 11, 13, 15, 18, 43]
19 | ]
20 | ]
21 |
22 | it.each(testCases)(
23 | 'The countingSort of the array %p is %p',
24 | (input, expected) => {
25 | const res = countingSort(input, Math.min(...input), Math.max(...input))
26 | expect(res).toEqual(expected)
27 | }
28 | )
29 |
--------------------------------------------------------------------------------
/sorts/test/cycle_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { cycleSort } from '../cycle_sort'
2 |
3 | describe('Cycle Sort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(cycleSort([1, 4, 2, 5, 9, 6, 3, 8, 10, 7])).toStrictEqual([
6 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
7 | ])
8 | })
9 |
10 | it('should return the correct value for worst case', () => {
11 | expect(cycleSort([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])).toStrictEqual([
12 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
13 | ])
14 | })
15 |
16 | it('should return the correct value for best case', () => {
17 | expect(cycleSort([1, 4, 2, 9, 5, 7, 3, 8, 10, 6])).toStrictEqual([
18 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
19 | ])
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/gnome_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { gnomeSort } from '../gnome_sort'
2 |
3 | describe('Testing Gnome sort', () => {
4 | const testCases: number[][] = [
5 | [],
6 | [2, 1],
7 | [8, 3, 5, 9, 1, 7, 4, 2, 6],
8 | [9, 8, 7, 6, 5, 4, 3, 2, 1],
9 | [1, 2, 3, 4, 5, 6, 7, 8, 9],
10 | [1, 1, 1, 1, 1, 1, 1, 1, 1]
11 | ]
12 |
13 | test.each(testCases)(
14 | 'should return the correct value for test case: %#',
15 | (...arr: number[]) => {
16 | expect(gnomeSort([...arr])).toStrictEqual(
17 | [...arr].sort((a: number, b: number) => a - b)
18 | )
19 | }
20 | )
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/heap_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { HeapSort } from '../heap_sort'
2 |
3 | describe('Heap Sort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(HeapSort([1, 4, 2, 5, 9, 6, 3, 8, 10, 7])).toStrictEqual([
6 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
7 | ])
8 | })
9 |
10 | it('should return the correct value for worst case', () => {
11 | expect(HeapSort([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])).toStrictEqual([
12 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
13 | ])
14 | })
15 |
16 | it('should return the correct value for best case', () => {
17 | expect(HeapSort([1, 4, 2, 9, 5, 7, 3, 8, 10, 6])).toStrictEqual([
18 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
19 | ])
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/insertion_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { insertionSort } from '../insertion_sort'
2 |
3 | describe('Insertion Sort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(insertionSort([8, 3, 5, 1, 4, 2])).toStrictEqual([1, 2, 3, 4, 5, 8])
6 | })
7 |
8 | it('should return the correct value for worst case', () => {
9 | expect(insertionSort([9, 8, 7, 6, 5, 4, 3, 2, 1])).toStrictEqual([
10 | 1, 2, 3, 4, 5, 6, 7, 8, 9
11 | ])
12 | })
13 |
14 | it('should return the correct value for best case', () => {
15 | expect(insertionSort([1, 2, 3, 4, 5, 8])).toStrictEqual([1, 2, 3, 4, 5, 8])
16 | })
17 | })
18 |
--------------------------------------------------------------------------------
/sorts/test/merge_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { mergeSort } from '../merge_sort'
2 |
3 | describe('Merge Sort', () => {
4 | it('generating array with variable length and comparing with sorted array', () => {
5 | const arrLenArr = [10, 200, 40000]
6 |
7 | arrLenArr.forEach((arrLen: number) => {
8 | const inBuiltSortArr = Array(arrLen)
9 | for (let i = 0; i < arrLen; i++) {
10 | inBuiltSortArr[i] = Math.random() * 10000
11 | }
12 | const mergeSortArray = inBuiltSortArr.slice()
13 |
14 | inBuiltSortArr.sort((a, b) => a - b)
15 | expect(mergeSort(mergeSortArray)).toStrictEqual(inBuiltSortArr)
16 | })
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/sorts/test/quick_select.test.ts:
--------------------------------------------------------------------------------
1 | import { QuickSelect } from '../quick_select'
2 |
3 | describe('QuickSelect', () => {
4 | test('should return the kth smallest element in an array', () => {
5 | const array = [8, 3, 5, 1, 4, 2]
6 | expect(QuickSelect(array, 0)).toBe(1)
7 | expect(QuickSelect(array, 1)).toBe(2)
8 | expect(QuickSelect(array, 2)).toBe(3)
9 | expect(QuickSelect(array, 3)).toBe(4)
10 | expect(QuickSelect(array, 4)).toBe(5)
11 | expect(QuickSelect(array, 5)).toBe(8)
12 | })
13 |
14 | test('should work with arrays of size 1', () => {
15 | const array = [4]
16 | expect(QuickSelect(array, 0)).toBe(4)
17 | })
18 |
19 | test('should work with large arrays', () => {
20 | const array = Array.from({ length: 1000 }, (_, i) => i + 1)
21 | expect(QuickSelect(array, 499)).toBe(500)
22 | })
23 |
24 | test('should throw error when k is out of bounds', () => {
25 | const array = [8, 3, 5, 1, 4, 2]
26 | expect(() => QuickSelect(array, -1)).toThrow()
27 | expect(() => QuickSelect(array, 6)).toThrow()
28 | })
29 | })
30 |
--------------------------------------------------------------------------------
/sorts/test/quick_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { QuickSort } from '../quick_sort'
2 |
3 | describe('Quick Sort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(QuickSort([1, 4, 2, 5, 9, 6, 3, 8, 10, 7])).toStrictEqual([
6 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
7 | ])
8 | })
9 |
10 | it('should return the correct value for worst case', () => {
11 | expect(QuickSort([10, 9, 8, 7, 6, 5, 4, 3, 2, 1])).toStrictEqual([
12 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
13 | ])
14 | })
15 |
16 | it('should return the correct value for best case', () => {
17 | expect(QuickSort([1, 4, 2, 9, 5, 7, 3, 8, 10, 6])).toStrictEqual([
18 | 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
19 | ])
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/selection_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { selectionSort } from '../selection_sort'
2 |
3 | describe('Testing Selection sort', () => {
4 | const testCases: number[][] = []
5 |
6 | for (let i = 0; i < 10; i++) {
7 | const arr = []
8 | for (let j = 0; j < 100; j++) {
9 | arr.push(Math.floor(Math.random() * 100))
10 | }
11 | testCases.push(arr)
12 | }
13 | test.each(testCases)(
14 | 'should return the correct value for test case: %#',
15 | (...arr: number[]) => {
16 | expect(selectionSort([...arr])).toStrictEqual(
17 | [...arr].sort((a: number, b: number) => a - b)
18 | )
19 | }
20 | )
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/shell_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { shellSort } from '../shell_sort'
2 |
3 | describe('Shell Sort', () => {
4 | it('should return the correct value for average case', () => {
5 | expect(shellSort([4, 1, 8, 10, 3, 2, 5, 0, 7, 6, 9])).toStrictEqual([
6 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
7 | ])
8 | })
9 |
10 | it('should return the correct value for worst case', () => {
11 | expect(shellSort([10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0])).toStrictEqual([
12 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
13 | ])
14 | })
15 |
16 | it('should return the correct value for best case', () => {
17 | expect(shellSort([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10])).toStrictEqual([
18 | 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
19 | ])
20 | })
21 | })
22 |
--------------------------------------------------------------------------------
/sorts/test/swap_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { minSwapsToSort } from '../swap_sort'
2 |
3 | describe('SwapSort', () => {
4 | it.each([
5 | { input: [], expected: 0 },
6 | { input: [1, 2, 3, 4, 5, 6], expected: 0 },
7 | { input: [7, 6, 2, 5, 11, 0], expected: 2 },
8 | { input: [3, 3, 2, 1, 0], expected: 2 },
9 | { input: [3, 0, 2, 1, 9, 8, 7, 6], expected: 4 },
10 | { input: [1, 0, 14, 0, 8, 6, 8], expected: 3 }
11 | ])('should work for given input', ({ input, expected }) => {
12 | expect(minSwapsToSort(input)).toEqual(expected)
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/sorts/test/tree_sort.test.ts:
--------------------------------------------------------------------------------
1 | import { treeSort } from '../tree_sort'
2 |
3 | describe('TreeSort (numbers)', () => {
4 | it.each([
5 | { input: [], expected: [] },
6 | { input: [1, 18, 3, 4, -5, 6], expected: [-5, 1, 3, 4, 6, 18] },
7 | { input: [7, 6, 2, 5.2, 11, 0], expected: [0, 2, 5.2, 6, 7, 11] },
8 | { input: [3, 3, -2, 1, 0], expected: [-2, 0, 1, 3, 3] },
9 | {
10 | input: [3, 0, -2.4, 1, 9, 8, -7, 6],
11 | expected: [-7, -2.4, 0, 1, 3, 6, 8, 9]
12 | },
13 | { input: [1, 0, -14, 0, 8.6, 6, 8], expected: [-14, 0, 0, 1, 6, 8, 8.6] }
14 | ])('should work for given input', ({ input, expected }) => {
15 | expect(treeSort(input)).toEqual(expected)
16 | })
17 | })
18 |
19 | describe('TreeSort (strings)', () => {
20 | it.each([
21 | {
22 | input: ['e', 'egr', 'sse', 'aas', 'as', 'abs'],
23 | expected: ['aas', 'abs', 'as', 'e', 'egr', 'sse']
24 | }
25 | ])('should work for given input', ({ input, expected }) => {
26 | expect(treeSort(input)).toEqual(expected)
27 | })
28 | })
29 |
30 | describe('TreeSort (dates)', () => {
31 | it.each([
32 | {
33 | input: [
34 | new Date('2019-01-16'),
35 | new Date('2019-01-01'),
36 | new Date('2022-05-20')
37 | ],
38 | expected: [
39 | new Date('2019-01-01'),
40 | new Date('2019-01-16'),
41 | new Date('2022-05-20')
42 | ]
43 | }
44 | ])('should work for given input', ({ input, expected }) => {
45 | expect(treeSort(input)).toEqual(expected)
46 | })
47 | })
48 |
--------------------------------------------------------------------------------
/sorts/tree_sort.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * @author : tamaf96
3 | * @description
4 | * Tree Sort sorts a list by building a binary search tree and traversing it.
5 | * @param {T[]} arr - Array of comparable items
6 | * @return {T[]} - The sorted Array.
7 | * @see
8 | */
9 |
10 | import { BinarySearchTree } from '../data_structures/tree/binary_search_tree'
11 |
12 | export const treeSort = (arr: T[]): T[] => {
13 | const searchTree = new BinarySearchTree()
14 | for (const item of arr) {
15 | searchTree.insert(item)
16 | }
17 | return searchTree.inOrderTraversal()
18 | }
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Language and Environment */
4 | "target": "es2016" /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */,
5 |
6 | /* Modules */
7 | "module": "CommonJS" /* Specify what module code is generated. */,
8 |
9 | /* Interop Constraints */
10 | "esModuleInterop": true /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */,
11 | "forceConsistentCasingInFileNames": true /* Ensure that casing is correct in imports. */,
12 |
13 | /* Type Checking */
14 | "strict": true /* Enable all strict type-checking options. */,
15 | "noImplicitAny": true /* Enable error reporting for expressions and declarations with an implied 'any' type. */,
16 | "strictFunctionTypes": true /* Visit https://aka.ms/tsconfig to read more about this file */ /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */,
17 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
18 | }
19 | }
20 |
--------------------------------------------------------------------------------