From 2c82e31a29263d550857b30f2c91103804f1183c Mon Sep 17 00:00:00 2001 From: Revone Date: Sun, 20 Aug 2023 18:52:57 +0800 Subject: [PATCH] Optimize the insertAfter method of SinglyLinkedList, insertAfter, insertBefore, and delete methods of DoublyLinkedList to directly add an element after a given node's reference without the need for traversal. And conducted performance test. Modify the insertBefore and delete methods of SinglyLinkedList to align their method APIs with those of DoublyLinkedList. --- .../binary-tree/binary-indexed-tree.ts | 2 +- .../linked-list/doubly-linked-list.ts | 102 +++++++++++------- src/data-structures/linked-list/index.ts | 1 + .../linked-list/singly-linked-list.ts | 75 ++++++++----- .../linked-list/skip-linked-list.ts | 3 +- src/data-structures/matrix/matrix2d.ts | 26 ++--- src/data-structures/matrix/vector2d.ts | 50 ++++----- tests/unit/data-structures/constants/index.ts | 1 + .../data-structures/constants/magnitude.ts | 11 ++ .../linked-list/doubly-linked-list.test.ts | 18 ++-- .../unit/data-structures/linked-list/index.ts | 2 + .../linked-list/linked-list.test.ts | 37 +++++++ .../linked-list/singly-linked-list.test.ts | 16 +-- ...inked-list.ts => skip-linked-list.test.ts} | 6 +- .../priority-queue/max-priority-queue.test.ts | 8 +- 15 files changed, 228 insertions(+), 130 deletions(-) create mode 100644 tests/unit/data-structures/constants/index.ts create mode 100644 tests/unit/data-structures/constants/magnitude.ts create mode 100644 tests/unit/data-structures/linked-list/linked-list.test.ts rename tests/unit/data-structures/linked-list/{skip-linked-list.ts => skip-linked-list.test.ts} (60%) diff --git a/src/data-structures/binary-tree/binary-indexed-tree.ts b/src/data-structures/binary-tree/binary-indexed-tree.ts index 15e7e20..56880ce 100644 --- a/src/data-structures/binary-tree/binary-indexed-tree.ts +++ b/src/data-structures/binary-tree/binary-indexed-tree.ts @@ -61,7 +61,7 @@ export class BinaryIndexedTree { * the sum. * @returns the sum of the elements in the range specified by the start and end indices. */ - public getRangeSum(start: number, end: number): number { + getRangeSum(start: number, end: number): number { if (!(0 <= start && start <= end && end <= this._sumTree.length)) throw 'Index out of bounds'; return this.getPrefixSum(end) - this.getPrefixSum(start); diff --git a/src/data-structures/linked-list/doubly-linked-list.ts b/src/data-structures/linked-list/doubly-linked-list.ts index 72435c6..d5ad36f 100644 --- a/src/data-structures/linked-list/doubly-linked-list.ts +++ b/src/data-structures/linked-list/doubly-linked-list.ts @@ -243,7 +243,7 @@ export class DoublyLinkedList { * @returns The `insert` method returns a boolean value. It returns `true` if the insertion is successful, and `false` * if the index is out of bounds. */ - insert(index: number, val: T): boolean { + insertAt(index: number, val: T): boolean { if (index < 0 || index > this.length) return false; if (index === 0) { this.unshift(val); @@ -286,30 +286,37 @@ export class DoublyLinkedList { return removedNode!.val; } + delete(valOrNode: T): boolean; + delete(valOrNode: DoublyLinkedListNode): boolean; /** - * The `delete` function removes a node with a specific value from a doubly linked list. - * @param {T} val - The `val` parameter represents the value that you want to delete from the linked list. - * @returns The `delete` method returns a boolean value. It returns `true` if the value `val` is found and deleted from - * the linked list, and `false` if the value is not found in the linked list. + * The `delete` function removes a node from a doubly linked list based on either the node itself or its value. + * @param {T | DoublyLinkedListNode} valOrNode - The `valOrNode` parameter can accept either a value of type `T` or + * a `DoublyLinkedListNode` object. + * @returns The `delete` method returns a boolean value. It returns `true` if the value or node was successfully + * deleted from the doubly linked list, and `false` if the value or node was not found in the list. */ - delete(val: T): boolean { - let current = this.head; - while (current) { - if (current.val === val) { - if (current === this.head) { - this.shift(); - } else if (current === this.tail) { - this.pop(); - } else { - const prevNode = current.prev; - const nextNode = current.next; - prevNode!.next = nextNode; - nextNode!.prev = prevNode; - this.length--; - } - return true; + delete(valOrNode: T | DoublyLinkedListNode): boolean { + let node: DoublyLinkedListNode | null; + + if (valOrNode instanceof DoublyLinkedListNode) { + node = valOrNode; + } else { + node = this.findNode(valOrNode); + } + + if (node) { + if (node === this.head) { + this.shift(); + } else if (node === this.tail) { + this.pop(); + } else { + const prevNode = node.prev; + const nextNode = node.next; + prevNode!.next = nextNode; + nextNode!.prev = prevNode; + this.length--; } - current = current.next; + return true; } return false; } @@ -494,16 +501,25 @@ export class DoublyLinkedList { return accumulator; } + insertAfter(existingValueOrNode: T, newValue: T): boolean; + insertAfter(existingValueOrNode: DoublyLinkedListNode, newValue: T): boolean; /** - * The function inserts a new value after an existing value in a doubly linked list. - * @param {T} existingValue - The existing value is the value of the node after which we want to insert the new value. - * @param {T} newValue - The `newValue` parameter represents the value of the new node that you want to insert after - * the existing node. - * @returns The method is returning a boolean value. It returns true if the insertion is successful and false if the - * existing value is not found in the linked list. + * The `insertAfter` function inserts a new node with a given value after an existing node in a doubly linked list. + * @param {T | DoublyLinkedListNode} existingValueOrNode - The existing value or node in the doubly linked list + * after which the new value will be inserted. It can be either the value of the existing node or the existing node + * itself. + * @param {T} newValue - The value that you want to insert into the doubly linked list. + * @returns The method returns a boolean value. It returns true if the insertion is successful, and false if the + * existing value or node is not found in the doubly linked list. */ - insertAfter(existingValue: T, newValue: T): boolean { - const existingNode = this.findNode(existingValue); + insertAfter(existingValueOrNode: T | DoublyLinkedListNode, newValue: T): boolean { + let existingNode; + + if (existingValueOrNode instanceof DoublyLinkedListNode) { + existingNode = existingValueOrNode; + } else { + existingNode = this.findNode(existingValueOrNode); + } if (existingNode) { const newNode = new DoublyLinkedListNode(newValue); @@ -523,16 +539,26 @@ export class DoublyLinkedList { return false; } + insertBefore(existingValueOrNode: T, newValue: T): boolean; + insertBefore(existingValueOrNode: DoublyLinkedListNode, newValue: T): boolean; /** - * The `insertBefore` function inserts a new value before an existing value in a doubly linked list. - * @param {T} existingValue - The existing value is the value of the node that you want to insert the new value before. - * @param {T} newValue - The `newValue` parameter represents the value of the new node that you want to insert before - * the existing node. - * @returns The method is returning a boolean value. It returns true if the insertion is successful and false if the - * existing value is not found in the linked list. + * The `insertBefore` function inserts a new value before an existing value or node in a doubly linked list. + * @param {T | DoublyLinkedListNode} existingValueOrNode - The existing value or node in the doubly linked list + * before which the new value will be inserted. It can be either the value of the existing node or the existing node + * itself. + * @param {T} newValue - The `newValue` parameter represents the value that you want to insert into the doubly linked + * list. + * @returns The method returns a boolean value. It returns `true` if the insertion is successful, and `false` if the + * insertion fails. */ - insertBefore(existingValue: T, newValue: T): boolean { - const existingNode = this.findNode(existingValue); + insertBefore(existingValueOrNode: T | DoublyLinkedListNode, newValue: T): boolean { + let existingNode; + + if (existingValueOrNode instanceof DoublyLinkedListNode) { + existingNode = existingValueOrNode; + } else { + existingNode = this.findNode(existingValueOrNode); + } if (existingNode) { const newNode = new DoublyLinkedListNode(newValue); diff --git a/src/data-structures/linked-list/index.ts b/src/data-structures/linked-list/index.ts index 0bdd0b6..e489ecc 100644 --- a/src/data-structures/linked-list/index.ts +++ b/src/data-structures/linked-list/index.ts @@ -1,2 +1,3 @@ export * from './singly-linked-list'; export * from './doubly-linked-list'; +export * from './skip-linked-list'; diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts index 1595df0..7f2805b 100644 --- a/src/data-structures/linked-list/singly-linked-list.ts +++ b/src/data-structures/linked-list/singly-linked-list.ts @@ -220,17 +220,23 @@ export class SinglyLinkedList { return removedNode!.val; } + delete(valueOrNode: T): boolean; + delete(valueOrNode: SinglyLinkedListNode): boolean; /** - * The `delete` function removes a specified value from a linked list and returns true if the value was found and - * removed, otherwise it returns false. - * @param {T} value - The value parameter represents the value of the node that needs to be deleted from the linked - * list. - * @returns The `delete` method returns a boolean value. It returns `true` if the value was successfully deleted from - * the linked list, and `false` if the value was not found in the linked list. + * The delete function removes a node with a specific value from a singly linked list. + * @param {T | SinglyLinkedListNode} valueOrNode - The `valueOrNode` parameter can accept either a value of type `T` + * or a `SinglyLinkedListNode` object. + * @returns The `delete` method returns a boolean value. It returns `true` if the value or node is found and + * successfully deleted from the linked list, and `false` if the value or node is not found in the linked list. */ - delete(value: T): boolean { - let current = this.head; - let prev = null; + delete(valueOrNode: T | SinglyLinkedListNode): boolean { + let value: T; + if (valueOrNode instanceof SinglyLinkedListNode) { + value = valueOrNode.val; + } else { + value = valueOrNode; + } + let current = this.head, prev = null; while (current) { if (current.val === value) { @@ -256,7 +262,7 @@ export class SinglyLinkedList { } /** - * The `insert` function inserts a value at a specified index in a singly linked list. + * The `insertAt` function inserts a value at a specified index in a singly linked list. * @param {number} index - The index parameter represents the position at which the new value should be inserted in the * linked list. It is of type number. * @param {T} val - The `val` parameter represents the value that you want to insert into the linked list at the @@ -264,7 +270,7 @@ export class SinglyLinkedList { * @returns The `insert` method returns a boolean value. It returns `true` if the insertion is successful, and `false` * if the index is out of bounds. */ - insert(index: number, val: T): boolean { + insertAt(index: number, val: T): boolean { if (index < 0 || index > this.length) return false; if (index === 0) { this.unshift(val); @@ -395,20 +401,25 @@ export class SinglyLinkedList { return null; } + insertBefore(existingValue: T, newValue: T): boolean + insertBefore(existingValue: SinglyLinkedListNode, newValue: T): boolean /** * The `insertBefore` function inserts a new value before an existing value in a singly linked list. - * @param {T} existingValue - The existing value is the value that already exists in the linked list and before which - * we want to insert a new value. + * @param {T | SinglyLinkedListNode} existingValueOrNode - The existing value or node that you want to insert the + * new value before. It can be either the value itself or a node containing the value in the linked list. * @param {T} newValue - The `newValue` parameter represents the value that you want to insert into the linked list. - * @returns The `insertBefore` function returns a boolean value. It returns `true` if the `newValue` is successfully - * inserted before the first occurrence of `existingValue` in the linked list. It returns `false` if the - * `existingValue` is not found in the linked list. + * @returns The method `insertBefore` returns a boolean value. It returns `true` if the new value was successfully + * inserted before the existing value, and `false` otherwise. */ - insertBefore(existingValue: T, newValue: T): boolean { - if (!this.head) { - return false; - } + insertBefore(existingValueOrNode: T | SinglyLinkedListNode, newValue: T): boolean { + if (!this.head) return false; + let existingValue: T; + if (existingValueOrNode instanceof SinglyLinkedListNode) { + existingValue = existingValueOrNode.val; + } else { + existingValue = existingValueOrNode; + } if (this.head.val === existingValue) { this.unshift(newValue); return true; @@ -429,16 +440,24 @@ export class SinglyLinkedList { return false; } + insertAfter(existingValueOrNode: T, newValue: T): boolean + insertAfter(existingValueOrNode: SinglyLinkedListNode, newValue: T): boolean /** - * The function inserts a new value after an existing value in a singly linked list. - * @param {T} existingValue - The existing value is the value of the node after which we want to insert the new value. - * @param {T} newValue - The `newValue` parameter represents the value that you want to insert into the linked list - * after the node with the `existingValue`. - * @returns The method is returning a boolean value. It returns true if the insertion is successful and false if the - * existing value is not found in the linked list. + * The `insertAfter` function inserts a new node with a given value after an existing node in a singly linked list. + * @param {T | SinglyLinkedListNode} existingValueOrNode - The existing value or node in the linked list after which + * the new value will be inserted. It can be either the value of the existing node or the existing node itself. + * @param {T} newValue - The value that you want to insert into the linked list after the existing value or node. + * @returns The method returns a boolean value. It returns true if the new value was successfully inserted after the + * existing value or node, and false if the existing value or node was not found in the linked list. */ - insertAfter(existingValue: T, newValue: T): boolean { - const existingNode = this.findNode(existingValue); + insertAfter(existingValueOrNode: T | SinglyLinkedListNode, newValue: T): boolean { + let existingNode: T | SinglyLinkedListNode | null; + + if (existingValueOrNode instanceof SinglyLinkedListNode) { + existingNode = existingValueOrNode; + } else { + existingNode = this.findNode(existingValueOrNode); + } if (existingNode) { const newNode = new SinglyLinkedListNode(newValue); diff --git a/src/data-structures/linked-list/skip-linked-list.ts b/src/data-structures/linked-list/skip-linked-list.ts index 693da49..9d79cfe 100644 --- a/src/data-structures/linked-list/skip-linked-list.ts +++ b/src/data-structures/linked-list/skip-linked-list.ts @@ -1 +1,2 @@ -export {} \ No newline at end of file +export class SkipLinkedList { +} \ No newline at end of file diff --git a/src/data-structures/matrix/matrix2d.ts b/src/data-structures/matrix/matrix2d.ts index 1a5a7a4..d07a291 100644 --- a/src/data-structures/matrix/matrix2d.ts +++ b/src/data-structures/matrix/matrix2d.ts @@ -33,7 +33,7 @@ export class Matrix2D { * The function returns a 2D array with three empty arrays. * @returns An empty 2-dimensional array with 3 empty arrays inside. */ - public static get empty(): number[][] { + static get empty(): number[][] { return [[], [], []] } @@ -41,7 +41,7 @@ export class Matrix2D { * The above function returns a 3x3 identity matrix. * @returns The method is returning a 2-dimensional array of numbers representing the identity matrix. */ - public static get identity(): number[][] { + static get identity(): number[][] { return [ [1, 0, 0], [0, 1, 0], @@ -53,7 +53,7 @@ export class Matrix2D { * @returns The getter method is returning the value of the private variable `_matrix`, which is a two-dimensional * array of numbers. */ - public get m(): number[][] { + get m(): number[][] { return this._matrix } @@ -63,7 +63,7 @@ export class Matrix2D { * @returns A new instance of the Vector2D class is being returned. The values of the returned vector are taken from * the first column of the matrix. */ - public get toVector(): Vector2D { + get toVector(): Vector2D { return new Vector2D(this._matrix[0][0], this._matrix[1][0]) } @@ -73,7 +73,7 @@ export class Matrix2D { * @param {Matrix2D} matrix2 - The parameter `matrix2` is a Matrix2D object. * @returns a new instance of the Matrix2D class, which is created using the result array. */ - public static add(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { + static add(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { const result = Matrix2D.empty for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { @@ -90,7 +90,7 @@ export class Matrix2D { * representing the matrix elements. * @returns a new instance of the Matrix2D class, which is created using the result array. */ - public static subtract(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { + static subtract(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { const result = Matrix2D.empty for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { @@ -106,7 +106,7 @@ export class Matrix2D { * @param {Matrix2D} matrix2 - The parameter `matrix2` is a 2D matrix of size 3x3. * @returns a new instance of the Matrix2D class, created using the result array. */ - public static multiply(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { + static multiply(matrix1: Matrix2D, matrix2: Matrix2D): Matrix2D { const result = Matrix2D.empty for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { @@ -126,7 +126,7 @@ export class Matrix2D { * @param {number} value - The `value` parameter is a number that you want to multiply each element of the `matrix` by. * @returns a new instance of the Matrix2D class, which is created using the result array. */ - public static multiplyByValue(matrix: Matrix2D, value: number): Matrix2D { + static multiplyByValue(matrix: Matrix2D, value: number): Matrix2D { const result = Matrix2D.empty for (let i = 0; i < 3; i++) { for (let j = 0; j < 3; j++) { @@ -142,7 +142,7 @@ export class Matrix2D { * @param {Vector2D} vector - The "vector" parameter is a 2D vector, represented by an object of type Vector2D. * @returns a Vector2D. */ - public static multiplyByVector(matrix: Matrix2D, vector: Vector2D): Vector2D { + static multiplyByVector(matrix: Matrix2D, vector: Vector2D): Vector2D { return Matrix2D.multiply(matrix, new Matrix2D(vector)).toVector } @@ -154,7 +154,7 @@ export class Matrix2D { * calculate the centerY value, which is the vertical center of the view. * @returns a Matrix2D object. */ - public static view(width: number, height: number): Matrix2D { + static view(width: number, height: number): Matrix2D { const scaleStep = 1 // Scale every vector * scaleStep const centerX = width / 2 const centerY = height / 2 @@ -172,7 +172,7 @@ export class Matrix2D { * should be scaled. * @returns the result of multiplying a new instance of Matrix2D by the given factor. */ - public static scale(factor: number) { + static scale(factor: number) { return Matrix2D.multiplyByValue(new Matrix2D(), factor) } @@ -181,7 +181,7 @@ export class Matrix2D { * @param {number} radians - The "radians" parameter is the angle in radians by which you want to rotate an object. * @returns The code is returning a new instance of a Matrix2D object. */ - public static rotate(radians: number) { + static rotate(radians: number) { const cos = Math.cos(radians) const sin = Math.sin(radians) @@ -197,7 +197,7 @@ export class Matrix2D { * and y, and an optional w component. * @returns The method is returning a new instance of the Matrix2D class. */ - public static translate(vector: Vector2D): Matrix2D { + static translate(vector: Vector2D): Matrix2D { return new Matrix2D([ [1, 0, vector.x], [0, 1, vector.y], diff --git a/src/data-structures/matrix/vector2d.ts b/src/data-structures/matrix/vector2d.ts index 6163194..4d6bdd8 100644 --- a/src/data-structures/matrix/vector2d.ts +++ b/src/data-structures/matrix/vector2d.ts @@ -17,7 +17,7 @@ export class Vector2D { * The function checks if the x and y values of a point are both zero. * @returns A boolean value indicating whether both the x and y properties of the object are equal to 0. */ - public get isZero(): boolean { + get isZero(): boolean { return this.x === 0 && this.y === 0 } @@ -25,7 +25,7 @@ export class Vector2D { * The above function calculates the length of a vector using the Pythagorean theorem. * @returns The length of a vector, calculated using the Pythagorean theorem. */ - public get length(): number { + get length(): number { return Math.sqrt((this.x * this.x) + (this.y * this.y)) } @@ -33,7 +33,7 @@ export class Vector2D { * The function calculates the square of the length of a vector. * @returns The method is returning the sum of the squares of the x and y values. */ - public get lengthSq(): number { + get lengthSq(): number { return (this.x * this.x) + (this.y * this.y) } @@ -42,7 +42,7 @@ export class Vector2D { * @returns The method is returning a new instance of the Vector2D class with the x and y values rounded to the nearest * whole number. */ - public get rounded(): Vector2D { + get rounded(): Vector2D { return new Vector2D(Math.round(this.x), Math.round(this.y)) } @@ -56,7 +56,7 @@ export class Vector2D { * @returns The method is returning a new instance of the Vector2D class with the x and y components of the two input * vectors added together. */ - public static add(vector1: Vector2D, vector2: Vector2D): Vector2D { + static add(vector1: Vector2D, vector2: Vector2D): Vector2D { return new Vector2D(vector1.x + vector2.x, vector1.y + vector2.y) } @@ -71,7 +71,7 @@ export class Vector2D { * @returns The method is returning a new Vector2D object with the x and y components subtracted from vector1 and * vector2. */ - public static subtract(vector1: Vector2D, vector2: Vector2D): Vector2D { + static subtract(vector1: Vector2D, vector2: Vector2D): Vector2D { return new Vector2D(vector1.x - vector2.x, vector1.y - vector2.y) } @@ -84,7 +84,7 @@ export class Vector2D { * of the "vector" parameter. * @returns A new Vector2D object with the x and y values subtracted by the given value. */ - public static subtractValue(vector: Vector2D, value: number): Vector2D { + static subtractValue(vector: Vector2D, value: number): Vector2D { return new Vector2D(vector.x - value, vector.y - value) } @@ -96,7 +96,7 @@ export class Vector2D { * of the vector will be multiplied. * @returns A new Vector2D object with the x and y values multiplied by the given value. */ - public static multiply(vector: Vector2D, value: number): Vector2D { + static multiply(vector: Vector2D, value: number): Vector2D { return new Vector2D(vector.x * value, vector.y * value) } @@ -108,7 +108,7 @@ export class Vector2D { * vector. * @returns A new instance of the Vector2D class with the x and y values divided by the given value. */ - public static divide(vector: Vector2D, value: number): Vector2D { + static divide(vector: Vector2D, value: number): Vector2D { return new Vector2D(vector.x / value, vector.y / value) } @@ -119,7 +119,7 @@ export class Vector2D { * @param {Vector2D} vector2 - The parameter "vector2" is of type Vector2D. * @returns a boolean value, which indicates whether the two input vectors are equal or not. */ - public static equals(vector1: Vector2D, vector2: Vector2D): boolean { + static equals(vector1: Vector2D, vector2: Vector2D): boolean { return vector1.x === vector2.x && vector1.y === vector2.y } @@ -133,7 +133,7 @@ export class Vector2D { * roundingFactor, the vectors are considered equal. * @returns a boolean value. */ - public static equalsRounded(vector1: Vector2D, vector2: Vector2D, roundingFactor = 12): boolean { + static equalsRounded(vector1: Vector2D, vector2: Vector2D, roundingFactor = 12): boolean { const vector = Vector2D.abs(Vector2D.subtract(vector1, vector2)) if (vector.x < roundingFactor && vector.y < roundingFactor) { return true @@ -148,7 +148,7 @@ export class Vector2D { * @returns the normalized vector if its length is greater than a very small value (epsilon), otherwise it returns the * original vector. */ - public static normalize(vector: Vector2D): Vector2D { + static normalize(vector: Vector2D): Vector2D { const length = vector.length if (length > 2.220446049250313e-16) { // Epsilon return Vector2D.divide(vector, length) @@ -165,7 +165,7 @@ export class Vector2D { * @returns either the original vector or a truncated version of the vector, depending on whether the length of the * vector is greater than the maximum value specified. */ - public static truncate(vector: Vector2D, max: number): Vector2D { + static truncate(vector: Vector2D, max: number): Vector2D { if (vector.length > max) { return Vector2D.multiply(Vector2D.normalize(vector), max) } @@ -178,7 +178,7 @@ export class Vector2D { * @param {Vector2D} vector - The parameter "vector" is of type Vector2D. * @returns A new Vector2D object is being returned. */ - public static perp(vector: Vector2D): Vector2D { + static perp(vector: Vector2D): Vector2D { return new Vector2D(-vector.y, vector.x) } @@ -188,7 +188,7 @@ export class Vector2D { * has two properties: "x" and "y", which represent the x and y components of the vector, respectively. * @returns A new Vector2D object with the negated x and y values of the input vector. Returns the vector that is the reverse of this vector */ - public static reverse(vector: Vector2D): Vector2D { + static reverse(vector: Vector2D): Vector2D { return new Vector2D(-vector.x, -vector.y) } @@ -200,7 +200,7 @@ export class Vector2D { * @returns The method is returning a new Vector2D object with the absolute values of the x and y components of the * input vector. */ - public static abs(vector: Vector2D): Vector2D { + static abs(vector: Vector2D): Vector2D { return new Vector2D(Math.abs(vector.x), Math.abs(vector.y)) } @@ -211,7 +211,7 @@ export class Vector2D { * with an x and y component. * @returns The dot product of the two input vectors. */ - public static dot(vector1: Vector2D, vector2: Vector2D): number { + static dot(vector1: Vector2D, vector2: Vector2D): number { return (vector1.x * vector2.x) + (vector1.y * vector2.y) } @@ -219,7 +219,7 @@ export class Vector2D { // * Transform vectors based on the current tranformation matrices: translation, rotation and scale // * @param vectors The vectors to transform // */ - // public static transform(vector: Vector2D, transformation: Matrix2D): Vector2D { + // static transform(vector: Vector2D, transformation: Matrix2D): Vector2D { // return Matrix2D.multiplyByVector(transformation, vector) // } @@ -227,7 +227,7 @@ export class Vector2D { // * Transform vectors based on the current tranformation matrices: translation, rotation and scale // * @param vectors The vectors to transform // */ - // public static transformList(vectors: Vector2D[], transformation: Matrix2D): Vector2D[] { + // static transformList(vectors: Vector2D[], transformation: Matrix2D): Vector2D[] { // return vectors.map(vector => Matrix2D.multiplyByVector(transformation, vector)) // } @@ -241,7 +241,7 @@ export class Vector2D { * the vector in a 2D space. * @returns The distance between vector1 and vector2. */ - public static distance(vector1: Vector2D, vector2: Vector2D): number { + static distance(vector1: Vector2D, vector2: Vector2D): number { const ySeparation = vector2.y - vector1.y const xSeparation = vector2.x - vector1.x return Math.sqrt((ySeparation * ySeparation) + (xSeparation * xSeparation)) @@ -255,7 +255,7 @@ export class Vector2D { * properties `x` and `y` which represent the coordinates of the vector. * @returns the square of the distance between the two input vectors. */ - public static distanceSq(vector1: Vector2D, vector2: Vector2D): number { + static distanceSq(vector1: Vector2D, vector2: Vector2D): number { const ySeparation = vector2.y - vector1.y const xSeparation = vector2.x - vector1.x return (ySeparation * ySeparation) + (xSeparation * xSeparation) @@ -270,7 +270,7 @@ export class Vector2D { * vector2. Both vector1 and vector2 are of type Vector2D. * @returns either -1 or 1. Returns positive if v2 is clockwise of this vector, negative if counterclockwise */ - public static sign(vector1: Vector2D, vector2: Vector2D): number { + static sign(vector1: Vector2D, vector2: Vector2D): number { if (vector1.y * vector2.x > vector1.x * vector2.y) { return -1 } @@ -285,7 +285,7 @@ export class Vector2D { * respectively. * @returns the angle between the given vector and the vector (0, -1) in radians.Returns the angle between origin and the given vector in radians */ - public static angle(vector: Vector2D): number { + static angle(vector: Vector2D): number { const origin = new Vector2D(0, -1) const radian = Math.acos(Vector2D.dot(vector, origin) / (vector.length * origin.length)) return Vector2D.sign(vector, origin) === 1 ? ((Math.PI * 2) - radian) : radian @@ -298,7 +298,7 @@ export class Vector2D { * random vector. * @returns a new instance of the Vector2D class with random x and y values. */ - public static random(maxX: number, maxY: number): Vector2D { + static random(maxX: number, maxY: number): Vector2D { const randX = Math.floor(Math.random() * maxX - (maxX / 2)) const randY = Math.floor(Math.random() * maxY - (maxY / 2)) return new Vector2D(randX, randY) @@ -307,7 +307,7 @@ export class Vector2D { /** * The function sets the values of x and y to zero. */ - public zero(): void { + zero(): void { this.x = 0 this.y = 0 } diff --git a/tests/unit/data-structures/constants/index.ts b/tests/unit/data-structures/constants/index.ts new file mode 100644 index 0000000..27b2090 --- /dev/null +++ b/tests/unit/data-structures/constants/index.ts @@ -0,0 +1 @@ +export * from './magnitude'; \ No newline at end of file diff --git a/tests/unit/data-structures/constants/magnitude.ts b/tests/unit/data-structures/constants/magnitude.ts new file mode 100644 index 0000000..eaed3fe --- /dev/null +++ b/tests/unit/data-structures/constants/magnitude.ts @@ -0,0 +1,11 @@ +const orderReducedBy = 2; // reduction of magnitude's order compared to the baseline magnitude + +export const magnitude = { + CONSTANT: Math.floor(Number.MAX_SAFE_INTEGER / Math.pow(10, orderReducedBy)), + LOG_N: Math.pow(10, 9 - orderReducedBy), + LINEAR: Math.pow(10, 6 - orderReducedBy), + N_LOG_N: Math.pow(10, 5 - orderReducedBy), + SQUARED: Math.pow(10, 4 - orderReducedBy), + CUBED: Math.pow(10, 3 - orderReducedBy), + FACTORIAL: 20 - orderReducedBy +} \ No newline at end of file diff --git a/tests/unit/data-structures/linked-list/doubly-linked-list.test.ts b/tests/unit/data-structures/linked-list/doubly-linked-list.test.ts index 8a0b052..bdc68de 100644 --- a/tests/unit/data-structures/linked-list/doubly-linked-list.test.ts +++ b/tests/unit/data-structures/linked-list/doubly-linked-list.test.ts @@ -1,4 +1,5 @@ import {DoublyLinkedList} from '../../../../src'; +import {magnitude} from '../constants'; describe('DoublyLinkedList Operation Test', () => { let list: DoublyLinkedList; @@ -39,19 +40,19 @@ describe('DoublyLinkedList Operation Test', () => { list.push(3); // Inserting at the beginning - list.insert(0, 0); + list.insertAt(0, 0); expect(list.length).toBe(4); expect(list.getAt(0)).toBe(0); expect(list.getAt(1)).toBe(1); // Inserting in the middle - list.insert(2, 1.5); + list.insertAt(2, 1.5); expect(list.length).toBe(5); expect(list.getAt(2)).toBe(1.5); expect(list.getAt(3)).toBe(2); // Inserting at the end - list.insert(5, 4); + list.insertAt(5, 4); expect(list.length).toBe(6); expect(list.getAt(5)).toBe(4); expect(list.tail!.val).toBe(4); @@ -344,24 +345,21 @@ describe('DoublyLinkedList Operation Test', () => { describe('DoublyLinkedList Performance Test', () => { describe('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => { const list = new DoublyLinkedList(); - const iterations = 10000; const startPushTime = performance.now(); - for (let i = 0; i < iterations; i++) { + for (let i = 0; i < magnitude.LINEAR; i++) { list.unshift(i); } - expect(performance.now() - startPushTime).toBeLessThan(iterations * 1000); + expect(performance.now() - startPushTime).toBeLessThan(magnitude.LINEAR * 1000); const startPopTime = performance.now(); expect(list.length).toBeGreaterThan(0); - for (let i = 0; i < iterations; i++) { + for (let i = 0; i < magnitude.LINEAR; i++) { list.shift(); } expect(list.pop()).toBeNull(); expect(list.length).toBe(0); - expect(performance.now() - startPopTime).toBeLessThan(iterations * 1000); - + expect(performance.now() - startPopTime).toBeLessThan(magnitude.LINEAR * 1000); }); - }); \ No newline at end of file diff --git a/tests/unit/data-structures/linked-list/index.ts b/tests/unit/data-structures/linked-list/index.ts index 832f869..3b50503 100644 --- a/tests/unit/data-structures/linked-list/index.ts +++ b/tests/unit/data-structures/linked-list/index.ts @@ -1,2 +1,4 @@ export * from './singly-linked-list.test'; export * from './doubly-linked-list.test'; +export * from './linked-list.test'; +export * from './skip-linked-list.test'; diff --git a/tests/unit/data-structures/linked-list/linked-list.test.ts b/tests/unit/data-structures/linked-list/linked-list.test.ts new file mode 100644 index 0000000..a2844c7 --- /dev/null +++ b/tests/unit/data-structures/linked-list/linked-list.test.ts @@ -0,0 +1,37 @@ +import {DoublyLinkedList, DoublyLinkedListNode, SinglyLinkedList, SinglyLinkedListNode} from '../../../../src'; +import {magnitude} from '../constants'; + +describe('LinkedList Performance Test', () => { + it('should DoublyLinkedList insertBefore faster than SinglyLinkedList', () => { + const doublyList = new DoublyLinkedList(); + + const startPushTime = performance.now(); + let midNode: DoublyLinkedListNode | null = null; + const midIndex = Math.floor((magnitude.SQUARED) / 2); + for (let i = 0; i < magnitude.SQUARED; i++) { + doublyList.push(i); + if (i === midIndex) { + midNode = doublyList.findNode(i); + } else if (i > midIndex && midNode) { + doublyList.insertBefore(midNode, i); + } + } + const doublyListPushCost = performance.now() - startPushTime; + + const singlyList = new SinglyLinkedList(); + let midSinglyNode: SinglyLinkedListNode | null = null; + + const startSinglyPushTime = performance.now(); + for (let i = 0; i < magnitude.SQUARED; i++) { + singlyList.push(i); + if (i === midIndex) { + midSinglyNode = singlyList.findNode(i); + } else if (i > midIndex && midSinglyNode) { + singlyList.insertBefore(midSinglyNode.val, i); + } + } + + const singlyListPushCost = performance.now() - startSinglyPushTime; + expect(doublyListPushCost).toBeLessThan(Math.sqrt(singlyListPushCost) + 1000); + }); +}); \ No newline at end of file diff --git a/tests/unit/data-structures/linked-list/singly-linked-list.test.ts b/tests/unit/data-structures/linked-list/singly-linked-list.test.ts index a46917d..83fa389 100644 --- a/tests/unit/data-structures/linked-list/singly-linked-list.test.ts +++ b/tests/unit/data-structures/linked-list/singly-linked-list.test.ts @@ -1,4 +1,5 @@ import {SinglyLinkedList} from '../../../../src'; +import {magnitude} from '../constants'; describe('SinglyLinkedList Operation Test', () => { let list: SinglyLinkedList; @@ -296,9 +297,9 @@ describe('SinglyLinkedList Operation Test', () => { describe('insert and toArray', () => { it('should insert elements and return array correctly', () => { - list.insert(0, 1); - list.insert(1, 3); - list.insert(1, 2); + list.insertAt(0, 1); + list.insertAt(1, 3); + list.insertAt(1, 2); expect(list.toArray()).toEqual([1, 2, 3]); }); }); @@ -381,21 +382,20 @@ describe('SinglyLinkedList Operation Test', () => { describe('SinglyLinkedList Performance Test', () => { describe('should the push and pop methods adhere to a time complexity of O(n) and executed correctly under large scale data', () => { const list = new SinglyLinkedList(); - const iterations = 10000; // Adjust the number of iterations as needed const startPushTime = performance.now(); - for (let i = 0; i < iterations; i++) { + for (let i = 0; i < magnitude.LINEAR; i++) { list.push(i); } - expect(performance.now() - startPushTime).toBeLessThan(iterations * 1000); + expect(performance.now() - startPushTime).toBeLessThan(magnitude.LINEAR * 1000); const startPopTime = performance.now(); - for (let i = 0; i < iterations; i++) { + for (let i = 0; i < magnitude.LINEAR; i++) { list.pop(); } - expect(performance.now() - startPopTime).toBeLessThan(iterations * 1000); + expect(performance.now() - startPopTime).toBeLessThan(magnitude.LINEAR * 1000); }); }); \ No newline at end of file diff --git a/tests/unit/data-structures/linked-list/skip-linked-list.ts b/tests/unit/data-structures/linked-list/skip-linked-list.test.ts similarity index 60% rename from tests/unit/data-structures/linked-list/skip-linked-list.ts rename to tests/unit/data-structures/linked-list/skip-linked-list.test.ts index 6ba4fd6..42f3953 100644 --- a/tests/unit/data-structures/linked-list/skip-linked-list.ts +++ b/tests/unit/data-structures/linked-list/skip-linked-list.test.ts @@ -1,11 +1,13 @@ +import {SkipLinkedList} from '../../../../src' + describe('SkipLinkedList Operation Test', () => { it('should xxx', function () { - + const xxx = new SkipLinkedList(); }); }); describe('SkipLinkedList Performance Test', () => { it('should xxx', function () { - + const xxx = new SkipLinkedList(); }); }); \ No newline at end of file diff --git a/tests/unit/data-structures/priority-queue/max-priority-queue.test.ts b/tests/unit/data-structures/priority-queue/max-priority-queue.test.ts index cd056be..02f3428 100644 --- a/tests/unit/data-structures/priority-queue/max-priority-queue.test.ts +++ b/tests/unit/data-structures/priority-queue/max-priority-queue.test.ts @@ -1,4 +1,5 @@ import {MaxPriorityQueue} from '../../../../src'; +import {magnitude} from '../constants'; describe('MaxPriorityQueue Operation Test', () => { @@ -79,9 +80,8 @@ describe('MaxPriorityQueue Operation Test', () => { describe('MaxPriorityQueue Performance Test', () => { it('should the poll method adheres to a time complexity of O(log n) and executed correctly under large scale distinct data', () => { - const magnitude = 10000; - const nodes = Array.from(new Set(Array.from(new Array(magnitude), () => Math.floor(Math.random() * magnitude * 100)))); - expect(nodes.length).toBeGreaterThan(magnitude / 2); + const nodes = Array.from(new Set(Array.from(new Array(magnitude.LINEAR), () => Math.floor(Math.random() * magnitude.LINEAR * 100)))); + expect(nodes.length).toBeGreaterThan(magnitude.LINEAR / 2); const maxPQ = new MaxPriorityQueue({nodes}); let prev = Number.MAX_SAFE_INTEGER; @@ -93,7 +93,7 @@ describe('MaxPriorityQueue Performance Test', () => { prev = polled; } } - expect(performance.now() - startTime).toBeLessThan(Math.log2(magnitude) * 1000); + expect(performance.now() - startTime).toBeLessThan(Math.log2(magnitude.LINEAR) * 1000); }); it('should sorted.length to be the same as original data', () => {