From e7be7b559db20cfa1ec4a8dfa20f2a9117fec20b Mon Sep 17 00:00:00 2001 From: Revone Date: Fri, 17 Nov 2023 16:11:31 +0800 Subject: [PATCH] feat: Implemented Deque independently and conducted performance tests, and eliminated the redundant data structure ArrayDeque. Add performance test cases for DoublyLinkedList. --- README.md | 6 +- package.json | 2 +- src/data-structures/queue/deque.ts | 869 +++++++++++++----- .../linked-list/doubly-linked-list.test.ts | 21 + .../data-structures/queue/deque.test.ts | 27 + test/unit/data-structures/queue/deque.test.ts | 138 +-- 6 files changed, 696 insertions(+), 367 deletions(-) diff --git a/README.md b/README.md index e49afe2..32b1d16 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ const { Security - Carefully designed security for member variables and methods. Data structure software does not need to consider other security aspects. + Carefully designed security for member variables and methods. Read-write separation. Data structure software does not need to consider other security aspects. Scalability @@ -807,8 +807,8 @@ Array.from(dijkstraResult?.seen ?? []).map(vertex => vertex.key) // ['A', 'B', ' [//]: # (No deletion!!! Start of Replace Section)
-
priority-queue
-
test nametime taken (ms)executions per secsample deviation
100,000 add & pop24.0041.664.11e-4
100,000 CPT add & pop28.2335.438.86e-4
+
deque
+
test nametime taken (ms)executions per secsample deviation
1,000,000 push36.6727.270.00
1,000,000 shift35.7827.950.00
1,000,000 push35.9627.810.00
1,000,000 push & pop39.2925.450.00
1,000,000 push & shift39.4625.340.00
1,000,000 unshift & shift36.9327.080.00
[//]: # (No deletion!!! End of Replace Section) diff --git a/package.json b/package.json index 3a2c32e..e0d9ca5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-structure-typed", - "version": "1.45.3", + "version": "1.45.4", "description": "Data Structures of Javascript & TypeScript. Binary Tree, BST, Graph, Heap, Priority Queue, Linked List, Queue, Deque, Stack, AVL Tree, Tree Multiset, Trie, Directed Graph, Undirected Graph, Singly Linked List, Doubly Linked List, Max Heap, Max Priority Queue, Min Heap, Min Priority Queue.", "main": "dist/cjs/index.js", "module": "dist/mjs/index.js", diff --git a/src/data-structures/queue/deque.ts b/src/data-structures/queue/deque.ts index c6c2ed1..97050af 100644 --- a/src/data-structures/queue/deque.ts +++ b/src/data-structures/queue/deque.ts @@ -5,14 +5,634 @@ * @copyright Copyright (c) 2022 Tyler Zeng * @license MIT License */ -import { DoublyLinkedList } from '../linked-list'; -// O(n) time complexity of obtaining the value -// O(1) time complexity of adding at the beginning and the end -export class Deque extends DoublyLinkedList { + +/** + * Deque can provide random access with O(1) time complexity + * Deque is usually more compact and efficient in memory usage because it does not require additional space to store pointers. + * Deque may experience performance jitter, but DoublyLinkedList will not + * Deque is implemented using a dynamic array. Inserting or deleting beyond both ends of the array may require moving elements or reallocating space. + */ + +export class Deque { + /** + * The constructor initializes the capacity, elements array, and head and tail offsets of a data + * structure. + * @param {number} [capacity=10] - The `capacity` parameter represents the maximum number of elements + * that the data structure can hold. It is an optional parameter with a default value of 10. + */ + constructor(capacity: number = 10) { + this._capacity = capacity; + this._elements = new Array(this.capacity); + this._headOffset = Math.floor(capacity / 2); + this._tailOffset = this._headOffset; + } + + protected _elements: E[]; + + get elements() { + return this._elements; + } + + protected _headOffset: number; + + get headOffset() { + return this._headOffset; + } + + protected _tailOffset: number; + + get tailOffset() { + return this._tailOffset; + } + + protected _capacity: number; + + get capacity() { + return this._capacity; + } + + get size(): number { + return this.tailOffset - this.headOffset; + } + + /** + * Time Complexity: O(n) - Iterates over the input array once. + * Space Complexity: O(n) - Creates a new deque of size n. + */ + + /** + * Time Complexity: O(n) - Iterates over the input array once. + * Space Complexity: O(n) - Creates a new deque of size n. + * + * The `fromArray` function creates a new Deque instance from an array of elements. + * @param {E[]} data - The `data` parameter is an array of elements of type `E`. + * @returns a Deque object. + */ + static fromArray(data: E[]): Deque { + const list = new Deque(data.length); + for (const item of data) { + list.push(item); + } + return list; + } + + /** + * Time Complexity: Amortized O(1) - Generally constant time, but resizing when the deque is full leads to O(n). + * Space Complexity: O(n) - In worst case, resizing doubles the array size. + */ + + /** + * Time Complexity: O(1) + * Space Complexity: O(n) - In worst case, resizing doubles the array size. + * + * The push function adds an element to the end of an array-like data structure, resizing it if + * necessary. + * @param {E} element - The `element` parameter represents the value that you want to add to the data + * structure. + */ + push(element: E): void { + if (this.tailOffset === this.capacity) { + this._resize(); + } + this._elements[this.tailOffset] = element; + this._tailOffset++; + } + + /** + * Time Complexity: O(1) - Removes the last element. + * Space Complexity: O(1) - Operates in-place. + */ + + /** + * Time Complexity: O(1) - Removes the last element. + * Space Complexity: O(1) - Operates in-place. + * + * The `pop()` function removes and returns the last element from an array-like data structure. + * @returns The method is returning the element at the end of the array, which is the element that + * was most recently added. + */ + pop(): E | undefined { + if (this.size === 0) { + return undefined; + } + return this._elements[--this._tailOffset]; + } + + /** + * Time Complexity: Amortized O(1) - Similar to push, resizing leads to O(n). + * Space Complexity: O(n) - Due to potential resizing. + */ + + /** + * Time Complexity: O(1). + * Space Complexity: O(n) - Due to potential resizing. + * The unshift function adds an element to the beginning of an array-like data structure. + * @param {E} element - The "element" parameter represents the element that you want to add to the + * beginning of the array. + */ + unshift(element: E): void { + if (this.headOffset === 0) { + this._resize(); + } + this._elements[--this._headOffset] = element; + } + + /** + * Time Complexity: O(1) - Removes the first element. + * Space Complexity: O(1) - In-place operation. + */ + + /** + * Time Complexity: O(1) - Removes the first element. + * Space Complexity: O(1) - In-place operation. + * + * The shift() function removes and returns the first element from an array-like data structure. + * @returns The element at the front of the array is being returned. + */ + shift(): E | undefined { + if (this.size === 0) { + return undefined; + } + return this._elements[this._headOffset++]; + } + + /** + * Time Complexity: Amortized O(1) - Generally constant time, but resizing when the deque is full leads to O(n). + * Space Complexity: O(n) - In worst case, resizing doubles the array size. + */ + + /** + * Time Complexity: O(1) + * Space Complexity: O(n) - In worst case, resizing doubles the array size. + * + * The addLast function adds an element to the end of an array. + * @param {E} element - The element parameter represents the value that you want to add to the end of the + * data structure. + */ + addLast(element: E): void { + this.push(element); + } + + /** + * Time Complexity: O(1) - Removes the last element. + * Space Complexity: O(1) - Operates in-place. + */ + + /** + * Time Complexity: O(1) - Removes the last element. + * Space Complexity: O(1) - Operates in-place. + * + * The function "popLast" removes and returns the last element of an array. + * @returns The last element of the array is being returned. + */ + popLast(): E | undefined { + return this.pop(); + } + + /** + * Time Complexity: Amortized O(1) - Similar to push, resizing leads to O(n). + * Space Complexity: O(n) - Due to potential resizing. + */ + + /** + * Time Complexity: O(1). + * Space Complexity: O(n) - Due to potential resizing. + * + * The "addFirst" function adds an element to the beginning of an array. + * @param {E} element - The parameter "element" represents the element that you want to add to the + * beginning of the data structure. + */ + addFirst(element: E): void { + this.unshift(element); + } + + /** + * Time Complexity: O(1) - Removes the first element. + * Space Complexity: O(1) - In-place operation. + */ + + /** + * Time Complexity: O(1) - Removes the first element. + * Space Complexity: O(1) - In-place operation. + * + * The function "popFirst" removes and returns the first element of an array. + * @returns The method `popFirst()` is returning the first element of the array after removing it + * from the beginning. If the array is empty, it will return `undefined`. + */ + popFirst(): E | undefined { + return this.shift(); + } + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + */ + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + * + * The function returns the first element of an array if it exists, otherwise it returns undefined. + * @returns The method `getFirst()` is returning the first element of the `_elements` array if the + * size of the array is greater than 0. Otherwise, it returns `undefined`. + */ + getFirst(): E | undefined { + return this.size > 0 ? this._elements[this.headOffset] : undefined; + } + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + */ + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + * + * The `getLast` function returns the last element in an array-like data structure, or `undefined` if + * the structure is empty. + * @returns The method `getLast()` returns the last element in the `_elements` array, or `undefined` + * if the array is empty. + */ + getLast(): E | undefined { + if (this.size === 0) { + return undefined; + } + return this._elements[this._tailOffset - 1]; + } + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + */ + + /** + * Time Complexity: O(1) - Direct access to elements. + * Space Complexity: O(1) - No extra space used. + * + * The `getAt` function returns the element at a specified index in an array-like data structure, or + * `undefined` if the index is out of bounds. + * @param {number} index - The `index` parameter is a number that represents the position of the + * element we want to retrieve from the `_elements` array. + * @returns The method `getAt(index: number)` returns the element at the specified index if it + * exists, otherwise it returns `undefined`. + */ + getAt(index: number): E | undefined { + const actualIndex = this.headOffset + index; + if (actualIndex < this.headOffset || actualIndex >= this.tailOffset) { + return undefined; + } + return this._elements[actualIndex]; + } + + /** + * Time Complexity: O(n) - In worst case, all elements might need to be shifted. + * Space Complexity: O(n) - Resizing could happen. + */ + + /** + * Time Complexity: O(n) - In worst case, all elements might need to be shifted. + * Space Complexity: O(n) - Resizing could happen. + * + * The `insertAt` function inserts an element at a specified index in an array-like data structure, + * shifting existing elements to make room. + * @param {number} index - The index parameter is the position at which the element should be inserted + * in the array. It is of type number. + * @param {E} element - The element to be inserted at the specified index. + * @returns The method `insertAt` returns a boolean element. It returns `true` if the insertion was + * successful, and `false` if the index is out of bounds. + */ + insertAt(index: number, element: E): boolean { + if (index < 0 || index > this.size) { + return false; + } + + // If inserted at the head + if (index === 0) { + this.unshift(element); + return true; + } + + //If inserted at the end + if (index === this.size) { + this.push(element); + return true; + } + + // Intermediate insertion requires processing of arrays + this._ensureCapacityForInsert(); + const actualIndex = this.headOffset + index; + for (let i = this.tailOffset; i > actualIndex; i--) { + this._elements[i] = this._elements[i - 1]; + } + this._elements[actualIndex] = element; + this._tailOffset++; + return true; + } + + /** + * Time Complexity: O(n) - Elements may need to be shifted. + * Space Complexity: O(1) - Operates in-place. + */ + + /** + * Time Complexity: O(n) - Elements may need to be shifted. + * Space Complexity: O(1) - Operates in-place. + * + * The `deleteAt` function removes an element at a specified index from an array-like data structure + * and returns the removed element. + * @param {number} index - The index parameter represents the position of the element that needs to + * be deleted from the data structure. It is of type number. + * @returns The method `deleteAt(index: number)` returns the element that was removed from the data + * structure, or `undefined` if the index is out of bounds. + */ + deleteAt(index: number): E | undefined { + if (index < 0 || index >= this.size) { + return undefined; + } + + const actualIndex = this.headOffset + index; + const removedElement = this._elements[actualIndex]; + + for (let i = actualIndex; i < this.tailOffset - 1; i++) { + this._elements[i] = this._elements[i + 1]; + } + + this._tailOffset--; + return removedElement; + } + + /** + * Time Complexity: O(n) - May need to scan the entire deque. + * Space Complexity: O(1) - No extra space required. + */ + + /** + * Time Complexity: O(n) - May need to scan the entire deque. + * Space Complexity: O(1) - No extra space required. + * + * The function returns the index of a given element in an array-like data structure. + * @param {E} element - The parameter "element" represents the element that you want to find the index of + * in the array. + * @returns The method `indexOf` returns the index of the first occurrence of the specified element in + * the array. If the element is not found, it returns -1. + */ + indexOf(element: E): number { + for (let i = this.headOffset; i < this.tailOffset; i++) { + if (this._elements[i] === element) { + return i - this.headOffset; + } + } + return -1; + } + + /** + * Time Complexity: O(1) - Resets values to defaults. + * Space Complexity: O(1) - Directly changes existing properties. + */ + + /** + * Time Complexity: O(1) - Resets values to defaults. + * Space Complexity: O(1) - Directly changes existing properties. + * + * The clear function resets the elements array and head and tail offsets to their initial values. + */ + clear(): void { + this._elements = []; + this._headOffset = 0; + this._tailOffset = 0; + } + + /** + * Time Complexity: O(n) - Iterates over half the deque. + * Space Complexity: O(1) - In-place reversal. + */ + + /** + * Time Complexity: O(n) - Iterates over half the deque. + * Space Complexity: O(1) - In-place reversal. + * + * The reverse() function reverses the order of elements in an array. + */ + reverse(): void { + let start = this.headOffset; + let end = this.tailOffset - 1; + while (start < end) { + const temp = this._elements[start]; + this._elements[start] = this._elements[end]; + this._elements[end] = temp; + start++; + end--; + } + } + + /** + * Time Complexity: O(n) - Copies elements to a new array. + * Space Complexity: O(n) - New array of deque size. + */ + + /** + * Time Complexity: O(n) - Copies elements to a new array. + * Space Complexity: O(n) - New array of deque size. + * + * The toArray() function returns an array of elements from a specific index to the end of the array. + * @returns The `toArray()` method is returning an array of elements (`E[]`). The elements being + * returned are a slice of the `_elements` array starting from the `headOffset` index. + */ + toArray(): E[] { + return this._elements.slice(this.headOffset); + } + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + */ + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + * + * The `find` function iterates over the elements in a collection and returns the first element that + * satisfies a given condition. + * @param callback - The `callback` parameter is a function that takes an element of type `E` (the + * element type of the data structure) and returns a boolean element. It is used to determine whether a + * given element satisfies a certain condition. + * @returns The method `find` returns the first element in the array that satisfies the provided + * callback function. If no element satisfies the callback function, it returns `undefined`. + */ + find(callback: (element: E) => boolean): E | undefined { + for (let i = this.headOffset; i < this.tailOffset; i++) { + if (callback(this._elements[i])) { + return this._elements[i]; + } + } + return undefined; + } + + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + */ + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + * + * The forEach function iterates over the elements of an array-like object and executes a callback + * function for each element. + * @param callback - The callback parameter is a function that takes two arguments: element and index. + * The element argument represents the current element being iterated over, and the index argument + * represents the index of the current element in the iteration. + */ + forEach(callback: (element: E, index: number) => void): void { + let index = 0; + for (let i = this.headOffset; i < this.tailOffset; i++) { + callback(this._elements[i], index++); + } + } + + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + */ + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + * + * The `map` function takes a callback function and applies it to each element in the deque, + * returning a new deque with the transformed values. + * @param callback - The `callback` parameter is a function that takes an element of type `E` (the type + * of elements in the deque) and returns an element of type `U`. This function is applied to each + * element in the deque, and the resulting values are used to create a new deque of type ` + * @returns The `map` method is returning a new `Deque` object with the transformed values based on + * the provided callback function. + */ + map(callback: (element: E) => U): Deque { + const newList = new Deque(this.capacity); + for (let i = this.headOffset; i < this.tailOffset; i++) { + newList.push(callback(this._elements[i])); + } + return newList; + } + + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + */ + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + * + * The `filter` function creates a new Deque object with elements that pass a given callback + * function. + * @param callback - The `callback` parameter is a function that takes an element of type `E` (the + * element type of the `Deque`) as its argument and returns a boolean element. This function is used to + * determine whether an element should be included in the filtered `Deque` or not. + * @returns The `filter` method is returning a new `Deque` object that contains only the elements + * that satisfy the condition specified by the `callback` function. + */ + filter(callback: (element: E) => boolean): Deque { + const newList = new Deque(this.capacity); + for (let i = this.headOffset; i < this.tailOffset; i++) { + if (callback(this._elements[i])) { + newList.push(this._elements[i]); + } + } + return newList; + } + + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + */ + + /** + * Time Complexity: O(n) - Iterates through all elements. + * Space Complexity: O(n) - For methods like map and filter, which create a new deque. + * + * The `reduce` function iterates over the elements of a collection and applies a callback function + * to each element, accumulating a single element. + * @param callback - The `callback` parameter is a function that takes two arguments: `accumulator` + * and `element`. It is called for each element in the collection and is used to accumulate a single + * result. + * @param {U} initialValue - The `initialValue` parameter is the initial element of the accumulator. It + * is the element that will be passed as the first argument to the `callback` function when reducing + * the elements. + * @returns The `reduce` method is returning the final element of the accumulator after iterating over + * all the elements in the collection. + */ + reduce(callback: (accumulator: U, element: E) => U, initialValue: U): U { + let accumulator = initialValue; + for (let i = this.headOffset; i < this.tailOffset; i++) { + accumulator = callback(accumulator, this._elements[i]); + } + return accumulator; + } + + /** + * Time Complexity: O(1) - Checks the size property. + * Space Complexity: O(1) - No additional space. + */ + + /** + * Time Complexity: O(1) - Checks the size property. + * Space Complexity: O(1) - No additional space. + * + * The function checks if the size of an object is equal to zero and returns a boolean element. + * @returns A boolean element indicating whether the size of the object is 0 or not. + */ + isEmpty(): boolean { + return this.size === 0; + } + + /** + * Time Complexity: O(n) - Involves creating a new array and copying elements. + * Space Complexity: O(n) - New array is twice the size of the old one. + */ + + /** + * Time Complexity: O(n) - Involves creating a new array and copying elements. + * Space Complexity: O(n) - New array is twice the size of the old one. + * + * The `_resize` function is used to increase the capacity of an array by doubling it and + * repositioning the elements. + */ + protected _resize() { + const newCapacity = this.capacity * 2; + const newElements = new Array(newCapacity); + const newHeadOffset = Math.floor((newCapacity - this.size) / 2); + + for (let i = 0; i < this.size; i++) { + newElements[newHeadOffset + i] = this._elements[this.headOffset + i]; + } + + this._elements = newElements; + this._capacity = newCapacity; + this._headOffset = newHeadOffset; + this._tailOffset = newHeadOffset + this.size; + } + + /** + * The function `_ensureCapacityForInsert` checks if there is enough capacity in an array and resizes + * it if necessary. + * @returns The method is returning nothing (void). + */ + protected _ensureCapacityForInsert() { + if (this.tailOffset < this.capacity) { + return; + } + this._resize(); + } } -// O(1) time complexity of obtaining the value +// O(1) time complexity of obtaining the element // O(n) time complexity of adding at the beginning and the end // todo tested slowest one export class ObjectDeque { @@ -59,11 +679,11 @@ export class ObjectDeque { * Time Complexity: O(1) * Space Complexity: O(1) * - * The "addFirst" function adds a value to the beginning of an array-like data structure. - * @param {E} value - The `value` parameter represents the value that you want to add to the beginning of the data + * The "addFirst" function adds an element to the beginning of an array-like data structure. + * @param {E} element - The `element` parameter represents the element that you want to add to the beginning of the data * structure. */ - addFirst(value: E) { + addFirst(element: E) { if (this.size === 0) { const mid = Math.floor(this.capacity / 2); this._first = mid; @@ -71,7 +691,7 @@ export class ObjectDeque { } else { this._first--; } - this.nodes[this.first] = value; + this.nodes[this.first] = element; this._size++; } @@ -84,10 +704,10 @@ export class ObjectDeque { * Time Complexity: O(1) * Space Complexity: O(1) * - * The addLast function adds a value to the end of an array-like data structure. - * @param {E} value - The `value` parameter represents the value that you want to add to the end of the data structure. + * The addLast function adds an element to the end of an array-like data structure. + * @param {E} element - The `element` parameter represents the element that you want to add to the end of the data structure. */ - addLast(value: E) { + addLast(element: E) { if (this.size === 0) { const mid = Math.floor(this.capacity / 2); this._first = mid; @@ -95,7 +715,7 @@ export class ObjectDeque { } else { this._last++; } - this.nodes[this.last] = value; + this.nodes[this.last] = element; this._size++; } @@ -109,15 +729,15 @@ export class ObjectDeque { * Space Complexity: O(1) * * The function `popFirst()` removes and returns the first element in a data structure. - * @returns The value of the first element in the data structure. + * @returns The element of the first element in the data structure. */ popFirst() { if (!this.size) return; - const value = this.getFirst(); + const element = this.getFirst(); delete this.nodes[this.first]; this._first++; this._size--; - return value; + return element; } /** @@ -146,16 +766,16 @@ export class ObjectDeque { * Space Complexity: O(1) * * The `popLast()` function removes and returns the last element in a data structure. - * @returns The value that was removed from the data structure. + * @returns The element that was removed from the data structure. */ popLast() { if (!this.size) return; - const value = this.getLast(); + const element = this.getLast(); delete this.nodes[this.last]; this._last--; this._size--; - return value; + return element; } /** @@ -187,220 +807,17 @@ export class ObjectDeque { * @param {number} index - The index parameter is a number that represents the position of the element you want to * retrieve from the array. * @returns The element at the specified index in the `_nodes` array is being returned. If there is no element at that - * index, `null` is returned. + * index, `undefined` is returned. */ get(index: number) { - return this.nodes[this.first + index] || null; + return this.nodes[this.first + index] || undefined; } /** * The function checks if the size of a data structure is less than or equal to zero. - * @returns The method is returning a boolean value indicating whether the size of the object is less than or equal to 0. + * @returns The method is returning a boolean element indicating whether the size of the object is less than or equal to 0. */ isEmpty() { return this.size <= 0; } -} - -// O(1) time complexity of obtaining the value -// O(n) time complexity of adding at the beginning and the end -export class ArrayDeque { - protected _nodes: E[] = []; - - get nodes(): E[] { - return this._nodes; - } - - get size() { - return this.nodes.length; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function "addLast" adds a value to the end of an array. - * @param {E} value - The value parameter represents the value that you want to add to the end of the array. - * @returns The return value is the new length of the array after the value has been added. - */ - addLast(value: E) { - return this.nodes.push(value); - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The function "popLast" returns and removes the last element from an array, or returns null if the array is empty. - * @returns The method `popLast()` returns the last element of the `_nodes` array, or `null` if the array is empty. - */ - popLast(): E | null { - return this.nodes.pop() ?? null; - } - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - * - * The `popFirst` function removes and returns the first element from an array, or returns null if the array is empty. - * @returns The `popFirst()` function returns the first element of the `_nodes` array, or `null` if the array is - * empty. - */ - popFirst(): E | null { - return this.nodes.shift() ?? null; - } - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - * - * The function "addFirst" adds a value to the beginning of an array. - * @param {E} value - The value parameter represents the value that you want to add to the beginning of the array. - * @returns The return value of the `addFirst` function is the new length of the array `_nodes` after adding the - * `value` at the beginning. - */ - addFirst(value: E) { - return this.nodes.unshift(value); - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The `getFirst` function returns the first element of an array or null if the array is empty. - * @returns The function `getFirst()` is returning the first element (`E`) of the `_nodes` array. If the array is - * empty, it will return `null`. - */ - getFirst(): E | null { - return this.nodes[0] ?? null; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The `getLast` function returns the last element of an array or null if the array is empty. - * @returns The method `getLast()` returns the last element of the `_nodes` array, or `null` if the array is empty. - */ - getLast(): E | null { - return this.nodes[this.nodes.length - 1] ?? null; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The get function returns the element at the specified index in an array, or null if the index is out of bounds. - * @param {number} index - The index parameter is a number that represents the position of the element you want to - * retrieve from the array. - * @returns The method is returning the element at the specified index in the `_nodes` array. If the element exists, it - * will be returned. If the element does not exist (i.e., the index is out of bounds), `null` will be returned. - */ - get(index: number): E | null { - return this.nodes[index] ?? null; - } - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(1) - * Space Complexity: O(1) - * - * The set function assigns a value to a specific index in an array. - * @param {number} index - The index parameter is a number that represents the position of the element in the array - * that you want to set a new value for. - * @param {E} value - The value parameter represents the new value that you want to set at the specified index in the - * _nodes array. - * @returns The value that is being set at the specified index in the `_nodes` array. - */ - set(index: number, value: E) { - return (this.nodes[index] = value); - } - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - * - * The insert function adds a value at a specified index in an array. - * @param {number} index - The index parameter specifies the position at which the value should be inserted in the - * array. It is a number that represents the index of the array where the value should be inserted. The index starts - * from 0, so the first element of the array has an index of 0, the second element has - * @param {E} value - The value parameter represents the value that you want to insert into the array at the specified - * index. - * @returns The splice method returns an array containing the removed elements, if any. In this case, since no elements - * are being removed, an empty array will be returned. - */ - insert(index: number, value: E) { - return this.nodes.splice(index, 0, value); - } - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - */ - - /** - * Time Complexity: O(n) - * Space Complexity: O(1) - * - * The delete function removes an element from an array at a specified index. - * @param {number} index - The index parameter specifies the position of the element to be removed from the array. It - * is a number that represents the index of the element to be removed. - * @returns The method is returning an array containing the removed element. - */ - delete(index: number) { - return this.nodes.splice(index, 1); - } - - /** - * The function checks if an array called "_nodes" is empty. - * @returns The method `isEmpty()` is returning a boolean value. It returns `true` if the length of the `_nodes` array - * is 0, indicating that the array is empty. Otherwise, it returns `false`. - */ - isEmpty() { - return this.nodes.length === 0; - } -} +} \ No newline at end of file diff --git a/test/performance/data-structures/linked-list/doubly-linked-list.test.ts b/test/performance/data-structures/linked-list/doubly-linked-list.test.ts index b52fc6d..042a4fa 100644 --- a/test/performance/data-structures/linked-list/doubly-linked-list.test.ts +++ b/test/performance/data-structures/linked-list/doubly-linked-list.test.ts @@ -7,6 +7,25 @@ import { isCompetitor } from '../../../config'; const suite = new Benchmark.Suite(); const { LINEAR } = magnitude; +suite.add(`${LINEAR.toLocaleString()} push`, () => { + const list = new DoublyLinkedList(); + + for (let i = 0; i < LINEAR; i++) { + list.push(i); + } +}); + + +if (isCompetitor) { + suite.add(`${LINEAR.toLocaleString()} CPT push`, () => { + const list = new CLinkedList(); + + for (let i = 0; i < LINEAR; i++) { + list.pushBack(i); + } + }); +} + suite.add(`${LINEAR.toLocaleString()} unshift`, () => { const list = new DoublyLinkedList(); @@ -14,6 +33,7 @@ suite.add(`${LINEAR.toLocaleString()} unshift`, () => { list.unshift(i); } }); + if (isCompetitor) { suite.add(`${LINEAR.toLocaleString()} CPT unshift`, () => { const list = new CLinkedList(); @@ -23,6 +43,7 @@ if (isCompetitor) { } }); } + suite .add(`${LINEAR.toLocaleString()} unshift & shift`, () => { const list = new DoublyLinkedList(); diff --git a/test/performance/data-structures/queue/deque.test.ts b/test/performance/data-structures/queue/deque.test.ts index dddad4e..0019241 100644 --- a/test/performance/data-structures/queue/deque.test.ts +++ b/test/performance/data-structures/queue/deque.test.ts @@ -28,3 +28,30 @@ suite.add(`${LINEAR.toLocaleString()} shift`, () => { deque.shift(); } }); + +suite.add(`${LINEAR.toLocaleString()} push`, () => { + const list = new Deque(); + + for (let i = 0; i < LINEAR; i++) { + list.push(i); + } +}); +suite.add(`${LINEAR.toLocaleString()} push & pop`, () => { + const list = new Deque(); + + for (let i = 0; i < LINEAR; i++) list.push(i); + for (let i = 0; i < LINEAR; i++) list.pop(); + +}); +suite.add(`${LINEAR.toLocaleString()} push & shift`, () => { + const list = new Deque(); + + for (let i = 0; i < LINEAR; i++) list.push(i); + for (let i = 0; i < LINEAR; i++) list.shift(); +}); +suite.add(`${LINEAR.toLocaleString()} unshift & shift`, () => { + const list = new Deque(); + + for (let i = 0; i < LINEAR; i++) list.unshift(i); + for (let i = 0; i < LINEAR; i++) list.shift(); +}); diff --git a/test/unit/data-structures/queue/deque.test.ts b/test/unit/data-structures/queue/deque.test.ts index ca30dd4..d4e7fef 100644 --- a/test/unit/data-structures/queue/deque.test.ts +++ b/test/unit/data-structures/queue/deque.test.ts @@ -1,4 +1,4 @@ -import { ArrayDeque, Deque, ObjectDeque } from '../../../../src'; +import { Deque, ObjectDeque } from '../../../../src'; import { bigO } from '../../../utils'; import { isDebugTest } from '../../../config'; @@ -93,43 +93,6 @@ describe('Deque Tests', () => { // Add more test cases as needed }); - - // Test cases for the ArrayDeque class - describe('ArrayDeque', () => { - let arrayDeque: ArrayDeque; - - beforeEach(() => { - arrayDeque = new ArrayDeque(); - }); - - it('should add elements at the beginning and end', () => { - arrayDeque.addFirst(1); - arrayDeque.addLast(2); - expect(arrayDeque.getFirst()).toBe(1); - expect(arrayDeque.getLast()).toBe(2); - }); - - it('should delete elements from the beginning and end', () => { - arrayDeque.addFirst(1); - arrayDeque.addLast(2); - arrayDeque.popFirst(); - arrayDeque.popLast(); - expect(arrayDeque.isEmpty()).toBe(true); - }); - - it('should handle edge case when removing from an empty deque', () => { - const result = arrayDeque.popFirst(); - expect(result).toBeNull(); - }); - - it('should correctly report its size', () => { - arrayDeque.addFirst(1); - arrayDeque.addLast(2); - expect(arrayDeque.size).toBe(2); - }); - - // Add more test cases as needed - }); }); describe('Deque Performance Test', () => { @@ -208,105 +171,6 @@ describe('Deque', () => { }); }); -describe('ArrayDeque', () => { - let deque: ArrayDeque; - - beforeEach(() => { - deque = new ArrayDeque(); - }); - - it('should initialize an empty deque', () => { - expect(deque.size).toBe(0); - expect(deque.isEmpty()).toBe(true); - }); - - it('should add elements to the front and back', () => { - deque.addFirst(1); - deque.addLast(2); - - expect(deque.size).toBe(2); - expect(deque.getFirst()).toBe(1); - expect(deque.getLast()).toBe(2); - }); - - it('should remove elements from the front and back', () => { - deque.addFirst(1); - deque.addLast(2); - - const firstElement = deque.popFirst(); - const lastElement = deque.popLast(); - - expect(deque.size).toBe(0); - expect(firstElement).toBe(1); - expect(lastElement).toBe(2); - }); - - it('should get elements by index', () => { - deque.addLast(1); - deque.addLast(2); - deque.addLast(3); - - expect(deque.get(0)).toBe(1); - expect(deque.get(1)).toBe(2); - expect(deque.get(2)).toBe(3); - }); - - it('should return null for out-of-bounds index', () => { - expect(deque.get(0)).toBe(null); - expect(deque.get(1)).toBe(null); - expect(deque.get(-1)).toBe(null); - }); - - it('should check if the deque is empty', () => { - expect(deque.isEmpty()).toBe(true); - - deque.addLast(1); - expect(deque.isEmpty()).toBe(false); - - deque.popFirst(); - expect(deque.isEmpty()).toBe(true); - }); - - it('should set elements at a specific index', () => { - deque.addLast(1); - deque.addLast(2); - deque.addLast(3); - - deque.set(1, 4); - - expect(deque.get(0)).toBe(1); - expect(deque.get(1)).toBe(4); - expect(deque.get(2)).toBe(3); - }); - - it('should insert elements at a specific index', () => { - deque.addLast(1); - deque.addLast(2); - deque.addLast(3); - - deque.insert(1, 4); - - expect(deque.size).toBe(4); - expect(deque.get(0)).toBe(1); - expect(deque.get(1)).toBe(4); - expect(deque.get(2)).toBe(2); - expect(deque.get(3)).toBe(3); - }); - - it('should delete elements at a specific index', () => { - deque.addLast(1); - deque.addLast(2); - deque.addLast(3); - - const deletedElement = deque.delete(1); - - expect(deque.size).toBe(2); - expect(deletedElement[0]).toBe(2); - expect(deque.get(0)).toBe(1); - expect(deque.get(1)).toBe(3); - }); -}); - describe('ObjectDeque', () => { let deque: ObjectDeque;