diff --git a/clone-graph/wogha95.js b/clone-graph/wogha95.js new file mode 100644 index 000000000..ac357f6da --- /dev/null +++ b/clone-graph/wogha95.js @@ -0,0 +1,74 @@ +/** + * TC: O(V + E) + * 모든 정점를 방문하게 되고 + * 방문한 정점에서 연결된 간선을 queue에 추가하는 반복문을 실행합니다. + * 따라서 시간복잡도는 정점 + 간선에 선형적으로 비례합니다. + * + * SC: O(V + E) + * memory, visitedNodeVal 에서 V만큼 데이터 공간을 가집니다. + * 그리고 queue에서 최대 모든 간선 갯수 * 2 만큼 갖게 됩니다. + * 따라서 O(V + 2E) = O(V + E)로 계산했습니다. + * + * V: vertex, E: edge + */ + +/** + * // Definition for a _Node. + * function _Node(val, neighbors) { + * this.val = val === undefined ? 0 : val; + * this.neighbors = neighbors === undefined ? [] : neighbors; + * }; + */ + +/** + * @param {_Node} node + * @return {_Node} + */ +var cloneGraph = function (node) { + if (!node) { + return null; + } + + const memory = new Map(); + const visitedNodeVal = new Set(); + + return bfsClone(node); + + // 1. bfs로 순회하면서 deep clone한 graph의 head를 반환 + function bfsClone(start) { + const queue = [start]; + const clonedHeadNode = new _Node(start.val); + memory.set(start.val, clonedHeadNode); + + while (queue.length > 0) { + const current = queue.shift(); + if (visitedNodeVal.has(current.val)) { + continue; + } + + const clonedCurrentNode = getCloneNode(current.val); + + for (const neighbor of current.neighbors) { + const clonedNeighborNode = getCloneNode(neighbor.val); + clonedCurrentNode.neighbors.push(clonedNeighborNode); + + queue.push(neighbor); + } + + visitedNodeVal.add(current.val); + } + + return clonedHeadNode; + } + + // 2. memory에 val에 해당하는 node 반환 (없을 경우 생성) + function getCloneNode(val) { + if (!memory.has(val)) { + const node = new _Node(val); + memory.set(val, node); + return node; + } + + return memory.get(val); + } +}; diff --git a/longest-common-subsequence/wogha95.js b/longest-common-subsequence/wogha95.js new file mode 100644 index 000000000..6da8776c1 --- /dev/null +++ b/longest-common-subsequence/wogha95.js @@ -0,0 +1,46 @@ +/** + * 알고달레 풀이 참고해서 풀었습니다. + * @see https://www.algodale.com/problems/longest-common-subsequence/ + * + * DP + 2차원 배열을 이용한 풀이 + * 행은 text1, 열은 text2에 대응하고 + * index는 text의 처음부터 몇개 문자를 사용할건지 의미합니다. + * 마지막 문자가 동일하다면 dp[index1][index2] = dp[index1 - 1][index2 - 1] + 1 + * 마지막 문자가 다르다면 dp[index1][index2] = Math.max(dp[index1 - 1][index2], dp[index1][index2 - 1]) + * + * TC: O(T1 * T2) + * 2차원 배열을 순회 + * + * SC: O(T1 * T2) + * 2차원 배열 생성 + * + * T1: text1.length, T2: text2.length + */ + +/** + * @param {string} text1 + * @param {string} text2 + * @return {number} + */ +var longestCommonSubsequence = function (text1, text2) { + const LENGTH1 = text1.length; + const LENGTH2 = text2.length; + const memory = Array.from({ length: LENGTH1 + 1 }, () => + Array.from({ length: LENGTH2 + 1 }, () => 0) + ); + + for (let index1 = 1; index1 <= LENGTH1; index1++) { + for (let index2 = 1; index2 <= LENGTH2; index2++) { + if (text1[index1 - 1] === text2[index2 - 1]) { + memory[index1][index2] = memory[index1 - 1][index2 - 1] + 1; + } else { + memory[index1][index2] = Math.max( + memory[index1 - 1][index2], + memory[index1][index2 - 1] + ); + } + } + } + + return memory[LENGTH1][LENGTH2]; +}; diff --git a/longest-repeating-character-replacement/wogha95.js b/longest-repeating-character-replacement/wogha95.js new file mode 100644 index 000000000..33fec2857 --- /dev/null +++ b/longest-repeating-character-replacement/wogha95.js @@ -0,0 +1,50 @@ +/** + * 알고달레 풀이 참고 + * @see https://www.algodale.com/problems/longest-repeating-character-replacement/ + * + * TC: O(S) + * right의 순회 + left의 순회 + * (곱이 아닌 더하기인 이유는 right 순회동안 left 순회의 최대 총합이 S이기 때문입니다.) + * + * SC: O(1) + * 최악의 경우 26개의 소문자를 저장하는 memoryMap으로 인해 상수 복잡도를 갖게 됩니다. + * + * S: s.length + */ + +/** + * @param {string} s + * @param {number} k + * @return {number} + */ +var characterReplacement = function (s, k) { + // 1. 가장 긴 subString 길이 + let result = 0; + + // 2. left, right 포인터 사이에서 등장한 문자 횟수를 기록한 Map과 최다 등장한 횟수를 기록한 변수 + const memory = new Map(); + let maxCount = 0; + + let left = 0; + let right = 0; + + while (right < s.length) { + // 3. '새로운 문자(s[right])의 갯수 기록'과 '최다 등장한 횟수 갱신' + const newCount = (memory.has(s[right]) ? memory.get(s[right]) : 0) + 1; + memory.set(s[right], newCount); + maxCount = Math.max(maxCount, newCount); + + // 4. k만큼 변경가능한 subString 길이를 맞추기 위해 left 이동 + while (right - left + 1 - maxCount > k) { + const newCount = memory.get(s[left]) - 1; + memory.set(s[left], newCount); + left += 1; + } + + // 5. 가장 긴 subString 길이 갱신, right 이동 + result = Math.max(result, right - left + 1); + right += 1; + } + + return result; +}; diff --git a/merge-two-sorted-lists/wogha95.js b/merge-two-sorted-lists/wogha95.js new file mode 100644 index 000000000..595dc7219 --- /dev/null +++ b/merge-two-sorted-lists/wogha95.js @@ -0,0 +1,66 @@ +/** + * TC: O(List1 + List2) + * List1, List2 전체 순회 1번씩 합니다. + * + * SC: O(1) + * List1, List2의 길이와 무관한 고정된 데이터 공간을 사용합니다. (head, pointer 변수들) + * + * List1: list1.length, List2.length; + */ + +/** + * Definition for singly-linked list. + * function ListNode(val, next) { + * this.val = (val===undefined ? 0 : val) + * this.next = (next===undefined ? null : next) + * } + */ +/** + * @param {ListNode} list1 + * @param {ListNode} list2 + * @return {ListNode} + */ +var mergeTwoLists = function (list1, list2) { + // 1. 둘 중 하나의 list가 없는 경우 반대편의 list를 반환 + if (!list1) { + return list2; + } + if (!list2) { + return list1; + } + + // 2. 정답을 반환할 시작점(head)와 list 순회시 필요한 pointer + const head = new ListNode(); + let headPointer = head; + let list1Pointer = list1; + let list2Pointer = list2; + + // 3. 두 list 모두 노드를 가진 경우 + while (list1Pointer && list2Pointer) { + if (list1Pointer.val < list2Pointer.val) { + list1Pointer = connectHeadAndListPointer(list1Pointer); + } else { + list2Pointer = connectHeadAndListPointer(list2Pointer); + } + } + + // 4. 한쪽 list의 남은 노드 연결 + while (list1Pointer) { + list1Pointer = connectHeadAndListPointer(list1Pointer); + } + + while (list2Pointer) { + list2Pointer = connectHeadAndListPointer(list2Pointer); + } + + return head.next; + + // 5. head의 list로 연결 후 다음 노드로 pointer 이동 + function connectHeadAndListPointer(listPointer) { + headPointer.next = listPointer; + listPointer = listPointer.next; + headPointer = headPointer.next; + + return listPointer; + } +}; diff --git a/sum-of-two-integers/wogha95.js b/sum-of-two-integers/wogha95.js new file mode 100644 index 000000000..58555069a --- /dev/null +++ b/sum-of-two-integers/wogha95.js @@ -0,0 +1,23 @@ +/** + * TC: O(Bit) + * 올림 비트가 0일때까지 while을 실행하게된다. + * + * SC: O(log(max(a, b))) + * + * Bit: 2진수 a 와 b 중 비트 길이가 긴 것의 비트 길이 + */ + +/** + * @param {number} a + * @param {number} b + * @return {number} + */ +var getSum = function (a, b) { + while (b !== 0) { + const carry = (a & b) << 1; + a = a ^ b; + b = carry; + } + + return a; +};