From 21d10a703a1cf447ecbcf17bddfe7a4d226a919a Mon Sep 17 00:00:00 2001 From: hiiisiii <8243.hk@gmail.com> Date: Tue, 26 May 2026 16:47:50 +0900 Subject: [PATCH] feat: implement sprint 13 algorithms --- algorithm/BinarySearchTree.js | 129 +++++++++++++++++++++++++++ algorithm/DoublyLinkedList.js | 123 ++++++++++++++++++++++++++ algorithm/LinkedList.js | 95 ++++++++++++++++++++ algorithm/Queue.js | 43 +++++++++ algorithm/Stack.js | 43 +++++++++ algorithm/sorts.js | 160 ++++++++++++++++++++++++++++++++++ 6 files changed, 593 insertions(+) create mode 100644 algorithm/BinarySearchTree.js create mode 100644 algorithm/DoublyLinkedList.js create mode 100644 algorithm/LinkedList.js create mode 100644 algorithm/Queue.js create mode 100644 algorithm/Stack.js create mode 100644 algorithm/sorts.js diff --git a/algorithm/BinarySearchTree.js b/algorithm/BinarySearchTree.js new file mode 100644 index 00000000..f01a12eb --- /dev/null +++ b/algorithm/BinarySearchTree.js @@ -0,0 +1,129 @@ +class Node { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +class BinarySearchTree { + constructor() { + this.root = null; + } + + insert(value) { + const newNode = new Node(value); + + if (this.root === null) { + this.root = newNode; + return newNode; + } + + let currentNode = this.root; + + while (currentNode !== null) { + if (value === currentNode.value) { + return null; + } + + if (value < currentNode.value) { + if (currentNode.left === null) { + currentNode.left = newNode; + return newNode; + } + + currentNode = currentNode.left; + } else { + if (currentNode.right === null) { + currentNode.right = newNode; + return newNode; + } + + currentNode = currentNode.right; + } + } + + return null; + } + + find(value) { + let currentNode = this.root; + + while (currentNode !== null) { + if (value === currentNode.value) { + return currentNode; + } + + if (value < currentNode.value) { + currentNode = currentNode.left; + } else { + currentNode = currentNode.right; + } + } + + return null; + } + + remove(value) { + const removeNode = (node, targetValue) => { + if (node === null) { + return null; + } + + if (targetValue < node.value) { + node.left = removeNode(node.left, targetValue); + return node; + } + + if (targetValue > node.value) { + node.right = removeNode(node.right, targetValue); + return node; + } + + if (node.left === null) { + return node.right; + } + + if (node.right === null) { + return node.left; + } + + let minimumNode = node.right; + + while (minimumNode.left !== null) { + minimumNode = minimumNode.left; + } + + node.value = minimumNode.value; + node.right = removeNode(node.right, minimumNode.value); + return node; + }; + + this.root = removeNode(this.root, value); + } +} + +module.exports = BinarySearchTree; + +if (require.main === module) { + const bst = new BinarySearchTree(); + bst.insert(10); + bst.insert(5); + bst.insert(15); + bst.insert(3); + bst.insert(7); + bst.insert(12); + bst.insert(18); + + console.log("find 7:", bst.find(7)); + console.log("find 100:", bst.find(100)); + + bst.remove(3); + console.log("after remove leaf 3:", JSON.stringify(bst)); + + bst.remove(15); + console.log("after remove 15:", JSON.stringify(bst)); + + bst.remove(10); + console.log("after remove root 10:", JSON.stringify(bst)); +} diff --git a/algorithm/DoublyLinkedList.js b/algorithm/DoublyLinkedList.js new file mode 100644 index 00000000..7fcd6550 --- /dev/null +++ b/algorithm/DoublyLinkedList.js @@ -0,0 +1,123 @@ +class Node { + constructor(value) { + this.value = value; + this.prev = null; + this.next = null; + } +} + +class DoublyLinkedList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } + + addToHead(value) { + const newNode = new Node(value); + + if (this.head === null) { + this.head = newNode; + this.tail = newNode; + } else { + newNode.next = this.head; + this.head.prev = newNode; + this.head = newNode; + } + + this.length++; + return newNode; + } + + addToTail(value) { + const newNode = new Node(value); + + if (this.tail === null) { + this.head = newNode; + this.tail = newNode; + } else { + this.tail.next = newNode; + newNode.prev = this.tail; + this.tail = newNode; + } + + this.length++; + return newNode; + } + + insertAfter(targetValue, newValue) { + const targetNode = this.findNode(targetValue); + + if (targetNode === null) { + return null; + } + + const newNode = new Node(newValue); + newNode.prev = targetNode; + newNode.next = targetNode.next; + + if (targetNode.next !== null) { + targetNode.next.prev = newNode; + } else { + this.tail = newNode; + } + + targetNode.next = newNode; + this.length++; + return newNode; + } + + findNode(value) { + let currentNode = this.head; + + while (currentNode !== null) { + if (currentNode.value === value) { + return currentNode; + } + + currentNode = currentNode.next; + } + + return null; + } + + removeNode(value) { + const removedNode = this.findNode(value); + + if (removedNode === null) { + return null; + } + + if (removedNode.prev !== null) { + removedNode.prev.next = removedNode.next; + } else { + this.head = removedNode.next; + } + + if (removedNode.next !== null) { + removedNode.next.prev = removedNode.prev; + } else { + this.tail = removedNode.prev; + } + + removedNode.prev = null; + removedNode.next = null; + this.length--; + return removedNode; + } +} + +module.exports = DoublyLinkedList; + +if (require.main === module) { + const list = new DoublyLinkedList(); + list.addToHead(2); + list.addToHead(1); + list.addToTail(3); + list.insertAfter(2, 4); + console.log("find 4:", list.findNode(4)); + console.log("remove 1:", list.removeNode(1)); + console.log("remove 3:", list.removeNode(3)); + console.log("head:", list.head); + console.log("tail:", list.tail); +} diff --git a/algorithm/LinkedList.js b/algorithm/LinkedList.js new file mode 100644 index 00000000..a37d8aee --- /dev/null +++ b/algorithm/LinkedList.js @@ -0,0 +1,95 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +class LinkedList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } + + addNode(value) { + const newNode = new Node(value); + + if (this.head === null) { + this.head = newNode; + this.tail = newNode; + } else { + this.tail.next = newNode; + this.tail = newNode; + } + + this.length++; + return newNode; + } + + findNode(value) { + let currentNode = this.head; + + while (currentNode !== null) { + if (currentNode.value === value) { + return currentNode; + } + + currentNode = currentNode.next; + } + + return null; + } + + insertAfter(targetValue, newValue) { + const targetNode = this.findNode(targetValue); + + if (targetNode === null) { + return null; + } + + const newNode = new Node(newValue); + newNode.next = targetNode.next; + targetNode.next = newNode; + + if (targetNode === this.tail) { + this.tail = newNode; + } + + this.length++; + return newNode; + } + + removeAfter(targetValue) { + const targetNode = this.findNode(targetValue); + + if (targetNode === null || targetNode.next === null) { + return null; + } + + const removedNode = targetNode.next; + targetNode.next = removedNode.next; + + if (removedNode === this.tail) { + this.tail = targetNode; + } + + removedNode.next = null; + this.length--; + return removedNode; + } +} + +module.exports = LinkedList; + +if (require.main === module) { + const list = new LinkedList(); + list.addNode(1); + list.addNode(2); + list.addNode(3); + console.log("find 2:", list.findNode(2)); + list.insertAfter(2, 4); + console.log("after insert:", JSON.stringify(list)); + console.log("removed:", list.removeAfter(2)); + console.log("after remove:", JSON.stringify(list)); +} diff --git a/algorithm/Queue.js b/algorithm/Queue.js new file mode 100644 index 00000000..d356e3e8 --- /dev/null +++ b/algorithm/Queue.js @@ -0,0 +1,43 @@ +class Queue { + constructor() { + this.items = []; + } + + enqueue(value) { + this.items.push(value); + } + + dequeue() { + if (this.isEmpty()) { + return null; + } + + return this.items.shift(); + } + + peek() { + if (this.isEmpty()) { + return null; + } + + return this.items[0]; + } + + isEmpty() { + return this.items.length === 0; + } +} + +module.exports = Queue; + +if (require.main === module) { + const queue = new Queue(); + console.log(queue.isEmpty()); + queue.enqueue(1); + queue.enqueue(2); + queue.enqueue(3); + console.log(queue.peek()); + console.log(queue.dequeue()); + console.log(queue.dequeue()); + console.log(queue.isEmpty()); +} diff --git a/algorithm/Stack.js b/algorithm/Stack.js new file mode 100644 index 00000000..4a5d2349 --- /dev/null +++ b/algorithm/Stack.js @@ -0,0 +1,43 @@ +class Stack { + constructor() { + this.items = []; + } + + push(value) { + this.items.push(value); + } + + pop() { + if (this.isEmpty()) { + return null; + } + + return this.items.pop(); + } + + peek() { + if (this.isEmpty()) { + return null; + } + + return this.items[this.items.length - 1]; + } + + isEmpty() { + return this.items.length === 0; + } +} + +module.exports = Stack; + +if (require.main === module) { + const stack = new Stack(); + console.log(stack.isEmpty()); + stack.push(1); + stack.push(2); + stack.push(3); + console.log(stack.peek()); + console.log(stack.pop()); + console.log(stack.pop()); + console.log(stack.isEmpty()); +} diff --git a/algorithm/sorts.js b/algorithm/sorts.js new file mode 100644 index 00000000..d9c02cac --- /dev/null +++ b/algorithm/sorts.js @@ -0,0 +1,160 @@ +function selectionSort(arr) { + for (let i = 0; i < arr.length; i++) { + let minIndex = i; + + for (let j = i + 1; j < arr.length; j++) { + if (arr[j] < arr[minIndex]) { + minIndex = j; + } + } + + const temp = arr[i]; + arr[i] = arr[minIndex]; + arr[minIndex] = temp; + } + + return arr; +} + +function insertionSort(arr) { + for (let i = 1; i < arr.length; i++) { + const currentValue = arr[i]; + let j = i - 1; + + while (j >= 0 && arr[j] > currentValue) { + arr[j + 1] = arr[j]; + j--; + } + + arr[j + 1] = currentValue; + } + + return arr; +} + +function merge(left, right) { + const result = []; + let leftIndex = 0; + let rightIndex = 0; + + while (leftIndex < left.length && rightIndex < right.length) { + if (left[leftIndex] <= right[rightIndex]) { + result.push(left[leftIndex]); + leftIndex++; + } else { + result.push(right[rightIndex]); + rightIndex++; + } + } + + return result.concat(left.slice(leftIndex), right.slice(rightIndex)); +} + +function mergeSort(arr) { + if (arr.length <= 1) { + return arr.slice(); + } + + const middle = Math.floor(arr.length / 2); + const left = mergeSort(arr.slice(0, middle)); + const right = mergeSort(arr.slice(middle)); + + return merge(left, right); +} + +function swap(arr, i, j) { + const temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; +} + +function partition(arr, left, right) { + const pivot = arr[right]; + let pivotIndex = left; + + for (let i = left; i < right; i++) { + if (arr[i] < pivot) { + swap(arr, i, pivotIndex); + pivotIndex++; + } + } + + swap(arr, pivotIndex, right); + return pivotIndex; +} + +function quickSort(arr, left = 0, right = arr.length - 1) { + if (left < right) { + const pivotIndex = partition(arr, left, right); + + quickSort(arr, left, pivotIndex - 1); + quickSort(arr, pivotIndex + 1, right); + } + + return arr; +} + +function heapify(arr, index, heapSize) { + let largestIndex = index; + const leftChildIndex = 2 * index + 1; + const rightChildIndex = 2 * index + 2; + + if (leftChildIndex < heapSize && arr[leftChildIndex] > arr[largestIndex]) { + largestIndex = leftChildIndex; + } + + if (rightChildIndex < heapSize && arr[rightChildIndex] > arr[largestIndex]) { + largestIndex = rightChildIndex; + } + + if (largestIndex !== index) { + swap(arr, index, largestIndex); + heapify(arr, largestIndex, heapSize); + } +} + +function heapsort(arr) { + const n = arr.length; + + for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { + heapify(arr, i, n); + } + + for (let i = n - 1; i >= 1; i--) { + swap(arr, 0, i); + heapify(arr, 0, i); + } + + return arr; +} + +module.exports = { + selectionSort, + insertionSort, + mergeSort, + quickSort, + heapsort, +}; + +if (require.main === module) { + const arr1 = [5, 3, 8, 1, 2]; + selectionSort(arr1); + console.log("selectionSort:", arr1); + + const arr2 = [5, 3, 8, 1, 2]; + insertionSort(arr2); + console.log("insertionSort:", arr2); + + const arr3 = [5, 3, 8, 1, 2]; + const sortedArr3 = mergeSort(arr3); + console.log("mergeSort original:", arr3); + console.log("mergeSort result:", sortedArr3); + + const arr4 = [5, 3, 8, 1, 2]; + quickSort(arr4); + console.log("quickSort:", arr4); + + const arr5 = [6, 1, 4, 7, 10, 3, 8, 5, 1, 5, 7, 4, 2, 1]; + heapsort(arr5); + console.log("heapsort:", arr5); +}