diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..6ff0bf9
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 3a0dbdb..7d8a9a1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "data-structure-typed",
- "version": "1.15.2",
+ "version": "1.16.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "data-structure-typed",
- "version": "1.15.2",
+ "version": "1.16.1",
"license": "MIT",
"devDependencies": {
"@types/jest": "^29.5.3",
diff --git a/package.json b/package.json
index a4f27aa..1c7c87f 100644
--- a/package.json
+++ b/package.json
@@ -55,7 +55,5 @@
"ts-jest": "^29.1.1",
"typedoc": "^0.24.8",
"typescript": "^4.9.5"
- },
- "dependencies": {
}
}
diff --git a/src/data-structures/linked-list/singly-linked-list.ts b/src/data-structures/linked-list/singly-linked-list.ts
index 7527fac..beb80a7 100644
--- a/src/data-structures/linked-list/singly-linked-list.ts
+++ b/src/data-structures/linked-list/singly-linked-list.ts
@@ -1,757 +1,326 @@
-/**
- * data-structure-typed
- *
- * @author Tyler Zeng
- * @copyright Copyright (c) 2022 Tyler Zeng
- * @license MIT License
- */
+export class SinglyLinkedListNode {
-
-/* The SinglyLinkedListNode class represents a node in a singly linked list and provides methods for inserting, removing,
-and accessing nodes. */
-export class SinglyLinkedListNode {
- constructor(val: NodeVal, prev?: SinglyLinkedListNode | null, next?: SinglyLinkedListNode | null, list?: SinglyLinkedList | null) {
- this._val = val;
- this._prev = prev || null;
- this._next = next || null;
- this._list = list || null;
- }
-
- protected _val: NodeVal;
-
- get val(): NodeVal {
+ private _val: T;
+ get val(): T {
return this._val;
}
- set val(value: NodeVal) {
+ set val(value: T) {
this._val = value;
}
- protected _prev: SinglyLinkedListNode | null;
-
- get prev(): SinglyLinkedListNode | null {
- return this._prev;
- }
-
- set prev(value: SinglyLinkedListNode | null) {
- this._prev = value;
- }
-
- protected _next: SinglyLinkedListNode | null
-
- get next(): SinglyLinkedListNode | null {
+ private _next: SinglyLinkedListNode | null;
+ get next(): SinglyLinkedListNode | null {
return this._next;
}
- set next(value: SinglyLinkedListNode | null) {
+ set next(value: SinglyLinkedListNode | null) {
this._next = value;
}
-
- protected _list: SinglyLinkedList | null
-
- get list(): SinglyLinkedList | null {
- return this._list;
- }
-
- set list(value: SinglyLinkedList | null) {
- this._list = value;
- }
-
- get index() {
- if (!this.list) {
- return undefined;
- }
- return this.list.findIndex((value) => value === this.val);
- }
-
- /**
- * The `insertBefore` function inserts a new node with the given value before the current node in a singly linked list.
- * @param {NodeVal} val - The parameter "val" is of type "NodeVal". It represents the value of the node that you want
- * to insert before the current node.
- * @returns The method is returning a SinglyLinkedList.
- */
- insertBefore(val: NodeVal): SinglyLinkedList {
- return this.list !== null
- ? this.list.insertBefore(this, val)
- : new SinglyLinkedList(val, this.val);
- }
-
- /**
- * The function inserts a new node with the given value after the current node in a singly linked list.
- * @param {NodeVal} val - The parameter `val` is the value of the node that you want to insert after the current node.
- * @returns The method is returning a SinglyLinkedList.
- */
- insertAfter(val: NodeVal): SinglyLinkedList {
- return this.list !== null
- ? this.list.insertAfter(this, val)
- : new SinglyLinkedList(this.val, val);
- }
-
- /**
- * The `remove()` function removes a node from a singly linked list.
- * @returns The remove() method is returning a SinglyLinkedListNode object.
- */
- remove(): SinglyLinkedListNode {
- if (this.list === null) {
- throw new ReferenceError('Node does not belong to any list');
- }
- return this.list.removeNode(this);
+ constructor(val: T) {
+ this._val = val;
+ this._next = null;
}
}
-export class SinglyLinkedList {
+export class SinglyLinkedList {
- /**
- * The constructor initializes a linked list with the given arguments as nodes.
- * @param {NodeVal[]} args - args is a rest parameter that allows the constructor to accept an arbitrary number of
- * arguments of type NodeVal.
- */
- constructor(...args: NodeVal[]) {
- this._head = null;
- this._tail = null;
- this._size = 0;
-
- for (let i = 0; i < arguments.length; i++) {
- this.append(args[i]);
- }
- }
-
- protected _head: SinglyLinkedListNode | null;
-
- get head(): SinglyLinkedListNode | null {
+ private _head: SinglyLinkedListNode | null;
+ get head(): SinglyLinkedListNode | null {
return this._head;
}
- set head(value: SinglyLinkedListNode | null) {
+ set head(value: SinglyLinkedListNode | null) {
this._head = value;
}
- protected _tail: SinglyLinkedListNode | null;
-
- get tail(): SinglyLinkedListNode | null {
+ private _tail: SinglyLinkedListNode | null;
+ get tail(): SinglyLinkedListNode | null {
return this._tail;
}
- set tail(value: SinglyLinkedListNode | null) {
+ set tail(value: SinglyLinkedListNode | null) {
this._tail = value;
}
- protected _size: number;
-
- get size(): number {
- return this._size;
+ private _length: number;
+ get length(): number {
+ return this._length;
}
- set size(value: number) {
- this._size = value;
+ protected set length(value: number) {
+ this._length = value;
}
- /**
- * The `from` function in TypeScript creates a new SinglyLinkedList instance from an iterable object.
- * @param iterable - The `iterable` parameter is an object that can be iterated over, such as an array or a string. It
- * contains a collection of elements of type `T`.
- * @returns The method is returning a new instance of the SinglyLinkedList class.
- */
- static from(iterable: Iterable): SinglyLinkedList {
- return new SinglyLinkedList(...iterable);
+ constructor() {
+ this._head = null;
+ this._tail = null;
+ this._length = 0;
}
- /**
- * The `get` function returns the value of a node at a given index in a data structure.
- * @param {number} index - The index parameter is a number that represents the position of the node in the data
- * structure.
- * @returns The method is returning the value of the node at the specified index if the node exists, otherwise it
- * returns undefined.
- */
- get(index: number): NodeVal | undefined {
- const node = this.getNode(index);
- return node !== undefined ? node.val : undefined;
- }
-
- /**
- * The function `getNode` returns the node at a given index in a singly linked list.
- * @param {number} index - The `index` parameter is a number that represents the position of the node we want to
- * retrieve from the linked list.
- * @returns a SinglyLinkedListNode object or undefined.
- */
- getNode(index: number): SinglyLinkedListNode | undefined {
- if (this.head === null || index < 0 || index >= this.size) {
- return undefined;
+ push(data: T): void {
+ const newNode = new SinglyLinkedListNode(data);
+ if (!this.head) {
+ this.head = newNode;
+ this.tail = newNode;
+ } else {
+ this.tail!.next = newNode;
+ this.tail = newNode;
}
- const asc = index < this.size / 2;
- const stopAt = asc ? index : this.size - index - 1;
- const nextNode = asc ? 'next' : 'prev';
- let currentNode = asc ? this.head : this.tail;
- // TODO after no-non-null-assertion not ensure the logic
- for (let currentIndex = 0; currentIndex < stopAt; currentIndex++) {
- if (currentNode) {
- currentNode = currentNode[nextNode];
- }
- }
- return currentNode || undefined;
+ this.length++;
}
- /**
- * The function `findNodeIndex` searches for a node in a singly linked list that satisfies a given condition and
- * returns its index and the node itself.
- * @param callbackFn - The callbackFn parameter is a function that takes three arguments: data, index, and list. It is
- * used to determine whether a node in the singly linked list matches a certain condition. The function should return a
- * boolean value indicating whether the condition is met for the given node.
- * @returns The function `findNodeIndex` returns an object with two properties: `node` and `index`. The `node` property
- * contains the node that matches the condition specified in the `callbackFn` function, and the `index` property
- * contains the index of that node in the linked list. If no node matches the condition, the function returns
- * `undefined`.
- */
- findNodeIndex(callbackFn: (
- data: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => boolean): ({
- node: SinglyLinkedListNode,
- index: number,
- }) | undefined {
- let currentIndex = 0;
- let currentNode = this.head;
- while (currentNode) {
- if (callbackFn(currentNode.val, currentIndex, this)) {
- return {
- index: currentIndex,
- node: currentNode,
- };
+ pop(): T | undefined {
+ if (!this.head) return undefined;
+ if (this.head === this.tail) {
+ const val = this.head.val;
+ this.head = null;
+ this.tail = null;
+ this.length--;
+ return val;
+ }
+
+ let current = this.head;
+ while (current.next !== this.tail) {
+ current = current.next!;
+ }
+ const val = this.tail!.val;
+ current.next = null;
+ this.tail = current;
+ this.length--;
+ return val;
+ }
+
+ get(index: number): T | undefined {
+ if (index < 0 || index >= this.length) return undefined;
+ let current = this.head;
+ for (let i = 0; i < index; i++) {
+ current = current!.next;
+ }
+ return current!.val;
+ }
+
+ remove(index: number): T | undefined {
+ if (index < 0 || index >= this.length) return undefined;
+ if (index === 0) return this.shift();
+ if (index === this.length - 1) return this.pop();
+
+ let prevNode = this.getNodeAtIndex(index - 1);
+ const removedNode = prevNode!.next;
+ prevNode!.next = removedNode!.next;
+ this.length--;
+ return removedNode!.val;
+ }
+
+ private getNodeAtIndex(index: number): SinglyLinkedListNode | null {
+ let current = this.head;
+ for (let i = 0; i < index; i++) {
+ current = current!.next;
+ }
+ return current;
+ }
+
+ shift(): T | undefined {
+ if (!this.head) return undefined;
+ const removedNode = this.head;
+ this.head = this.head.next;
+ this.length--;
+ return removedNode.val;
+ }
+
+ unshift(val: T): void {
+ const newNode = new SinglyLinkedListNode(val);
+ if (!this.head) {
+ this.head = newNode;
+ this.tail = newNode;
+ } else {
+ newNode.next = this.head;
+ this.head = newNode;
+ }
+ this.length++;
+ }
+
+ insert(index: number, val: T): boolean {
+ if (index < 0 || index > this.length) return false;
+ if (index === 0) {
+ this.unshift(val);
+ return true;
+ }
+ if (index === this.length) {
+ this.push(val);
+ return true;
+ }
+
+ const newNode = new SinglyLinkedListNode(val);
+ const prevNode = this.getNodeAtIndex(index - 1);
+ newNode.next = prevNode!.next;
+ prevNode!.next = newNode;
+ this.length++;
+ return true;
+ }
+
+
+ getLength(): number {
+ return this.length;
+ }
+
+ isEmpty(): boolean {
+ return this.length === 0;
+ }
+
+ clear(): void {
+ this.head = null;
+ this.tail = null;
+ this.length = 0;
+ }
+
+ toArray(): T[] {
+ const array: T[] = [];
+ let current = this.head;
+ while (current) {
+ array.push(current.val);
+ current = current.next;
+ }
+ return array;
+ }
+
+ reverse(): void {
+ if (!this.head || this.head === this.tail) return;
+
+ let prev: SinglyLinkedListNode | null = null;
+ let current: SinglyLinkedListNode | null = this.head;
+ let next: SinglyLinkedListNode | null = null;
+
+ while (current) {
+ next = current.next;
+ current.next = prev;
+ prev = current;
+ current = next;
+ }
+
+ [this.head, this.tail] = [this.tail!, this.head!];
+ }
+
+
+ find(callback: (val: T) => boolean): T | undefined {
+ let current = this.head;
+ while (current) {
+ if (callback(current.val)) {
+ return current.val;
}
- currentNode = currentNode.next;
- currentIndex += 1;
+ current = current.next;
}
return undefined;
}
- /**
- * The findNode function searches for a node in a singly linked list based on a given callback function.
- * @param callbackFn - A callback function that takes three parameters: data, index, and list. It returns a boolean
- * value indicating whether the current node matches the desired criteria.
- * @returns The function `findNode` returns a `SinglyLinkedListNode` if a node satisfying the condition
- * specified by the `callbackFn` is found in the linked list. If no such node is found, it returns `undefined`.
- */
- findNode(callbackFn: (
- data: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => boolean): SinglyLinkedListNode | undefined {
- const nodeIndex = this.findNodeIndex(callbackFn);
- return nodeIndex !== undefined ? nodeIndex.node : undefined;
- }
+ removeByValue(value: T): boolean {
+ let current = this.head;
+ let prev = null;
- /**
- * The `find` function in TypeScript searches for a node in a singly linked list based on a given callback function and
- * returns the value of the found node.
- * @param callbackFn - A callback function that takes three parameters: data, index, and list. It returns a boolean
- * value indicating whether the condition is met for a particular node in the linked list.
- * @returns The method `find` returns the `NodeVal` value of the first node in the linked list that satisfies the
- * condition specified by the `callbackFn` function. If no node satisfies the condition, it returns `undefined`.
- */
- find(callbackFn: (
- data: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => boolean): NodeVal | undefined {
- const nodeIndex = this.findNodeIndex(callbackFn);
- return nodeIndex !== undefined ? nodeIndex.node.val : undefined;
- }
-
- /**
- * The findIndex function returns the index of the first node in a singly linked list that satisfies a given condition,
- * or -1 if no such node is found.
- * @param callbackFn - A callback function that takes three parameters: data, index, and list. It returns a boolean
- * value indicating whether the condition is met for a particular node in the singly linked list.
- * @returns The method `findIndex` returns a number.
- */
- findIndex(callbackFn: (
- data: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => boolean): number {
- const nodeIndex = this.findNodeIndex(callbackFn);
- return nodeIndex !== undefined ? nodeIndex.index : -1;
- }
-
- /* The above code is a comment in TypeScript. It is using the triple hash symbol ( */
- append(...args: NodeVal[]): SinglyLinkedList {
- for (const val of args) {
- const node = new SinglyLinkedListNode(val, this.tail, null, this);
- if (this.head === null) {
- this.head = node;
- }
- if (this.tail !== null) {
- this.tail.next = node;
- }
- this.tail = node;
- this.size += 1;
- }
- return this;
- }
-
- /**
- * The push function appends multiple NodeVal objects to a data structure and returns the new size of the data
- * structure.
- * @param {NodeVal[]} args - args is a rest parameter of type NodeVal[]. It allows the function to accept any number
- * of arguments of type NodeVal.
- * @returns The size of the data structure after the nodes are appended.
- */
- push(...args: NodeVal[]): number {
- this.append(...args);
- return this.size;
- }
-
- /**
- * The `prepend` function adds new nodes to the beginning of a singly linked list.
- * @param {NodeVal[]} args - An array of NodeVal objects.
- * @returns The `prepend` method is returning the updated `SinglyLinkedList` object.
- */
- prepend(...args: NodeVal[]): SinglyLinkedList {
- const reverseArgs = Array.from(args).reverse();
- for (const val of reverseArgs) {
- const node = new SinglyLinkedListNode(val, null, this.head, this);
- if (this.tail === null) {
- this.tail = node;
- }
- if (this.head !== null) {
- this.head.prev = node;
- }
- this.head = node;
- this.size += 1;
- }
- return this;
- }
-
- /**
- * The `insertAt` function inserts a value at a specified index in a singly linked list.
- * @param {number} index - The index parameter is a number that represents the position at which the new node should be
- * inserted in the linked list.
- * @param {NodeVal} val - The `val` parameter represents the value of the node that you want to insert into the linked
- * list.
- * @returns The method `insertAt` returns the updated `SinglyLinkedList` object.
- */
- insertAt(index: number, val: NodeVal): SinglyLinkedList {
- if (this.head === null) {
- return this.append(val);
- }
- if (index <= 0) {
- return this.prepend(val);
- }
-
- let currentNode = this.head;
- let currentIndex = 0;
- while (currentIndex < index - 1 && currentNode.next !== null) {
- currentIndex += 1;
- currentNode = currentNode.next;
- }
- currentNode.insertAfter(val);
- return this;
- }
-
- /**
- * The removeNode function removes a node from a singly linked list and updates the head, tail, and size properties
- * accordingly.
- * @param node - The `node` parameter is of type `SinglyLinkedListNode`, which represents a node in a singly
- * linked list.
- * @returns the removed node.
- */
- removeNode(node: SinglyLinkedListNode): SinglyLinkedListNode {
- if (node.list !== this) {
- throw new ReferenceError('Node does not belong to this list');
- }
-
- if (node.prev !== null) {
- node.prev.next = node.next;
- }
-
- if (node.next !== null) {
- node.next.prev = node.prev;
- }
-
- if (this.head === node) {
- this.head = node.next;
- }
-
- if (this.tail === node) {
- this.tail = node.prev;
- }
-
- this.size -= 1;
- node.next = null;
- node.prev = null;
- node.list = null;
- return node;
- }
-
- /**
- * The `removeAt` function removes a node at a specified index from a singly linked list.
- * @param {number} index - The index parameter is a number that represents the position of the node to be removed in
- * the singly linked list.
- * @returns The method `removeAt` returns a `SinglyLinkedListNode` if the node at the specified index is
- * found and removed successfully. If the node is not found, it returns `undefined`.
- */
- removeAt(index: number): SinglyLinkedListNode | undefined {
- const node = this.getNode(index);
- return node !== undefined ? this.removeNode(node) : undefined;
- }
-
- /**
- * The `insertBefore` function inserts a new node with a given value before a specified reference node in a singly
- * linked list.
- * @param referenceNode - The referenceNode parameter is the node in the linked list before which the new node will be
- * inserted.
- * @param {NodeVal} val - The value of the new node that will be inserted before the reference node.
- * @returns The method is returning the updated SinglyLinkedList object.
- */
- insertBefore(
- referenceNode: SinglyLinkedListNode,
- val: NodeVal,
- ): SinglyLinkedList {
- const node = new SinglyLinkedListNode(val, referenceNode.prev, referenceNode, this);
- if (referenceNode.prev === null) {
- this.head = node;
- }
- if (referenceNode.prev !== null) {
- referenceNode.prev.next = node;
- }
- referenceNode.prev = node;
- this.size += 1;
- return this;
- }
-
- /**
- * The `sort` function uses the quicksort algorithm to sort the elements of a singly linked list based on a provided
- * comparison function.
- * @param start - The `start` parameter is the starting node of the sublist that needs to be sorted.
- * @param end - The `end` parameter is a reference to the last node in the linked list. It is used as the pivot element
- * for the quicksort algorithm.
- * @returns The `sort` method is returning the sorted `SinglyLinkedList` object.
- */
- sort(compare: (a: NodeVal, b: NodeVal) => boolean): SinglyLinkedList {
- if (this.head === null || this.tail === null) {
- return this;
- }
- if (this.size < 2) {
- return this;
- }
-
- const quicksort = (
- start: SinglyLinkedListNode,
- end: SinglyLinkedListNode,
- ) => {
- if (start === end) {
- return;
- }
- const pivotData = end.val;
- let current: SinglyLinkedListNode | null = start;
- let split: SinglyLinkedListNode = start;
- while (current && current !== end) {
- const sort = compare(current.val, pivotData);
- if (sort) {
- if (current !== split) {
- const temp = split.val;
- split.val = current.val;
- current.val = temp;
+ while (current) {
+ if (current.val === value) {
+ if (prev === null) {
+ this.head = current.next;
+ if (current === this.tail) {
+ this.tail = null;
}
- // TODO after no-non-null-assertion not ensure the logic
- if (split.next) {
- split = split.next;
+ } else {
+ prev.next = current.next;
+ if (current === this.tail) {
+ this.tail = prev;
}
}
- current = current.next;
+ this.length--;
+ return true;
}
- end.val = split.val;
- split.val = pivotData;
+ prev = current;
+ current = current.next;
+ }
- if (start.next === end.prev) {
- return;
+ return false;
+ }
+
+ indexOf(value: T): number {
+ let index = 0;
+ let current = this.head;
+
+ while (current) {
+ if (current.val === value) {
+ return index;
}
+ index++;
+ current = current.next;
+ }
- if (split.prev && split !== start) {
- quicksort(start, split.prev);
+ return -1;
+ }
+
+ findNodeByValue(value: T): SinglyLinkedListNode | null {
+ let current = this.head;
+
+ while (current) {
+ if (current.val === value) {
+ return current;
}
- if (split.next && split !== end) {
- quicksort(split.next, end);
+ current = current.next;
+ }
+
+ return null;
+ }
+
+ insertBefore(existingValue: T, newValue: T): boolean {
+ if (!this.head) {
+ return false;
+ }
+
+ if (this.head.val === existingValue) {
+ this.unshift(newValue);
+ return true;
+ }
+
+ let current = this.head;
+ while (current.next) {
+ if (current.next.val === existingValue) {
+ const newNode = new SinglyLinkedListNode(newValue);
+ newNode.next = current.next;
+ current.next = newNode;
+ this.length++;
+ return true;
}
- };
-
- quicksort(this.head, this.tail);
- return this;
- }
-
- /**
- * The `insertAfter` function inserts a new node with a given value after a specified reference node in a singly linked
- * list.
- * @param referenceNode - The referenceNode parameter is the node after which the new node will be inserted.
- * @param {NodeVal} val - The value of the new node that will be inserted after the reference node.
- * @returns The `insertAfter` method is returning the updated `SinglyLinkedList` object.
- */
- insertAfter(
- referenceNode: SinglyLinkedListNode,
- val: NodeVal,
- ): SinglyLinkedList {
- const node = new SinglyLinkedListNode(val, referenceNode, referenceNode.next, this);
- if (referenceNode.next === null) {
- this.tail = node;
- }
- if (referenceNode.next !== null) {
- referenceNode.next.prev = node;
- }
- referenceNode.next = node;
- this.size += 1;
- return this;
- }
-
- /**
- * The `shift()` function removes and returns the first element from a linked list.
- * @returns The `shift()` method is returning a value of type `NodeVal` or `undefined`.
- */
- shift(): NodeVal | undefined {
- return this.removeFromAnyEnd(this.head);
- }
-
- /**
- * The `pop()` function removes and returns the last element from a linked list.
- * @returns The `pop()` method is returning a value of type `NodeVal` or `undefined`.
- */
- pop(): NodeVal | undefined {
- return this.removeFromAnyEnd(this.tail);
- }
-
- /**
- * The merge function merges two singly linked lists by updating the next and prev pointers, as well as the head, tail,
- * and size properties.
- * @param list - The parameter "list" is a SinglyLinkedList object that contains nodes with data of type NodeVal.
- */
- merge(list: SinglyLinkedList): void {
- if (this.tail !== null) {
- this.tail.next = list.head;
- }
- if (list.head !== null) {
- list.head.prev = this.tail;
- }
- this.head = this.head || list.head;
- this.tail = list.tail || this.tail;
- this.size += list.size;
- list.size = this.size;
- list.head = this.head;
- list.tail = this.tail;
- }
-
- /**
- * The clear() function resets the linked list by setting the head and tail to null and the size to 0.
- * @returns The "this" object is being returned.
- */
- clear() {
- this.head = null;
- this.tail = null;
- this.size = 0;
- return this;
- }
-
- /**
- * The `slice` function returns a new SinglyLinkedList containing a portion of the original list, starting from the
- * specified index and ending at the optional end index.
- * @param {number} start - The `start` parameter is a number that represents the index at which to start slicing the
- * linked list.
- * @param {number} [end] - The `end` parameter is an optional number that specifies the index at which to end the
- * slicing. If no value is provided for `end`, or if the provided value is less than the `start` index, the slicing
- * will continue until the end of the list.
- * @returns a new SinglyLinkedList containing the sliced elements from the original list.
- */
- slice(start: number, end?: number): SinglyLinkedList {
- const list = new SinglyLinkedList();
- let finish = end;
-
- if (this.head === null || this.tail === null) {
- return list;
- }
- if (finish === undefined || finish < start) {
- finish = this.size;
+ current = current.next;
}
- let head: SinglyLinkedListNode | null | undefined = this.getNode(start);
- for (let i = 0; i < finish - start && head !== null && head !== undefined; i++) {
- list.append(head.val);
- head = head.next;
- }
- return list;
+ return false;
}
- /**
- * The reverse() function reverses the order of nodes in a singly linked list.
- * @returns The reverse() method is returning the reversed SinglyLinkedList.
- */
- reverse(): SinglyLinkedList {
- let currentNode = this.head;
- while (currentNode) {
- const next = currentNode.next;
- currentNode.next = currentNode.prev;
- currentNode.prev = next;
- currentNode = currentNode.prev;
- }
- const tail = this.tail;
- this.tail = this.head;
- this.head = tail;
- return this;
- }
+ insertAfter(existingValue: T, newValue: T): boolean {
+ const existingNode = this.findNodeByValue(existingValue);
- /**
- * The `forEach` function iterates over a singly linked list and applies a callback function to each node, either in
- * forward or reverse order.
- * @param callbackFn - A callback function that will be called for each element in the linked list. It takes three
- * parameters:
- * @param [reverse=false] - A boolean value indicating whether to iterate over the linked list in reverse order. If set
- * to true, the iteration will start from the tail of the linked list and move towards the head. If set to false
- * (default), the iteration will start from the head and move towards the tail.
- */
- forEach(callbackFn: (
- data: any,
- index: number,
- list: SinglyLinkedList,
- ) => any, reverse = false): void {
- let currentIndex = reverse ? this.size - 1 : 0;
- let currentNode = reverse ? this.tail : this.head;
- const modifier = reverse ? -1 : 1;
- const nextNode = reverse ? 'prev' : 'next';
- while (currentNode) {
- callbackFn(currentNode.val, currentIndex, this);
- currentNode = currentNode[nextNode];
- currentIndex += modifier;
- }
- }
-
- /**
- * The map function takes a callback function and applies it to each element in the linked list, returning a new linked
- * list with the results.
- * @param callbackFn - A callback function that will be applied to each element in the linked list. It takes three
- * parameters:
- * @param [reverse=false] - The `reverse` parameter is a boolean value that determines whether the mapping should be
- * done in reverse order or not. If `reverse` is set to `true`, the mapping will be done in reverse order. If `reverse`
- * is set to `false` or not provided, the mapping will be
- * @returns The `map` function is returning a new `SinglyLinkedList` object.
- */
- map(callbackFn: (
- data: any,
- index: number,
- list: SinglyLinkedList,
- ) => any, reverse = false): SinglyLinkedList {
- const list = new SinglyLinkedList();
- this.forEach((val, index) => list.append(callbackFn(val, index, this)), reverse);
- return list;
- }
-
- /**
- * The `filter` function filters the elements of a singly linked list based on a given callback function.
- * @param callbackFn - A callback function that takes three parameters: data, index, and list. It should return a
- * boolean value indicating whether the current element should be included in the filtered list or not.
- * @param [reverse=false] - The `reverse` parameter is a boolean value that determines whether the filtered list should
- * be reversed or not. If `reverse` is set to `true`, the filtered list will be in reverse order. If `reverse` is set
- * to `false` or not provided, the filtered list will be in
- * @returns The `filter` method is returning a new `SinglyLinkedList` object.
- */
- filter(callbackFn: (
- data: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => boolean, reverse = false): SinglyLinkedList {
- const list = new SinglyLinkedList();
- this.forEach((val, index) => {
- if (callbackFn(val, index, this)) {
- list.append(val);
+ if (existingNode) {
+ const newNode = new SinglyLinkedListNode(newValue);
+ newNode.next = existingNode.next;
+ existingNode.next = newNode;
+ if (existingNode === this.tail) {
+ this.tail = newNode;
}
- }, reverse);
- return list;
- }
-
- /**
- * The `reduce` function iterates over a singly linked list and applies a callback function to each element,
- * accumulating a single value.
- * @param callbackFn - A callback function that will be called for each element in the linked list. It takes four
- * parameters:
- * @param {any} [start] - The `start` parameter is an optional initial value for the accumulator. If provided, the
- * `reduce` function will start accumulating from this value. If not provided, the `reduce` function will use the value
- * of the first element in the linked list as the initial value.
- * @param [reverse=false] - A boolean value indicating whether to iterate over the linked list in reverse order. If set
- * to true, the iteration will start from the tail of the linked list and move towards the head. If set to false
- * (default), the iteration will start from the head and move towards the tail.
- * @returns The `reduce` method returns the accumulated value after applying the callback function to each element in
- * the linked list.
- */
- reduce(
- callbackFn: (
- accumulator: any,
- currentNode: NodeVal,
- index: number,
- list: SinglyLinkedList,
- ) => any,
- start?: any,
- reverse = false,
- ): any {
- let currentIndex = reverse ? this.size - 1 : 0;
- const modifier = reverse ? -1 : 1;
- const nextNode = reverse ? 'prev' : 'next';
- let currentElement = reverse ? this.tail : this.head;
- let result;
-
- if (start !== undefined) {
- result = start;
- } else if (currentElement) {
- result = currentElement.val;
- currentElement = currentElement[nextNode];
- } else {
- throw new TypeError('Reduce of empty LinkedList with no initial value');
+ this.length++;
+ return true;
}
- while (currentElement) {
- result = callbackFn(result, currentElement.val, currentIndex, this);
- currentIndex += modifier;
- currentElement = currentElement[nextNode];
+ return false;
+ }
+
+ countOccurrences(value: T): number {
+ let count = 0;
+ let current = this.head;
+
+ while (current) {
+ if (current.val === value) {
+ count++;
+ }
+ current = current.next;
}
- return result;
- }
-
- /**
- * The toArray() function converts a NodeVal object into an array of NodeVal objects.
- * @returns An array of NodeVal objects.
- */
- toArray(): NodeVal[] {
- return [...this];
- }
-
- /**
- * The `toString` function takes an optional separator and returns a string representation of an array, with each
- * element separated by the specified separator.
- * @param [separator= ] - The separator parameter is a string that specifies the character(s) to be used as a separator
- * between each element in the array when converting it to a string. By default, the separator is set to a space
- * character (' ').
- * @returns The toString method is being returned as a string.
- */
- toString(separator = ' '): string {
- return this.reduce((s, val) => `${s}${separator}${val}`);
- }
-
- /**
- * The function is an iterator that returns the values of each node in a linked list.
- */
- public* [Symbol.iterator](): IterableIterator {
- let element = this.head;
-
- while (element !== null) {
- yield element.val;
- element = element.next;
- }
- }
-
- /**
- * The function removes a node from either end of a singly linked list and returns its value.
- * @param {SinglyLinkedListNode | null} node - The `node` parameter is a reference to a node in a singly
- * linked list. It can be either a `SinglyLinkedListNode` object or `null`.
- * @returns The value of the removed node if the node is not null, otherwise undefined.
- */
- protected removeFromAnyEnd(node: SinglyLinkedListNode | null) {
- return node !== null ? this.removeNode(node).val : undefined;
+ return count;
}
}
diff --git a/tests/unit/data-structures/linked-list/doubly-linked-list.ts b/tests/unit/data-structures/linked-list/doubly-linked-list.ts
new file mode 100644
index 0000000..8161410
--- /dev/null
+++ b/tests/unit/data-structures/linked-list/doubly-linked-list.ts
@@ -0,0 +1,12 @@
+
+describe('DoublyLinkedList Operation Test', () => {
+ it('should xxx', function () {
+
+ });
+});
+
+describe('DoublyLinkedList Performance Test', () => {
+ it('should xxx', function () {
+
+ });
+});
\ 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
new file mode 100644
index 0000000..f4b2fbc
--- /dev/null
+++ b/tests/unit/data-structures/linked-list/index.ts
@@ -0,0 +1,2 @@
+export * from './singly-linked-list.test';
+export * from './doubly-linked-list';
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
new file mode 100644
index 0000000..bb8cc0f
--- /dev/null
+++ b/tests/unit/data-structures/linked-list/singly-linked-list.test.ts
@@ -0,0 +1,371 @@
+import {SinglyLinkedList} from '../../../../src';
+
+describe('SinglyLinkedList Operation Test', () => {
+ let list: SinglyLinkedList;
+
+ beforeEach(() => {
+ list = new SinglyLinkedList();
+ });
+
+ describe('push', () => {
+ it('should add elements to the end of the list', () => {
+ list.push(1);
+ list.push(2);
+ expect(list.toArray()).toEqual([1, 2]);
+ });
+ });
+
+ describe('pop', () => {
+ it('should remove and return the last element of the list', () => {
+ list.push(1);
+ list.push(2);
+ const popped = list.pop();
+ expect(popped).toBe(2);
+ expect(list.toArray()).toEqual([1]);
+ });
+
+ it('should return undefined if the list is empty', () => {
+ const popped = list.pop();
+ expect(popped).toBeUndefined();
+ });
+ });
+
+ describe('shift', () => {
+ it('should remove and return the first element of the list', () => {
+ list.push(1);
+ list.push(2);
+ const shifted = list.shift();
+ expect(shifted).toBe(1);
+ expect(list.toArray()).toEqual([2]);
+ });
+
+ it('should return undefined if the list is empty', () => {
+ const shifted = list.shift();
+ expect(shifted).toBeUndefined();
+ });
+ });
+
+ describe('unshift', () => {
+ it('should add elements to the beginning of the list', () => {
+ list.unshift(1);
+ list.unshift(2);
+ expect(list.toArray()).toEqual([2, 1]);
+ });
+ });
+
+ describe('get', () => {
+ it('should return the element at the specified index', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const element = list.get(1);
+ expect(element).toBe(2);
+ });
+
+ it('should return undefined for an out-of-bounds index', () => {
+ list.push(1);
+ const element = list.get(1);
+ expect(element).toBeUndefined();
+ });
+ });
+
+
+ describe('insertAfter', () => {
+ it('should insert an element after an existing value', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ list.insertAfter(2, 4);
+ expect(list.toArray()).toEqual([1, 2, 4, 3]);
+ });
+
+ it('should return false if the existing value is not found', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const result = list.insertAfter(5, 4);
+ expect(result).toBe(false);
+ expect(list.toArray()).toEqual([1, 2, 3]);
+ });
+ });
+
+ describe('countOccurrences', () => {
+ it('should count occurrences of a value in the list', () => {
+ list.push(1);
+ list.push(2);
+ list.push(2);
+ list.push(3);
+ const count = list.countOccurrences(2);
+ expect(count).toBe(2);
+ });
+
+ it('should return 0 if the value is not found', () => {
+ list.push(1);
+ list.push(2);
+ const count = list.countOccurrences(3);
+ expect(count).toBe(0);
+ });
+ });
+
+ describe('removeValue', () => {
+ it('should remove the first occurrence of a value from the list', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const removed = list.removeByValue(2);
+ expect(removed).toBe(true);
+ expect(list.toArray()).toEqual([1, 3]);
+ });
+
+ it('should return false if the value is not found', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const removed = list.removeByValue(4);
+ expect(removed).toBe(false);
+ expect(list.toArray()).toEqual([1, 2, 3]);
+ });
+ });
+
+
+ describe('isEmpty', () => {
+ it('should return true for an empty list', () => {
+ expect(list.isEmpty()).toBe(true);
+ });
+
+ it('should return false for a non-empty list', () => {
+ list.push(1);
+ expect(list.isEmpty()).toBe(false);
+ });
+ });
+
+ describe('clear', () => {
+ it('should clear all elements from the list', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ list.clear();
+ expect(list.toArray()).toEqual([]);
+ expect(list.getLength()).toBe(0);
+ expect(list.isEmpty()).toBe(true);
+ });
+ });
+
+ describe('reverse', () => {
+ it('should reverse the order of elements in the list', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ list.reverse();
+ expect(list.toArray()).toEqual([3, 2, 1]);
+ });
+
+ it('should handle an empty list', () => {
+ list.reverse();
+ expect(list.toArray()).toEqual([]);
+ });
+
+ it('should handle a list with a single element', () => {
+ list.push(1);
+ list.reverse();
+ expect(list.toArray()).toEqual([1]);
+ });
+ });
+
+ describe('indexOf', () => {
+ it('should return the index of the first occurrence of a value', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const index = list.indexOf(2);
+ expect(index).toBe(1);
+ });
+
+ it('should return -1 if the value is not found', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const index = list.indexOf(4);
+ expect(index).toBe(-1);
+ });
+ });
+
+ describe('toArray', () => {
+ it('should convert the list to an array', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const array = list.toArray();
+ expect(array).toEqual([1, 2, 3]);
+ });
+
+ it('should return an empty array for an empty list', () => {
+ const array = list.toArray();
+ expect(array).toEqual([]);
+ });
+ });
+
+ describe('insertBefore', () => {
+ it('should insert an element before an existing value', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ list.insertBefore(2, 4);
+ expect(list.toArray()).toEqual([1, 4, 2, 3]);
+ });
+
+ it('should insert an element at the beginning', () => {
+ list.push(1);
+ list.push(2);
+ list.insertBefore(1, 3);
+ expect(list.toArray()).toEqual([3, 1, 2]);
+ });
+
+ it('should return false if the existing value is not found', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const result = list.insertBefore(5, 4);
+ expect(result).toBe(false);
+ expect(list.toArray()).toEqual([1, 2, 3]);
+ });
+ });
+
+ describe('getLength', () => {
+ it('should return the correct length of the list', () => {
+ expect(list.getLength()).toBe(0);
+ list.push(1);
+ list.push(2);
+ expect(list.getLength()).toBe(2);
+ });
+ });
+
+ describe('remove', () => {
+ it('should remove and return the element at the specified index', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const removed = list.remove(1);
+ expect(removed).toBe(2);
+ expect(list.toArray()).toEqual([1, 3]);
+ });
+
+ it('should return undefined for an out-of-bounds index', () => {
+ list.push(1);
+ const removed = list.remove(1);
+ expect(removed).toBeUndefined();
+ });
+
+ it('should remove and return the first element', () => {
+ list.push(1);
+ list.push(2);
+ const removed = list.remove(0);
+ expect(removed).toBe(1);
+ expect(list.toArray()).toEqual([2]);
+ });
+
+ it('should remove and return the last element', () => {
+ list.push(1);
+ list.push(2);
+ const removed = list.remove(1);
+ expect(removed).toBe(2);
+ expect(list.toArray()).toEqual([1]);
+ });
+ });
+
+ describe('push and pop', () => {
+ it('should push and pop elements correctly', () => {
+ list.push(1);
+ list.push(2);
+ expect(list.pop()).toBe(2);
+ expect(list.pop()).toBe(1);
+ expect(list.pop()).toBeUndefined();
+ });
+ });
+
+ describe('shift and unshift', () => {
+ it('should shift and unshift elements correctly', () => {
+ list.unshift(1);
+ list.unshift(2);
+ expect(list.shift()).toBe(2);
+ expect(list.shift()).toBe(1);
+ expect(list.shift()).toBeUndefined();
+ });
+ });
+
+ describe('insert and toArray', () => {
+ it('should insert elements and return array correctly', () => {
+ list.insert(0, 1);
+ list.insert(1, 3);
+ list.insert(1, 2);
+ expect(list.toArray()).toEqual([1, 2, 3]);
+ });
+ });
+
+ describe('find', () => {
+ it('should find elements using a callback function', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ const result = list.find((data) => data % 2 === 0);
+ expect(result).toBe(2);
+ });
+
+ it('should return undefined if element is not found', () => {
+ list.push(1);
+ list.push(3);
+ const result = list.find((data) => data % 2 === 0);
+ expect(result).toBeUndefined();
+ });
+ });
+
+ describe('reverse', () => {
+ it('should reverse the order of elements', () => {
+ list.push(1);
+ list.push(2);
+ list.push(3);
+ list.reverse();
+ expect(list.toArray()).toEqual([3, 2, 1]);
+ });
+ });
+
+ describe('countOccurrences', () => {
+ it('should count occurrences of a value', () => {
+ list.push(1);
+ list.push(2);
+ list.push(2);
+ list.push(3);
+ const count = list.countOccurrences(2);
+ expect(count).toBe(2);
+ });
+
+ it('should return 0 if value is not found', () => {
+ list.push(1);
+ list.push(2);
+ const count = list.countOccurrences(3);
+ expect(count).toBe(0);
+ });
+ });
+});
+
+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++) {
+ list.push(i);
+ }
+ expect(performance.now() - startPushTime).toBeLessThan(iterations * 1000);
+
+ const startPopTime = performance.now();
+
+ for (let i = 0; i < iterations; i++) {
+ list.pop();
+ }
+
+ expect(performance.now() - startPopTime).toBeLessThan(iterations * 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.ts
new file mode 100644
index 0000000..3c4f75d
--- /dev/null
+++ b/tests/unit/data-structures/linked-list/skip-linked-list.ts
@@ -0,0 +1,12 @@
+
+describe('SkipLinkedList Operation Test', () => {
+ it('should xxx', function () {
+
+ });
+});
+
+describe('SkipLinkedList Performance Test', () => {
+ it('should xxx', function () {
+
+ });
+});
\ No newline at end of file