diff --git a/3sum/Zero-1016.ts b/3sum/Zero-1016.ts new file mode 100644 index 0000000000..31d12a22ef --- /dev/null +++ b/3sum/Zero-1016.ts @@ -0,0 +1,43 @@ +/** + * 시간 복잡도 O(n²) + * 공간 복잡도 O(log n) + * + * 접근: 오름차순 정렬 + 투 포인터 + * - 기준 인덱스 i를 고정하고, left = i + 1, right = 끝에서 탐색 + * - 정렬된 상태에서 같은 값을 건너뛰는 방식으로 중복 제거 (Set 불필요) + */ +function threeSum(nums: number[]): number[][] { + const result: number[][] = []; + nums.sort((a, b) => a - b); // 오름차순 정렬 + + for (let i = 0; i < nums.length - 2; i++) { + // 기준 숫자가 이전과 같으면 동일한 조합이 나오므로 건너뜀 + if (i > 0 && nums[i] === nums[i - 1]) continue; + + // 최적화: 기준 숫자가 양수면 뒤의 숫자들도 모두 양수 → 합이 0 불가능 + if (nums[i] > 0) break; + + let left = i + 1; + let right = nums.length - 1; + + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + + if (sum === 0) { + result.push([nums[i], nums[left], nums[right]]); + + while (left < right && nums[left] === nums[left + 1]) left++; + while (left < right && nums[right] === nums[right - 1]) right--; + + left++; + right--; + } else if (sum < 0) { + left++; + } else { + right--; + } + } + } + + return result; +} diff --git a/climbing-stairs/Zero-1016.ts b/climbing-stairs/Zero-1016.ts new file mode 100644 index 0000000000..d708cb82e5 --- /dev/null +++ b/climbing-stairs/Zero-1016.ts @@ -0,0 +1,19 @@ +/** + * 시간 복잡도 O(n) + * 공간 복잡도 O(n) + */ +function climbStairs(n: number): number { + // NOTE: n이 1일 경우 바로 종료 + if (n === 1) return 1; + + const dp = new Array(n).fill(0); + + dp[0] = 1; + dp[1] = 2; + + for (let i = 2; i < dp.length; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + + return dp[dp.length - 1]; +} diff --git a/product-of-array-except-self/Zero-1016.ts b/product-of-array-except-self/Zero-1016.ts new file mode 100644 index 0000000000..a52974b31e --- /dev/null +++ b/product-of-array-except-self/Zero-1016.ts @@ -0,0 +1,23 @@ +/** + * 시간복잡도 O(n) + * 공간복잡도 O(n) + */ +function productExceptSelf(nums: number[]): number[] { + const n_length = nums.length; + const result = new Array(n_length).fill(1); + + // NOTE: 왼쪽 -> 오른쪽, 오른쪽 -> 왼쪽 순회하면서 값을 누적함. + let left = 1; + for (let i = 0; i < n_length; i++) { + result[i] *= left; + left *= nums[i]; + } + + let right = 1; + for (let i = n_length - 1; i >= 0; i--) { + result[i] *= right; + right *= nums[i]; + } + + return result; +} diff --git a/valid-anagram/Zero-1016.ts b/valid-anagram/Zero-1016.ts new file mode 100644 index 0000000000..8cb23de94d --- /dev/null +++ b/valid-anagram/Zero-1016.ts @@ -0,0 +1,26 @@ +/** + * 242. Valid Anagram + * Given two strings s and t, return true if t is an anagram of s, and false otherwise. + * + * 시간복잡도: O(n) + * 공간복잡도: O(n) + */ +function isAnagram(s: string, t: string): boolean { + if (s.length !== t.length) return false; + + const sMap = new Map(); + const tMap = new Map(); + + // 문자열 s와 t를 순회하면서 각 문자의 빈도수를 계산 + for (let i = 0; i < s.length; i++) { + sMap.set(s[i], (sMap.get(s[i]) || 0) + 1); + tMap.set(t[i], (tMap.get(t[i]) || 0) + 1); + } + + // 문자열 s와 t의 빈도수를 비교 + for (const [key, value] of sMap) { + if (value !== tMap.get(key)) return false; + } + + return true; +} diff --git a/validate-binary-search-tree/Zero-1016.ts b/validate-binary-search-tree/Zero-1016.ts new file mode 100644 index 0000000000..b909a89138 --- /dev/null +++ b/validate-binary-search-tree/Zero-1016.ts @@ -0,0 +1,40 @@ +class TreeNode { + val: number; + left: TreeNode | null; + right: TreeNode | null; + constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { + this.val = val === undefined ? 0 : val; + this.left = left === undefined ? null : left; + this.right = right === undefined ? null : right; + } +} +type Frame = { + node: TreeNode | null; + min: number; + max: number; +}; + +/** + * 반복적 DFS로 각 노드가 부모 노드가 정한 값 범위 (min, max) 안에 있는지 검사한다. + * - 왼쪽 자식으로 내려가면 상한(max)이 현재 노드 값으로 좁아지고, + * - 오른쪽 자식으로 내려가면 하한(min)이 현재 노드 값으로 좁아진다. + * + * 시간 복잡도: O(n) — 모든 노드를 한 번씩 방문 + * 공간 복잡도: O(n) — 최악의 경우 스택 크기 (평균적으로는 O(h)) + */ +function isValidBST(root: TreeNode | null): boolean { + const stack: Frame[] = [{ node: root, min: -Infinity, max: Infinity }]; + + while (stack.length > 0) { + const { node, min, max } = stack.pop()!; + if (!node) continue; + + // NOTE 부모 노드들이 정한 범위를 벗어나면 유효한 BST가 아니다. + if (node.val <= min || node.val >= max) return false; + + stack.push({ node: node.left, min, max: node.val }); + stack.push({ node: node.right, min: node.val, max }); + } + + return true; +}