Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions src/algorithms/math/catalan-number/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
# Catalan Number

In combinatorial mathematics, the **Catalan numbers** form a sequence of natural numbers that occur in various counting problems, often involving recursively-defined objects.

The Catalan numbers on nonnegative integers `n` are a sequence `Cn` which begins:

```
C0=1, C1=1, C2=2, C3=5, C4=14, C5=42, C6=132, C7=429, C8=1430, C9=4862, C10=16796, ...
```

## Formula

The nth Catalan number can be expressed directly in terms of binomial coefficients:

![Catalan Formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/2d9f0d0d64c8b5e8f8f8c8f8f8f8f8f8f8f8f8f8)

```
C(n) = (2n)! / ((n + 1)! * n!)
```

Or using the recursive formula:

```
C(0) = 1
C(n) = sum of C(i) * C(n-1-i) for i = 0 to n-1
```

## Applications

Catalan numbers have many applications in combinatorics:

1. **Binary Search Trees**: `Cn` is the number of different binary search trees with `n` keys
2. **Parentheses**: `Cn` is the number of expressions containing `n` pairs of correctly matched parentheses
3. **Polygon Triangulation**: `Cn` is the number of ways to triangulate a convex polygon with `n+2` sides
4. **Path Counting**: `Cn` is the number of paths in a grid from `(0,0)` to `(n,n)` that don't cross the diagonal
5. **Dyck Words**: `Cn` is the number of Dyck words of length `2n`
6. **Mountain Ranges**: `Cn` is the number of "mountain ranges" you can draw using `n` upstrokes and `n` downstrokes

## Examples

### Binary Search Trees
With 3 keys (1, 2, 3), there are `C3 = 5` different BSTs:

```
1 1 2 3 3
\ \ / \ / /
2 3 1 3 1 2
\ / \ /
3 2 2 1
```

### Parentheses
With 3 pairs of parentheses, there are `C3 = 5` valid combinations:

```
((()))
(()())
(())()
()(())
()()()
```

### Polygon Triangulation
A hexagon (6 sides = n+2, so n=4) can be triangulated in `C4 = 14` different ways.

## Complexity

- **Time Complexity**: `O(n²)` - Using dynamic programming approach
- **Space Complexity**: `O(n)` - To store intermediate results

## References

