├── .editorconfig
├── .gitignore
├── README.md
├── algorithms
├── binary-search
│ ├── README.md
│ ├── index.ts
│ └── test.ts
├── damerau-levenshtein-distance
│ ├── README.md
│ └── index.ts
├── eulers-totient
│ ├── README.md
│ └── index.ts
├── exponential-search
│ ├── README.md
│ └── index.ts
├── fibonacci-numbers
│ ├── README.md
│ ├── index.ts
│ └── test.ts
├── fisher-yates-shuffle
│ ├── README.md
│ └── index.ts
├── hash
│ ├── djb2
│ │ └── index.ts
│ ├── loselose
│ │ └── index.ts
│ └── sdbm
│ │ └── index.ts
├── interpolation-search
│ ├── README.md
│ └── index.ts
├── levenshtein-distance
│ ├── README.md
│ └── index.ts
├── linear-congruential-generator
│ ├── README.md
│ └── index.ts
├── linear-search
│ ├── README.md
│ └── index.ts
├── lucas-numbers
│ ├── README.md
│ ├── index.ts
│ └── test.ts
├── mersenne-twister
│ ├── README.md
│ ├── index.ts
│ └── test.ts
├── phi
│ ├── README.md
│ └── index.ts
├── sieve-of-eratosthenes
│ ├── README.md
│ └── index.ts
├── sort
│ ├── bubble-sort
│ │ ├── README.md
│ │ └── index.ts
│ ├── comb-sort
│ │ ├── README.md
│ │ └── index.ts
│ ├── heapsort
│ │ ├── README.md
│ │ └── index.ts
│ ├── insertion-sort
│ │ ├── README.md
│ │ └── index.ts
│ ├── merge-sort
│ │ ├── README.md
│ │ └── index.ts
│ ├── quicksort
│ │ ├── README.md
│ │ └── index.ts
│ ├── selection-sort
│ │ ├── README.md
│ │ └── index.ts
│ └── shellsort
│ │ ├── README.md
│ │ └── index.ts
└── ternary-search
│ ├── README.md
│ └── index.ts
└── data-structures
├── avl-tree
├── README.md
└── index.ts
├── binary-search-tree
├── README.md
├── index.ts
└── test.ts
├── binary-tree
├── README.md
└── index.ts
├── cartesian-tree
└── README.md
├── circular-doubly-linked-list
├── README.md
├── index.ts
└── test.ts
├── circular-linked-list
├── README.md
├── index.ts
└── test.ts
├── deque
├── README.md
└── index.ts
├── dictionary
├── README.md
└── index.ts
├── doubly-linked-list
├── README.md
├── index.ts
└── test.ts
├── graph
├── README.md
├── index.ts
└── test.ts
├── hash-table
├── README.md
└── index.ts
├── linked-list
├── README.md
├── index.ts
└── test.ts
├── queue
├── README.md
└── index.ts
├── red-black-tree
├── README.md
└── index.ts
├── skip-list
├── README.md
└── index.ts
├── stack
├── README.md
├── index.ts
└── test.ts
├── tree
├── README.md
└── index.ts
├── trie
├── README.md
└── index.ts
└── unrolled-linked-list
├── README.md
├── index.ts
└── test.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | end_of_line = lf
5 | insert_final_newline = true
6 |
7 | [*.{js,ts,json}]
8 | charset = utf-8
9 | indent_style = space
10 | indent_size = 2
11 |
12 |
13 | [*.{md,txt}]
14 | indent_size = 2
15 | indent_style = space
16 | trim_trailing_whitespace = false
17 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | data/dictionary
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Algorithms and Data Structures
2 |
3 | WIP
4 |
5 | Beautifully crafted and well documented examples of Algorithms and Data Structures.
6 |
7 | ## Algorithms
8 |
9 | [Damerau Levenshtein Distance](./algorithms/damerau-levenshtein-distance/index.ts)
10 |
11 | [Euler's Totient](./algorithms/eulers-totient/index.ts)
12 |
13 | [Fibonacci Numbers](./algorithms/fibonacci-numbers/index.ts)
14 |
15 | [Fisher–Yates Shuffle](./algorithms/fisher-yates-shuffle/index.ts)
16 |
17 | [Levenshtein Distance](./algorithms/levenshtein-distance/index.ts)
18 |
19 | [Linear Congruential Generator](./algorithms/linear-congruential-generator/index.ts)
20 |
21 | [Lucas Numbers](./algorithms/lucas-numbers/index.ts)
22 |
23 | [Mersenne Twister](./algorithms/mersenne-twister/index.ts)
24 |
25 | [Phi](./algorithms/phi/index.ts)
26 |
27 | [Sieve of Eratosthenes](./algorithms/sieve-of-eratosthenes/index.ts)
28 |
29 | ### Search Algorithms
30 |
31 | [Binary Search](./algorithms/binary-search/index.ts)
32 |
33 | [Exponential Search](./algorithms/exponential-search/index.ts)
34 |
35 | [Interpolation Search](./algorithms/interpolation-search/index.ts)
36 |
37 | [Linear Search](./algorithms/linear-search/index.ts)
38 |
39 | [Ternary Search](./algorithms/ternary-search/index.ts)
40 |
41 | ### Hash Algorithms
42 |
43 | [djb2](./algorithms/hash/djb2/index.ts)
44 |
45 | [sdbm](./algorithms/hash/sdbm/index.ts)
46 |
47 | [loselose](./algorithms/hash/loselose/index.ts)
48 |
49 | ### Sort Algorithms
50 |
51 | [Bubble Sort](./algorithms/sorting/bubble-sort/index.ts)
52 |
53 | [Comb Sort](./algorithms/sorting/comb-sort/index.ts)
54 |
55 | [Heapsort](./algorithms/sorting/heapsort/index.ts)
56 |
57 | [Insertion Sort](./algorithms/sorting/insertion-sort/index.ts)
58 |
59 | [Merge Sort](./algorithms/sorting/merge-sort/index.ts)
60 |
61 | [Quicksort](./algorithms/sorting/quicksort/index.ts)
62 |
63 | [Selection Sort](./algorithms/sorting/selection-sort/index.ts)
64 |
65 | [Shellsort](./algorithms/sorting/shellsort/index.ts)
66 |
67 | ## Data Structures
68 |
69 | [AVL Tree](./data-structures/avl-tree/index.ts)
70 |
71 | [Binary Search Tree](./data-structures/binary-search-tree/index.ts)
72 |
73 | [Binary Tree](./data-structures/binary-tree/index.ts)
74 |
75 | [Circular Doubly Linked List](./data-structures/circular-doubly-linked-list/index.ts)
76 |
77 | [Circular Linked List](./data-structures/circular-linked-list/index.ts)
78 |
79 | [Deque](./data-structures/deque/index.ts)
80 |
81 | [Dictionary](./data-structures/dictionary/index.ts)
82 |
83 | [Doubly Linked List](./data-structures/doubly-linked-list/index.ts)
84 |
85 | [Graph](./data-structures/graph/index.ts)
86 |
87 | [Hash Table](./data-structures/hash-table/index.ts)
88 |
89 | [Linked List](./data-structures/linked-list/index.ts)
90 |
91 | [Queue](./data-structures/queue/index.ts)
92 |
93 | [Red Black Tree](./data-structures/red-black-tree/index.ts)
94 |
95 | [Skip List](./data-structures/skip-list/index.ts)
96 |
97 | [Stack](./data-structures/stack/index.ts)
98 |
99 | [Tree](./data-structures/tree/index.ts)
100 |
101 | [Trie](./data-structures/trie/index.ts)
102 |
103 | [Unrolled Linked List](./data-structures/unrolled-linked-list/index.ts)
104 |
105 | ## Licence
106 |
107 | MIT
108 |
--------------------------------------------------------------------------------
/algorithms/binary-search/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search
2 |
3 | In computer science, binary search, also known as half-interval search, logarithmic search, or binary chop, is a search algorithm that finds the position of a target value within a sorted array. Binary search compares the target value to the middle element of the array. If they are not equal, the half in which the target cannot lie is eliminated and the search continues on the remaining half, again taking the middle element to compare to the target value, and repeating this until the target value is found. If the search ends with the remaining half being empty, the target is not in the array. [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_algorithm)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Best |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Θ(log(n)) |
19 | O(1) |
20 | Θ(log(n)) |
21 | O(1) |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/algorithms/binary-search/index.ts:
--------------------------------------------------------------------------------
1 | export function binarySearch(
2 | haystack: number[],
3 | needle: number,
4 | left: number = 0,
5 | right: number = haystack.length - 1
6 | ): number | undefined {
7 | while (left <= right) {
8 | const middle = Math.floor((left + right) / 2);
9 | if (haystack[middle] < needle) {
10 | left = middle + 1;
11 | } else if (haystack[middle] > needle) {
12 | right = middle - 1;
13 | } else {
14 | return middle;
15 | }
16 | }
17 | }
18 |
19 | export function binarySearchLeftmost(
20 | haystack: number[],
21 | needle: number,
22 | left: number = 0,
23 | right: number = haystack.length
24 | ) {
25 | while (left < right) {
26 | const middle = Math.floor((left + right) / 2);
27 | if (haystack[middle] < needle) {
28 | left = middle + 1;
29 | } else {
30 | right = middle;
31 | }
32 | }
33 | return left;
34 | }
35 |
36 | export function binarySearchRightmost(
37 | haystack: number[],
38 | needle: number,
39 | left: number = 0,
40 | right: number = haystack.length
41 | ) {
42 | while (left < right) {
43 | const middle = Math.floor((left + right) / 2);
44 | if (haystack[middle] > needle) {
45 | right = middle;
46 | } else {
47 | left = middle + 1;
48 | }
49 | }
50 | return right - 1;
51 | }
52 |
53 | // const array = [3, 3];
54 | // console.log(binarySearchLeftmost(array, 3));
55 | // console.log(binarySearchRightmost(array, 3));
56 |
--------------------------------------------------------------------------------
/algorithms/binary-search/test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | binarySearch,
3 | binarySearchLeftmost,
4 | binarySearchRightmost,
5 | } from "./index.ts";
6 |
7 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
8 |
9 | Deno.test("Binary search odd sized array", () => {
10 | const array = [0, 1, 2];
11 | assertEquals(binarySearch(array, 1), 1);
12 | assertEquals(binarySearch(array, 3), undefined);
13 | });
14 |
15 | Deno.test("Binary search even sized array", () => {
16 | const array = [0, 1];
17 | assertEquals(binarySearch(array, 1), 1);
18 | assertEquals(binarySearch(array, 2), undefined);
19 | });
20 |
21 | Deno.test("Binary search empty array returns undefined", () => {
22 | assertEquals(binarySearch([], 1), undefined);
23 | });
24 |
25 | Deno.test("Binary search duplicates", () => {
26 | const array = [3, 3];
27 | assertEquals(binarySearchLeftmost(array, 3), 0);
28 | assertEquals(binarySearchRightmost(array, 3), 1);
29 | });
30 |
--------------------------------------------------------------------------------
/algorithms/damerau-levenshtein-distance/README.md:
--------------------------------------------------------------------------------
1 | # Damerau Levenshtein Distance
2 |
3 | In information theory and computer science, the Damerau–Levenshtein distance (named after Frederick J. Damerau and Vladimir I. Levenshtein) is a string metric for measuring the edit distance between two sequences. Informally, the Damerau–Levenshtein distance between two words is the minimum number of operations (consisting of insertions, deletions or substitutions of a single character, or transposition of two adjacent characters) required to change one word into the other.
4 |
5 | The Damerau–Levenshtein distance differs from the classical Levenshtein distance by including transpositions among its allowable operations in addition to the three classical single-character edit operations (insertions, deletions and substitutions).
6 |
7 | In his seminal paper, Damerau stated that more than 80% of all human misspellings can be expressed by a single error of one of the four types. Damerau's paper considered only misspellings that could be corrected with at most one edit operation. While the original motivation was to measure distance between human misspellings to improve applications such as spell checkers, Damerau–Levenshtein distance has also seen uses in biology to measure the variation between protein sequences. [Wikipedia](https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance)
8 |
--------------------------------------------------------------------------------
/algorithms/damerau-levenshtein-distance/index.ts:
--------------------------------------------------------------------------------
1 | // https://en.wikipedia.org/wiki/Damerau%E2%80%93Levenshtein_distance
2 |
3 | export function optimalStringAlignmentDistance(a: string, b: string) {
4 | let i;
5 | let j;
6 |
7 | const d: number[][] = [];
8 |
9 | for (i = 0; i <= a.length; i++) {
10 | d[i] = [i];
11 | }
12 |
13 | for (j = 0; j <= b.length; j++) {
14 | d[0][j] = j;
15 | }
16 |
17 | for (i = 1; i <= a.length; i++) {
18 | for (j = 1; j <= b.length; j++) {
19 | d[i][j] = Math.min(
20 | d[i - 1][j] + 1, // deletion
21 | d[i][j - 1] + 1, // insertion
22 | d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1) // substitution
23 | );
24 | if (i > 1 && j > 1 && a[i] === b[j - 1] && a[i - 1] === b[j]) {
25 | d[i][j] = Math.min(d[i][j], d[i - 2][j - 2] + 1);
26 | } // transposition
27 | }
28 | }
29 |
30 | return d[a.length][b.length];
31 | }
32 |
33 | export function damerauLevenshteinDistance(a: string, b: string) {
34 | let i: number;
35 | let j: number;
36 |
37 | const da: { [key: string]: number } = {};
38 |
39 | a = a.toUpperCase();
40 | b = b.toUpperCase();
41 |
42 | for (const letter of a + b) {
43 | if (!da[letter]) {
44 | da[letter] = 0;
45 | }
46 | }
47 |
48 | const d: number[][] = new Array(a.length);
49 |
50 | for (i = -1; i <= a.length; i++) {
51 | d[i] = new Array(b.length);
52 | for (j = -1; j <= b.length; j++) {
53 | d[i][j] = 0;
54 | }
55 | }
56 |
57 | const INFINITY = a.length + b.length;
58 | d[-1][-1] = INFINITY;
59 |
60 | for (i = 0; i < a.length; i++) {
61 | d[i][-1] = INFINITY;
62 | d[i][0] = i;
63 | }
64 |
65 | for (j = 0; j < b.length; j++) {
66 | d[-1][j] = INFINITY;
67 | d[0][j] = j;
68 | }
69 |
70 | for (i = 1; i <= a.length; i++) {
71 | let db = 0;
72 | for (j = 1; j <= b.length; j++) {
73 | const k: number = da[b[j - 1]];
74 | const l: number = db;
75 |
76 | let cost;
77 |
78 | if (a[i - 1] === b[j - 1]) {
79 | db = j;
80 | cost = 0;
81 | } else {
82 | cost = 1;
83 | }
84 |
85 | d[i][j] = Math.min(
86 | d[i - 1][j - 1] + cost, // substitution
87 | d[i][j - 1] + 1, // insertion
88 | d[i - 1][j] + 1, // deletion
89 | d[k - 1][l - 1] + (i - k - 1) + 1 + (j - l - 1) // transposition
90 | );
91 | }
92 |
93 | da[a[i - 1]] = i;
94 | }
95 |
96 | // console.log(d);
97 |
98 | // console.log(da);
99 |
100 | return d[a.length][b.length];
101 | }
102 |
103 | // 3
104 | console.log(optimalStringAlignmentDistance('ca', 'abc'));
105 |
106 | // 2
107 | console.log(damerauLevenshteinDistance('ca', 'abc'));
108 |
109 | // 2
110 | console.log(damerauLevenshteinDistance('a cat', 'a abct'));
111 |
112 | // 1
113 | console.log(damerauLevenshteinDistance('smtih', 'smith'));
114 |
115 | // 1
116 | console.log(damerauLevenshteinDistance('cat', 'cta'));
117 |
--------------------------------------------------------------------------------
/algorithms/eulers-totient/README.md:
--------------------------------------------------------------------------------
1 | # Euler's Totient
2 |
3 | In number theory, Euler's totient function counts the positive integers up to a given integer n that are relatively prime to n. It is written using the Greek letter phi as φ(n) or ϕ(n), and may also be called Euler's phi function. In other words, it is the number of integers k in the range 1 ≤ k ≤ n for which the greatest common divisor gcd(n, k) is equal to 1. The integers k of this form are sometimes referred to as totatives of n.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/algorithms/eulers-totient/index.ts:
--------------------------------------------------------------------------------
1 | function gcd(a: number, b: number): number {
2 | if (a === 0) {
3 | return b;
4 | }
5 |
6 | return gcd(b % a, a);
7 | }
8 |
9 | export function eulersTotient(n: number) {
10 | let totient = 1;
11 |
12 | for (let i = 2; i < n; i++) {
13 | if (gcd(i, n) === 1) {
14 | totient++;
15 | }
16 | }
17 |
18 | return totient;
19 | }
20 |
21 | for (let i = 1; i <= 10; i++) {
22 | console.log(eulersTotient(i));
23 | }
24 |
--------------------------------------------------------------------------------
/algorithms/exponential-search/README.md:
--------------------------------------------------------------------------------
1 | # Exponential Search
2 |
3 | In computer science, an exponential search (also called doubling search or galloping search or Struzik search) is an algorithm, created by Jon Bentley and Andrew Chi-Chih Yao in 1976, for searching sorted, unbounded/infinite lists. [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_algorithm)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Best |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Θ(log(n)) |
19 | O(1) |
20 | Θ(log(n)) |
21 | O(1) |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/algorithms/exponential-search/index.ts:
--------------------------------------------------------------------------------
1 | import { binarySearch } from "../binary-search/index.ts";
2 |
3 | export function exponentialSearch(
4 | haystack: number[],
5 | needle: number
6 | ): number | undefined {
7 | // If hackstack is empty return.
8 | if (!haystack.length) {
9 | return;
10 | }
11 |
12 | let bound = 1;
13 |
14 | while (bound < haystack.length && haystack[bound] < needle) {
15 | bound *= 2;
16 | }
17 |
18 | return binarySearch(
19 | haystack,
20 | needle,
21 | bound / 2,
22 | Math.min(bound + 1, haystack.length)
23 | );
24 | }
25 |
26 | const array = [...Array(100).keys()];
27 | console.log(exponentialSearch(array, 100));
28 |
--------------------------------------------------------------------------------
/algorithms/fibonacci-numbers/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Fibonacci sequence
4 |
5 | In mathematics, the Fibonacci numbers, commonly denoted Fn, form a sequence, called the Fibonacci sequence, such that each number is the sum of the two preceding ones, starting from 0 and 1. [Wikipedia](https://en.wikipedia.org/wiki/Fibonacci_number)
6 |
7 |
--------------------------------------------------------------------------------
/algorithms/fibonacci-numbers/index.ts:
--------------------------------------------------------------------------------
1 | // Find the nth fibonacci number
2 | export function fibonacci(n: number): number {
3 | const sequence = [0, 1];
4 |
5 | if (n < 2) {
6 | return sequence[n];
7 | }
8 |
9 | for (let i = 2; i < n + 1; i++) {
10 | const tmp = sequence[0] + sequence[1];
11 | // Swap
12 | sequence[0] = sequence[1];
13 | // Set
14 | sequence[1] = tmp;
15 | }
16 |
17 | return sequence[1];
18 | }
19 |
20 | // Recursive
21 | export function fibonacciRecursive(n: number): number {
22 | const sequence = [0, 1];
23 | if (n < 2) {
24 | return sequence[n];
25 | }
26 | return fibonacciRecursive(n - 1) + fibonacciRecursive(n - 2);
27 | }
28 |
29 | // Phi
30 | import { phi } from '../phi/index.ts';
31 | export function fibonacciPhi(n: number): number {
32 | const sequence = [0, 1, 1, 2, 3, 5];
33 |
34 | if (n < 6) {
35 | return sequence[n];
36 | }
37 |
38 | let f = 5;
39 |
40 | const PHI = phi();
41 |
42 | for (let i = 5; i < n; i++) {
43 | f = Math.round(f * PHI);
44 | }
45 |
46 | return f;
47 | }
48 |
49 | // console.log(fibonacci(1000));
50 |
51 | // console.log(fibonacciPhi(1000));
52 |
--------------------------------------------------------------------------------
/algorithms/fibonacci-numbers/test.ts:
--------------------------------------------------------------------------------
1 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
2 | import { fibonacci } from "./index.ts";
3 |
4 | // Minimum needed proof
5 | Deno.test("fibonacci(0) is 0", () => {
6 | assertEquals(fibonacci(0), 0);
7 | });
8 |
9 | Deno.test("fibonacci(1) is 1", () => {
10 | assertEquals(fibonacci(1), 1);
11 | });
12 |
13 | Deno.test("fibonacci(2) is 1", () => {
14 | assertEquals(fibonacci(2), 1);
15 | });
16 |
17 | Deno.test("fibonacci(3) is 2", () => {
18 | assertEquals(fibonacci(3), 2);
19 | });
20 |
21 | // Deno.test("fibonacci(4) is 3", () => {
22 | // assertEquals(fibonacci(4), 3);
23 | // });
24 |
25 | // Deno.test("fibonacci(5) is 5", () => {
26 | // assertEquals(fibonacci(5), 5);
27 | // });
28 |
--------------------------------------------------------------------------------
/algorithms/fisher-yates-shuffle/README.md:
--------------------------------------------------------------------------------
1 | # Fisher–Yates Shuffle
2 |
3 | The Fisher–Yates shuffle is an algorithm for generating a random permutation of a finite sequence—in plain terms, the algorithm shuffles the sequence. The algorithm effectively puts all the elements into a hat; it continually determines the next element by randomly drawing an element from the hat until no elements remain. The algorithm produces an unbiased permutation: every permutation is equally likely. The modern version of the algorithm is efficient: it takes time proportional to the number of items being shuffled and shuffles them in place. [Wikipedia](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle)
4 |
--------------------------------------------------------------------------------
/algorithms/fisher-yates-shuffle/index.ts:
--------------------------------------------------------------------------------
1 | export function shuffle(array: T[]): T[] {
2 | for (let i = 0; i < array.length - 1; i++) {
3 | const j = Math.floor(Math.random() * (i + 1));
4 | [array[i], array[j]] = [array[j], array[i]];
5 | }
6 | return array;
7 | }
8 |
9 | // console.log(shuffle([0, 1, 2]));
10 |
--------------------------------------------------------------------------------
/algorithms/hash/djb2/index.ts:
--------------------------------------------------------------------------------
1 | // this algorithm (k=33) was first reported by dan bernstein many years ago in comp.lang.c. another version of this
2 | // algorithm (now favored by bernstein) uses xor: hash(i) = hash(i - 1) * 33 ^ str[i]; the magic of number 33
3 | // (why it works better than many other constants, prime or not) has never been adequately explained.
4 | export function djb2(string: string) {
5 | let hash = 5381;
6 |
7 | for (const c of string) {
8 | hash = (hash << 5) + hash + c.charCodeAt(0); // hash * 33 + c
9 | // hash = 33 * hash + c.charCodeAt(0);
10 | }
11 |
12 | // Unsigned shift right
13 | return hash >>> 1;
14 | }
15 |
--------------------------------------------------------------------------------
/algorithms/hash/loselose/index.ts:
--------------------------------------------------------------------------------
1 | export function loselose(string: string) {
2 | let hash = 0;
3 |
4 | for (const c of string) {
5 | hash += c.charCodeAt(0);
6 | }
7 |
8 | return hash;
9 | }
10 |
--------------------------------------------------------------------------------
/algorithms/hash/sdbm/index.ts:
--------------------------------------------------------------------------------
1 | // this algorithm was created for sdbm (a public-domain reimplementation of ndbm) database library. it was found to do
2 | // well in scrambling bits, causing better distribution of the keys and fewer splits. it also happens to be a good
3 | // general hashing function with good distribution. the actual function is hash(i) = hash(i - 1) * 65599 + str[i];
4 | // what is included below is the faster version used in gawk. [there is even a faster, duff-device version] the
5 | // magic constant 65599 was picked out of thin air while experimenting with different constants, and turns
6 | // out to be a prime. this is one of the algorithms used in berkeley db (see sleepycat) and elsewhere.
7 | export function sdbm(string: string) {
8 | let hash = 0;
9 |
10 | for (const c of string) {
11 | hash = c.charCodeAt(0) + (hash << 6) + (hash << 16) - hash;
12 | }
13 |
14 | // Unsigned shift right
15 | return hash >>> 1;
16 | }
17 |
--------------------------------------------------------------------------------
/algorithms/interpolation-search/README.md:
--------------------------------------------------------------------------------
1 | # Interpolation search
2 |
3 | Interpolation search is an algorithm for searching for a key in an array that has been ordered by numerical values assigned to the keys (key values). It was first described by W. W. Peterson in 1957. Interpolation search resembles the method by which people search a telephone directory for a name (the key value by which the book's entries are ordered): in each step the algorithm calculates where in the remaining search space the sought item might be, based on the key values at the bounds of the search space and the value of the sought key, usually via a linear interpolation. The key value actually found at this estimated position is then compared to the key value being sought. If it is not equal, then depending on the comparison, the remaining search space is reduced to the part before or after the estimated position. This method will only work if calculations on the size of differences between key values are sensible. [Wikipedia](https://en.wikipedia.org/wiki/Interpolation_search)
4 |
--------------------------------------------------------------------------------
/algorithms/interpolation-search/index.ts:
--------------------------------------------------------------------------------
1 | function interpolationSearch(haystack: number[], needle: number) {
2 | let low = 0;
3 | let middle = -1;
4 | let high = haystack.length - 1;
5 |
6 | const match =
7 | haystack[high] !== haystack[low] &&
8 | needle >= haystack[low] &&
9 | needle <= haystack[high];
10 |
11 | while (match) {
12 | middle =
13 | low +
14 | ((high - low) / (haystack[high] - haystack[low])) *
15 | (needle - haystack[low]);
16 |
17 | console.log({ haystack, middle, needle });
18 |
19 | if (haystack[middle] === needle) {
20 | console.log(`Found needle in haystack #${middle}`);
21 | return haystack[middle];
22 | } else if (haystack[middle] < needle) {
23 | low = middle + 1;
24 | } else if (haystack[middle] > needle) {
25 | high = middle - 1;
26 | }
27 | }
28 |
29 | if (needle === haystack[low]) {
30 | return low;
31 | }
32 | }
33 |
34 | const array = [...Array(100).keys()];
35 | // delete array[99];
36 | console.log(interpolationSearch(array, 99));
37 |
--------------------------------------------------------------------------------
/algorithms/levenshtein-distance/README.md:
--------------------------------------------------------------------------------
1 | # Levenshtein Distance
2 |
3 | n information theory, linguistics and computer science, the Levenshtein distance is a string metric for measuring the difference between two sequences. Informally, the Levenshtein distance between two words is the minimum number of single-character edits (insertions, deletions or substitutions) required to change one word into the other. It is named after the Soviet mathematician Vladimir Levenshtein, who considered this distance in 1965. [Wikipedia](https://en.wikipedia.org/wiki/Levenshtein_distance)
4 |
--------------------------------------------------------------------------------
/algorithms/levenshtein-distance/index.ts:
--------------------------------------------------------------------------------
1 | // https://en.wikipedia.org/wiki/Wagner%E2%80%93Fischer_algorithm
2 |
3 | export function levenshtein(a: string, b: string) {
4 | let i;
5 | let j;
6 |
7 | const d: number[][] = [];
8 |
9 | for (i = 0; i <= a.length; i++) {
10 | d[i] = [i];
11 | }
12 |
13 | for (j = 0; j <= b.length; j++) {
14 | d[0][j] = j;
15 | }
16 |
17 | for (i = 1; i <= a.length; i++) {
18 | for (j = 1; j <= b.length; j++) {
19 | d[i][j] = Math.min(
20 | d[i - 1][j] + 1, // deletion
21 | d[i][j - 1] + 1, // insertion
22 | d[i - 1][j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1) // substitution
23 | );
24 | }
25 | }
26 |
27 | return d[a.length][b.length];
28 | }
29 |
30 | // 2
31 | console.log(levenshtein('AC', 'CA'));
32 |
33 | // 2
34 | console.log(levenshtein('CAT', 'CTA'));
35 |
--------------------------------------------------------------------------------
/algorithms/linear-congruential-generator/README.md:
--------------------------------------------------------------------------------
1 | # Linear Congruential Generator
2 |
3 | A linear congruential generator (LCG) is an algorithm that yields a sequence of pseudo-randomized numbers calculated with a discontinuous piecewise linear equation. The method represents one of the oldest and best-known pseudorandom number generator algorithms. The theory behind them is relatively easy to understand, and they are easily implemented and fast, especially on computer hardware which can provide modular arithmetic by storage-bit truncation. [Wikipedia](https://en.wikipedia.org/wiki/Linear_congruential_generator)
4 |
--------------------------------------------------------------------------------
/algorithms/linear-congruential-generator/index.ts:
--------------------------------------------------------------------------------
1 | // m - modulus
2 | let m = 16;
3 |
4 | // c - increment
5 | let c = 1;
6 |
7 | // a - multiplier
8 | let a = 5;
9 |
10 | export function random(x: number = 1) {
11 | x = (a * x + c) % m;
12 | return x;
13 | }
14 |
15 | for (let i = 0; i < 16; i++) {
16 | const rng = random(i);
17 | console.log({
18 | index: i,
19 | integer: rng,
20 | binary: rng.toString(2),
21 | float: rng / m,
22 | boolean: Math.round(rng / m),
23 | });
24 | }
25 |
--------------------------------------------------------------------------------
/algorithms/linear-search/README.md:
--------------------------------------------------------------------------------
1 | # Linear Search
2 |
3 | In computer science, a linear search or sequential search is a method for finding an element within a list. It sequentially checks each element of the list until a match is found or the whole list has been searched. [Wikipedia](https://en.wikipedia.org/wiki/Linear_search)
4 |
--------------------------------------------------------------------------------
/algorithms/linear-search/index.ts:
--------------------------------------------------------------------------------
1 | // Linear search, left to right
2 | export function linearSearch(haystack: T[], needle: T): number | undefined {
3 | for (let i = 0; i < haystack.length; i++) {
4 | if (haystack[i] === needle) {
5 | return i;
6 | }
7 | }
8 | }
9 |
10 | // 2
11 | console.log(linearSearch(["d", "w", "s"], "s"));
12 |
13 | // 2
14 | console.log(linearSearch([1, 2, 3], 3));
15 |
--------------------------------------------------------------------------------
/algorithms/lucas-numbers/README.md:
--------------------------------------------------------------------------------
1 | # Lucas numbers
2 |
3 | The Lucas numbers or Lucas series are an integer sequence named after the mathematician François Édouard Anatole Lucas (1842–91), who studied both that sequence and the closely related Fibonacci numbers. Lucas numbers and Fibonacci numbers form complementary instances of Lucas sequences. [Wikipedia](https://en.wikipedia.org/wiki/Lucas_number)
4 |
--------------------------------------------------------------------------------
/algorithms/lucas-numbers/index.ts:
--------------------------------------------------------------------------------
1 | // Find the nth lucas number
2 | export function lucas(n: number): number {
3 | const sequence = [2, 1];
4 |
5 | if (n < 2) {
6 | return sequence[n];
7 | }
8 |
9 | for (let i = 2; i < n + 1; i++) {
10 | const tmp = sequence[0] + sequence[1];
11 | // Swap
12 | sequence[0] = sequence[1];
13 | // Set
14 | sequence[1] = tmp;
15 | }
16 |
17 | return sequence[1];
18 | }
19 |
20 | export function lucasRecursive(n: number): number {
21 | const sequence = [2, 1];
22 |
23 | if (n < 2) {
24 | return sequence[n];
25 | }
26 |
27 | return lucas(n - 1) + lucas(n - 2);
28 | }
29 |
30 | console.log(lucas(9));
31 | console.log(lucasRecursive(9));
32 |
--------------------------------------------------------------------------------
/algorithms/lucas-numbers/test.ts:
--------------------------------------------------------------------------------
1 | import { assertEquals } from 'https://deno.land/std/testing/asserts.ts';
2 | import { lucas } from './index.ts';
3 |
4 | Deno.test('lucas(0) is 2', () => {
5 | assertEquals(lucas(0), 2);
6 | });
7 |
8 | Deno.test('lucas(1) is 1', () => {
9 | assertEquals(lucas(1), 1);
10 | });
11 |
12 | Deno.test('lucas(2) is 3', () => {
13 | assertEquals(lucas(2), 3);
14 | });
15 |
16 | Deno.test('lucas(3) is 4', () => {
17 | assertEquals(lucas(3), 4);
18 | });
19 |
20 | Deno.test('lucas(4) is 7', () => {
21 | assertEquals(lucas(4), 7);
22 | });
23 |
24 | Deno.test('lucas(5) is 11', () => {
25 | assertEquals(lucas(5), 11);
26 | });
27 |
--------------------------------------------------------------------------------
/algorithms/mersenne-twister/README.md:
--------------------------------------------------------------------------------
1 | # Mersenne Twister
2 |
3 | The Mersenne Twister is a pseudorandom number generator (PRNG). It is by far the most widely used general-purpose PRNG. Its name derives from the fact that its period length is chosen to be a Mersenne prime.
4 |
5 | The Mersenne Twister was developed in 1997 by Makoto Matsumoto [ja] (松本 眞) and Takuji Nishimura (西村 拓士). It was designed specifically to rectify most of the flaws found in older PRNGs.
6 |
--------------------------------------------------------------------------------
/algorithms/mersenne-twister/index.ts:
--------------------------------------------------------------------------------
1 | // Coefficients
2 | const w = 32,
3 | n = 624,
4 | m = 397,
5 | r = 31;
6 |
7 | const a = 0x9908b0df;
8 |
9 | const u = 11,
10 | d = 0xffffffff;
11 |
12 | const s = 7,
13 | b = 0x9d2c5680;
14 |
15 | const t = 15,
16 | c = 0xefc60000;
17 |
18 | const l = 18;
19 |
20 | const f = 1812433253;
21 |
22 | const mt = new Uint32Array(n);
23 |
24 | const lowerMask = (1 << r) - 1;
25 |
26 | const upperMask = ~lowerMask >>> 0;
27 |
28 | let index = n + 1;
29 |
30 | export function seed(seed: number = Date.now()) {
31 | index = n;
32 | mt[0] = seed >>> w;
33 | for (index = 1; index < n; index++) {
34 | const z = mt[index - 1] ^ (mt[index - 1] >>> (w - 2));
35 | mt[index] =
36 | ((f * ((z & 0xffff0000) >>> 16)) << 16) + (f * (z & 0x0000ffff) + index);
37 | }
38 | }
39 |
40 | export function twist() {
41 | for (let i = 0; i < n; i++) {
42 | let x = (mt[i] & upperMask) + (mt[(i + 1) % n] & lowerMask);
43 | let xa = x >>> 1;
44 | if (x % 2 !== 0) {
45 | xa = xa ^ a;
46 | }
47 | mt[i] = mt[(i + m) % n] ^ xa;
48 | }
49 | index = 0;
50 | }
51 |
52 | export function random() {
53 | if (index >= n) {
54 | if (index > n) {
55 | seed(5489);
56 | }
57 | twist();
58 | }
59 |
60 | let y = mt[index++];
61 |
62 | y ^= (y >>> u) & d;
63 | y ^= (y << s) & b;
64 | y ^= (y << t) & c;
65 | y ^= y >>> l;
66 |
67 | return y >>> w;
68 | }
69 |
--------------------------------------------------------------------------------
/algorithms/mersenne-twister/test.ts:
--------------------------------------------------------------------------------
1 | import { random, seed } from "./index.ts";
2 |
3 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
4 |
5 | // https://github.com/pigulla/mersennetwister/blob/master/tests/MersenneTwister.spec.js
6 | var expected = [
7 | 1791095845,
8 | 4282876139,
9 | 3093770124,
10 | 4005303368,
11 | 491263,
12 | 550290313,
13 | 1298508491,
14 | 4290846341,
15 | 630311759,
16 | 1013994432,
17 | 396591248,
18 | 1703301249,
19 | 799981516,
20 | 1666063943,
21 | 1484172013,
22 | 2876537340,
23 | 1704103302,
24 | 4018109721,
25 | 2314200242,
26 | 3634877716,
27 | 1800426750,
28 | 1345499493,
29 | 2942995346,
30 | 2252917204,
31 | 878115723,
32 | 1904615676,
33 | 3771485674,
34 | 986026652,
35 | 117628829,
36 | 2295290254,
37 | 2879636018,
38 | 3925436996,
39 | 1792310487,
40 | 1963679703,
41 | 2399554537,
42 | 1849836273,
43 | 602957303,
44 | 4033523166,
45 | 850839392,
46 | 3343156310,
47 | 3439171725,
48 | 3075069929,
49 | 4158651785,
50 | 3447817223,
51 | 1346146623,
52 | 398576445,
53 | 2973502998,
54 | 2225448249,
55 | 3764062721,
56 | 3715233664,
57 | 3842306364,
58 | 3561158865,
59 | 365262088,
60 | 3563119320,
61 | 167739021,
62 | 1172740723,
63 | 729416111,
64 | 254447594,
65 | 3771593337,
66 | 2879896008,
67 | 422396446,
68 | 2547196999,
69 | 1808643459,
70 | 2884732358,
71 | 4114104213,
72 | 1768615473,
73 | 2289927481,
74 | 848474627,
75 | 2971589572,
76 | 1243949848,
77 | 1355129329,
78 | 610401323,
79 | 2948499020,
80 | 3364310042,
81 | 3584689972,
82 | 1771840848,
83 | 78547565,
84 | 146764659,
85 | 3221845289,
86 | 2680188370,
87 | 4247126031,
88 | 2837408832,
89 | 3213347012,
90 | 1282027545,
91 | 1204497775,
92 | 1916133090,
93 | 3389928919,
94 | 954017671,
95 | 443352346,
96 | 315096729,
97 | 1923688040,
98 | 2015364118,
99 | 3902387977,
100 | 413056707,
101 | 1261063143,
102 | 3879945342,
103 | 1235985687,
104 | 513207677,
105 | 558468452,
106 | 2253996187,
107 | 83180453,
108 | 359158073,
109 | 2915576403,
110 | 3937889446,
111 | 908935816,
112 | 3910346016,
113 | 1140514210,
114 | 1283895050,
115 | 2111290647,
116 | 2509932175,
117 | 229190383,
118 | 2430573655,
119 | 2465816345,
120 | 2636844999,
121 | 630194419,
122 | 4108289372,
123 | 2531048010,
124 | 1120896190,
125 | 3005439278,
126 | 992203680,
127 | 439523032,
128 | 2291143831,
129 | 1778356919,
130 | 4079953217,
131 | 2982425969,
132 | 2117674829,
133 | 1778886403,
134 | 2321861504,
135 | 214548472,
136 | 3287733501,
137 | 2301657549,
138 | 194758406,
139 | 2850976308,
140 | 601149909,
141 | 2211431878,
142 | 3403347458,
143 | 4057003596,
144 | 127995867,
145 | 2519234709,
146 | 3792995019,
147 | 3880081671,
148 | 2322667597,
149 | 590449352,
150 | 1924060235,
151 | 598187340,
152 | 3831694379,
153 | 3467719188,
154 | 1621712414,
155 | 1708008996,
156 | 2312516455,
157 | 710190855,
158 | 2801602349,
159 | 3983619012,
160 | 1551604281,
161 | 1493642992,
162 | 2452463100,
163 | 3224713426,
164 | 2739486816,
165 | 3118137613,
166 | 542518282,
167 | 3793770775,
168 | 2964406140,
169 | 2678651729,
170 | 2782062471,
171 | 3225273209,
172 | 1520156824,
173 | 1498506954,
174 | 3278061020,
175 | 1159331476,
176 | 1531292064,
177 | 3847801996,
178 | 3233201345,
179 | 1838637662,
180 | 3785334332,
181 | 4143956457,
182 | 50118808,
183 | 2849459538,
184 | 2139362163,
185 | 2670162785,
186 | 316934274,
187 | 492830188,
188 | 3379930844,
189 | 4078025319,
190 | 275167074,
191 | 1932357898,
192 | 1526046390,
193 | 2484164448,
194 | 4045158889,
195 | 1752934226,
196 | 1631242710,
197 | 1018023110,
198 | 3276716738,
199 | 3879985479,
200 | 3313975271,
201 | 2463934640,
202 | 1294333494,
203 | 12327951,
204 | 3318889349,
205 | 2650617233,
206 | 656828586,
207 | ];
208 |
209 | Deno.test("generates correct numbers", () => {
210 | seed(1);
211 | for (let i = 0; i < expected.length; i++) {
212 | assertEquals(random(), expected[i]);
213 | }
214 | });
215 |
--------------------------------------------------------------------------------
/algorithms/phi/README.md:
--------------------------------------------------------------------------------
1 | # Phi
2 |
3 | The golden ratio approx 1.6180339887... in mathematics, art, and architecture.
4 |
5 |
6 |
--------------------------------------------------------------------------------
/algorithms/phi/index.ts:
--------------------------------------------------------------------------------
1 | export const phi = (Math.sqrt(5) + 1) / 2;
2 |
--------------------------------------------------------------------------------
/algorithms/sieve-of-eratosthenes/README.md:
--------------------------------------------------------------------------------
1 | # Sieve of Eratosthenes
2 |
3 | In mathematics, the sieve of Eratosthenes is an ancient algorithm for finding all prime numbers up to any given limit. [Wikipedia](https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes)
4 |
--------------------------------------------------------------------------------
/algorithms/sieve-of-eratosthenes/index.ts:
--------------------------------------------------------------------------------
1 | export function sieveOfEraosthenes(n: number) {
2 | const array = [...Array(n)].map((i) => i).fill(true);
3 |
4 | for (let i = 2; i < Math.sqrt(n); i++) {
5 | if (array[i]) {
6 | for (let j = Math.pow(i, 2); j < n; j += i) {
7 | array[j] = false;
8 | }
9 | }
10 | }
11 |
12 | // If v is true, print prime key
13 | for (const [k, v] of array.entries()) {
14 | if (v) {
15 | console.log(k);
16 | }
17 | }
18 | }
19 |
20 | sieveOfEraosthenes(100);
21 |
--------------------------------------------------------------------------------
/algorithms/sort/bubble-sort/README.md:
--------------------------------------------------------------------------------
1 | # Bubble sort
2 |
3 | Bubble sort, sometimes referred to as sinking sort, is a simple sorting algorithm that repeatedly steps through the list, compares adjacent elements and swaps them if they are in the wrong order. The pass through the list is repeated until the list is sorted. The algorithm, which is a comparison sort, is named for the way smaller or larger elements "bubble" to the top of the list.
4 |
5 | This simple algorithm performs poorly in real world use and is used primarily as an educational tool. More efficient algorithms such as timsort, or merge sort are used by the sorting libraries built into popular programming languages such as Python and Java. [Wikipedia](https://en.wikipedia.org/wiki/Bubble_sort)
6 |
7 |
8 |
9 |
10 | Time Complexity |
11 | Space Complexity |
12 |
13 |
14 | Best |
15 | Average |
16 | Worst |
17 | Worst |
18 |
19 |
20 | Ω(n) |
21 | Θ(n^2) |
22 | O(n^2) |
23 | O(1) |
24 |
25 |
26 |
27 |
28 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
29 |
--------------------------------------------------------------------------------
/algorithms/sort/bubble-sort/index.ts:
--------------------------------------------------------------------------------
1 | export function bubbleSort(array: number[]) {
2 | while (true) {
3 | let swapped = false;
4 | for (let i = 1; i < array.length; i++) {
5 | if (array[i - 1] > array[i]) {
6 | [array[i - 1], array[i]] = [array[i], array[i - 1]];
7 | swapped = true;
8 | }
9 | }
10 | // We haven't swapped anything on this run, break.
11 | if (!swapped) {
12 | break;
13 | }
14 | }
15 |
16 | return array;
17 | }
18 |
19 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
20 |
21 | const array = shuffle([...Array(100).keys()]);
22 |
23 | console.log(bubbleSort(array));
24 |
--------------------------------------------------------------------------------
/algorithms/sort/comb-sort/README.md:
--------------------------------------------------------------------------------
1 | # Comb sort
2 |
3 | Comb sort is a relatively simple sorting algorithm originally designed by Włodzimierz Dobosiewicz and Artur Borowy in 1980, later rediscovered by Stephen Lacey and Richard Box in 1991. Comb sort improves on bubble sort.
4 |
--------------------------------------------------------------------------------
/algorithms/sort/comb-sort/index.ts:
--------------------------------------------------------------------------------
1 | function combSort(array: number[]) {
2 | // Gap
3 | let gap = array.length;
4 |
5 | // Shrink factor
6 | let shrink = 1.3;
7 |
8 | let sorted = false;
9 |
10 | while (!sorted) {
11 | gap = Math.floor(gap / shrink);
12 |
13 | if (gap <= 1) {
14 | gap = 1;
15 | sorted = true;
16 | }
17 |
18 | for (let i = 0; i + gap < array.length; i++) {
19 | if (array[i] > array[i + gap]) {
20 | [array[i], array[i + gap]] = [array[i + gap], array[i]];
21 | sorted = false;
22 | }
23 | }
24 | }
25 |
26 | return array;
27 | }
28 |
29 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
30 |
31 | const array = shuffle([...Array(100).keys()]);
32 |
33 | console.log(combSort(array));
34 |
--------------------------------------------------------------------------------
/algorithms/sort/heapsort/README.md:
--------------------------------------------------------------------------------
1 | # Heapsort
2 |
3 | In computer science, heapsort is a comparison-based sorting algorithm. Heapsort can be thought of as an improved selection sort: like selection sort, heapsort divides its input into a sorted and an unsorted region, and it iteratively shrinks the unsorted region by extracting the largest element from it and inserting it into the sorted region. Unlike selection sort, heapsort does not waste time with a linear-time scan of the unsorted region; rather, heap sort maintains the unsorted region in a heap data structure to more quickly find the largest element in each step. [Wikipedia](https://en.wikipedia.org/wiki/Heapsort)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Best |
13 | Average |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Ω(n log(n)) |
19 | Θ(n log(n)) |
20 | O(n log(n)) |
21 | O(1) |
22 |
23 |
24 |
25 |
26 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
27 |
--------------------------------------------------------------------------------
/algorithms/sort/heapsort/index.ts:
--------------------------------------------------------------------------------
1 | function parent(i: number) {
2 | return Math.floor((i - 1) / 2);
3 | }
4 |
5 | function leftChild(i: number) {
6 | return 2 * i + 1;
7 | }
8 |
9 | function rightChild(i: number) {
10 | return 2 * i + 2;
11 | }
12 |
13 | function siftDown(array: number[], start: number, end: number) {
14 | let root = start;
15 |
16 | while (leftChild(root) <= end) {
17 | let child = leftChild(root);
18 | let swap = root;
19 |
20 | if (array[swap] < array[child]) {
21 | swap = child;
22 | }
23 |
24 | if (child + 1 <= end && array[swap] < array[child + 1]) {
25 | swap = child + 1;
26 | }
27 |
28 | if (swap === root) {
29 | return;
30 | } else {
31 | // Swap
32 | [array[root], array[swap]] = [array[swap], array[root]];
33 | root = swap;
34 | }
35 | }
36 | }
37 |
38 | function heapify(array: number[]) {
39 | let start = parent(array.length - 1);
40 | while (start >= 0) {
41 | // Sift down
42 | siftDown(array, start, array.length - 1);
43 |
44 | // Decrement start
45 | start--;
46 | }
47 | }
48 |
49 | function heapsort(array: number[]) {
50 | // heapify array
51 | heapify(array);
52 |
53 | let end = array.length - 1;
54 |
55 | while (end > 0) {
56 | // swap
57 | [array[end], array[0]] = [array[0], array[end]];
58 | // reduce size
59 | end--;
60 | // restore heap
61 | siftDown(array, 0, end);
62 | }
63 |
64 | return array;
65 | }
66 |
67 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
68 |
69 | const array = shuffle([...Array(100).keys()]);
70 |
71 | console.log(heapsort(array));
72 |
73 | let index = 1;
74 |
75 | console.log({
76 | index,
77 | parent: parent(index),
78 | leftChild: leftChild(index),
79 | rightChild: rightChild(index),
80 | });
81 |
--------------------------------------------------------------------------------
/algorithms/sort/insertion-sort/README.md:
--------------------------------------------------------------------------------
1 | # Insertion sort
2 |
3 | Insertion sort iterates, consuming one input element each repetition, and growing a sorted output list. At each iteration, insertion sort removes one element from the input data, finds the location it belongs within the sorted list, and inserts it there. It repeats until no input elements remain. [Wikipedia](https://en.wikipedia.org/wiki/Insertion_sort)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Best |
13 | Average |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Ω(n) |
19 | Θ(n^2) |
20 | O(n^2) |
21 | O(1) |
22 |
23 |
24 |
25 |
26 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
27 |
--------------------------------------------------------------------------------
/algorithms/sort/insertion-sort/index.ts:
--------------------------------------------------------------------------------
1 | export function insertionSort(array: number[]) {
2 | for (let i = 1; i < array.length; i++) {
3 | for (let j = i; j > 0 && array[j - 1] > array[j]; j--) {
4 | // Swap
5 | [array[j - 1], array[j]] = [array[j], array[j - 1]];
6 | }
7 | }
8 | return array;
9 | }
10 |
11 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
12 |
13 | const array = shuffle([...Array(100).keys()]);
14 |
15 | console.log(insertionSort(array));
16 |
--------------------------------------------------------------------------------
/algorithms/sort/merge-sort/README.md:
--------------------------------------------------------------------------------
1 | # Merge sort
2 |
3 | In computer science, merge sort (also commonly spelled mergesort) is an efficient, general-purpose, comparison-based sorting algorithm. Most implementations produce a stable sort, which means that the order of equal elements is the same in the input and output. Merge sort is a divide and conquer algorithm that was invented by John von Neumann in 1945. A detailed description and analysis of bottom-up mergesort appeared in a report by Goldstine and von Neumann as early as 1948. [Wikipedia](https://en.wikipedia.org/wiki/Merge_sort)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Best |
13 | Average |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Ω(n log(n)) |
19 | Θ(n log(n)) |
20 | O(n log(n)) |
21 | O(n) |
22 |
23 |
24 |
25 |
26 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
27 |
--------------------------------------------------------------------------------
/algorithms/sort/merge-sort/index.ts:
--------------------------------------------------------------------------------
1 | function merge(left: number[], right: number[]) {
2 | const array: number[] = [];
3 |
4 | let i = 0;
5 | let j = 0;
6 | let k = 0;
7 |
8 | while (i < left.length && j < right.length) {
9 | if (left[i] <= right[j]) {
10 | array[k++] = left[i++];
11 | } else {
12 | array[k++] = right[j++];
13 | }
14 | }
15 |
16 | while (i < left.length) {
17 | array[k++] = left[i++];
18 | }
19 |
20 | while (j < right.length) {
21 | array[k++] = right[j++];
22 | }
23 |
24 | return array;
25 | }
26 |
27 | function mergeSort(array: number[]): number[] {
28 | if (array.length < 2) {
29 | return array;
30 | }
31 |
32 | // Divide
33 | const middle = Math.floor(array.length / 2);
34 | const left = array.slice(0, middle);
35 | const right = array.slice(middle, array.length);
36 |
37 | // Conquer && Combine
38 | return merge(mergeSort(left), mergeSort(right));
39 | }
40 |
41 | // Test
42 |
43 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
44 |
45 | const array = shuffle([...Array(100).keys()]);
46 |
47 | console.log(array);
48 |
49 | console.log(mergeSort(array));
50 |
--------------------------------------------------------------------------------
/algorithms/sort/quicksort/README.md:
--------------------------------------------------------------------------------
1 | # Quicksort
2 |
3 | Quicksort (sometimes called partition-exchange sort) is an efficient sorting algorithm. Developed by British computer scientist Tony Hoare in 1959 and published in 1961, it is still a commonly used algorithm for sorting. When implemented well, it can be about two or three times faster than its main competitors, merge sort and heapsort. [Wikipedia](https://en.wikipedia.org/wiki/Quicksort)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Best |
13 | Average |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Ω(n log(n)) |
19 | Θ(n log(n)) |
20 | O(n^2) |
21 | O(log(n)) |
22 |
23 |
24 |
25 |
26 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
27 |
--------------------------------------------------------------------------------
/algorithms/sort/quicksort/index.ts:
--------------------------------------------------------------------------------
1 | export function partition(array: number[], low: number, high: number) {
2 | // console.log(array, high);
3 | const pivot = array[high];
4 |
5 | // Index of smallest element
6 | let i = low - 1;
7 |
8 | for (let j = low; j < high; j++) {
9 | // If current element is less than pivot
10 | if (array[j] < pivot) {
11 | i++;
12 | // swap arr[i] and arr[j]
13 | const temp = array[i];
14 | array[i] = array[j];
15 | array[j] = temp;
16 | }
17 | }
18 |
19 | // swap array[i+1] and array[high] (or pivot)
20 | const temp1 = array[i + 1];
21 | array[i + 1] = array[high];
22 | array[high] = temp1;
23 |
24 | return i + 1;
25 | }
26 |
27 | export function quicksort(array: number[], low: number, high: number) {
28 | if (low < high) {
29 | const p = partition(array, low, high);
30 | quicksort(array, low, p - 1);
31 | quicksort(array, p + 1, high);
32 | }
33 |
34 | return array;
35 | }
36 |
37 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
38 |
39 | const array = shuffle([...Array(100).keys()]);
40 |
41 | console.log(array);
42 |
43 | quicksort(array, 0, array.length - 1);
44 |
45 | console.log(array);
46 |
--------------------------------------------------------------------------------
/algorithms/sort/selection-sort/README.md:
--------------------------------------------------------------------------------
1 | # Selection sort
2 |
3 | In computer science, selection sort is an in-place comparison sorting algorithm. It has an O(n2) time complexity, which makes it inefficient on large lists, and generally performs worse than the similar insertion sort. Selection sort is noted for its simplicity and has performance advantages over more complicated algorithms in certain situations, particularly where auxiliary memory is limited.
4 |
5 | The algorithm divides the input list into two parts: a sorted sublist of items which is built up from left to right at the front (left) of the list and a sublist of the remaining unsorted items that occupy the rest of the list. Initially, the sorted sublist is empty and the unsorted sublist is the entire input list. The algorithm proceeds by finding the smallest (or largest, depending on sorting order) element in the unsorted sublist, exchanging (swapping) it with the leftmost unsorted element (putting it in sorted order), and moving the sublist boundaries one element to the right.
6 |
7 | The time efficiency of selection sort is quadratic, so there are a number of sorting techniques which have better time complexity than selection sort. One thing which distinguishes selection sort from other sorting algorithms is that it makes the minimum possible number of swaps, n − 1 in the worst case. [Wikipedia](https://en.wikipedia.org/wiki/Selection_sort)
8 |
9 |
10 |
11 |
12 | Time Complexity |
13 | Space Complexity |
14 |
15 |
16 | Best |
17 | Average |
18 | Worst |
19 | Worst |
20 |
21 |
22 | Ω(n^2) |
23 | Θ(n^2) |
24 | O(n^2) |
25 | O(1) |
26 |
27 |
28 |
29 |
30 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
31 |
--------------------------------------------------------------------------------
/algorithms/sort/selection-sort/index.ts:
--------------------------------------------------------------------------------
1 | export function selectionSort(array: number[]) {
2 | for (let i = 0; i < array.length; i++) {
3 | let minimum = i;
4 | for (let j = i + 1; j < array.length; j++) {
5 | if (array[j] < array[minimum]) {
6 | // New minimum found
7 | minimum = j;
8 | }
9 | }
10 |
11 | if (minimum !== i) {
12 | // Swap
13 | [array[i], array[minimum]] = [array[minimum], array[i]];
14 | }
15 | }
16 |
17 | return array;
18 | }
19 |
20 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
21 |
22 | const array = shuffle([...Array(100).keys()]);
23 |
24 | console.log(selectionSort(array));
25 |
--------------------------------------------------------------------------------
/algorithms/sort/shellsort/README.md:
--------------------------------------------------------------------------------
1 | # Shellsort
2 |
3 | Shellsort, also known as Shell sort or Shell's method, is an in-place comparison sort. It can be seen as either a generalization of sorting by exchange (bubble sort) or sorting by insertion (insertion sort). The method starts by sorting pairs of elements far apart from each other, then progressively reducing the gap between elements to be compared. By starting with far apart elements, it can move some out-of-place elements into position faster than a simple nearest neighbor exchange. [Wikipedia](https://en.wikipedia.org/wiki/Shellsort)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Best |
13 | Average |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Ω(n log(n)) |
19 | Θ(n(log(n))^2) |
20 | O(n(log(n))^2) |
21 | O(1) |
22 |
23 |
24 |
25 |
26 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
27 |
--------------------------------------------------------------------------------
/algorithms/sort/shellsort/index.ts:
--------------------------------------------------------------------------------
1 | // Using Marcin Ciura's gap sequence, with an inner insertion sort.
2 | function shellsort(array: number[]) {
3 | const n = array.length;
4 | const gaps = [701, 301, 132, 57, 23, 10, 4, 1];
5 |
6 | // Start with the largest gap and work down to a gap of 1
7 | for (const gap of gaps) {
8 | // Do a gapped insertion sort for this gap size.
9 | // The first gap elements a[0..gap-1] are already in gapped order
10 | // keep adding one more element until the entire array is gap sorted
11 | for (let i = gap; i < n; i += 1) {
12 | // add a[i] to the elements that have been gap sorted
13 | // save a[i] in temp and make a hole at position i
14 | let temp = array[i];
15 | // shift earlier gap-sorted elements up until the correct location for a[i] is found
16 | let j;
17 | for (j = i; j >= gap && array[j - gap] > temp; j -= gap) {
18 | array[j] = array[j - gap];
19 | }
20 | // put temp (the original a[i]) in its correct location
21 | array[j] = temp;
22 | }
23 | }
24 | return array;
25 | }
26 |
27 | import { shuffle } from "../../fisher-yates-shuffle/index.ts";
28 |
29 | const array = shuffle([...Array(100).keys()]);
30 |
31 | console.log("Before", array);
32 |
33 | console.log("After", shellsort(array));
34 |
--------------------------------------------------------------------------------
/algorithms/ternary-search/README.md:
--------------------------------------------------------------------------------
1 | # Ternary Search
2 |
3 | A ternary search algorithm is a technique in computer science for finding the minimum or maximum of a unimodal function. A ternary search determines either that the minimum or maximum cannot be in the first third of the domain or that it cannot be in the last third of the domain, then repeats on the remaining two thirds. A ternary search is an example of a divide and conquer algorithm. [Wikipedia](https://en.wikipedia.org/wiki/Ternary_search)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Best |
14 | Worst |
15 | Worst |
16 |
17 |
18 | Θ(log(n)) |
19 | O(1) |
20 | Θ(log(n)) |
21 | O(1) |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/algorithms/ternary-search/index.ts:
--------------------------------------------------------------------------------
1 | export function ternarySearch(
2 | haystack: number[],
3 | needle: number,
4 | left: number = 0,
5 | right: number = haystack.length - 1
6 | ): number | undefined {
7 | if (right >= left) {
8 | // Thirds, so we have two middles
9 | let leftMiddle = Math.round(left + Math.abs(left - right) / 3);
10 | let rightMiddle = Math.round(right - Math.abs(left - right) / 3);
11 |
12 | if (haystack[leftMiddle] === needle) {
13 | return leftMiddle;
14 | } else if (haystack[rightMiddle] === needle) {
15 | return rightMiddle;
16 | }
17 |
18 | if (needle < haystack[leftMiddle]) {
19 | return ternarySearch(haystack, needle, left, leftMiddle - 1);
20 | } else if (needle > haystack[rightMiddle]) {
21 | return ternarySearch(haystack, needle, rightMiddle + 1, right);
22 | }
23 |
24 | return ternarySearch(haystack, needle, leftMiddle + 1, rightMiddle - 1);
25 | }
26 | }
27 |
28 | const array = [...Array(90).keys()];
29 |
30 | // 3
31 | console.log(ternarySearch(array, 3));
32 |
--------------------------------------------------------------------------------
/data-structures/avl-tree/README.md:
--------------------------------------------------------------------------------
1 | # AVL Tree
2 |
3 | In computer science, an AVL tree (named after inventors Adelson-Velsky and Landis) is a self-balancing binary search tree. It was the first such data structure to be invented. In an AVL tree, the heights of the two child subtrees of any node differ by at most one; if at any time they differ by more than one, rebalancing is done to restore this property. Lookup, insertion, and deletion all take O(log n) time in both the average and worst cases, where n is the number of nodes in the tree prior to the operation. Insertions and deletions may require the tree to be rebalanced by one or more tree rotations. [Wikipedia](https://en.wikipedia.org/wiki/AVL_tree)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | Θ(log(n)) |
29 | Θ(log(n)) |
30 | Θ(log(n)) |
31 | Θ(log(n)) |
32 | O(log(n)) |
33 | O(log(n)) |
34 | O(log(n)) |
35 | O(log(n)) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 |
42 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
43 |
--------------------------------------------------------------------------------
/data-structures/avl-tree/index.ts:
--------------------------------------------------------------------------------
1 | import { BinarySearchTree } from "../binary-search-tree/index.ts";
2 | import { BinaryTreeNode } from "../binary-tree/index.ts";
3 |
4 | export function add(
5 | root: AvlTreeNode | undefined,
6 | node: AvlTreeNode
7 | ): AvlTreeNode {
8 | // Step 1 - Perform normal BST
9 | console.log("Step 1 - Perform normal BST");
10 | if (!root) {
11 | return node;
12 | } else if (node.key < root.key) {
13 | root.left = add(root.left, node);
14 | } else if (node.key > root.key) {
15 | root.right = add(root.right, node);
16 | } else {
17 | return root;
18 | }
19 |
20 | // Step 2 - Update the height of the ancestor node
21 | console.log("Step 2 - Update the height of the ancestor node");
22 | root.height = Math.max(getHeight(root.left), getHeight(root.right)) + 1;
23 |
24 | // Step 3 - Get the balance factor
25 | console.log("Step 3 - Get the balance factor");
26 | const balance = getBalanceFactor(root);
27 | // console.log("Balance factor 1:", balance);
28 |
29 | // Step 4 - If the node is unbalanced,
30 | // then try out the 4 cases
31 |
32 | // The left subtree of α's left son was inserted once //rorateRight(α)
33 | // The right subtree of the left son of α was inserted once //rorateLeft(α.left), rorateRight(α)
34 | // The left son of α's right son was inserted once //rorateRight(α.right), rorateLeft(α)
35 | // The right subtree of α's right son was inserted once //rorateLeft(α)
36 |
37 | // Case 1 - Left Left
38 | if (balance > 1 && node.key < (>root.left).key) {
39 | console.log("Step 4 - Case 1 - Left Left");
40 | return rightRotate(root);
41 | } else if (balance < -1 && node.key > (>root.right).key) {
42 | // Case 2 - Right Right
43 | console.log("Step 4 - Case 2 - Right Right");
44 | return leftRotate(root);
45 | } else if (balance > 1 && node.key > (>root.left).key) {
46 | // Case 3 - Left Right
47 | console.log("Step 4 - Case 3 - Left Right");
48 | root.left = leftRotate(>root.left);
49 | return rightRotate(root);
50 | } else if (balance < -1 && node.key < (>root.right).key) {
51 | // Case 4 - Right Left
52 | console.log("Step 4 - Case 4 - Right Left");
53 | root.right = rightRotate(>root.right);
54 | return leftRotate(root);
55 | }
56 |
57 | // console.log("Balance factor 2:", balance);
58 |
59 | return root;
60 | }
61 |
62 | export function remove(key: K): AvlTreeNode | undefined {
63 | // TODO: Implement
64 | throw Error("Not implemented");
65 | return;
66 | }
67 |
68 | function getHeight(node?: AvlTreeNode) {
69 | if (!node) {
70 | return 0;
71 | }
72 |
73 | return node.height;
74 | }
75 |
76 | function getBalanceFactor(node: AvlTreeNode) {
77 | if (!node) {
78 | return 0;
79 | }
80 |
81 | return getHeight(node.left) - getHeight(node.right);
82 | }
83 |
84 | // (x) (y)
85 | // / \ / \
86 | // 'a (y) ===> (x) 'c
87 | // / \ / \
88 | // 'b 'c 'a 'b
89 |
90 | // In LL Rotation every node moves one position to left from the current position.
91 | function leftRotate(x: AvlTreeNode) {
92 | if (!x.right) {
93 | throw Error("Cannot perform left rotation on this node!");
94 | }
95 |
96 | // Get Alpha
97 | const α = x.left;
98 |
99 | // Get Y
100 | const y = x.right;
101 |
102 | // Get Beta
103 | const β = y.left;
104 |
105 | // Get Gamma
106 | const γ = y.right;
107 |
108 | // Rotation
109 | y.left = x;
110 |
111 | x.right = β;
112 |
113 | // Fix X height max(α,β)
114 | x.height = Math.max(getHeight(α), getHeight(β)) + 1;
115 |
116 | // Fix Y height max(β,γ)
117 | y.height = Math.max(getHeight(β), getHeight(γ)) + 1;
118 |
119 | return y;
120 | }
121 |
122 | // (y) (x)
123 | // / \ / \
124 | // (x) 'c ===> 'a (y)
125 | // / \ / \
126 | // 'a 'b 'b 'c
127 |
128 | // In RR Rotation every node moves one position to right from the current position.
129 | function rightRotate(y: AvlTreeNode) {
130 | if (!y.left) {
131 | throw Error("Cannot perform right rotate on this node!");
132 | }
133 |
134 | // Get X
135 | const x = y.left;
136 |
137 | // Get Alpha
138 | const α = x.left;
139 |
140 | // Get Beta
141 | const β = x.right;
142 |
143 | // Get Gamma
144 | const γ = y.right;
145 |
146 | // Rotation
147 | x.right = y;
148 | y.left = β;
149 |
150 | // Fix Y height max(β,γ)
151 | y.height = Math.max(getHeight(β), getHeight(γ)) + 1;
152 |
153 | // Fix X height max(α,β)
154 | x.height = Math.max(getHeight(α), getHeight(β)) + 1;
155 |
156 | return x;
157 | }
158 |
159 | interface AvlTreeNode extends BinaryTreeNode {
160 | left?: AvlTreeNode;
161 | right?: AvlTreeNode;
162 | height: number;
163 | }
164 |
165 | class AvlTreeNode extends BinaryTreeNode
166 | implements AvlTreeNode {
167 | left?: AvlTreeNode;
168 | right?: AvlTreeNode;
169 | height: number;
170 | constructor(
171 | key: K,
172 | value?: V,
173 | left?: AvlTreeNode,
174 | right?: AvlTreeNode,
175 | parent?: AvlTreeNode
176 | ) {
177 | super(key, value, left, right, parent);
178 | this.height = 1;
179 | }
180 | }
181 |
182 | interface AvlTree extends BinarySearchTree {
183 | root?: AvlTreeNode;
184 | getHeight(node: AvlTreeNode): number;
185 | getBalanceFactor(node: AvlTreeNode): number;
186 | leftRotate(node: AvlTreeNode): AvlTreeNode;
187 | rightRotate(node: AvlTreeNode): AvlTreeNode;
188 | }
189 |
190 | class AvlTree extends BinarySearchTree implements AvlTree {
191 | root?: AvlTreeNode;
192 | add(node: AvlTreeNode): AvlTreeNode {
193 | this.root = add(this.root, node);
194 | return node;
195 | }
196 | remove(key: K): AvlTreeNode | undefined {
197 | return this.remove(key);
198 | }
199 | getHeight(node?: AvlTreeNode) {
200 | return getHeight(node);
201 | }
202 |
203 | getBalanceFactor(node: AvlTreeNode) {
204 | return getBalanceFactor(node);
205 | }
206 |
207 | leftRotate(node: AvlTreeNode) {
208 | return leftRotate(node);
209 | }
210 |
211 | rightRotate(node: AvlTreeNode) {
212 | return rightRotate(node);
213 | }
214 | }
215 |
216 | const avlTree = new AvlTree();
217 |
218 | [10, 20, 30, 40, 50, 25].forEach((i: number) => {
219 | const node = new AvlTreeNode(i);
220 | avlTree.add(node);
221 | });
222 |
223 | avlTree.preorder();
224 |
225 | console.log("Root", JSON.stringify(avlTree.root));
226 |
--------------------------------------------------------------------------------
/data-structures/binary-search-tree/README.md:
--------------------------------------------------------------------------------
1 | # Binary Search Tree
2 |
3 | In computer science, binary search trees (BST), sometimes called ordered or sorted binary trees, are a particular type of container: a data structure that stores "items" (such as numbers, names etc.) in memory. They allow fast lookup, addition and removal of items, and can be used to implement either dynamic sets of items, or lookup tables that allow finding an item by its key (e.g., finding the phone number of a person by name). [Wikipedia](https://en.wikipedia.org/wiki/Binary_search_tree)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | Θ(log(n)) |
29 | Θ(log(n)) |
30 | Θ(log(n)) |
31 | Θ(log(n)) |
32 | O(n) |
33 | O(n) |
34 | O(n) |
35 | O(n) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
42 |
--------------------------------------------------------------------------------
/data-structures/binary-search-tree/index.ts:
--------------------------------------------------------------------------------
1 | import { BinaryTreeNode } from "../binary-tree/index.ts";
2 |
3 | // BINARY SEARCH TREE is a node based binary tree which further has right and left subtree that too are binary search tree.
4 | // Insertion, deletion, searching of an element is faster in BINARY SEARCH TREE than BINARY TREE due to the ordered characteristics
5 | // IN BINARY SEARCH TREE the left subtree has elements less than the nodes element and the right subtree has elements greater than the nodes element.
6 |
7 | export function find(
8 | root: BinaryTreeNode | undefined,
9 | key: K
10 | ): BinaryTreeNode | undefined {
11 | if (!root) {
12 | return;
13 | } else if (root.key === key) {
14 | return root;
15 | } else if (root.key > key) {
16 | return find(root.left, key);
17 | } else {
18 | return find(root.right, key);
19 | }
20 | }
21 |
22 | export function add(
23 | // root to insert
24 | root: BinaryTreeNode | undefined,
25 | // node to add
26 | node: BinaryTreeNode
27 | ): BinaryTreeNode {
28 | if (!root) {
29 | return node;
30 | } else if (root.key < node.key) {
31 | root.right = add(root.right, node);
32 | } else {
33 | root.left = add(root.left, node);
34 | }
35 | return root;
36 | }
37 |
38 | export function remove(
39 | root: BinaryTreeNode | undefined,
40 | key: K
41 | ): BinaryTreeNode | undefined {
42 | if (!root) {
43 | return root;
44 | } else if (key < root.key) {
45 | root.left = remove(root.left, key);
46 | return root;
47 | } else if (key > root.key) {
48 | root.right = remove(root.right, key);
49 | return root;
50 | }
51 |
52 | // at this point we've recursed to the correct key node
53 | console.log("Node to delete", root);
54 |
55 | // both children
56 | if (!root.left && !root.right) {
57 | console.log("No children");
58 | root = undefined;
59 | return root;
60 | }
61 |
62 | // one child
63 | if (!root.left) {
64 | console.log("One child - Right");
65 | root = root.right;
66 | return root;
67 | } else if (!root.right) {
68 | console.log("One child - Left");
69 | root = root.left;
70 | return root;
71 | }
72 |
73 | // two children
74 | console.log("Two children");
75 | const successor = minimum(root.right);
76 |
77 | root.key = successor.key;
78 |
79 | root.right = remove(root.right, successor.key);
80 |
81 | return root;
82 | }
83 |
84 | export function contains(
85 | root: BinaryTreeNode | undefined,
86 | key: K
87 | ): boolean {
88 | if (!root) {
89 | return false;
90 | } else if (root.key === key) {
91 | return true;
92 | } else if (root.key > key) {
93 | return contains(root.left, key);
94 | } else {
95 | return contains(root.right, key);
96 | }
97 | }
98 |
99 | export function findParent(
100 | root: BinaryTreeNode | undefined,
101 | node: BinaryTreeNode
102 | ): BinaryTreeNode | undefined {
103 | console.log("find parent", JSON.stringify(root));
104 | if (node.parent) {
105 | return node.parent;
106 | } else if (!root) {
107 | console.log("no root");
108 | return;
109 | } else if (node === root.left || node === root.right) {
110 | return root;
111 | } else {
112 | if (node.key < root.key) {
113 | return findParent(root.left, node);
114 | } else {
115 | return findParent(root.right, node);
116 | }
117 | }
118 | }
119 |
120 | function inorder(node: BinaryTreeNode | undefined) {
121 | if (!node) {
122 | return;
123 | }
124 |
125 | if (node.left) {
126 | inorder(node.left);
127 | }
128 |
129 | console.log(node.key);
130 |
131 | if (node.right) {
132 | inorder(node.right);
133 | }
134 | }
135 |
136 | function preorder(node: BinaryTreeNode | undefined) {
137 | if (!node) {
138 | return;
139 | }
140 |
141 | console.log(node.key);
142 |
143 | if (node.left) {
144 | preorder(node.left);
145 | }
146 |
147 | if (node.right) {
148 | preorder(node.right);
149 | }
150 | }
151 |
152 | function postorder(node: BinaryTreeNode | undefined) {
153 | if (!node) {
154 | return;
155 | }
156 |
157 | if (node.left) {
158 | postorder(node.left);
159 | }
160 | if (node.right) {
161 | postorder(node.right);
162 | }
163 |
164 | console.log(node.key);
165 | }
166 |
167 | export function minimum(
168 | node: BinaryTreeNode
169 | ): BinaryTreeNode {
170 | if (!node.left) {
171 | return node;
172 | }
173 |
174 | return minimum(node.left);
175 | }
176 |
177 | export function maximum(
178 | node: BinaryTreeNode
179 | ): BinaryTreeNode {
180 | if (!node.right) {
181 | return node;
182 | }
183 |
184 | return maximum(node.right);
185 | }
186 |
187 | export function validate(
188 | node: BinaryTreeNode | undefined,
189 | minimum: number = 0,
190 | maximum: number = Number.MAX_SAFE_INTEGER
191 | ): boolean {
192 | if (!node) {
193 | return true;
194 | } else if (node.key < minimum || node.key > maximum) {
195 | return false;
196 | }
197 |
198 | return (
199 | validate(node.left, minimum, node.key - 1) &&
200 | validate(node.right, node.key + 1, maximum)
201 | );
202 | }
203 |
204 | export interface BinarySearchTree {
205 | root?: BinaryTreeNode;
206 | find(key: K, root: BinaryTreeNode): BinaryTreeNode | undefined;
207 | add(node?: BinaryTreeNode): BinaryTreeNode;
208 | remove(key: K, root?: BinaryTreeNode): BinaryTreeNode | undefined;
209 | contains(key: K, root?: BinaryTreeNode): boolean;
210 | findParent(
211 | node: BinaryTreeNode | undefined,
212 | root: BinaryTreeNode
213 | ): BinaryTreeNode | undefined;
214 | inorder(node: BinaryTreeNode): void;
215 | preorder(node: BinaryTreeNode): void;
216 | postorder(node: BinaryTreeNode): void;
217 | minimum(node: BinaryTreeNode): BinaryTreeNode;
218 | maximum(node: BinaryTreeNode): BinaryTreeNode;
219 | validate(): boolean;
220 | }
221 |
222 | export class BinarySearchTree implements BinarySearchTree {
223 | root?: BinaryTreeNode;
224 |
225 | find(node: K, root = this.root) {
226 | return find(root, node);
227 | }
228 |
229 | add(node: BinaryTreeNode) {
230 | this.root = add(this.root, node);
231 | return node;
232 | }
233 |
234 | remove(key: K, root = this.root) {
235 | return remove(root, key);
236 | }
237 |
238 | contains(key: K, root = this.root) {
239 | return contains(root, key);
240 | }
241 |
242 | findParent(node: BinaryTreeNode, root = this.root) {
243 | return findParent(root, node);
244 | }
245 |
246 | inorder(node = this.root) {
247 | inorder(node);
248 | }
249 |
250 | preorder(node = this.root) {
251 | preorder(node);
252 | }
253 |
254 | postorder(node = this.root) {
255 | postorder(node);
256 | }
257 |
258 | minimum(node: BinaryTreeNode) {
259 | return minimum(node);
260 | }
261 |
262 | maximum(node: BinaryTreeNode) {
263 | return maximum(node);
264 | }
265 |
266 | validate(): boolean {
267 | return validate(this.root);
268 | }
269 | }
270 |
271 | // const binarySearchTree = new BinarySearchTree();
272 |
273 | // // Build BST for testing
274 | // // 13, 3, 4, 12, 14, 10, 5, 1, 8, 2, 7, 9, 11, 6, 18 in that order, starting from an empty tree.
275 | // [13, 3, 4, 12, 14, 10, 5, 1, 8, 2, 7, 9, 11, 6, 18].forEach((i: number) => {
276 | // const node = new BinaryTreeNode(i);
277 | // binarySearchTree.add(node);
278 | // });
279 |
280 | // console.log("Valid Binary Search Tree:", binarySearchTree.validate());
281 |
--------------------------------------------------------------------------------
/data-structures/binary-search-tree/test.ts:
--------------------------------------------------------------------------------
1 | import { BinarySearchTree } from "./index.ts";
2 | import { BinaryTreeNode } from "../binary-tree/index.ts";
3 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
4 |
5 | Deno.test("binary search tree root is undefined", () => {
6 | const bst = new BinarySearchTree();
7 | assertEquals(bst.root, undefined);
8 | });
9 |
10 | Deno.test("binary search tree root is binary tree node after insert", () => {
11 | const tree = new BinarySearchTree();
12 | const node = new BinaryTreeNode(0);
13 | tree.add(node);
14 | assertEquals(tree.root, node);
15 | });
16 |
17 | Deno.test("findParent locates parent", () => {
18 | const tree = new BinarySearchTree();
19 | const root = new BinaryTreeNode(0);
20 | tree.add(root);
21 | const node = new BinaryTreeNode(1);
22 | tree.add(node);
23 | assertEquals(tree.findParent(node), root);
24 | });
25 |
26 | Deno.test("validates binary search tree", () => {
27 | const tree = new BinarySearchTree();
28 | // 13, 3, 4, 12, 14, 10, 5, 1, 8, 2, 7, 9, 11, 6, 18 in that order, starting from an empty tree.
29 | [13, 3, 4, 12, 14, 10, 5, 1, 8, 2, 7, 9, 11, 6, 18].forEach((i: number) => {
30 | const node = new BinaryTreeNode(i);
31 | tree.add(node);
32 | });
33 | assertEquals(tree.validate(), true);
34 | });
35 |
36 | Deno.test("invalidates invalid binary search tree", () => {
37 | const tree = new BinarySearchTree();
38 |
39 | // An invalid tree
40 | const root = new BinaryTreeNode(20);
41 | root.left = new BinaryTreeNode(10);
42 | root.right = new BinaryTreeNode(30);
43 | root.right.left = new BinaryTreeNode(5);
44 | root.right.right = new BinaryTreeNode(40);
45 |
46 | tree.add(root);
47 |
48 | assertEquals(tree.validate(), false);
49 | });
50 |
--------------------------------------------------------------------------------
/data-structures/binary-tree/README.md:
--------------------------------------------------------------------------------
1 | # Binary Tree
2 |
3 | In computer science, a binary tree is a tree data structure in which each node has at most two children, which are referred to as the left child and the right child. A recursive definition using just set theory notions is that a (non-empty) binary tree is a tuple (L, S, R), where L and R are binary trees or the empty set and S is a singleton set containing the root. Some authors allow the binary tree to be the empty set as well. [Wikipedia](https://en.wikipedia.org/wiki/Binary_tree)
4 |
--------------------------------------------------------------------------------
/data-structures/binary-tree/index.ts:
--------------------------------------------------------------------------------
1 | // import { add, find, remove } from "../binary-search-tree/index.ts";
2 |
3 | import { Queue } from "../queue/index.ts";
4 |
5 | // BINARY TREE is a non linear data structure where each node can have almost two child nodes.
6 | // BINARY TREE is unordered hence slower in process of insertion, deletion and searching.
7 | // IN BINARY TREE there is no ordering in terms of how the nodes are arranged
8 |
9 | export function add(root: BinaryTreeNode, key: K, value?: V) {
10 | console.log("Binary tree add", root, key);
11 |
12 | const queue = new Queue>();
13 | queue.enqueue(root);
14 |
15 | while (queue.size) {
16 | root = queue.peek();
17 | queue.dequeue();
18 |
19 | if (!root.left) {
20 | root.left = new BinaryTreeNode(key, value);
21 | break;
22 | } else {
23 | queue.enqueue(root.left);
24 | }
25 |
26 | if (!root.right) {
27 | root.right = new BinaryTreeNode(key, value);
28 | break;
29 | } else {
30 | queue.enqueue(root.right);
31 | }
32 | }
33 | }
34 |
35 | export function find(
36 | root: BinaryTreeNode | undefined,
37 | key: K
38 | ): BinaryTreeNode | undefined {
39 | if (!root) {
40 | return;
41 | }
42 |
43 | if (root.key === key) {
44 | return root;
45 | }
46 |
47 | const left = find(root.left, key);
48 |
49 | return left ? left : find(root.right, key);
50 | }
51 |
52 | export interface BinaryTreeNode {
53 | key: K;
54 | value?: V;
55 | left?: BinaryTreeNode;
56 | right?: BinaryTreeNode;
57 | parent?: BinaryTreeNode;
58 | }
59 |
60 | export class BinaryTreeNode implements BinaryTreeNode {
61 | constructor(
62 | key: K,
63 | value?: V,
64 | left?: BinaryTreeNode,
65 | right?: BinaryTreeNode,
66 | parent?: BinaryTreeNode
67 | ) {
68 | this.key = key;
69 | this.value = value;
70 | this.left = left;
71 | this.right = right;
72 | this.parent = parent;
73 | }
74 |
75 | // Insert
76 | add(key: K, value?: V) {
77 | return add(this, key, value);
78 | }
79 |
80 | // Search
81 | find(key: K) {
82 | return find(this, key);
83 | }
84 |
85 | // TODO: Delete
86 | // Ref: https://www.geeksforgeeks.org/deletion-binary-tree
87 | remove(key: K) {
88 | // remove(this, key);
89 | }
90 |
91 | get grandparent() {
92 | return this.parent?.parent;
93 | }
94 | get uncle() {
95 | return this.parent?.sibling;
96 | }
97 | get sibling() {
98 | if (!this?.parent) {
99 | return;
100 | }
101 | if (this === this.parent.left) {
102 | return this.parent.right;
103 | } else {
104 | return this.parent.left;
105 | }
106 | }
107 | }
108 |
109 | // // Initilise root node
110 | // const root = new BinaryTreeNode(0);
111 |
112 | // root.left = new BinaryTreeNode(1);
113 | // root.left.left = new BinaryTreeNode(2);
114 |
115 | // root.right = new BinaryTreeNode(3);
116 | // root.right.right = new BinaryTreeNode(4);
117 |
118 | // console.log(root.find(3));
119 |
120 | // root.add(5);
121 |
122 | // console.log(root);
123 |
--------------------------------------------------------------------------------
/data-structures/cartesian-tree/README.md:
--------------------------------------------------------------------------------
1 | # Cartesian Tree
2 |
3 | In computer science, a Cartesian tree is a binary tree derived from a sequence of numbers; it can be uniquely defined from the properties that it is heap-ordered and that a symmetric (in-order) traversal of the tree returns the original sequence. Introduced by Vuillemin (1980) in the context of geometric range searching data structures, Cartesian trees have also been used in the definition of the treap and randomized binary search tree data structures for binary search problems. The Cartesian tree for a sequence may be constructed in linear time using a stack-based algorithm for finding all nearest smaller values in a sequence. [Wikipedia](https://en.wikipedia.org/wiki/Cartesian_tree)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | N/A |
29 | Θ(log(n)) |
30 | Θ(log(n)) |
31 | Θ(log(n)) |
32 | N/A |
33 | O(n) |
34 | O(n) |
35 | O(n) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
42 |
--------------------------------------------------------------------------------
/data-structures/circular-doubly-linked-list/README.md:
--------------------------------------------------------------------------------
1 | # Circular Doubly Linked List
--------------------------------------------------------------------------------
/data-structures/circular-doubly-linked-list/index.ts:
--------------------------------------------------------------------------------
1 | import {
2 | DoublyLinkedList,
3 | DoublyLinkedListNode,
4 | } from "../doubly-linked-list/index.ts";
5 |
6 | export interface CirularDoublyLinkedListNode
7 | extends DoublyLinkedListNode {
8 | previous: CirularDoublyLinkedListNode;
9 | next: CirularDoublyLinkedListNode;
10 | }
11 |
12 | export class CirularDoublyLinkedListNode
13 | implements CirularDoublyLinkedListNode {
14 | previous: CirularDoublyLinkedListNode;
15 | next: CirularDoublyLinkedListNode;
16 | constructor(
17 | value: T,
18 | previous?: CirularDoublyLinkedListNode,
19 | next?: CirularDoublyLinkedListNode
20 | ) {
21 | this.value = value;
22 | this.previous = previous ? previous : this;
23 | this.next = next ? next : this;
24 | }
25 | }
26 |
27 | export class CircularDoublyLinkedList extends DoublyLinkedList {
28 | head?: CirularDoublyLinkedListNode;
29 | tail?: CirularDoublyLinkedListNode;
30 | addHead(value: T): CirularDoublyLinkedListNode {
31 | const node = new CirularDoublyLinkedListNode(value, this.tail, this.head);
32 |
33 | if (!this.head) {
34 | this.head = node;
35 | this.tail = node;
36 | } else {
37 | this.head = >(
38 | this.addBefore(this.head, node)
39 | );
40 | }
41 |
42 | return node;
43 | }
44 | addTail(value: T): CirularDoublyLinkedListNode {
45 | if (!this.tail) {
46 | return this.addHead(value);
47 | }
48 |
49 | const node = new CirularDoublyLinkedListNode(value, this.tail, this.head);
50 |
51 | this.tail = >this.addAfter(this.tail, node);
52 |
53 | return node;
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/data-structures/circular-doubly-linked-list/test.ts:
--------------------------------------------------------------------------------
1 | import { CircularDoublyLinkedList } from "./index.ts";
2 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
3 |
4 | Deno.test("head is cicular", () => {
5 | const list = new CircularDoublyLinkedList();
6 | list.addHead(1);
7 | list.addHead(2);
8 | list.addHead(3);
9 | assertEquals(list.head?.next.next.next.value, 3);
10 | assertEquals(list.head?.previous.previous.previous.value, 3);
11 | });
12 |
13 | Deno.test("tail is cicular", () => {
14 | const list = new CircularDoublyLinkedList();
15 | list.addTail(1);
16 | list.addTail(2);
17 | list.addTail(3);
18 |
19 | assertEquals(list.tail?.previous.previous.previous.value, 3);
20 | assertEquals(list.tail?.next.next.next.value, 3);
21 | });
22 |
--------------------------------------------------------------------------------
/data-structures/circular-linked-list/README.md:
--------------------------------------------------------------------------------
1 | # Circular Linked List
--------------------------------------------------------------------------------
/data-structures/circular-linked-list/index.ts:
--------------------------------------------------------------------------------
1 | import { LinkedList, LinkedListNode } from "../linked-list/index.ts";
2 |
3 | export interface CirularLinkedListNode extends LinkedListNode {
4 | next: CirularLinkedListNode;
5 | }
6 |
7 | export class CirularLinkedListNode implements CirularLinkedListNode {
8 | next: CirularLinkedListNode;
9 | constructor(value: T, next?: CirularLinkedListNode) {
10 | this.value = value;
11 | this.next = next ? next : this;
12 | }
13 | }
14 |
15 | export class CircularLinkedList extends LinkedList {
16 | head?: CirularLinkedListNode;
17 | addHead(value: T): CirularLinkedListNode {
18 | const node = new CirularLinkedListNode(value, this.head);
19 |
20 | this.head = node;
21 |
22 | this.length++;
23 |
24 | return this.head;
25 | }
26 | addTail(value: T): CirularLinkedListNode {
27 | if (!this.head) {
28 | return this.addHead(value);
29 | }
30 |
31 | const node = new CirularLinkedListNode(value, this.head);
32 |
33 | for (let tmp = this.head; tmp !== undefined; tmp = tmp.next) {
34 | if (tmp.next === this.head) {
35 | tmp.next = node;
36 | break;
37 | }
38 | }
39 |
40 | this.length++;
41 |
42 | return node;
43 | }
44 | }
45 |
46 | const list = new CircularLinkedList();
47 |
48 | console.log(list.addTail(1));
49 | console.log(list.addTail(2));
50 | console.log(list.addTail(3));
51 |
52 | console.log(list);
53 |
54 | // Value should be 1
55 | console.log(list.head?.next?.next?.next);
56 |
--------------------------------------------------------------------------------
/data-structures/circular-linked-list/test.ts:
--------------------------------------------------------------------------------
1 | import { CircularLinkedList } from "./index.ts";
2 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
3 |
4 | Deno.test("head is cicular", () => {
5 | const list = new CircularLinkedList();
6 | list.addTail(1);
7 | list.addTail(2);
8 | list.addTail(3);
9 | assertEquals(list.head?.next?.next?.next?.value, 1);
10 | });
11 |
--------------------------------------------------------------------------------
/data-structures/deque/README.md:
--------------------------------------------------------------------------------
1 | # Deque
2 |
3 | In computer science, a double-ended queue (abbreviated to deque, pronounced deck) is an abstract data type that generalizes a queue, for which elements can be added to or removed from either the front (head) or back (tail). It is also often called a head-tail linked list, though properly this refers to a specific data structure implementation of a deque.
4 |
--------------------------------------------------------------------------------
/data-structures/deque/index.ts:
--------------------------------------------------------------------------------
1 | import { DoublyLinkedList } from "../doubly-linked-list/index.ts";
2 |
3 | export class Deque extends DoublyLinkedList {
4 | addHead(value: T) {
5 | return super.addHead(value);
6 | }
7 | addTail(value: T) {
8 | return super.addTail(value);
9 | }
10 | removeHead() {
11 | return super.removeHead();
12 | }
13 | removeTail() {
14 | return super.removeTail();
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/data-structures/dictionary/README.md:
--------------------------------------------------------------------------------
1 | # Dictionary
2 |
3 | In computer science, an associative array, map, symbol table, or dictionary is an abstract data type composed of a collection of (key, value) pairs, such that each possible key appears at most once in the collection. [Wikipedia](https://en.wikipedia.org/wiki/Associative_array)
4 |
--------------------------------------------------------------------------------
/data-structures/dictionary/index.ts:
--------------------------------------------------------------------------------
1 | export interface Dictionary {
2 | readonly size: number;
3 | clear(): void;
4 | delete(key: string | number): boolean;
5 | get(key: string | number): T | undefined;
6 | has(key: string | number): boolean;
7 | set(key: string | number, value: T): this;
8 | }
9 |
10 | export class Dictionary {
11 | // Dictionary key will always be set as a string.
12 | private table: { [key: string]: T } = {};
13 |
14 | readonly size: number = 0;
15 |
16 | clear() {
17 | this.table = {};
18 | }
19 |
20 | delete(key: string | number) {
21 | delete this.table[key.toString()];
22 | return true;
23 | }
24 |
25 | get(key: string | number) {
26 | return this.table[key.toString()];
27 | }
28 |
29 | has(key: string | number) {
30 | return this.table[key.toString()] !== undefined;
31 | }
32 |
33 | set(key: string | number, value: T) {
34 | this.table[key.toString()] = value;
35 |
36 | return this;
37 | }
38 | }
39 |
40 | const dictionary = new Dictionary();
41 |
42 | dictionary.set(1, 1);
43 | dictionary.set("foo", "bar");
44 |
45 | console.log(dictionary.set(1, 1).get(1));
46 | // console.log(JSON.stringify(dictionary));
47 |
--------------------------------------------------------------------------------
/data-structures/doubly-linked-list/README.md:
--------------------------------------------------------------------------------
1 | # Doubly Linked List
2 |
3 | In computer science, a doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains three fields: two link fields (references to the previous and to the next node in the sequence of nodes) and one data field. The beginning and ending nodes' previous and next links, respectively, point to some kind of terminator, typically a sentinel node or null, to facilitate traversal of the list. If there is only one sentinel node, then the list is circularly linked via the sentinel node. It can be conceptualized as two singly linked lists formed from the same data items, but in opposite sequential orders. [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | Θ(n) |
29 | Θ(n) |
30 | Θ(1) |
31 | Θ(1) |
32 | O(n) |
33 | O(n) |
34 | O(1) |
35 | O(1) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
42 |
--------------------------------------------------------------------------------
/data-structures/doubly-linked-list/index.ts:
--------------------------------------------------------------------------------
1 | import { LinkedList, LinkedListNode } from "../linked-list/index.ts";
2 |
3 | export interface DoublyLinkedListNode extends LinkedListNode {
4 | previous?: DoublyLinkedListNode;
5 | next?: DoublyLinkedListNode;
6 | value: T;
7 | }
8 |
9 | export class DoublyLinkedListNode extends LinkedListNode
10 | implements DoublyLinkedListNode {
11 | previous?: DoublyLinkedListNode;
12 | next?: DoublyLinkedListNode;
13 | constructor(
14 | value: T,
15 | next?: DoublyLinkedListNode,
16 | previous?: DoublyLinkedListNode
17 | ) {
18 | super(value, next);
19 | this.previous = previous;
20 | }
21 | }
22 |
23 | export interface DoublyLinkedList extends LinkedList {
24 | head?: DoublyLinkedListNode;
25 | tail?: DoublyLinkedListNode;
26 | length: number;
27 | }
28 |
29 | export class DoublyLinkedList extends LinkedList
30 | implements DoublyLinkedList {
31 | head?: DoublyLinkedListNode;
32 | tail?: DoublyLinkedListNode;
33 | length = 0;
34 |
35 | clear() {
36 | console.log("clearing here...");
37 | super.clear();
38 | this.tail = undefined;
39 | }
40 |
41 | addBefore(n1: DoublyLinkedListNode, n2: DoublyLinkedListNode) {
42 | n2.next = n1;
43 | if (!n1.previous) {
44 | this.head = n2;
45 | } else {
46 | n2.previous = n1.previous;
47 | n1.previous.next = n2;
48 | }
49 | n1.previous = n2;
50 |
51 | this.length++;
52 |
53 | return n2;
54 | }
55 |
56 | addAfter(n1: DoublyLinkedListNode, n2: DoublyLinkedListNode) {
57 | n2.previous = n1;
58 | if (!n1.next) {
59 | this.tail = n2;
60 | } else {
61 | n2.next = n1.next;
62 | n1.next.previous = n2;
63 | }
64 | n1.next = n2;
65 |
66 | this.length++;
67 |
68 | return n2;
69 | }
70 |
71 | addHead(value: T) {
72 | const node = new DoublyLinkedListNode(value);
73 |
74 | if (!this.head) {
75 | this.head = node;
76 | this.tail = node;
77 | } else {
78 | this.head = this.addBefore(this.head, node);
79 | }
80 |
81 | return this.head;
82 | }
83 |
84 | addTail(value: T): DoublyLinkedListNode {
85 | if (!this.tail) {
86 | return this.addHead(value);
87 | }
88 |
89 | const node = new DoublyLinkedListNode(value);
90 |
91 | this.tail = this.addAfter(this.tail, node);
92 |
93 | return this.tail;
94 | }
95 |
96 | remove(value: T): void {
97 | const node: DoublyLinkedListNode | undefined = super.find(value);
98 |
99 | this.removeNode(node);
100 | }
101 |
102 | removeNode(node?: DoublyLinkedListNode): void {
103 | if (!node) {
104 | return;
105 | }
106 |
107 | if (!node.previous) {
108 | this.head = node.next;
109 | } else {
110 | node.previous.next = node.next;
111 | }
112 |
113 | if (!node.next) {
114 | this.tail = node.previous;
115 | } else {
116 | node.next.previous = node.previous;
117 | }
118 |
119 | this.length--;
120 | }
121 |
122 | removeHead() {
123 | this.removeNode(this.head);
124 | }
125 |
126 | removeTail() {
127 | this.removeNode(this.tail);
128 | }
129 | }
130 |
131 | // const doublyLinkedList = new DoublyLinkedList();
132 |
133 | // console.log(doublyLinkedList);
134 |
135 | // doublyLinkedList.addHead(1);
136 | // doublyLinkedList.addHead(2);
137 | // doublyLinkedList.addHead(3);
138 |
139 | // doublyLinkedList.addTail(1);
140 | // doublyLinkedList.addTail(2);
141 | // doublyLinkedList.addTail(3);
142 |
143 | // console.log(doublyLinkedList);
144 | // doublyLinkedList.removeTail();
145 | // console.log(doublyLinkedList);
146 |
--------------------------------------------------------------------------------
/data-structures/doubly-linked-list/test.ts:
--------------------------------------------------------------------------------
1 | import { DoublyLinkedList, DoublyLinkedListNode } from "./index.ts";
2 |
3 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
4 |
5 | Deno.test("head & tail is undefined", () => {
6 | const list = new DoublyLinkedList();
7 | assertEquals(list.head, undefined);
8 | assertEquals(list.tail, undefined);
9 | });
10 |
11 | Deno.test("length is 0", () => {
12 | const list = new DoublyLinkedList();
13 | assertEquals(list.length, 0);
14 | });
15 |
16 | Deno.test("tail is node after an element is pushed onto the list", () => {
17 | const list = new DoublyLinkedList();
18 | list.addTail(1);
19 | assertEquals(list.tail, new DoublyLinkedListNode(1));
20 | });
21 |
--------------------------------------------------------------------------------
/data-structures/graph/README.md:
--------------------------------------------------------------------------------
1 | # Graph
2 |
3 | In computer science, a graph is an abstract data type that is meant to implement the undirected graph and directed graph concepts from the field of graph theory within mathematics.
4 |
5 | A graph data structure consists of a finite (and possibly mutable) set of vertices (also called nodes or points), together with a set of unordered pairs of these vertices for an undirected graph or a set of ordered pairs for a directed graph. These pairs are known as edges (also called links or lines), and for a directed graph are also known as arrows. The vertices may be part of the graph structure, or may be external entities represented by integer indices or references. [Wikipedia](https://en.wikipedia.org/wiki/Graph_(abstract_data_type))
6 |
--------------------------------------------------------------------------------
/data-structures/graph/index.ts:
--------------------------------------------------------------------------------
1 | export interface Graph {
2 | verticies: Map;
3 | edges: Map>;
4 | adjacent(x: X, y: Y): boolean;
5 | neighbors(x: X): Map;
6 | addVertex(x: X): void;
7 | removeVertex(x: X): void;
8 | getVertexValue(x: X): V;
9 | setVertexValue(x: X, v: V): void;
10 | addEdge(x: X, y: Y): void;
11 | removeEdge(x: X, y: Y): void;
12 | }
13 |
14 | export class Graph implements Graph {
15 | verticies: Map = new Map();
16 | edges: Map> = new Map();
17 | adjacent(x: X, y: Y) {
18 | return this.edges.get(x)?.has(y);
19 | }
20 | neighbors(x: X) {
21 | return this.edges.get(x);
22 | }
23 | addVertex(x: X) {
24 | this.verticies.set(x, undefined);
25 | if (!this.edges.has(x)) {
26 | this.edges.set(x, new Map());
27 | }
28 | }
29 | removeVertex(x: X) {
30 | // Remove vertex
31 | this.verticies.delete(x);
32 |
33 | // Remove edges for x
34 | this.edges.delete(x);
35 |
36 | // Iterate over edges for other verticies and remove x if found.
37 | for (const edge of this.edges.values()) {
38 | if (edge.has(x)) {
39 | edge.delete(x);
40 | }
41 | }
42 | }
43 | getVertexValue(x: X) {
44 | return this.verticies.get(x);
45 | }
46 | setVertexValue(x: X, v: V) {
47 | this.verticies.set(x, v);
48 | }
49 | addEdge(x: X, y: Y) {
50 | // TODO: Add case for Undirected/Bi-directional
51 | this.edges.get(x)?.set(y, undefined);
52 |
53 | this.edges.get(y)?.set(x, undefined);
54 | }
55 | removeEdge(x: X, y: Y) {
56 | this.edges.get(x)?.delete(y);
57 |
58 | this.edges.get(y)?.delete(x);
59 | }
60 | getEdgeValue(x: X, y: Y) {
61 | return this.edges.get(x)?.get(y);
62 | }
63 | setEdgeValue(x: X, y: Y, v: V) {
64 | return this.edges.get(x)?.set(y, v);
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/data-structures/graph/test.ts:
--------------------------------------------------------------------------------
1 | import { Graph } from "./index.ts";
2 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
3 |
4 | Deno.test("adds vertex to verticies map", () => {
5 | const graph = new Graph();
6 | graph.addVertex("foo");
7 | assertEquals(graph.verticies.size, 1);
8 | });
9 |
10 | Deno.test("adds edge map for vertex", () => {
11 | const graph = new Graph();
12 | graph.addVertex("foo");
13 | assertEquals(graph.edges.size, 1);
14 | });
15 |
16 | Deno.test("adds multiple verticies to verticies map", () => {
17 | const graph = new Graph();
18 | for (const vertex of ["foo", "bar", "baz"]) {
19 | graph.addVertex(vertex);
20 | }
21 | assertEquals(graph.verticies.size, 3);
22 | });
23 |
24 | Deno.test("adds multiple edge maps for multiple verticies", () => {
25 | const graph = new Graph();
26 | for (const vertex of ["foo", "bar", "baz"]) {
27 | graph.addVertex(vertex);
28 | }
29 | assertEquals(graph.edges.size, 3);
30 | });
31 |
32 | Deno.test("edge map x set y, and vice versa for bi-directional", () => {
33 | const graph = new Graph();
34 | for (const vertex of ["foo", "bar"]) {
35 | graph.addVertex(vertex);
36 | }
37 | graph.addEdge("foo", "bar");
38 | assertEquals(graph.edges.get("foo")?.has("bar"), true);
39 | assertEquals(graph.edges.get("bar")?.has("foo"), true);
40 |
41 | assertEquals(graph.adjacent("foo", "bar"), true);
42 | assertEquals(graph.adjacent("bar", "foo"), true);
43 |
44 | assertEquals(graph.neighbors("foo")?.size, 1);
45 | assertEquals(graph.neighbors("bar")?.size, 1);
46 | });
47 |
48 | Deno.test("edge map x delete y, and vice versa for bi-directional", () => {
49 | const graph = new Graph();
50 | for (const vertex of ["foo", "bar"]) {
51 | graph.addVertex(vertex);
52 | }
53 | graph.addEdge("foo", "bar");
54 | graph.removeEdge("foo", "bar");
55 | assertEquals(graph.edges.get("foo")?.has("bar"), false);
56 | assertEquals(graph.edges.get("bar")?.has("foo"), false);
57 |
58 | assertEquals(graph.adjacent("foo", "bar"), false);
59 | assertEquals(graph.adjacent("bar", "foo"), false);
60 |
61 | assertEquals(graph.neighbors("foo")?.size, 0);
62 | assertEquals(graph.neighbors("bar")?.size, 0);
63 | });
64 |
65 | Deno.test("sets edge value", () => {
66 | const graph = new Graph();
67 | for (const vertex of ["foo", "bar"]) {
68 | graph.addVertex(vertex);
69 | }
70 | graph.addEdge("foo", "bar");
71 | graph.setEdgeValue("foo", "bar", "baz");
72 |
73 | assertEquals(graph.getEdgeValue("foo", "bar"), "baz");
74 | });
75 |
76 | Deno.test("sets vertex value", () => {
77 | const graph = new Graph();
78 | graph.addVertex("foo");
79 | graph.setVertexValue("foo", "bar");
80 | assertEquals(graph.getVertexValue("foo"), "bar");
81 | });
82 |
--------------------------------------------------------------------------------
/data-structures/hash-table/README.md:
--------------------------------------------------------------------------------
1 | # Hash Table
2 |
3 | In computing, a hash table (hash map) is a data structure that implements an associative array abstract data type, a structure that can map keys to values. A hash table uses a hash function to compute an index, also called a hash code, into an array of buckets or slots, from which the desired value can be found. During lookup, the key is hashed and the resulting hash indicates where the corresponding value is stored. [Wikipedia](https://en.wikipedia.org/wiki/Hash_table)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | N/A |
29 | Θ(1) |
30 | Θ(1) |
31 | Θ(1) |
32 | N/A |
33 | O(n) |
34 | O(n) |
35 | O(n) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
42 |
--------------------------------------------------------------------------------
/data-structures/hash-table/index.ts:
--------------------------------------------------------------------------------
1 | import { LinkedList } from "../linked-list/index.ts";
2 | import { djb2 } from "../../algorithms/hash/djb2";
3 |
4 | export class HashTable {
5 | n: number;
6 | hash: (string: string) => number;
7 | buckets: LinkedList[];
8 | constructor(n: number, hash = djb2) {
9 | this.n = n;
10 | this.hash = hash;
11 | this.buckets = new Array(this.n)
12 | .fill(undefined)
13 | .map(() => new LinkedList());
14 | }
15 | set(key: string, value: T) {
16 | this.buckets[this.hash(key) % this.n].addTail(value);
17 | }
18 | get(key: string) {
19 | return this.buckets[this.hash(key) % this.n];
20 | }
21 | }
22 |
23 | const decoder = new TextDecoder("utf-8");
24 | // Dictionary used taken from
25 | // wget https://cdn.cs50.net/2019/fall/psets/5/speller/speller.zip
26 | const data = await Deno.readFile("./dictionary");
27 | const lines = decoder.decode(data).split("\n");
28 | const hashTable = new HashTable(lines.length);
29 |
30 | // Load a dictionary
31 | for (const line of decoder.decode(data).split("\n")) {
32 | hashTable.set(line, line);
33 | }
34 |
35 | console.log(hashTable.get("hello"));
36 |
--------------------------------------------------------------------------------
/data-structures/linked-list/README.md:
--------------------------------------------------------------------------------
1 | # Linked List
2 |
3 | In computer science, a linked list is a linear collection of data elements, whose order is not given by their physical placement in memory. Instead, each element points to the next. It is a data structure consisting of a collection of nodes which together represent a sequence. In its most basic form, each node contains: data, and a reference (in other words, a link) to the next node in the sequence. [Wikipedia](https://en.wikipedia.org/wiki/Linked_list)
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | Θ(n) |
29 | Θ(n) |
30 | Θ(1) |
31 | Θ(1) |
32 | O(n) |
33 | O(n) |
34 | O(1) |
35 | O(1) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
42 |
--------------------------------------------------------------------------------
/data-structures/linked-list/index.ts:
--------------------------------------------------------------------------------
1 | export interface LinkedListNode {
2 | next?: LinkedListNode;
3 | value: T;
4 | }
5 |
6 | export class LinkedListNode implements LinkedListNode {
7 | constructor(value: T, next?: LinkedListNode) {
8 | this.value = value;
9 | this.next = next;
10 | }
11 | }
12 |
13 | export interface LinkedList {
14 | head?: LinkedListNode;
15 | length: number;
16 | }
17 |
18 | export class LinkedList implements LinkedList {
19 | head?: LinkedListNode;
20 | length = 0;
21 |
22 | clear() {
23 | this.head = undefined;
24 | this.length = 0;
25 | }
26 |
27 | contains(value: T) {
28 | for (let tmp = this.head; tmp !== undefined; tmp = tmp?.next) {
29 | if (tmp.value === value) {
30 | return true;
31 | }
32 | }
33 | }
34 |
35 | find(value: T): LinkedListNode | undefined {
36 | for (let tmp = this.head; tmp !== undefined; tmp = tmp?.next) {
37 | if (tmp.value === value) {
38 | return tmp;
39 | }
40 | }
41 | }
42 |
43 | findLast(value: T): LinkedListNode | undefined {
44 | let last;
45 | for (let tmp = this.head; tmp !== undefined; tmp = tmp?.next) {
46 | if (tmp.value === value) {
47 | last = tmp;
48 | }
49 | }
50 | return last;
51 | }
52 |
53 | addHead(value: T) {
54 | const node = new LinkedListNode(value, this.head);
55 |
56 | this.head = node;
57 |
58 | this.length++;
59 |
60 | return this.head;
61 | }
62 |
63 | addTail(value: T) {
64 | if (!this.head) {
65 | return this.addHead(value);
66 | }
67 |
68 | const node = new LinkedListNode(value);
69 | for (let tmp = this.head; tmp !== undefined; tmp = tmp.next) {
70 | if (!tmp.next) {
71 | tmp.next = node;
72 | break;
73 | }
74 | }
75 |
76 | this.length++;
77 |
78 | return node;
79 | }
80 |
81 | remove(value: T) {
82 | for (
83 | let tmp = this.head;
84 | tmp !== undefined && tmp.next?.value === value;
85 | tmp = tmp?.next
86 | ) {
87 | if (this.length === 1) {
88 | return this.clear();
89 | }
90 | // Set before to after
91 | tmp.next = tmp.next.next;
92 | }
93 |
94 | this.length--;
95 | }
96 |
97 | removeHead() {
98 | if (this.length === 1) {
99 | return this.clear();
100 | }
101 |
102 | this.head = this.head?.next;
103 |
104 | this.length--;
105 | }
106 |
107 | removeTail() {
108 | if (this.length === 1) {
109 | return this.clear();
110 | }
111 |
112 | let tmp = this.head;
113 |
114 | while (tmp?.next?.next) {
115 | tmp = tmp.next;
116 | }
117 |
118 | if (tmp) {
119 | tmp.next = undefined;
120 | }
121 |
122 | this.length--;
123 | }
124 | }
125 |
126 | // const list = new LinkedList();
127 |
128 | // console.log(list.addHead(1));
129 | // console.log(list.addHead(2));
130 | // console.log(list.addHead(3));
131 |
132 | // console.log(list.addTail(1));
133 | // console.log(list.addTail(2));
134 | // console.log(list.addTail(3));
135 |
136 | // list.removeHead();
137 | // list.removeHead();
138 | // list.removeHead();
139 |
140 | // list.removeLast();
141 | // list.removeTail();
142 | // list.removeTail();
143 |
144 | // console.log(list.find(2));
145 |
--------------------------------------------------------------------------------
/data-structures/linked-list/test.ts:
--------------------------------------------------------------------------------
1 | import { LinkedList, LinkedListNode } from "./index.ts";
2 |
3 | import { assertEquals } from "https://deno.land/std/testing/asserts.ts";
4 |
5 | Deno.test("head is undefined", () => {
6 | const list = new LinkedList();
7 | assertEquals(list.head, undefined);
8 | });
9 |
10 | Deno.test("length is 0", () => {
11 | const list = new LinkedList();
12 | assertEquals(list.length, 0);
13 | });
14 |
15 | Deno.test("length is 1 after an element is pushed onto the list", () => {
16 | const list = new LinkedList();
17 | list.addHead(1);
18 | assertEquals(list.length, 1);
19 | });
20 |
21 | Deno.test("head is node after an element is pushed onto the list", () => {
22 | const list = new LinkedList();
23 | list.addHead(1);
24 | assertEquals(list.head, new LinkedListNode(1));
25 | });
26 |
--------------------------------------------------------------------------------
/data-structures/queue/README.md:
--------------------------------------------------------------------------------
1 | # Queue
2 |
3 | In computer science, a queue is a collection of entities that are maintained in a sequence and can be modified by the addition of entities at one end of the sequence and the removal of entities from the other end of the sequence. [Wikipedia](https://en.wikipedia.org/wiki/Queue_(abstract_data_type))
4 |
5 |
6 |
7 |
8 | Time Complexity |
9 | Space Complexity |
10 |
11 |
12 | Average |
13 | Worst |
14 | Worst |
15 |
16 |
17 | Access |
18 | Search |
19 | Insertion |
20 | Deletion |
21 | Access |
22 | Search |
23 | Insertion |
24 | Deletion |
25 | |
26 |
27 |
28 | Θ(n) |
29 | Θ(n) |
30 | Θ(1) |
31 | Θ(1) |
32 | O(n) |
33 | O(n) |
34 | O(1) |
35 | O(1) |
36 | O(n) |
37 |
38 |
39 |
40 |
41 | [Big-O Cheat Sheet](https://www.bigocheatsheet.com)
--------------------------------------------------------------------------------
/data-structures/queue/index.ts:
--------------------------------------------------------------------------------
1 | export interface Queue {
2 | items: T[];
3 | maximum: number;
4 | head: number;
5 | tail: number;
6 | peek(): T;
7 | enqueue(item: T): void;
8 | dequeue(): void;
9 | }
10 |
11 | export class Queue implements Queue {
12 | items: T[];
13 | maximum: number;
14 | head: number;
15 | tail: number;
16 | size: number;
17 | constructor(size: number = Math.pow(2, 32)) {
18 | this.items = new Array(size - 1);
19 | this.maximum = size;
20 | this.head = 0;
21 | this.tail = -1;
22 | this.size = 0;
23 | }
24 |
25 | peek(): T {
26 | return this.items[this.head];
27 | }
28 |
29 | enqueue(item: T) {
30 | if (this.size === this.maximum) {
31 | throw Error("Overflow error");
32 | }
33 |
34 | this.tail = (this.tail + 1) % this.maximum;
35 |
36 | console.log(`Enqueuing #${this.tail}`);
37 |
38 | this.items[this.tail] = item;
39 |
40 | console.log(`Tail #${this.tail}`);
41 |
42 | this.size++;
43 | }
44 |
45 | dequeue() {
46 | if (!this.size) {
47 | throw Error("Underflow error");
48 | }
49 |
50 | console.log(`Dequeuing #${this.head}`);
51 |
52 | // Circular
53 | this.head = (this.head + 1) % this.maximum;
54 |
55 | console.log(`Head #${this.head}`);
56 |
57 | this.size--;
58 | }
59 | }
60 |
61 | // const n = 64;
62 |
63 | // const queue = new Queue(n);
64 |
65 | // for (let i = 0; i < n; i++) {
66 | // queue.enqueue(i);
67 | // }
68 |
69 | // for (let i = 0; i < n / 2; i++) {
70 | // queue.dequeue();
71 | // }
72 |
73 | // console.log(queue);
74 |
75 | // // 32
76 | // console.log(queue.peek());
77 |
--------------------------------------------------------------------------------
/data-structures/red-black-tree/README.md:
--------------------------------------------------------------------------------
1 | # Red Black Tree
2 |
3 | In computer science, a red–black tree is a kind of self-balancing binary search tree. Each node stores an extra bit representing color, used to ensure that the tree remains approximately balanced during insertions and deletions.
4 |
5 | When the tree is modified, the new tree is rearranged and repainted to restore the coloring properties that constrain how unbalanced the tree can become in the worst case. The properties are designed such that this rearranging and recoloring can be performed efficiently.
6 |
7 | The re-balancing is not perfect, but guarantees searching in O(log n) time, where n is the number of nodes of the tree. The insertion and deletion operations, along with the tree rearrangement and recoloring, are also performed in O(log n) time.
8 |
9 | Tracking the color of each node requires only 1 bit of information per node because there are only two colors. The tree does not contain any other data specific to its being a red–black tree so its memory footprint is almost identical to a classic (uncolored) binary search tree. In many cases, the additional bit of information can be stored at no additional memory cost. [Wikipedia](https://en.wikipedia.org/wiki/Red%E2%80%93black_tree)
10 |
--------------------------------------------------------------------------------
/data-structures/red-black-tree/index.ts:
--------------------------------------------------------------------------------
1 | import { BinarySearchTree } from "../binary-search-tree/index.ts";
2 | import { BinaryTreeNode } from "../binary-tree/index.ts";
3 |
4 | // https://en.wikipedia.org/wiki/Red%E2%80%93black_tree
5 | // TODO: Working, but needs refactoring and tidy up.
6 |
7 | enum Color {
8 | BLACK,
9 | RED,
10 | }
11 |
12 | export interface RedBlackTreeNode extends BinaryTreeNode {
13 | key: K;
14 | value?: V;
15 | left?: RedBlackTreeNode;
16 | right?: RedBlackTreeNode;
17 | parent?: RedBlackTreeNode;
18 | color?: Color;
19 | }
20 |
21 | export class RedBlackTreeNode extends BinaryTreeNode
22 | implements RedBlackTreeNode {
23 | constructor(
24 | key: K,
25 | value?: V,
26 | left?: RedBlackTreeNode,
27 | right?: RedBlackTreeNode,
28 | parent?: RedBlackTreeNode,
29 | color?: Color
30 | ) {
31 | super(key, value, left, right, parent);
32 | this.color = color;
33 | }
34 | }
35 |
36 | function rotateLeft(node: RedBlackTreeNode) {
37 | // Since the leaves of a red-black tree are empty, they cannot become internal nodes.
38 | if (!node?.right) {
39 | return;
40 | }
41 | const nnew = node.right;
42 |
43 | const p = node.parent;
44 |
45 | node.right = nnew.left;
46 | nnew.left = node;
47 | node.parent = nnew;
48 |
49 | // Handle other child/parent pointers.
50 | if (node.right) {
51 | node.right.parent = node;
52 | }
53 |
54 | // Initially n could be the root.
55 | if (p) {
56 | if (node == p.left) {
57 | p.left = nnew;
58 | } else if (node == p.right) {
59 | p.right = nnew;
60 | }
61 | }
62 | nnew.parent = p;
63 | }
64 |
65 | function rotateRight(n: RedBlackTreeNode) {
66 | // Since the leaves of a red-black tree are empty, they cannot become internal nodes.
67 | if (!n?.left) {
68 | return;
69 | }
70 |
71 | const nnew = n.left;
72 |
73 | const p = n.parent;
74 |
75 | n.left = nnew.right;
76 | nnew.right = n;
77 | n.parent = nnew;
78 |
79 | // Handle other child/parent pointers.
80 | if (n.left) {
81 | n.left.parent = n;
82 | }
83 |
84 | // Initially n could be the root.
85 | if (p) {
86 | if (n == p.left) {
87 | p.left = nnew;
88 | } else if (n == p.right) {
89 | p.right = nnew;
90 | }
91 | }
92 |
93 | nnew.parent = p;
94 | }
95 |
96 | function InsertRecurse(
97 | root: RedBlackTreeNode,
98 | node: RedBlackTreeNode
99 | ) {
100 | // Recursively descend the tree until a leaf is found.
101 | if (root) {
102 | if (node.key < root.key) {
103 | if (root.left) {
104 | InsertRecurse(root.left, node);
105 | return;
106 | } else {
107 | root.left = node;
108 | }
109 | } else {
110 | if (root.right) {
111 | InsertRecurse(root.right, node);
112 | return;
113 | } else {
114 | root.right = node;
115 | }
116 | }
117 | }
118 |
119 | // Insert new Node n.
120 | node.parent = root;
121 | node.left = undefined;
122 | node.right = undefined;
123 | node.color = Color.RED;
124 | }
125 |
126 | function InsertRepairTree(node: RedBlackTreeNode) {
127 | console.log("Insert repair tree");
128 | if (!node.parent) {
129 | InsertCase1(node);
130 | } else if (node.parent.color === Color.BLACK) {
131 | InsertCase2(node);
132 | } else if ((>node.uncle)?.color === Color.RED) {
133 | InsertCase3(node);
134 | } else {
135 | InsertCase4(node);
136 | }
137 | }
138 |
139 | function InsertCase1(node: RedBlackTreeNode) {
140 | console.log("Insert Case 1");
141 | node.color = Color.BLACK;
142 | }
143 |
144 | function InsertCase2(node: RedBlackTreeNode) {
145 | console.log("Insert Case 2");
146 | return;
147 | }
148 |
149 | function InsertCase3(node: RedBlackTreeNode) {
150 | console.log("Insert Case 3");
151 | (>node.parent).color = Color.BLACK;
152 | (