diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..496ee2ca --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.DS_Store \ No newline at end of file diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 00000000..f678f2a6 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "tabWidth": 2, + "trailingComma": "all", + "printWidth": 80 +} \ No newline at end of file diff --git a/algorithm/BinarySearchTree.js b/algorithm/BinarySearchTree.js new file mode 100644 index 00000000..b8270734 --- /dev/null +++ b/algorithm/BinarySearchTree.js @@ -0,0 +1,89 @@ +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; + } + + let current = this.root; + while (true) { + if (value < current.value) { + if (current.left === null) { + current.left = newNode; + return; + } + current = current.left; + } else { + if (current.right === null) { + current.right = newNode; + return; + } + current = current.right; + } + } + } + + find(value) { + let current = this.root; + while (current !== null) { + if (value === current.value) return current; + current = value < current.value ? current.left : current.right; + } + return null; + } + + remove(value) { + this.root = this._removeNode(this.root, value); + } + + _removeNode(node, value) { + if (node === null) return null; + + if (value < node.value) { + node.left = this._removeNode(node.left, value); + } else if (value > node.value) { + node.right = this._removeNode(node.right, value); + } else { + // 자식이 없는 경우 + if (node.left === null && node.right === null) return null; + // 자식이 하나인 경우 + if (node.left === null) return node.right; + if (node.right === null) return node.left; + // 자식이 둘인 경우 오른쪽 서브트리의 최솟값(inorder successor)으로 대체 + let successor = node.right; + while (successor.left !== null) { + successor = successor.left; + } + node.value = successor.value; + node.right = this._removeNode(node.right, successor.value); + } + return node; + } +} + +const bst = new BinarySearchTree(); +bst.insert(10); +bst.insert(5); +bst.insert(15); +bst.insert(3); +bst.insert(7); + +console.log(bst.find(7).value); // 7 +console.log(bst.find(99)); // null + +bst.remove(5); +console.log(bst.find(5)); // null +console.log(bst.find(7).value); // 7 (여전히 존재) diff --git a/algorithm/DoublyLinkedList.js b/algorithm/DoublyLinkedList.js new file mode 100644 index 00000000..2bdea13d --- /dev/null +++ b/algorithm/DoublyLinkedList.js @@ -0,0 +1,99 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + this.prev = null; + } +} + +class DoublyLinkedList { + constructor() { + this.head = null; + this.tail = null; + } + + 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; + } + } + + 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; + } + } + + findNode(value) { + let current = this.head; + while (current !== null) { + if (current.value === value) return current; + current = current.next; + } + return null; + } + + insertAfter(targetValue, newValue) { + const targetNode = this.findNode(targetValue); + if (targetNode === null) return; + + const newNode = new Node(newValue); + + if (targetNode === this.tail) { + this.tail.next = newNode; + newNode.prev = this.tail; + this.tail = newNode; + } else { + newNode.next = targetNode.next; + newNode.prev = targetNode; + targetNode.next.prev = newNode; + targetNode.next = newNode; + } + } + + removeNode(value) { + const nodeToRemove = this.findNode(value); + if (nodeToRemove === null) return; + + if (nodeToRemove === this.head && nodeToRemove === this.tail) { + this.head = null; + this.tail = null; + } else if (nodeToRemove === this.head) { + this.head = this.head.next; + if (this.head) this.head.prev = null; + } else if (nodeToRemove === this.tail) { + this.tail = this.tail.prev; + if (this.tail) this.tail.next = null; + } else { + nodeToRemove.prev.next = nodeToRemove.next; + nodeToRemove.next.prev = nodeToRemove.prev; + } + } +} + +const list = new DoublyLinkedList(); +list.addToTail(1); +list.addToTail(2); +list.addToTail(3); +list.addToHead(0); + +console.log(list.findNode(2).value); // 2 +console.log(list.findNode(2).prev.value); // 1 + +list.insertAfter(2, 2.5); +console.log(list.findNode(2).next.value); // 2.5 + +list.removeNode(2.5); +console.log(list.findNode(2).next.value); // 3 diff --git a/algorithm/LinkedList.js b/algorithm/LinkedList.js new file mode 100644 index 00000000..92920ada --- /dev/null +++ b/algorithm/LinkedList.js @@ -0,0 +1,71 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +class LinkedList { + constructor() { + this.head = null; + this.tail = null; + } + + 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; + } + } + + findNode(value) { + let current = this.head; + while (current !== null) { + if (current.value === value) return current; + current = current.next; + } + return null; + } + + insertAfter(targetValue, newValue) { + const targetNode = this.findNode(targetValue); + if (targetNode === null) return; + + const newNode = new Node(newValue); + newNode.next = targetNode.next; + targetNode.next = newNode; + + if (targetNode === this.tail) { + this.tail = newNode; + } + } + + removeAfter(targetValue) { + const targetNode = this.findNode(targetValue); + if (targetNode === null || targetNode.next === null) return; + + const nodeToRemove = targetNode.next; + targetNode.next = nodeToRemove.next; + + if (nodeToRemove === this.tail) { + this.tail = targetNode; + } + } +} + +const list = new LinkedList(); +list.addNode(1); +list.addNode(2); +list.addNode(3); + +console.log(list.findNode(2)); // Node { value: 2, next: Node { value: 3, ... } } + +list.insertAfter(2, 2.5); +console.log(list.findNode(2).next.value); // 2.5 + +list.removeAfter(2); +console.log(list.findNode(2).next.value); // 3 diff --git a/algorithm/Queue.js b/algorithm/Queue.js new file mode 100644 index 00000000..052e0a0a --- /dev/null +++ b/algorithm/Queue.js @@ -0,0 +1,58 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +class Queue { + constructor() { + this.front = null; + this.rear = null; + } + + isEmpty() { + return this.front === null; + } + + enqueue(value) { + const newNode = new Node(value); + if (this.isEmpty()) { + this.front = newNode; + this.rear = newNode; + } else { + this.rear.next = newNode; + this.rear = newNode; + } + } + dequeue() { + if (this.isEmpty()) return null; + + const value = this.front.value; + if (this.front === this.rear) { + this.front = null; + this.rear = null; + } else { + this.front = this.front.next; + } + return value; + } + + peek() { + return this.isEmpty() ? null : this.front.value; + } +} + +const q = new Queue(); +console.log(q.isEmpty()); // true + +q.enqueue("A"); +q.enqueue("B"); +q.enqueue("C"); + +console.log(q.peek()); // A +console.log(q.dequeue()); // A +console.log(q.dequeue()); // B +console.log(q.isEmpty()); // false +console.log(q.dequeue()); // C +console.log(q.isEmpty()); // true diff --git a/algorithm/Sorts.js b/algorithm/Sorts.js new file mode 100644 index 00000000..70c4135d --- /dev/null +++ b/algorithm/Sorts.js @@ -0,0 +1,34 @@ +function heapsort(arr) { + const n = arr.length; + + for (let i = Math.floor(n / 2) - 1; i >= 0; i--) { + heapify(arr, n, i); + } + + for (let i = n - 1; i > 0; i--) { + [arr[0], arr[i]] = [arr[i], arr[0]]; + heapify(arr, i, 0); + } +} + +function heapify(arr, size, rootIndex) { + let largest = rootIndex; + const left = 2 * rootIndex + 1; + const right = 2 * rootIndex + 2; + + if (left < size && arr[left] > arr[largest]) largest = left; + if (right < size && arr[right] > arr[largest]) largest = right; + + if (largest !== rootIndex) { + [arr[rootIndex], arr[largest]] = [arr[largest], arr[rootIndex]]; + heapify(arr, size, largest); + } +} + +const arr1 = [3, 1, 4, 1, 5, 9, 2, 6]; +heapsort(arr1); +console.log(arr1); // [1, 1, 2, 3, 4, 5, 6, 9] + +const arr2 = [10, -3, 0, 7, -1]; +heapsort(arr2); +console.log(arr2); // [-3, -1, 0, 7, 10] diff --git a/algorithm/Stack.js b/algorithm/Stack.js new file mode 100644 index 00000000..dba3edb5 --- /dev/null +++ b/algorithm/Stack.js @@ -0,0 +1,49 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +class Stack { + constructor() { + this.top = null; + } + + isEmpty() { + return this.top === null; + } + + push(value) { + const newNode = new Node(value); + newNode.next = this.top; + this.top = newNode; + } + + pop() { + if (this.isEmpty()) return null; + + const value = this.top.value; + this.top = this.top.next; + return value; + } + + peek() { + return this.isEmpty() ? null : this.top.value; + } +} + +const stack = new Stack(); +console.log(stack.isEmpty()); // true + +stack.push(1); +stack.push(2); +stack.push(3); + +console.log(stack.peek()); // 3 +console.log(stack.pop()); // 3 +console.log(stack.pop()); // 2 +console.log(stack.isEmpty()); // false +console.log(stack.pop()); // 1 +console.log(stack.isEmpty()); // true +console.log(stack.pop()); // null