Skip to content
Merged
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
21 changes: 21 additions & 0 deletions src/03-array/11-array-chunking.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
*/

// Approach 1: Using slice method
/**
* Splits an array into chunks of the given size using slice.
* @param array - The source array
* @param chunkSize - Size of each chunk (must be > 0)
* @returns Array of chunk arrays
* @complexity Time O(n) | Space O(n)
*/
export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
if (chunkSize <= 0) {
throw new Error('Chunk size must be greater than 0');
Expand All @@ -19,6 +26,13 @@ export function chunkArray<T>(array: T[], chunkSize: number): T[][] {
}

// Approach 2: Using splice method (modifies original array)
/**
* Splits a copy of the array into chunks using splice.
* @param array - The source array
* @param chunkSize - Size of each chunk (must be > 0)
* @returns Array of chunk arrays
* @complexity Time O(n) | Space O(n)
*/
export function chunkArraySplice<T>(array: T[], chunkSize: number): T[][] {
if (chunkSize <= 0) {
throw new Error('Chunk size must be greater than 0');
Expand All @@ -34,6 +48,13 @@ export function chunkArraySplice<T>(array: T[], chunkSize: number): T[][] {
}

// Approach 3: Using reduce method (functional approach)
/**
* Splits an array into chunks using reduce.
* @param array - The source array
* @param chunkSize - Size of each chunk (must be > 0)
* @returns Array of chunk arrays
* @complexity Time O(n) | Space O(n)
*/
export function chunkArrayReduce<T>(array: T[], chunkSize: number): T[][] {
if (chunkSize <= 0) {
throw new Error('Chunk size must be greater than 0');
Expand Down
31 changes: 31 additions & 0 deletions src/03-array/12-flatten-arrays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,35 @@
*/

// Approach 1: Using flat() method (ES2019+)
/**
* Flattens a nested array to the given depth using Array.flat().
* @param array - The nested array
* @param depth - Depth to flatten (default 1)
* @returns Flattened array
* @complexity Time O(n) | Space O(n) where n = total elements
*/
export function flattenSimple<T>(array: any[], depth: number = 1): T[] {
return array.flat(depth);
}

// Approach 2: Deep flatten using flat(Infinity)
/**
* Deeply flattens a nested array to a single level using flat(Infinity).
* @param array - The nested array
* @returns Fully flattened array
* @complexity Time O(n) | Space O(n)
*/
export function flattenDeep<T>(array: any[]): T[] {
return array.flat(Infinity);
}

// Approach 3: Recursive approach (custom implementation)
/**
* Deeply flattens a nested array recursively.
* @param array - The nested array
* @returns Fully flattened array
* @complexity Time O(n) | Space O(n)
*/
export function flattenRecursive<T>(array: any[]): T[] {
const result: T[] = [];

Expand All @@ -31,13 +50,25 @@ export function flattenRecursive<T>(array: any[]): T[] {
}

// Approach 4: Using reduce (functional approach)
/**
* Deeply flattens a nested array using reduce.
* @param array - The nested array
* @returns Fully flattened array
* @complexity Time O(n) | Space O(n)
*/
export function flattenReduce<T>(array: any[]): T[] {
return array.reduce((acc: T[], item: any) => {
return acc.concat(Array.isArray(item) ? flattenReduce<T>(item) : item);
}, []);
}

// Approach 5: Iterative approach using stack
/**
* Deeply flattens a nested array iteratively using a stack.
* @param array - The nested array
* @returns Fully flattened array
* @complexity Time O(n) | Space O(n)
*/
export function flattenIterative<T>(array: any[]): T[] {
const stack = [...array];
const result: T[] = [];
Expand Down
37 changes: 37 additions & 0 deletions src/03-array/13-remove-duplicates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,60 @@
*/

// Approach 1: Using Set (most efficient for primitives)
/**
* Removes duplicate values using a Set.
* @param array - The source array
* @returns Array with duplicates removed
* @complexity Time O(n) | Space O(n)
*/
export function removeDuplicatesSet<T>(array: T[]): T[] {
return [...new Set(array)];
}

// Approach 2: Using filter and indexOf
/**
* Removes duplicate values using filter and indexOf.
* @param array - The source array
* @returns Array with duplicates removed
* @complexity Time O(n²) | Space O(n)
*/
export function removeDuplicatesFilter<T>(array: T[]): T[] {
return array.filter((item, index) => array.indexOf(item) === index);
}

// Approach 3: Using reduce
/**
* Removes duplicate values using reduce.
* @param array - The source array
* @returns Array with duplicates removed
* @complexity Time O(n²) | Space O(n)
*/
export function removeDuplicatesReduce<T>(array: T[]): T[] {
return array.reduce((unique: T[], item: T) => {
return unique.includes(item) ? unique : [...unique, item];
}, []);
}

// Approach 4: Using a Map (preserves insertion order)
/**
* Removes duplicate values using a Map (preserves insertion order).
* @param array - The source array
* @returns Array with duplicates removed
* @complexity Time O(n) | Space O(n)
*/
export function removeDuplicatesMap<T>(array: T[]): T[] {
const map = new Map<T, boolean>();
array.forEach(item => map.set(item, true));
return Array.from(map.keys());
}

// Approach 5: Traditional loop approach
/**
* Removes duplicate values using a traditional loop with includes.
* @param array - The source array
* @returns Array with duplicates removed
* @complexity Time O(n²) | Space O(n)
*/
export function removeDuplicatesLoop<T>(array: T[]): T[] {
const unique: T[] = [];
for (let i = 0; i < array.length; i++) {
Expand All @@ -41,6 +71,13 @@ export function removeDuplicatesLoop<T>(array: T[]): T[] {
}

// Approach 6: Remove duplicates from array of objects (by property)
/**
* Removes duplicate objects from an array based on the value of a specified property.
* @param array - Array of objects
* @param property - Property key to deduplicate by
* @returns Array with duplicates removed
* @complexity Time O(n) | Space O(n)
*/
export function removeDuplicatesByProperty<T extends Record<string, any>>(
array: T[],
property: keyof T
Expand Down
35 changes: 35 additions & 0 deletions src/03-array/14-array-rotation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@
*/

// Approach 1: Rotate Right using slice and concat
/**
* Rotates the array k positions to the right using slice and concat.
* @param array - The source array
* @param k - Number of positions to rotate right
* @returns New rotated array
* @complexity Time O(n) | Space O(n)
*/
export function rotateRight<T>(array: T[], k: number): T[] {
if (array.length === 0) return array;

Expand All @@ -18,6 +25,13 @@ export function rotateRight<T>(array: T[], k: number): T[] {
}

// Approach 2: Rotate Left using slice and concat
/**
* Rotates the array k positions to the left using slice and concat.
* @param array - The source array
* @param k - Number of positions to rotate left
* @returns New rotated array
* @complexity Time O(n) | Space O(n)
*/
export function rotateLeft<T>(array: T[], k: number): T[] {
if (array.length === 0) return array;

Expand All @@ -29,6 +43,13 @@ export function rotateLeft<T>(array: T[], k: number): T[] {
}

// Approach 3: Rotate Right using spread operator
/**
* Rotates the array k positions to the right using the spread operator.
* @param array - The source array
* @param k - Number of positions to rotate right
* @returns New rotated array
* @complexity Time O(n) | Space O(n)
*/
export function rotateRightSpread<T>(array: T[], k: number): T[] {
if (array.length === 0) return array;

Expand All @@ -40,6 +61,13 @@ export function rotateRightSpread<T>(array: T[], k: number): T[] {
}

// Approach 4: In-place rotation using reverse (most efficient)
/**
* Rotates a copy of the array k positions to the right using the three-reversal algorithm.
* @param array - The source array
* @param k - Number of positions to rotate right
* @returns New rotated array
* @complexity Time O(n) | Space O(n) for the copy
*/
export function rotateRightInPlace<T>(array: T[], k: number): T[] {
const arr = [...array]; // Create copy to avoid modifying original
const n = arr.length;
Expand Down Expand Up @@ -68,6 +96,13 @@ export function rotateRightInPlace<T>(array: T[], k: number): T[] {
}

// Approach 5: Rotate using unshift and pop (simple but less efficient)
/**
* Rotates a copy of the array k positions to the right using unshift/pop.
* @param array - The source array
* @param k - Number of positions to rotate right
* @returns New rotated array
* @complexity Time O(n·k) | Space O(n)
*/
export function rotateRightSimple<T>(array: T[], k: number): T[] {
const arr = [...array];
k = k % arr.length;
Expand Down
66 changes: 66 additions & 0 deletions src/04-stack/__test__/tower-of-hanoi.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { describe, expect, test } from '@jest/globals';
import { towerOfHanoi } from '../tower-of-hanoi';
import Stack from '../stack';

function makeSourceStack(n: number): Stack<number> {
const source = new Stack<number>();
for (let i = n; i >= 1; i--) {
source.push(i);
}
return source;
}

describe('towerOfHanoi', () => {
test('n=0: destination stays empty, source stays empty', () => {
const source = new Stack<number>();
const auxiliary = new Stack<number>();
const destination = new Stack<number>();
towerOfHanoi(0, source, auxiliary, destination);
expect(destination.isEmpty()).toBe(true);
expect(source.isEmpty()).toBe(true);
});

test('n=1: destination has [1], source is empty', () => {
const source = makeSourceStack(1);
const auxiliary = new Stack<number>();
const destination = new Stack<number>();
towerOfHanoi(1, source, auxiliary, destination);
expect(source.isEmpty()).toBe(true);
expect(destination.peek()).toBe(1);
expect(destination.size).toBe(1);
});

test('n=2: destination has [2,1] (2 bottom, 1 top), source empty', () => {
const source = makeSourceStack(2);
const auxiliary = new Stack<number>();
const destination = new Stack<number>();
towerOfHanoi(2, source, auxiliary, destination);
expect(source.isEmpty()).toBe(true);
expect(destination.size).toBe(2);
expect(destination.pop()).toBe(1); // top
expect(destination.pop()).toBe(2); // bottom
});

test('n=3: destination has [3,2,1], source empty, auxiliary empty', () => {
const source = makeSourceStack(3);
const auxiliary = new Stack<number>();
const destination = new Stack<number>();
towerOfHanoi(3, source, auxiliary, destination);
expect(source.isEmpty()).toBe(true);
expect(auxiliary.isEmpty()).toBe(true);
expect(destination.size).toBe(3);
expect(destination.pop()).toBe(1);
expect(destination.pop()).toBe(2);
expect(destination.pop()).toBe(3);
});

test('n=3 results in all disks on destination', () => {
const source = makeSourceStack(3);
const auxiliary = new Stack<number>();
const destination = new Stack<number>();
towerOfHanoi(3, source, auxiliary, destination);
expect(destination.size).toBe(3);
expect(source.isEmpty()).toBe(true);
expect(auxiliary.isEmpty()).toBe(true);
});
});
33 changes: 33 additions & 0 deletions src/04-stack/stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,63 @@
class Stack<T> {
private items: T[] = [];

/**
* Pushes an item onto the top of the stack.
* @param item - The item to push
* @complexity Time O(1) | Space O(1)
*/
push(item: T): void {
this.items.push(item);
}

/**
* Removes and returns the top item; returns undefined if empty.
* @returns The top item, or undefined if the stack is empty
* @complexity Time O(1) | Space O(1)
*/
pop(): T | undefined {
return this.items.pop();
}

/**
* Returns the top item without removing it; returns undefined if empty.
* @returns The top item, or undefined if the stack is empty
* @complexity Time O(1) | Space O(1)
*/
peek(): T | undefined {
return this.items[this.items.length - 1];
}

/**
* Returns true if the stack has no items.
* @returns Whether the stack is empty
* @complexity Time O(1) | Space O(1)
*/
isEmpty(): boolean {
return this.items.length === 0;
}

/**
* Number of items in the stack.
* @complexity Time O(1) | Space O(1)
*/
get size(): number {
return this.items.length;
}

/**
* Removes all items from the stack.
* @complexity Time O(1) | Space O(1)
*/
clear(): void {
this.items = [];
}

/**
* Returns a comma-separated string representation of the stack contents.
* @returns String representation, or 'Empty Stack' if empty
* @complexity Time O(n) | Space O(n)
*/
toString(): string {
if (this.isEmpty()) {
return 'Empty Stack';
Expand Down
Loading
Loading