-
-
Notifications
You must be signed in to change notification settings - Fork 361
[togo26] WEEK 03 Solutions #2723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
f1e8a69
8d84daf
858598f
de255d3
1f68a57
54577e5
245a1b1
b71c9fa
b7af1f8
399b3a3
67296f6
ff613e8
a247f61
a272e08
66d06d3
f3179e9
2821b32
bc0a604
fc6f67f
fa8fa00
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| /** | ||
| * @param {number[]} candidates | ||
| * @param {number} target | ||
| * @return {number[][]} | ||
| */ | ||
|
|
||
| // C = candidates, T = target, m = smallest candidate value | ||
| // TC: O(C^(T/m)) -> 후보와 타겟 깊이 간 지수적 증가 | ||
| // SC: O(T/m) -> 재귀 스택 깊이 (*출력 결과는 복잡도 계산 제외) | ||
| var combinationSum = function (candidates, target) { | ||
| const result = []; | ||
|
|
||
| function go(candi, combi, acc, start) { | ||
| if (acc === target) { | ||
| result.push(combi); | ||
| return; | ||
| } | ||
|
|
||
| for (let i = start; i < candi.length; i++) { | ||
| const newAcc = acc + candi[i]; | ||
| const newCombi = [...combi, candi[i]]; | ||
| if (newAcc > target) break; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 정렬을 추가해서 연산 수를 줄이는 것도 좋은 아이디어네요 👍
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 애초에 정렬을 해서 불필요한 탐색을 줄이는 방법이 있네요 👍🏼 |
||
| go(candi, newCombi, newAcc, i); | ||
| } | ||
| } | ||
|
|
||
| go( | ||
| [...candidates].sort((a, b) => a - b), // 값 순서 보장으로 새로운 누적 값에서 조기 종료 가능. 오름차순에 따라 newAcc가 target을 넘어가면 이후의 후보들은 계산할 필요 없음. | ||
| [], | ||
| 0, | ||
| 0, | ||
| ); | ||
|
|
||
| return result; | ||
| }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
|
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(n) | O(n) | ✅ |
| Space | O(n) | O(n) | ✅ |
피드백: 가능한 한도에서 중복 부분 문제를 피하며 재귀 호출에 대한 결과를 저장한다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2: numDecodings — Time: ✅ O(n) → O(n) / Space: ✅ O(n) → O(n)
| 유저 분석 | 실제 분석 | 결과 | |
|---|---|---|---|
| Time | O(n) | O(n) | ✅ |
| Space | O(n) | O(n) | ✅ |
피드백: 입력 문자열 길이에 따라 선형적으로 동작하며 중복 계산을 피한다.
개선 제안: 현재 구현이 적절해 보입니다.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| /** | ||
| * @param {string} s | ||
| * @return {number} | ||
| */ | ||
|
|
||
| // TLE | ||
| // var numDecodings = function(s) { | ||
| // const codeMap = {}; | ||
| // for (let i = 1; i <= 26; i++) { | ||
| // codeMap[i] = String.fromCharCode(64 + i); | ||
| // } | ||
|
|
||
| // const memo = {}; | ||
| // let count = 0; | ||
| // function go(string, start) { | ||
| // if (start >= string.length) { | ||
| // count++; | ||
| // return; | ||
| // } | ||
|
|
||
| // const firstDigit = string[start]; | ||
| // const secondDigit = string[start + 1]; | ||
| // if (firstDigit === "0") return; | ||
|
|
||
| // if (codeMap[firstDigit]) { | ||
| // go(string, start + 1); | ||
| // } | ||
|
|
||
| // const twoDigits = firstDigit + secondDigit; | ||
| // if (Number(twoDigits) <= 26 && codeMap[twoDigits]) { | ||
| // go(string, start + 2); | ||
| // } | ||
| // } | ||
|
|
||
| // go(s, 0, ""); | ||
|
|
||
| // return count; | ||
| // }; | ||
|
|
||
| // TC: O(n) -> string 길이만큼 | ||
| // SC: O(n) -> string 길이만큼 스택 생성 | ||
| var numDecodings = function (s) { | ||
| const codeMap = {}; | ||
| for (let i = 1; i <= 26; i++) { | ||
| codeMap[i] = String.fromCharCode(64 + i); | ||
| } | ||
|
|
||
| const memo = {}; | ||
|
|
||
| function go(start) { | ||
| if (start >= s.length) return 1; | ||
| if (memo[start] !== undefined) return memo[start]; | ||
|
|
||
| const firstDigit = s[start]; | ||
| const secondDigit = s[start + 1]; | ||
|
|
||
| if (firstDigit === '0') return 0; | ||
|
|
||
| let ways = 0; | ||
|
|
||
| ways += go(start + 1); | ||
| const twoDigits = firstDigit + secondDigit; | ||
| if (Number(twoDigits) <= 26) { | ||
| ways += go(start + 2); | ||
| } | ||
|
|
||
| memo[start] = ways; | ||
|
|
||
| return ways; | ||
| } | ||
|
|
||
| return go(0); | ||
| }; |
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. O(n³)부터 O(n²), O(n)까지 모든 풀이를 남겨주셔서 시간 복잡도가 개선되는 과정이 잘 보여 인상 깊었습니다!
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 이전 누적값과 현재 값을 비교해 최적의 부분해를 갱신한다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /** | ||
| * @param {number[]} nums | ||
| * @return {number} | ||
| */ | ||
|
|
||
| // TLE | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 시도한 코드까지 정리해 두는 꼼꼼함..! 저도 배워갑니다 👍🏼👍🏼 |
||
| // var maxSubArray = function(nums) { | ||
| // if (nums.length <= 1) return nums[0]; | ||
|
|
||
| // const sum = (arr) => arr.reduce((acc, cur) => { | ||
| // acc += cur; | ||
| // return acc; | ||
| // }, 0); | ||
|
|
||
| // let total = -Infinity; | ||
| // for (let i = 0; i < nums.length; i++) { | ||
| // for (let j = i; j < nums.length; j++) { | ||
| // const result = sum(nums.slice(i, j + 1)); | ||
| // total = Math.max(total, result); | ||
| // } | ||
| // } | ||
|
|
||
| // return total; | ||
| // }; | ||
|
|
||
| // TLE | ||
| // var maxSubArray = function(nums) { | ||
| // if (nums.length <= 1) return nums[0]; | ||
|
|
||
| // let total = -Infinity; | ||
| // for (let i = 0; i < nums.length; i++) { | ||
| // let acc = 0; | ||
| // for (let j = i; j < nums.length; j++) { | ||
| // acc += nums[j]; | ||
| // total = Math.max(total, acc); | ||
| // } | ||
| // } | ||
|
|
||
| // return total; | ||
| // }; | ||
|
|
||
| // TC: O(n) / SC: O(1) | ||
| var maxSubArray = function (nums) { | ||
| let total = nums[0]; | ||
| let acc = nums[0]; | ||
|
|
||
| for (let i = 1; i < nums.length; i++) { | ||
| acc = Math.max(nums[i], acc + nums[i]); // 이전 누적값 선택 vs 현재값 선택 (카데인 알고리즘) | ||
| total = Math.max(total, acc); // 최대 합산 값 갱신 | ||
| } | ||
|
|
||
| return total; | ||
| }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 비트 카운트는 다양한 구현이 가능하나 주석에 따라 방법이 다름. 개선 제안: 현재 구현들이 모두 동일한 문제를 해결하므로 하나를 선택해 일관성 있게 유지하는 것이 좋습니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| /** | ||
| * @param {number} n | ||
| * @return {number} | ||
| */ | ||
|
|
||
| // 31 비트 범위는 상수로 취급 | ||
|
|
||
| // Log-based calculation | ||
| // TC: O(1) / SC: O(1) | ||
| // 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 덕분에 저도 자료형의 허용 범위 이상인 경우를 생각해봐야 한다는 걸 느꼈어요 👍🏼 |
||
| var hammingWeight = function (n) { | ||
| const BIT_RANGE = 31; | ||
| let bits = Array.from({ length: BIT_RANGE }).fill(0); | ||
|
|
||
| let remainder = n; | ||
| while (remainder > 0) { | ||
| const exp = Math.min(Math.floor(Math.log2(remainder)), BIT_RANGE - 1); | ||
| bits[exp] = true; | ||
| const value = remainder <= 1 ? 1 : Math.pow(2, exp); | ||
| remainder -= value; | ||
| } | ||
|
|
||
| return bits.filter(value => value).length; | ||
| }; | ||
|
|
||
| // Log-based calculation without an array | ||
| // TC: O(1) / SC: O(1) | ||
| // 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(1) | ||
| var hammingWeight = function (n) { | ||
| const BIT_RANGE = 31; | ||
| let bitCount = 0; | ||
|
|
||
| let remainder = n; | ||
| while (remainder > 0) { | ||
| const exp = Math.min(Math.floor(Math.log2(remainder)), BIT_RANGE - 1); | ||
| const value = remainder <= 1 ? 1 : Math.pow(2, exp); | ||
| remainder -= value; | ||
| bitCount++; | ||
| } | ||
|
|
||
| return bitCount; | ||
| }; | ||
|
|
||
| // Converting binary string with the JS feature | ||
| // TC: O(1) / SC: O(1) | ||
| // 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) | ||
| var hammingWeight = function (n) { | ||
| const binaryString = n.toString(2); | ||
| return [...binaryString].filter(v => v === '1').length; | ||
| }; | ||
|
|
||
| // Bit manipulation | ||
| // TC: O(1) / SC: O(1) | ||
| // 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) | ||
| var hammingWeight = function (n) { | ||
| let count = 0; | ||
|
|
||
| while (n > 0) { | ||
| n = n & (n - 1); | ||
| count++; | ||
| } | ||
|
|
||
| return count; | ||
| }; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 정규식 제거/두 포인터 방식으로 불필요한 문자열 생성 없이 해결한다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| /** | ||
| * @param {string} s | ||
| * @return {boolean} | ||
| */ | ||
| // TC: O(n) / SC: O(n) | ||
| var isPalindrome = function (s) { | ||
| const processed = s.toLowerCase().replaceAll(/[^a-z0-9]+/g, ''); | ||
| const reversed = [...processed].reverse().join(''); | ||
| return processed === reversed; | ||
| }; | ||
|
|
||
| // TC: O(n) / SC: O(1) | ||
| // With two pointers | ||
| var isPalindrome = function (s) { | ||
| const nonAlphanumeric = /[^a-zA-Z0-9]+/; | ||
| let left = 0; | ||
| let right = s.length - 1; | ||
|
|
||
| while (left < right) { | ||
| while (left < right && nonAlphanumeric.test(s[left])) left++; // 단일 문자 테스트 O(1) 상수 취급 | ||
| while (left < right && nonAlphanumeric.test(s[right])) right--; | ||
| if (s[left].toLowerCase() !== s[right].toLowerCase()) return false; | ||
| left++; | ||
| right--; | ||
| } | ||
|
|
||
| return true; | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 정렬된 후보를 이용해 불필요한 경로를 조기에 차단하고, 같은 후보를 재사용하여 조합을 구성한다.
개선 제안: 현재 구현이 적절해 보입니다.