diff --git a/wch2208/102_Binary_Tree_Level_Order_Traversal.js b/wch2208/102_Binary_Tree_Level_Order_Traversal.js new file mode 100644 index 0000000..3ce0c98 --- /dev/null +++ b/wch2208/102_Binary_Tree_Level_Order_Traversal.js @@ -0,0 +1,50 @@ +/** + * 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 + * @return {number[][]} + */ +var levelOrder = function (root) { + // 이 문제는 같은 레벨을 순회해야한다. + // BFS(너비 우선 탐색) 사용 + + // 빈 트리 처리 + if (!root) return []; + + // 초기화 result, queue + const result = []; + const queue = [root]; // TreeNode를 배열로 감싸서 배열 메서드(push, shift)를 사용할 수 있게 함 + + while (queue.length > 0) { + const levelSize = queue.length; // 현재 레벨에서 순회할 횟수 + const currentLevel = []; // 현재 레벨에 값들을 담을 배열 + + for (let i = 0; i < levelSize; i++) { + const node = queue.shift(); // 큐에서 가장 앞의 노드 추출 + + currentLevel.push(node.val); // 추출한 노드의 값을 현재 레벨 배열에 추가 + + // 다음 레벨 노드들을 큐에 추가 + if (node.left) { + queue.push(node.left); + } + if (node.right) { + queue.push(node.right); + } + } + + // for문이 끝나면 하나의 레벨 탐색이 끝난 것이므로 result에 추가 + result.push(currentLevel); + } + + return result; +}; + +// time: 1h 27m +// memory: 55.19MB diff --git a/wch2208/213_House_Robber_II.js b/wch2208/213_House_Robber_II.js new file mode 100644 index 0000000..8a12941 --- /dev/null +++ b/wch2208/213_House_Robber_II.js @@ -0,0 +1,48 @@ +/** + * @param {number[]} nums + * @return {number} + */ +var rob = function (nums) { + // 문제이해 + // 1. 인접한 집은 털 수 없는 경우, 최대로 많은 금액을 훔치면 얼마? (DP 계산) + // 추가 조건: 집이 원형으로 배치되서 첫 집과 마지막 집이 인접하다. + // 접근법 + // DP를 활용해서 최대값을 구하는 함수를 만든다. (1단계) + // 추가 조건을 고려한 추가 로직을 작성. (2단계) + // 2단계는 1단계 함수에 nums를 바꿔서 두 번 실행하고 비교한다. + // 왜냐하면 첫번째 집을 턴 경우는 마지막 집을 제거하면 되고 + // 첫번째 집을 안 턴 경우는 첫번째 집을 제외하고 DP를 계산하면 된다. + + // 예외 처리: 집이 1개인 경우 + if (nums.length === 1) return nums[0]; + + // dp로 최대값 구하는 함수 + const simpleRob = houses => { + // 예외 처리: 집이 1개인 경우 + if (houses.length === 1) return houses[0]; + + // dp 배열 생성 + let dp = new Array(houses.length); + + // 기저 조건 + dp[0] = houses[0]; + dp[1] = Math.max(houses[0], houses[1]); + + // for문으로 순회하면서 점화식 수행 + for (let i = 2; i < houses.length; i++) { + dp[i] = Math.max(houses[i] + dp[i - 2], dp[i - 1]); + } + + // 마지막 요소에 최대값이 담김 + return dp[dp.length - 1]; + }; + + // 첫집 턴 경우, 안 턴 경우 중 큰 값 + return Math.max( + simpleRob(nums.slice(0, nums.length - 1)), + simpleRob(nums.slice(1)) + ); +}; + +// time: 41m +// memory: 48.78MB diff --git a/wch2208/39_Combination_Sum.js b/wch2208/39_Combination_Sum.js new file mode 100644 index 0000000..eab5510 --- /dev/null +++ b/wch2208/39_Combination_Sum.js @@ -0,0 +1,36 @@ +/** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ +var combinationSum = function (candidates, target) { + const result = []; + + const findCombinations = (remaining, current, start) => { + // 종료조건 + if (remaining === 0) { + result.push([...current]); + return; + } + + if (remaining < 0) return; + + // 현재 단계에서 할 일 + for (let i = start; i < candidates.length; i++) { + // 1. 현재 숫자를 선택 + current.push(candidates[i]); + + // 2. 이 숫자를 선택한 상태에서 재귀 호출 + findCombinations(remaining - candidates[i], current, i); + + // 3. 백트래킹: 현재 숫자를 제거하고 다른 가능성 시도 + current.pop(); + } + }; + + findCombinations(target, [], 0); + + return result; +}; + +// time: 44m diff --git a/wch2208/572_Subtree_of_Another_Tree.js b/wch2208/572_Subtree_of_Another_Tree.js new file mode 100644 index 0000000..0114fbb --- /dev/null +++ b/wch2208/572_Subtree_of_Another_Tree.js @@ -0,0 +1,38 @@ +/** + * 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} + */ +var isSubtree = function (root, subRoot) { + if (!root) return false; + if (!subRoot) return true; // 서브 루트가 없으면 비교할 필요 없이 true + + // 현재 노드 비교 + if (isSameTree(root, subRoot)) return true; + + // 재귀호출: 왼쪽, 오른쪽 서브트리 확인 + // 둘 중 한 곳에서 서브트리와 일치하는 트리가 있으면 true + return isSubtree(root.left, subRoot) || isSubtree(root.right, subRoot); +}; + +function isSameTree(p, q) { + if (!p && !q) return true; // 둘 다 null이면 true + if (!p || !q) return false; // 하나만 null이면 false + + // 현재 노드 비교 + if (p.val !== q.val) return false; + + // 재귀 호출: 왼쪽과 오른쪽 트리 모두 같아야 함 + return isSameTree(p.left, q.left) && isSameTree(p.right, q.right); +} + +// time: 43m +// memory: 54.73MB diff --git a/wch2208/743_Network_Delay_Time.js b/wch2208/743_Network_Delay_Time.js new file mode 100644 index 0000000..5848e44 --- /dev/null +++ b/wch2208/743_Network_Delay_Time.js @@ -0,0 +1,76 @@ +/** + * @param {number[][]} times + * @param {number} n + * @param {number} k + * @return {number} + */ +var networkDelayTime = function (times, n, k) { + // 문제이해: 숫자 리스트와 n, k가 입력된다. k부터 시작해서 모든 노드에 신호를 보내는데 걸리는 최소 시간을 구한다 모든 노드에 도달할 수 없다면 -1 + // times[i] = (ui, vi, wi) 이 형태를 보고 그래프 자료구조임을 파악할 수 있다. + // 시작점 하나 + 모든 노드까지의 최단 거리/시간 + 양의 가중치, 이 조건으로 그리디 알고리즘 중 다익스트라 알고리즘을 사용할 수 있다. + + // 1. 그래프 생성 + const graph = new Map(); + + for (let i = 1; i <= n; i++) { + graph.set(i, []); + } + // console.log(graph) + // Map(4) { 1 => [], 2 => [], 3 => [], 4 => [] } + + for (const [u, v, w] of times) { + graph.get(u).push([v, w]); + } + // console.log(graph) + // Map(3) { 1 => [], 2 => [ [ 1, 1 ], [ 3, 1 ] ], 3 => [ [ 4, 1 ] ] } + + // 2. 거리 배열 초기화 + const distances = new Array(n + 1).fill(Infinity); + distances[k] = 0; + + // console.log(distances) + // [ Infinity, Infinity, 0, Infinity, Infinity ] + + // 3. 방문 배열 초기화 + const visited = new Array(n + 1).fill(false); + // console.log(visited) + // [ false, false, false, false, false ] + + // 4. 다익스트라 알고리즘 구현 + for (let i = 1; i <= n; i++) { + // 4-1. 현재 최단 거리인 노드 찾기 + let minDist = Infinity; + let minNode = -1; + for (let node = 1; node <= n; node++) { + if (!visited[node] && distances[node] < minDist) { + minDist = distances[node]; + minNode = node; + } + } + + // 4-2. 도달할 수 없는 노드가 있는 경우 + if (minNode === -1) break; + + // 4-3. 노드 방문 처리 + visited[minNode] = true; + + // 4-4. 인접한 노드들의 거리 갱신 + for (const [nextNode, time] of graph.get(minNode)) { + const newDist = distances[minNode] + time; + if (newDist < distances[nextNode]) { + distances[nextNode] = newDist; + } + } + } + + // 5. 결과 계산 + let maxDist = 0; + for (let i = 1; i <= n; i++) { + if (distances[i] === Infinity) return -1; + maxDist = Math.max(maxDist, distances[i]); + } + + return maxDist; +}; + +// time: 1h 14m diff --git a/wch2208/7_Reverse_Integer.js b/wch2208/7_Reverse_Integer.js new file mode 100644 index 0000000..8cfc69e --- /dev/null +++ b/wch2208/7_Reverse_Integer.js @@ -0,0 +1,18 @@ +/** + * @param {number} x + * @return {number} + */ +var reverse = function (x) { + const isNegative = x < 0; + + const reversed = Math.abs(x).toString().split("").reverse().join(""); + + const result = Number(reversed); + + // 32비트 범위를 넘어가는지 확인 최소값보다 더 작거나 최대값보다 더 큰 경우 + if (result < Math.pow(-2, 31) || result > Math.pow(2, 31) - 1) return 0; + + return isNegative ? -result : result; +}; + +// time: 18m 43s