diff --git a/climbing-stairs/whewchews.ts b/climbing-stairs/whewchews.ts new file mode 100644 index 000000000..da3386cf0 --- /dev/null +++ b/climbing-stairs/whewchews.ts @@ -0,0 +1,56 @@ +/* + * 아이디어 + * 층수 제한: 1 <= n <= 45 + * 1 or 2 step 만 올라갈 수 있음 + + * 1 -> [1] + * 2 -> [1,1] [2] + * 3 -> [1,1,1] [2,1] [1,2] + * 4 -> [1,1,1,1] [2,1,1] [1,2,1] [1,1,2] [2,2] + * 5 -> [1,1,1,1,1] [2,1,1,1] [1,2,1,1] [1,1,2,1] [1,1,1,2] [2,2,1], [1,2,2], [2,1,2] + * 6 -> [1,1,1,1,1,1] [2,1,1,1,1] [...] [1,1,1,1,2] [2,2,1,1], [2,1,2,1], [2,1,1,2] [1,1,2,2], [1,2,1,2], [1,2,2,1] + => (1:n, 2:0) n가지 (1:n-2, 2:1) / n가지 (1: n-4, 2: n/2) C(n, n/2) 가지 + */ +function climbStairs(n: number): number { + // # Solution 1 + + // const stair = {1: 1, 2:2} + // for(let i = 3; i<=n; i++){ + // stair[i] = stair[i-1] + stair[i-2] + // } + // TC: O(N) + // SC: O(N) + + // # Solution 2 + + // if(n < 3) return n + // let curr = 2 // 현재 계단을 오르는 방법 수 + // let prev = 1 // 이전 계단을 오르는 방법 수 + + // for(let i=0; i BFS + * 현재까지 사용한 동전의 개수와 현재까지 사용한 동전의 합을 queue에 넣는다. + * visited: 중복 방문을 방지하기 위한 set + * 누적액이 amount와 같아지면 count를 return + * visited에 누적액이 있으면 continue + * coins를 순회하면서 누적액에 동전을 더한 값이 amount보다 작으면 queue에 넣는다. + * queue가 빌때까지 반복 + * 큐가 비어있고 amount를 만들수 없으면 -1을 return + */ + const queue = [[0, 0]]; // [number of coins, accumulated amount] + const visited = new Set(); + + while (queue.length > 0) { + const [count, total] = queue.shift(); + if (total === amount) { + return count; + } + if (visited.has(total)) { + continue; + } + visited.add(total); + for (const coin of coins) { + if (total + coin <= amount) { + queue.push([count + 1, total + coin]); + } + } + } + return -1; +} +// TC: 각 금액(amount)마다 동전(coins)을 순회하므로 O(N^2*M) N: amount, M: coins.length +// SC: O(N) N: amount diff --git a/combination-sum/whewchews.ts b/combination-sum/whewchews.ts new file mode 100644 index 000000000..b891bc0bb --- /dev/null +++ b/combination-sum/whewchews.ts @@ -0,0 +1,118 @@ +function combinationSum(candidates: number[], target: number): number[][] { + /* + * 합이 target이 되는 candidates 경우의 수를 리턴 + * cadidates는 고유함 + * 합이 target이 되지 않는 경우, 빈배열 리턴 + * cadidate을 중복 사용 가능 + * 2 <= candidates[i] <= 40 + * => candidate이 1인 경우는 없음 + * candidates에서 target보다 같거나 작은 값만 filter하고 시작 (target보다 큰 값은 후보군이 될 수 없음) + * + + * [2,3,6,7] / 7 + * + * [] + * [2] / 5 + * [2, 2] 3 => X + * [2, 2, 2] 1 => X + * [2, 2, 2, 2] -1 => X + * [2, 2, 2, 3] -2 => X + * [2, 2, 2, 6] -5 => X + * [2, 2, 2, 7] -6 => X + [2, 2, 3] 0 => O + // ... + [2, 3] 2 => X + [2, 3, 2] 0 => O + // ... + [2, 6] -1 => X + // ... + + * + * 하나씩 값을 추가하면서 배열의 총합을 target 값과 비교한다 + * sum이 target값보다 작으면 계속 다음 값을 추가해준다 + * sum이 target과 같으면 결과 값 result 배열에 추가해준다. + * sum이 target보다 넘으면 마지막에 추가한 값을 뺀다. + * 이 과정을 반복하며 배열에서 결과 값을 찾는다. + * + */ + + + function backtrack(candidates: number[], start:number, total:number){ + if(target === total){ + result.push([...path]) + return + } + + if(target < total){ + return + } + + for(let i=start; i<=candidates.length-1; i++){ + path.push(candidates[i]) + backtrack(candidates, i,total + candidates[i]) + path.pop() + } + } + + const result = [] + const path = [] + // TC: O(NlogN) + // SC: O(N) + const filteredCandidates = candidates.filter(candidate => candidate<=target).sort((a,b)=> a-b) + backtrack(filteredCandidates, 0, 0) + return result + + }; + // TC: O(n^t) n = candidates.length, t = target 크기 + // SC: O(t) + + /* #Solution 2 : DP + * candidates을 가지고 target 값을 만들 수 있는 모든 조합을 미리 찾아둔다. + *candidates [2,3,6,7] / target 7 라고 했을때 + * 1) candidate = 2 + * dp[2] = [[2]] + * dp[4] = [[2,2]] + * dp[6] = [[2,2,2]] + * 2) candidate = 3 + * dp[3] = [[3]] + * dp[5] = [[2,3]] + * dp[6] = [[2,2,2], [3,3]] + * dp[7] = [[2,2,3]] + * 3) candidate = 6 + * dp[6] = [[[2,2,2], [3,3], [6]] + * 4) candidate = 7 + * dp[7] = [[2,2,3], [7]] + * + * => dp = [ + * [ [] ] + * [ [] ] + * [ [2] ] + * [[3]] + * [[2,2]] + * [[2,3,]] + * [[2,2,2], [3,3], [6]] + * [[2,2,3], [7]] + * ] + * ] + * / + + + + // SC: O(t) t = target + const dp = Array.from({ length: target + 1 }, () => []); + dp[0] = [[]]; + + + for (let candidate of candidates) { + for (let i = candidate; i <= target; i++) { + for (let combination of dp[i - candidate]) { + dp[i].push([...combination, candidate]); + } + } + } + + return dp[target]; +} + +// TC: O(n * t * 2^n) n = candidates.length, t = target +// SC: O((t*2^n) // 최악의 경우 모든 조합(2^n) 저장 가능 diff --git a/product-of-array-except-self/whewchews.ts b/product-of-array-except-self/whewchews.ts new file mode 100644 index 000000000..c7cb4fec9 --- /dev/null +++ b/product-of-array-except-self/whewchews.ts @@ -0,0 +1,70 @@ +/* #Solution 1 + * sum: 전체 곱을 구한다. + * zeroCount: 0의 개수가 몇개인지 센다. + * 1. 자신이 0이면, + * 1-1. 자신이외의 0이 있는지 확인하고 있으면 0을 return + * 1-2. 자신 이외에 0이 없으면 전체 곱을 return + * 2. 자신이 0이 아니면 + * 2-1. zeroCount가 있는지 보고 있으면 0을 return + * 2-2. zero가 없으면 sum/self를 return + * + * 그러나... 문제에 나누기 연산자를 쓰면 안된다고 했으므로 Solution 2로 가자. + */ +function productExceptSelf(nums: number[]): number[] { + // let zeroCount = 0; + // const sum = nums.reduce((p, c) => { + // if (c === 0) { + // zeroCount += 1; + // return p; + // } + // p = p * c; + // return p; + // }, 1); + + // const hasZero = zeroCount > 0; + + // if (zeroCount === nums.length) return Array(nums.length).fill(0); + + // return nums.map((n) => { + // if (n === 0) { + // // 자신 이외에 0이 있을때 + // if (zeroCount - 1 > 0) { + // return 0; + // } + + // return sum; + // } + + // if (hasZero) return 0; + // return sum / n; + // }); + // TC: O(N) + // SC: O(N) + + /* #Solution 2 + * 1. prefix: 자신을 제외한 자기 인덱스 앞까지의 곱을 저장한다. + * 2. suffix: 자신을 제외한 자기 뒤까지의 곱을 저장한다. + * 3. answer[i] = prefix[i] * suffix[i] + */ + const n = nums.length; + + const prefix = new Array(n).fill(1); + const suffix = new Array(n).fill(1); + + for (let i = 1; i < n; i++) { + prefix[i] = prefix[i - 1] * nums[i - 1]; + } + + for (let i = n - 2; i >= 0; i--) { + suffix[i] = suffix[i + 1] * nums[i + 1]; + } + + const answer = []; + for (let i = 0; i < n; i++) { + answer[i] = prefix[i] * suffix[i]; + } + + return answer; +} +// TC: O(N) +// SC: O(N) diff --git a/two-sum/whewchews.ts b/two-sum/whewchews.ts new file mode 100644 index 000000000..8952ef969 --- /dev/null +++ b/two-sum/whewchews.ts @@ -0,0 +1,27 @@ +/* + * 아이디어 + * 처음부터 배열을 순회하며 [0,1] [0,2] [0,3] [0, ...], [1,2], [1,3], [1,...] ... [nums.length-2, nums.length-1] 를 돌면서 두 인자의 합이 target이 되는 지점을 찾는다. + * 순서대로 돌면 최악의 경우 가장 마지막 자리까지 갈 수도 있다. + * 절대 값이 되지 못하는, 최소 조건을 생각해보자. + * 주의1:범위가 -10^9 <= nums[i] <= 10^9로 음수값도 있음 + * 주의2: 정렬된 순서가 아님. + * 값을 Map에 저장해두고 {value: index}, Map에 자신과 더했을때 target이 나오는 value가 있는지 확인 + */ +function twoSum(nums: number[], target: number): number[] { + // SC: O(N) + const dict = new Map(); + + // TC: O(N) + for (let i = 0; i <= nums.length - 1; i++) { + const curr = nums[i]; + const pairValue = target - curr; + if (dict.has(pairValue)) { + return [dict.get(pairValue), i]; + } + + dict.set(curr, i); + } +} + +// TC: O(N) +// SC: O(N)