Skip to content
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

[pepper] Week 03 Solutions #402

Merged
merged 9 commits into from
Aug 31, 2024
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
56 changes: 56 additions & 0 deletions climbing-stairs/whewchews.ts
Original file line number Diff line number Diff line change
@@ -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<n-2; i++){
// const next = prev + curr;
// prev = curr;
// curr = next;
// }

// return curr
// TC: O(N)
// SC: O(1)

// # Solution 3: 재귀
const memo = { 1: 1, 2: 2 };
Copy link
Contributor

Choose a reason for hiding this comment

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

안녕하세요 @whewchews 님! 사고과정을 잘 써주셔서 이해가 잘 됐습니다!

이 라인의 경우 인덱스가 숫자인 만큼 배열을 사용할수도 있을것같아요!

function calculateClimbingWay(n, memo) {
if (n in memo) return memo[n];

if (n < 3) {
return n;
}
memo[n] =
calculateClimbingWay(n - 1, memo) + calculateClimbingWay(n - 2, memo);

return memo[n];
}
return calculateClimbingWay(n, memo);
// TC: O(N)
// SC: O(N)
}
33 changes: 33 additions & 0 deletions coin-change/whewchews.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
function coinChange(coins: number[], amount: number): number {
/* # Solution 1: BFS
* 최소 경로를 찾는 문제 => 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();
whewchews marked this conversation as resolved.
Show resolved Hide resolved
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
118 changes: 118 additions & 0 deletions combination-sum/whewchews.ts
Original file line number Diff line number Diff line change
@@ -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) 저장 가능
70 changes: 70 additions & 0 deletions product-of-array-except-self/whewchews.ts
Original file line number Diff line number Diff line change
@@ -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)
27 changes: 27 additions & 0 deletions two-sum/whewchews.ts
Original file line number Diff line number Diff line change
@@ -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)