diff --git a/alien-dictionary/uraflower.js b/alien-dictionary/uraflower.js new file mode 100644 index 000000000..a4c3a22c1 --- /dev/null +++ b/alien-dictionary/uraflower.js @@ -0,0 +1,78 @@ +/** + * @param words: a list of words + * @return: a string which is correct order + */ +const alienOrder = (words) => { + const graph = {}; + + // 초기화 + for (const word of words) { + for (const char of word) { + if (!graph[char]) { + graph[char] = new Set(); + } + } + } + + // 그래프 생성 + for (let i = 1; i < words.length; i++) { + const prev = words[i - 1]; + const current = words[i]; + let found = false; + + const minLen = Math.min(prev.length, current.length); + for (let j = 0; j < minLen; j++) { + if (prev[j] !== current[j]) { + graph[prev[j]].add(current[j]); + found = true; + break; + } + } + // 모순 처리 + if (!found && prev.length > current.length) { + return ''; + } + } + + // 탐색 + const output = []; + const visiting = new Set(); + const visited = new Set(); + + function dfs(current) { + if (visiting.has(current)) { + return false; + } + if (visited.has(current)) { + return true; + } + + visiting.add(current); + for (const adj of graph[current]) { + if (!dfs(adj)) { + return false; + } + } + visiting.delete(current); + + visited.add(current); + output.push(current); + return true; + } + + // 순회 + for (const node in graph) { + if (!dfs(node)) { + return ''; + } + } + + return output.reverse().join(''); +} + + +// 방향 그래프로 선행되는 순서를 표현 +// 비슷한 문제: https://leetcode.com/problems/course-schedule/description/ + +// 위상정렬 사용해서 진입차수 기준 정렬하는 방법도 있음 +// 너무 어려웠다... diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/uraflower.js b/construct-binary-tree-from-preorder-and-inorder-traversal/uraflower.js new file mode 100644 index 000000000..2730a4088 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/uraflower.js @@ -0,0 +1,49 @@ +// 접근법 +// preorder: v l r +// inorder: l v r + +// preorder의 가장 첫 요소는 무조건 root +// inorder에서 root보다 왼쪽에 있는 요소는 전부 left임 +// 여기서 중복되는 value가 없어야 하는데 문제에서 없음을 보장함 +// 따라서 preorder[0]을 inorder에서 찾고 +// 그 왼쪽, 오른쪽으로 배열을 나눠서 이걸 반복 + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {number[]} preorder + * @param {number[]} inorder + * @return {TreeNode} + */ +const buildTree = function (preorder, inorder) { + let preorderIndex = 0; + const map = inorder.reduce((map, val, index) => { + map[val] = index; + return map; + }, {}); + + function build(start, end) { + if (start > end) { + return null; + } + + const root = new TreeNode(preorder[preorderIndex++]); + const index = map[root.val]; + + root.left = build(start, index - 1); + root.right = build(index + 1, end); + + return root; + } + + return build(0, inorder.length - 1); +}; + +// 시간복잡도: O(n) +// 공간복잡도: O(n) diff --git a/longest-palindromic-substring/uraflower.js b/longest-palindromic-substring/uraflower.js new file mode 100644 index 000000000..cc9330f1f --- /dev/null +++ b/longest-palindromic-substring/uraflower.js @@ -0,0 +1,30 @@ +/** + * @param {string} s + * @return {string} + */ +const longestPalindrome = function(s) { + let substr = ''; + + for (let i = 0; i < s.length; i++) { + const oddStr = getCurrentLongestPalindrome(i, i); // s[i] 기준 양옆으로 뻗어나감 => length는 항상 홀수 + const evenStr = getCurrentLongestPalindrome(i, i+1); // s[i] + s[i+1] 기준 양옆으로 => length는 항상 짝수 + + if (substr.length < oddStr.length) substr = oddStr; + if (substr.length < evenStr.length) substr = evenStr; + } + + // 기준 문자열을 포함하면서 가장 긴 팰린드롬을 찾아 반환하는 함수 (기준 문자열: s.slice(l, r + 1)) + const getCurrentLongestPalindrome = function (l, r) { + while (0 <= l && r < s.length && s[l] === s[r]) { + l--; + r++; + } + + return s.slice(l+1, r); + } + + return substr; +}; + +// 시간복잡도: O(n^2) +// 공간복잡도: O(s) (s: substr.length) diff --git a/rotate-image/uraflower.js b/rotate-image/uraflower.js new file mode 100644 index 000000000..91f7dfb13 --- /dev/null +++ b/rotate-image/uraflower.js @@ -0,0 +1,52 @@ +/** + * @param {number[][]} matrix + * @return {void} Do not return anything, modify matrix in-place instead. + */ +const rotate = function (matrix) { + const n = matrix.length; + + // 상하좌우 겉에서부터 한 겹씩 안으로 진입 + for (let layer = 0; layer < Math.floor(n / 2); layer++) { + + // layer 위의 각 칸들을 rotate + // 하나를 rotate했을 때 연쇄적으로 rotate되는 규칙을 이용해 + // 네 칸 rotate하는 걸 x번 반복함 + + // x = n - 2 * layer - 1 + // 전체 배열 크기 n에서 layer만큼 안쪽으로 들어가야 하는데 + // 상하, 좌우만큼 들어가야 하니까 2를 곱함 + // 1을 안 빼면 이미 rotate한 자리를 다시 rotate하므로 빼줌 + + for (let i = 0; i < n - 2 * layer - 1; i++) { + const top = layer; + const bottom = n - 1 - layer; + const left = top; + const right = bottom; + + const topLeft = matrix[top][left + i]; + matrix[top][left + i] = matrix[bottom - i][left]; + matrix[bottom - i][left] = matrix[bottom][right - i]; + matrix[bottom][right - i] = matrix[top + i][right]; + matrix[top + i][right] = topLeft; + } + } +}; + +// 시간복잡도: O(n^2) +// 공간복잡도: O(1) + +// 참고로 중첩 구조를 다음과 같이 바꿔도 됨 +const top = 0; +const bottom = n - 1; + +while (top < bototm) { + const left = top; + const right = bottom; + + for (let i = top; i < bottom; i ++) { + // rotate + } + + top++; + bottom--; +} diff --git a/subtree-of-another-tree/uraflower.js b/subtree-of-another-tree/uraflower.js new file mode 100644 index 000000000..150a2a1aa --- /dev/null +++ b/subtree-of-another-tree/uraflower.js @@ -0,0 +1,49 @@ +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @param {TreeNode} subRoot + * @return {boolean} + */ +const isSubtree = function(root, subRoot) { + const isSame = function(node1, node2) { + if (!node1 && !node2) return true; + if (node1?.val !== node2?.val) return false; + + return isSame(node1.left, node2.left) && isSame(node1.right, node2.right); + } + + const queue = [root]; + + while (queue.length) { + const node = queue.shift(); + + if (node.left) { + queue.push(node.left); + } + + if (node.right) { + queue.push(node.right); + } + + if (isSame(node, subRoot)) { + return true; + } + } + + return false; +}; + +// 다른 접근법: +// 정렬해서 비교하는 방식 => left, right 구분이 안가는 문제 +// 정렬할 때 left, right 정보를 포함해서 직렬화하면 됨 +// 이렇게 하면 복잡도 면에서 성능이 더 좋음 + +// 시간복잡도: O(n * m) (n: root size, m: subroot size) +// 공간복잡도: O(n + m)