Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions combination-sum/togo26.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Backtracking, Depth-First Search, Greedy
  • 설명: 백트래킹으로 모든 조합을 탐색하고, 목표값(target)에 도달하면 해를 추가합니다. 오름차순 정렬 후 누적합이 target을 넘으면 더 탐색을 중단하는 가지치기(pruning)로 DFS를 사용합니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(C^(T/m)) O(k * n^k)
Space O(T/m) O(target 깊이)

피드백: 정렬된 후보를 이용해 불필요한 경로를 조기에 차단하고, 같은 후보를 재사용하여 조합을 구성한다.

개선 제안: 현재 구현이 적절해 보입니다.

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;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

정렬을 추가해서 연산 수를 줄이는 것도 좋은 아이디어네요 👍

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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;
};
73 changes: 73 additions & 0 deletions decode-ways/togo26.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Dynamic Programming, Hash Map / Hash Set, Bit Manipulation
  • 설명: 문제는 문자열의 각 위치에서 한 글자 또는 두 글자를 해석해 경우의 수를 누적하는 DP 접근이다. memo 배열로 중복 계산을 피하는 구조가 DP의 핵심이며, 코드 맵은 dp 상태를 캐싱하기 위한 해시 맵을 활용한다.

📊 시간/공간 복잡도 분석

ℹ️ 이 파일에는 2가지 풀이가 포함되어 있어 각각 분석합니다.

풀이 1: numDecodings — Time: ✅ O(n) → O(n) / Space: ✅ O(n) → O(n)
유저 분석 실제 분석 결과
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);
};
53 changes: 53 additions & 0 deletions maximum-subarray/togo26.js

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

O(n³)부터 O(n²), O(n)까지 모든 풀이를 남겨주셔서 시간 복잡도가 개선되는 과정이 잘 보여 인상 깊었습니다!

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Greedy, Dynamic Programming
  • 설명: 카데인 알고리즘으로 알려진 최대 부분 배열 문제 풀이로, 현재 값을 비교해 누적합을 업데이트하고 전체 최댓값을 갱신합니다. 이는 최적해를 한 번의 선형 순회로 얻는 Greedy + DP 성격의 패턴에 해당합니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(n) O(n)
Space O(n) O(1)

피드백: 이전 누적값과 현재 값을 비교해 최적의 부분해를 갱신한다.

개선 제안: 현재 구현이 적절해 보입니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/**
* @param {number[]} nums
* @return {number}
*/

// TLE

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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;
};
64 changes: 64 additions & 0 deletions number-of-1-bits/togo26.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Bit Manipulation, Greedy, Divide and Conquer
  • 설명: 주 코드의 핵심은 이진수를 다루는 비트 연산(Bit Manipulation)을 통해 1의 개수를 구하는 패턴이다. 일부 구현은 로그 기반 탐색과 반복 감소로 구성되며, 대표적으로 비트를 제거하는 n & (n-1) 기법이 있다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(logn) O(1)
Space O(logn) O(1)

피드백: 비트 카운트는 다양한 구현이 가능하나 주석에 따라 방법이 다름.

개선 제안: 현재 구현들이 모두 동일한 문제를 해결하므로 하나를 선택해 일관성 있게 유지하는 것이 좋습니다.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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;
};
28 changes: 28 additions & 0 deletions valid-palindrome/togo26.js

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🏷️ 알고리즘 패턴 분석

  • 패턴: Two Pointers, Greedy
  • 설명: 두 포인터(left, right)를 사용해 문자열의 양쪽에서 비교하는 구간 탐색 방식이 핵심이며, 공통적으로 부분 문자열 비교를 통해 조건을 만족하는지 판단합니다. 또한 직접적인 최적화 없이 대칭 비교를 통해 문제를 해결하는 점에서 그리디적 기법과도 연결됩니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(n) O(n)
Space O(n) O(1)

피드백: 정규식 제거/두 포인터 방식으로 불필요한 문자열 생성 없이 해결한다.

개선 제안: 현재 구현이 적절해 보입니다.

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;
};
Loading