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
44 changes: 44 additions & 0 deletions combination-sum/smosco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Combination Sum
*
* 핵심 아이디어:
* - DFS + 백트래킹으로 모든 조합 탐색
* - 같은 숫자를 여러 번 사용 가능
* - start 인덱스로 중복 조합 방지 ([2,3]과 [3,2] 같은 것)
*
* 시간 복잡도: O(N^(T/M)) - N: 후보 개수, T: 타겟, M: 최소값
* 공간 복잡도: O(T/M) - 재귀 깊이
*/

const combinationSum = (candidates, target) => {
const result = [];

const dfs = (remain, start, path) => {
// Base case: 타겟 달성
if (remain === 0) {
result.push([...path]); // 현재 경로 저장
return;
}

// Base case: 타겟 초과 (pruning)
if (remain < 0) return;

// 모든 후보 탐색
for (let i = start; i < candidates.length; i++) {
// 가지치기: 남은 값보다 크면 스킵
if (candidates[i] > remain) continue;

// 선택
path.push(candidates[i]);

// 탐색 (i부터 시작 - 같은 숫자 재사용 가능)
dfs(remain - candidates[i], i, path);

// 되돌리기 (백트래킹)
path.pop();
}
};

dfs(target, 0, []);
return result;
};
43 changes: 43 additions & 0 deletions decode-ways/smosco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Decode Ways
*
* 핵심 아이디어:
* - 숫자 문자열을 1-26(A-Z)로 디코딩하는 경우의 수를 구하는 문제
* - dp[i] = 앞에서부터 i개의 문자를 디코딩하는 방법 수
* - 각 위치에서 2가지 선택 가능:
* 1) 한 자리 숫자로 해석 (1-9)
* 2) 두 자리 숫자로 해석 (10-26)
*
* 시간 복잡도: O(n) - 문자열을 한 번만 순회
* 공간 복잡도: O(n) - dp 배열 사용
*/

function numDecodings(s) {
// 예외 처리: 빈 문자열이거나 '0'으로 시작하면 불가능
if (!s || s[0] === '0') return 0;

const n = s.length;
const dp = new Array(n + 1).fill(0);

// 초기값 설정
dp[0] = 1; // 빈 문자열 (base case)
dp[1] = 1; // 첫 번째 문자 (이미 '0' 체크 완료)

// i번째 위치까지의 디코딩 방법 수 계산
for (let i = 2; i <= n; i++) {
// 1) 한 자리 숫자로 해석 (1~9만 가능, 0은 단독 불가)
const oneDigit = s[i - 1];
if (oneDigit !== '0') {
dp[i] += dp[i - 1]; // 이전까지의 모든 방법에 현재 숫자 추가
}

// 2) 두 자리 숫자로 해석 (10~26만 가능)
const twoDigits = s[i - 2] + s[i - 1];
const twoDigitsNum = parseInt(twoDigits);
if (twoDigitsNum >= 10 && twoDigitsNum <= 26) {
dp[i] += dp[i - 2]; // i-2까지의 모든 방법에 두 자리 숫자 추가
}
}

return dp[n];
}
48 changes: 48 additions & 0 deletions maximum-subarray/smosco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* Maximum Subarray - 브루트포스 (완전탐색)
*
* 핵심 아이디어:
* - 모든 가능한 연속 부분 배열의 합을 계산하여 최댓값 찾기
* - i번째부터 j번째까지의 합을 누적하면서 계산
*
* 시간 복잡도: O(n²) - 이중 반복문으로 모든 구간 탐색
* 공간 복잡도: O(1) - 변수 몇 개만 사용
*/
const maxSubArray = (nums) => {
const n = nums.length;
let maxSoFar = nums[0];

for (let i = 0; i < n; i++) {
let currentSum = 0;
for (let j = i; j < n; j++) {
currentSum += nums[j]; // i부터 j까지의 누적 합
maxSoFar = Math.max(maxSoFar, currentSum);
}
}

return maxSoFar;
};

/**
* Maximum Subarray - Kadane's Algorithm
*
* 핵심 아이디어:
* - 각 위치에서 "현재 숫자만으로 새로 시작 vs 이전 합에 현재 숫자 추가" 중 더 큰 값 선택
* - currentMax = max(현재 숫자, 지금까지 합 + 현재 숫자)
* - 즉, 이전까지의 합이 현재에 도움이 안 되면 버리고 새로 시작
*
* 시간 복잡도: O(n) - 배열을 한 번만 순회
* 공간 복잡도: O(1) - 변수 두 개만 사용
*/
const maxSubArrayKadane = (nums) => {
let currentMax = nums[0]; // 현재 위치까지의 최대 합
let globalMax = nums[0]; // 전체 최대 합

for (let i = 1; i < nums.length; i++) {
// 핵심: 이전 합에 더할지 vs 새로 시작할지
currentMax = Math.max(nums[i], currentMax + nums[i]);
globalMax = Math.max(globalMax, currentMax);
}

return globalMax;
};
21 changes: 21 additions & 0 deletions number-of-1-bits/smosco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/**
* @param {number} n
* @return {number}
*/
const hammingWeight = (n) => {
const binaryString = n.toString(2);
let oneCount = 0;

for (const bit of binaryString) {
if (bit === '1') {
oneCount += 1;
}
}

return oneCount;
};

// 다른 풀이
// const hammingWeight = (n) => {
// return n.toString(2).split('1').length - 1;
// };
40 changes: 40 additions & 0 deletions valid-palindrome/smosco.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/**
* 핵심 아이디어:
* 1. 정규표현식으로 알파벳과 숫자만 추출하여 소문자로 변환
* 2. Two Pointer 방식으로 양 끝에서 중앙으로 이동하며 비교
*
* @param {string} s - 검사할 문자열
* @return {boolean} - 팰린드롬 여부
*
* 시간 복잡도: O(n)
* - replace() 메서드: O(n) - 문자열 전체 순회
* - toLowerCase(): O(n) - cleaned 문자열 순회
* - while 루프: O(n/2) → O(n) - 최대 문자열 길이의 절반만큼 반복
* - 전체: O(n) + O(n) + O(n) = O(n)
*
* 공간 복잡도: O(n)
* - cleaned 문자열: O(n) - 최악의 경우 입력 문자열의 모든 문자가 알파벳/숫자
* - left, right 포인터 변수: O(1)
* - 전체: O(n)
*/
const isPalindrome = (s) => {
// 알파벳과 숫자만 남기고 소문자로 변환
// \W는 알파벳, 숫자, 언더스코어를 제외한 모든 문자
// _도 제거해야 하므로 [^a-zA-Z0-9] 사용
const cleaned = s.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();

// Two Pointer 접근법
let left = 0;
let right = cleaned.length - 1;

// 양 끝에서 중앙으로 이동하며 비교
while (left < right) {
if (cleaned[left] !== cleaned[right]) {
return false;
}
left++;
right--;
}

return true;
};