- [Wikipedia - Catalan Number](https://en.wikipedia.org/wiki/Catalan_number)
- [OEIS - Catalan Numbers](https://oeis.org/A000108)
- [YouTube - Catalan Numbers](https://www.youtube.com/watch?v=GlI17WaMrtw)
27 changes: 27 additions & 0 deletions src/algorithms/math/catalan-number/__test__/catalanNumber.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import catalanNumber from '../catalanNumber';

describe('catalanNumber', () => {
it('should calculate Catalan numbers correctly', () => {
expect(catalanNumber(0)).toBe(1);
expect(catalanNumber(1)).toBe(1);
expect(catalanNumber(2)).toBe(2);
expect(catalanNumber(3)).toBe(5);
expect(catalanNumber(4)).toBe(14);
expect(catalanNumber(5)).toBe(42);
expect(catalanNumber(6)).toBe(132);
expect(catalanNumber(7)).toBe(429);
expect(catalanNumber(8)).toBe(1430);
expect(catalanNumber(9)).toBe(4862);
expect(catalanNumber(10)).toBe(16796);
});

it('should throw error for negative numbers', () => {
expect(() => catalanNumber(-1)).toThrow('Catalan number is not defined for negative numbers');
expect(() => catalanNumber(-10)).toThrow('Catalan number is not defined for negative numbers');
});

it('should handle larger numbers', () => {
expect(catalanNumber(15)).toBe(9694845);
expect(catalanNumber(20)).toBe(6564120420);
});
});
48 changes: 48 additions & 0 deletions src/algorithms/math/catalan-number/catalanNumber.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Calculate nth Catalan number using dynamic programming approach.
*
* Catalan numbers form a sequence of natural numbers that occur in various
* counting problems, often involving recursively-defined objects.
*
* The nth Catalan number can be expressed directly in terms of binomial coefficients:
* C(n) = (2n)! / ((n + 1)! * n!)
*
* Or using the recursive formula:
* C(0) = 1
* C(n) = sum of C(i) * C(n-1-i) for i = 0 to n-1
*
* Applications:
* - Number of different Binary Search Trees with n keys
* - Number of expressions containing n pairs of parentheses
* - Number of ways to triangulate a polygon with n+2 sides
* - Number of paths in a grid from (0,0) to (n,n) without crossing diagonal
*
* @param {number} n - The position in Catalan sequence
* @return {number} - The nth Catalan number
*/
export default function catalanNumber(n) {
// Handle edge cases
if (n < 0) {
throw new Error('Catalan number is not defined for negative numbers');
}

// Base case
if (n === 0 || n === 1) {
return 1;
}

// Use dynamic programming to calculate Catalan number
// This approach has O(n^2) time complexity and O(n) space complexity
const catalan = new Array(n + 1).fill(0);
catalan[0] = 1;
catalan[1] = 1;

// Calculate Catalan numbers from 2 to n
for (let i = 2; i <= n; i += 1) {
for (let j = 0; j < i; j += 1) {
catalan[i] += catalan[j] * catalan[i - 1 - j];
}
}

return catalan[n];
}
63 changes: 63 additions & 0 deletions src/algorithms/sorting/pancake-sort/PancakeSort.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/* eslint-disable no-param-reassign */
import Sort from '../Sort';

export default class PancakeSort extends Sort {
/**
* Flip the array from index 0 to index i
* @param {*[]} array
* @param {number} endIndex
*/
flip(array, endIndex) {
let start = 0;
let end = endIndex;
while (start < end) {
// Swap elements
[array[start], array[end]] = [array[end], array[start]];
start += 1;
end -= 1;
}
}

/**
* Find the index of the maximum element in array[0...n]
* @param {*[]} array
* @param {number} n
* @return {number}
*/
findMaxIndex(array, n) {
let maxIndex = 0;
for (let i = 1; i <= n; i += 1) {
// Call visiting callback.
this.callbacks.visitingCallback(array[i]);

if (this.comparator.greaterThan(array[i], array[maxIndex])) {
maxIndex = i;
}
}
return maxIndex;
}

sort(originalArray) {
// Clone original array to prevent its modification.
const array = [...originalArray];

// Start from the complete array and one by one reduce current size by one
for (let currentSize = array.length - 1; currentSize > 0; currentSize -= 1) {
// Find index of the maximum element in array[0...currentSize]
const maxIndex = this.findMaxIndex(array, currentSize);

// Move the maximum element to end of current array if it's not already at the end
if (maxIndex !== currentSize) {
// First move maximum number to beginning if it's not already
if (maxIndex !== 0) {
this.flip(array, maxIndex);
}

// Now move the maximum number to end by reversing current array
this.flip(array, currentSize);
}
}

return array;
}
}
42 changes: 42 additions & 0 deletions src/algorithms/sorting/pancake-sort/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Pancake Sort

Pancake sorting is a sorting algorithm in which the only allowed operation is to "flip" one end of the list. The goal is to sort the array by repeatedly flipping portions of it.

The algorithm is inspired by the real-world problem of sorting a stack of pancakes by size using a spatula. A flip operation reverses the order of elements from the beginning of the array up to a specified index.

![Pancake Sort](https://upload.wikimedia.org/wikipedia/commons/0/0f/Pancake_sort_operation.png)

## Algorithm

The algorithm works by repeatedly finding the maximum element and moving it to its correct position at the end of the array through a series of flips:

1. Find the maximum element in the unsorted portion of the array
2. If it's not at the beginning, flip it to the beginning
3. Flip it to its correct position at the end
4. Reduce the size of the unsorted portion and repeat

## Example

Given an array: `[3, 6, 2, 8, 4, 5]`

**Step 1:** Find max (8) at index 3
- Flip to beginning: `[8, 6, 3, 2, 4, 5]`
- Flip to end: `[5, 4, 2, 3, 6, 8]`

**Step 2:** Find max (6) at index 4
- Flip to beginning: `[6, 4, 2, 3, 5, 8]`
- Flip to position 4: `[5, 3, 2, 4, 6, 8]`

**Step 3:** Continue until sorted: `[2, 3, 4, 5, 6, 8]`

## Complexity

| Name | Best | Average | Worst | Memory | Stable | Comments |
| --------------------- | :-------------: | :-----------------: | :-----------------: | :-------: | :-------: | :-------- |
| **Pancake sort** | n | n<sup>2</sup> | n<sup>2</sup> | 1 | No | |

## References

- [Wikipedia](https://en.wikipedia.org/wiki/Pancake_sorting)
- [GeeksforGeeks](https://www.geeksforgeeks.org/pancake-sorting/)
- [YouTube](https://www.youtube.com/watch?v=kk-_DDgoXfg)
60 changes: 60 additions & 0 deletions src/algorithms/sorting/pancake-sort/__test__/PancakeSort.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import PancakeSort from '../PancakeSort';
import {
equalArr,
notSortedArr,
reverseArr,
sortedArr,
SortTester,
} from '../../SortTester';

// Complexity constants.
const SORTED_ARRAY_VISITING_COUNT = 190;
const NOT_SORTED_ARRAY_VISITING_COUNT = 190;
const REVERSE_SORTED_ARRAY_VISITING_COUNT = 190;
const EQUAL_ARRAY_VISITING_COUNT = 190;

describe('PancakeSort', () => {
it('should sort array', () => {
SortTester.testSort(PancakeSort);
});

it('should sort array with custom comparator', () => {
SortTester.testSortWithCustomComparator(PancakeSort);
});

it('should sort negative numbers', () => {
SortTester.testNegativeNumbersSort(PancakeSort);
});

it('should visit EQUAL array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
PancakeSort,
equalArr,
EQUAL_ARRAY_VISITING_COUNT,
);
});

it('should visit SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
PancakeSort,
sortedArr,
SORTED_ARRAY_VISITING_COUNT,
);
});

it('should visit NOT SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
PancakeSort,
notSortedArr,
NOT_SORTED_ARRAY_VISITING_COUNT,
);
});

it('should visit REVERSE SORTED array element specified number of times', () => {
SortTester.testAlgorithmTimeComplexity(
PancakeSort,
reverseArr,
REVERSE_SORTED_ARRAY_VISITING_COUNT,
);
});
});
Loading