diff --git a/src/data-structures/sequences/linked-list/index.ts b/src/data-structures/sequences/linked-list/index.ts index 5a9f65c..002d8a2 100644 --- a/src/data-structures/sequences/linked-list/index.ts +++ b/src/data-structures/sequences/linked-list/index.ts @@ -47,7 +47,7 @@ class LinkedList implements Iterable { } /** - * Appends values to list from array + * Appends values to list from array ~ O(k) */ fromArray(arr: T[]): LinkedList { for (const val of arr) { @@ -71,7 +71,7 @@ class LinkedList implements Iterable { // INSERT /** * Adds node to the head ~ O(1) - * @param {T} val - value to add to head of list + * @param {T} val value to add to head of list * @return {boolean} */ unshift(val: T): boolean { @@ -96,7 +96,7 @@ class LinkedList implements Iterable { /** * Adds node to the tail ~ O(1) - * @param {T} val - value to add to tail of list + * @param {T} val value to add to tail of list * @return {boolean} */ push(val: T): boolean { @@ -121,8 +121,8 @@ class LinkedList implements Iterable { /** * Adds a node at specified index ~ O(n) - * @param {number} i - index - * @param {T} val - value to add to list + * @param {number} i index + * @param {T} val value to add to list * @return {boolean} */ addAt(i: number, val: T): boolean { @@ -175,7 +175,7 @@ class LinkedList implements Iterable { /** * Gets the value of node at index i - O(n) - * @param {number} i - index of element + * @param {number} i index of element * @returns {T | null} value of element at index i if list is defined and i exists in list */ get(i: number): T | null { @@ -198,7 +198,7 @@ class LinkedList implements Iterable { // SEARCH /** * Returns the index of the first occurrence of the specified value in the linked list. - * @param {T} value - value to search for + * @param {T} value value to search for * @return {number} the index of the first occurrence of the element, and -1 if the element does not exist. */ indexOf(value: T): number { @@ -219,7 +219,7 @@ class LinkedList implements Iterable { /** * Checks if value is a node in linked list. - * @param {T} value - value to search for + * @param {T} value value to search for * @returns {boolean} */ contains(value: T): boolean { @@ -229,7 +229,7 @@ class LinkedList implements Iterable { // DELETE /** * Removes node at head ~ O(1) - * @return {T | null} - value of removed head node if list is defined + * @return {T | null} value of removed head node if list is defined */ shift(): T | null { if (!this.list) return null; @@ -254,7 +254,7 @@ class LinkedList implements Iterable { /** * Removes node at tail ~ O(1) - * @return {T | null} - value of removed head + * @return {T | null} value of removed head */ pop(): T | null { if (!this.list) return null; @@ -278,8 +278,8 @@ class LinkedList implements Iterable { /** * Removes node at specified index ~ O(n) - * @param {number} i - index to remove - * @return {T | null} - value of removed node + * @param {number} i index to remove + * @return {T | null} value of removed node */ removeAt(i: number): T | null { if (!this.list) return null; @@ -317,8 +317,8 @@ class LinkedList implements Iterable { /** * Removes first occurrence of node with specified value. Returns value of removed node if * removal was successful, and null otherwise. ~ O(n) - * @param {T} val - value to remove - * @returns {T | null} - value of removed node + * @param {T} val value to remove + * @returns {T | null} value of removed node */ remove(val: T): T | null { const index = this.indexOf(val); // O(n) diff --git a/src/data-structures/sequences/stack/index.ts b/src/data-structures/sequences/stack/index.ts new file mode 100644 index 0000000..10bab5c --- /dev/null +++ b/src/data-structures/sequences/stack/index.ts @@ -0,0 +1,92 @@ +import { LinkedList } from '../linked-list'; +import { type TypedEqualityFunction } from '../../utils'; + +class Stack implements Iterable { + private list: LinkedList; + + constructor(equalsFunction?: TypedEqualityFunction) { + if (typeof equalsFunction === 'function') { + this.list = new LinkedList(equalsFunction); + } else { + this.list = new LinkedList(); + } + } + + // HELPERS + /** + * Returns size ~ O(1) + */ + size(): number { + return this.list.size(); + } + + /** + * Returns true if stack is empty ~ O(1) + */ + isEmpty(): boolean { + return this.list.isEmpty(); + } + + /** + * Appends values to stack from array ~ O(k) + */ + fromArray(arr: T[]): Stack { + this.list = this.list.fromArray(arr); + return this; + } + + /* + * Iterator + */ + + [Symbol.iterator](): Iterator { + return this.list[Symbol.iterator](); + } + + // INSERT + /** + * Adds new element ~ O(1) + * @param {T} element value to add to stack + * @return {boolean} + */ + push(element: T) { + return this.list.push(element); + } + + // ACCESS + /** + * Gets the value of element at top of stack ~ O(1) + * @returns {T | null} value of top element in stack + */ + peek(): T | null { + return this.list.peekBack(); + } + + // SEARCH + /** + * Checks if value exists in the stack. ~ O(n) + * @param {T} element value to search for + * @returns {boolean} + */ + contains(element: T) { + return this.list.contains(element); + } + + // DELETE + /** + * Removes element ~ O(1) + * @return {T | null} value of removed element + */ + pop(): T | null { + return this.list.pop(); + } + + /** + * Deletes all elements ~ O(1) + */ + clear() { + this.list.clear(); + } +} + +export { Stack }; diff --git a/test/data-structures/sequences/stack.test.ts b/test/data-structures/sequences/stack.test.ts new file mode 100644 index 0000000..4f7c514 --- /dev/null +++ b/test/data-structures/sequences/stack.test.ts @@ -0,0 +1,144 @@ +import { Stack } from '../../../src/data-structures/sequences/stack'; + +describe('Stack', () => { + let stack: Stack; + + beforeEach(() => { + stack = new Stack(); + }); + + describe('empty stack', () => { + it('returns null when pop() is called on empty stack', () => { + expect(stack.pop()).toBe(null); + }); + + it('returns null when peek() is called on empty stack', () => { + expect(stack.peek()).toBe(null); + }); + }); + + it('is empty', () => { + expect(stack.isEmpty()).toBe(true); + }); + + it('pushes', () => { + stack.push(1); + expect(stack.size()).toBe(1); + + stack.push(2); + expect(stack.size()).toBe(2); + + stack.push(3); + expect(stack.size()).toBe(3); + }); + + it('finds out if list contains element', () => { + expect(stack.contains(1)).toBe(false); + stack.push(1); + stack.push(2); + stack.push(3); + + expect(stack.contains(1)).toBe(true); + expect(stack.contains(3)).toBe(true); + expect(stack.contains(8)).toBe(false); + }); + + it('pops', () => { + stack.push(1); + stack.push(2); + stack.push(3); + + stack.pop(); + expect(stack.size()).toBe(2); + + stack.pop(); + expect(stack.size()).toBe(1); + + stack.pop(); + expect(stack.size()).toBe(0); + }); + + it('peeks', () => { + stack.push(1); + expect(stack.peek()).toBe(1); + + stack.push(2); + expect(stack.peek()).toBe(2); + }); + + it('clears the stack', () => { + stack.push(1); + stack.push(2); + stack.push(3); + stack.push(4); + stack.clear(); + expect(stack.isEmpty()).toBe(true); + + stack.push(1); + stack.clear(); + expect(stack.isEmpty()).toBe(true); + + stack.clear(); + expect(stack.isEmpty()).toBe(true); + }); + + it('fills a new stack from an array', () => { + stack.fromArray([1, 3, 7, 6]); + + expect(stack.size()).toBe(4); + expect(stack.pop()).toBe(6); + }); + + it('is iterable', () => { + const nums = [1, 2, 3]; + + for (const n of nums) { + stack.push(n); + } + + let i = 0; + for (const n of stack) { + expect(n).toBe(nums[i]); + i += 1; + } + }); +}); + +describe('Stack - complex object', () => { + class Car { + carId: number; + topSpeed: number; + engineSize: number; + + constructor(id: number) { + this.carId = id; + this.topSpeed = 100; + this.engineSize = 100; + } + } + + const sameHeroF = (a: Car, b: Car) => a.carId === b.carId; + + let stack: Stack; + + beforeAll(() => { + const ferrari = new Car(123); + const peugeot = new Car(456); + const honda = new Car(789); + + stack = new Stack(sameHeroF); + + stack.push(ferrari); + stack.push(peugeot); + stack.push(honda); + }); + + it('checks if stack contains hero', () => { + const knight = new Car(123); + const mage = new Car(789); + + expect(stack.contains(knight)).toBe(true); + expect(stack.contains(mage)).toBe(true); + expect(stack.contains(new Car(246))).toBe(false); + }); +});