From 0cc56e186418a0898f0394c48beace991fdc4932 Mon Sep 17 00:00:00 2001 From: Hyejin Date: Fri, 11 Apr 2025 21:08:35 +0900 Subject: [PATCH 1/5] Valid Anagram solution --- valid-anagram/clara-shin.js | 54 +++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 valid-anagram/clara-shin.js diff --git a/valid-anagram/clara-shin.js b/valid-anagram/clara-shin.js new file mode 100644 index 000000000..d8aaf317b --- /dev/null +++ b/valid-anagram/clara-shin.js @@ -0,0 +1,54 @@ +/** + * 두 문자열 s와 t가 서로 애너그램(anagram)인지 확인하는 문제 + * 애너그램: 글자의 배열 순서는 다르지만 구성하는 문자와 각 문자의 개수가 동일한 문자열 + * + * 접근 방법 몇 가지: + * 1. 정렬 방식: 두 문자열을 알파벳 순으로 정렬한 후 비교하는 방법 + * 2-1. 문자 카운팅 방식 (객체활용): 각 문자열에 등장하는 문자들의 빈도수를 계산하여 비교하는 방법 + * 2-2. 문자 카운팅 방식 (해시맵 객체활용): 문자를 키로, 빈도수를 값으로 하는 해시맵을 만들어 비교하는 방법 + * + * 팔로우업 질문: 입력에 유니 코드 문자가 포함되어 있으면 어떻게 해? 그러한 경우에 솔루션을 어떻게 조정할꺼야? + * JavaScript의 문자열은 기본적으로 UTF-16으로 인코딩되서 유니코드 문자도 처리 가능 + * Map 객체는 어떤 타입의 키도 허용 ➡️ 유니코드 문자를 키로 사용하는 데 문제가 없음 + * (방법 2-1)의 일반 객체도 유니코드 문자를 키로 지원하지만, Map을 사용하는 것이 좀 더 안전 => 해시맵으로 구현 ㄱㄱ + */ + +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +function isAnagram(s, t) { + // 길이가 다르면 애너그램이 될 수 없음 (빠른 리턴) + if (s.length !== t.length) { + return false; + } + + const charMap = new Map(); + + // 첫 번째 문자열의 각 문자 카운트 증가 + for (let char of s) { + charMap.set(char, (charMap.get(char) || 0) + 1); + } + + // 두 번째 문자열의 각 문자 카운트 감소 + for (let char of t) { + const count = charMap.get(char); + + // 해당 문자가 없거나 카운트가 0이면 애너그램이 아님 + if (!count) { + return false; + } + + charMap.set(char, count - 1); + } + + // 모든 카운트가 0인지 확인 + for (let count of charMap.values()) { + if (count !== 0) { + return false; + } + } + + return true; +} From 547e881ffd9b690aa8513cdada5edea3eb818629 Mon Sep 17 00:00:00 2001 From: Hyejin Date: Fri, 11 Apr 2025 21:28:22 +0900 Subject: [PATCH 2/5] Climbing Stairs solution --- climbing-stairs/clara-shin.js | 41 +++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 climbing-stairs/clara-shin.js diff --git a/climbing-stairs/clara-shin.js b/climbing-stairs/clara-shin.js new file mode 100644 index 000000000..3ebd69a77 --- /dev/null +++ b/climbing-stairs/clara-shin.js @@ -0,0 +1,41 @@ +/** + * 계단을 오르는 방법은 몇 가지? + * 총 n개의 계단 + * 한 번에 1계단 또는 2계단을 오를 수 있다 + * 계단 꼭대기에 도달하는 서로 다른 방법의 수를 구해보자 + * + * ✨동적 프로그래밍(Dynamic Programming)✨ + * 재귀 접근법 + * 메모이제이션을 사용한 재귀 + * 동적 프로그래밍(바텀업) + * 피보나치 수열: n번째 계단에 도달하는 방법의 수는 (n-1)번째와 (n-2)번째 계단에 도달하는 방법의 수의 합과 같음 + */ + +/** + * @param {number} n + * @return {number} + */ +function climbStairs(n) { + // 예외 처리: n이 1 이하인 경우 빨리 1 리턴하고 탈출 + if (n <= 1) { + return 1; + } + + // 피보나치 수열 계산을 위한 변수 + let first = 1; // f(0) = 1 + let second = 1; // f(1) = 1 + let result; + + // 피보나치 수열 계산 + for (let i = 2; i <= n; i++) { + result = first + second; + first = second; + second = result; + } + + return result; +} + +// 시간 복잡도: O(n) - n번의 반복문을 수행 +// 공간 복잡도: O(1) - 상수 개의 변수만 사용 +// 제약조건이 n은 최대 45까지니까 피보나치 접근법이 좋은 선택인 듯 From 92777cc7822fca8941648c6ceb6ebc5e66a914ea Mon Sep 17 00:00:00 2001 From: Hyejin Date: Sun, 13 Apr 2025 15:14:06 +0900 Subject: [PATCH 3/5] Product of Array Except Self solution --- product-of-array-except-self/clara-shin.js | 45 ++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 product-of-array-except-self/clara-shin.js diff --git a/product-of-array-except-self/clara-shin.js b/product-of-array-except-self/clara-shin.js new file mode 100644 index 000000000..cd89993e0 --- /dev/null +++ b/product-of-array-except-self/clara-shin.js @@ -0,0 +1,45 @@ +/** + * 문제: 배열에서 자신을 제외한 모든 원소의 곱을 구하는 함수 구하기 + * 제약조건: 나눗셈 사용하지 않고, O(n) 시간 복잡도로 풀어야 함 + * + * Brute Force 방법으로는 각 원소에 대해 나머지 원소들의 곱을 구하는 방법이 있지만, + * 이는 O(n^2) 시간 복잡도를 가지므로 제약조건에 어긋남 + * + * O(n) 시간 복잡도로 해결하기 위해서는, + * 두 번의 순회를 통해 각 원소의 왼쪽과 오른쪽 곱을 따로 계산한 후, + * 최종적으로 두 곱을 곱해주는 방법을 사용할 수 있음 + * + * 이 방법은 추가적인 공간을 사용하지 않고, + * 결과를 저장하는 배열을 사용하여 각 원소의 왼쪽 곱을 저장한 후 + * 오른쪽 곱을 계산하여 결과 배열에 곱해주는 방식으로 구현됨 + * + * 접근방법: + * 1. 왼쪽에서 오른쪽으로 누적 곱을 계산 (prefix 곱). + * 2. 오른쪽에서 왼쪽으로 누적 곱을 계산 (suffix 곱). + * 3. 각 위치 i에 대해 i 위치 이전까지의 prefix 곱과 i 위치 이후의 suffix 곱을 곱하여 결과를 저장 + */ + +/** + * @param {number[]} nums - 입력 배열 + * @return {number[]} - 결과 배열 (answer[i]는 nums[i]를 제외한 모든 원소의 곱) + */ +var productExceptSelf = function (nums) { + const n = nums.length; + + // 최종 리턴 할 결과 배열 초기화 (O(1) 추가 공간을 위해 출력 배열만 사용) + const result = Array.from({ length: n }, () => 1); + + let leftProduct = 1; + for (let i = 0; i < n; i++) { + result[i] = leftProduct; // 현재 위치 이전까지의 누적 곱 저장 + leftProduct *= nums[i]; // 다음 위치를 위해 현재 원소 곱해서 중간결과(result) 업데이트 + } + + let rightProduct = 1; + for (let i = n - 1; i >= 0; i--) { + result[i] *= rightProduct; // 현재 결과에 오른쪽 누적 곱을 곱해줌 + rightProduct *= nums[i]; // 다음 위치를 위해 현재 원소 곱함 + } + + return result; +}; From ae1880b56cd7c9ee620ab8251d2c3b33119ad18d Mon Sep 17 00:00:00 2001 From: Hyejin Date: Sun, 13 Apr 2025 16:18:40 +0900 Subject: [PATCH 4/5] 3Sum solution --- 3sum/clara-shin.js | 76 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) create mode 100644 3sum/clara-shin.js diff --git a/3sum/clara-shin.js b/3sum/clara-shin.js new file mode 100644 index 000000000..8c9bf6ffe --- /dev/null +++ b/3sum/clara-shin.js @@ -0,0 +1,76 @@ +/** + * 배열에서 합이 0이 되는 세 숫자를 찾기 + * + * 요구사항 & 제약조건: + * 세 개의 다른 인덱스에 있는 숫자들의 합이 0이 되어야 함 + * 결과에 중복된 조합이 없어야 함 + * 배열 크기는 3에서 3000까지 + * 배열 요소는 -10^5부터 10^5까지의 정수 + * + * 접근방법: + * 1. 브루트포스(이중for문, 완전탐색) - O(n^3) 시간복잡도 + * 2. 정렬 후 투 포인터 - O(n^2) 시간복잡도 ✅ + * + * 배열을 정렬 후, 배열을 순회하며 각 요소를 첫 번째 숫자로 고정 + * 고정된 숫자 다음부터 ~ 배열 끝까지 투 포인터를 사용 ➡️ 나머지 두 수의 합이 첫 번째 수의 음수가 되는 조합 찾기 + * 중복을 피하기 위해 같은 값을 가진 연속된 요소는 스킵 + */ + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function (nums) { + const result = []; + const n = nums.length; + + if (n < 3) return result; + + nums.sort((a, b) => a - b); // 오름차순 정렬 + + if (nums[0] > 0 || nums[n - 1] < 0) return result; + + // 배열을 순회하며 첫 번째 숫자 고정 + for (let i = 0; i < n - 2; i++) { + // 중복된 값 건너뛰기 (첫 번째 숫자) + if (i > 0 && nums[i] === nums[i - 1]) continue; + + // 첫 번째 숫자가 양수면 for문 탈출 + if (nums[i] > 0) break; + + // 현재 숫자와 가장 작은 두 숫자의 합 > 0 => 탈출 + if (nums[i] + nums[i + 1] + nums[i + 2] > 0) break; + + // 현재 숫자와 가장 큰 두 숫자의 합 < 0 => 건너뛰기 + if (nums[i] + nums[n - 2] + nums[n - 1] < 0) continue; + + // 두 번째, 세 번째 숫자를 찾기 위한 투 포인터 + let left = i + 1; + let right = n - 1; + + while (left < right) { + const sum = nums[i] + nums[left] + nums[right]; + + if (sum < 0) { + // 합이 0보다 작으면 left 포인터를 오른쪽으로 이동 + left++; + } else if (sum > 0) { + // 합이 0보다 크면 right 포인터를 왼쪽으로 이동 + right--; + } else { + // 합이 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--; + } + } + } + + return result; +}; From 63e1a6eb3231e0be53c100876ed55383735440cb Mon Sep 17 00:00:00 2001 From: Hyejin Date: Sun, 13 Apr 2025 16:44:25 +0900 Subject: [PATCH 5/5] Validate Binary Search Tree solution --- validate-binary-search-tree/clara-shin.js | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 validate-binary-search-tree/clara-shin.js diff --git a/validate-binary-search-tree/clara-shin.js b/validate-binary-search-tree/clara-shin.js new file mode 100644 index 000000000..54cab783d --- /dev/null +++ b/validate-binary-search-tree/clara-shin.js @@ -0,0 +1,32 @@ +/** + * 이진탐색트리 유효성 검사 (Valid Binary Search Tree) 확인하는 함수 만들기 + * 검사할 조건 3가지 + * 1. 왼쪽 서브트리의 모든 노드 값은 루트 노드 값보다 작아야 한다. + * 2. 오른쪽 서브트리의 모든 노드 값은 루트 노드 값보다 커야 한다. + * 3. 왼쪽 서브트리와 오른쪽 서브트리도 각각 이진탐색트리여야 한다. + */ + +/** + * 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) + * } + * val: 노드가 저장하는 값입니다. + * left: 왼쪽 자식 노드를 가리키는 참조 + * right: 오른쪽 자식 노드를 가리키는 참조 + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function (root) { + const searchBST = (node, min, max) => { + if (node === null) return true; + if (node.val <= min || node.val >= max) return false; + return searchBST(node.left, min, node.val) && searchBST(node.right, node.val, max); + }; + + return searchBST(root, -Infinity, Infinity); +};