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

[선재] WEEK06 문제 풀이 #476

Merged
merged 4 commits into from
Sep 21, 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
28 changes: 28 additions & 0 deletions container-with-most-water/sunjae95.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/**
* @description
* brainstorming:
* 1. brute force -> time limited
* 2. two pointer
*
* n: length of height
* time complexity: O(n)
* space complexity: O(1)
*/
var maxArea = function (height) {
let answer = 0;
let start = 0;
let end = height.length - 1;

while (start !== end) {
const w = end - start;
const h = Math.min(height[start], height[end]);
answer = Math.max(answer, w * h);
if (height[start] >= height[end]) {
end--;
} else if (height[start] < height[end]) {
start++;
}
}

return answer;
};
35 changes: 35 additions & 0 deletions longest-increasing-subsequence/sunjae95.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* @description
* brainstorming:
* 1. dfs -> time limited
* 2. memoization + dfs
*
* n: length of nums
* time complexity: O(n^2)
* space complexity: O(n)
*/
var lengthOfLIS = function (nums) {
const memo = new Array(nums.length).fill(-1);
let answer = 0;

const dfs = (index) => {
if (memo[index] !== -1) return memo[index];

let maxLength = 1;

for (let i = index + 1; i < nums.length; i++) {
if (nums[index] < nums[i]) {
maxLength = Math.max(maxLength, 1 + dfs(i));
}
}

memo[index] = maxLength;
return maxLength;
};
Comment on lines +15 to +28
Copy link
Contributor

Choose a reason for hiding this comment

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

memo[i] = nums[i]를 첫 원소로 가지는 LIS의 길이로 정의하셨군요 ㅎㅎ

저는 memo[i] = nums[i]를 마지막 원소로 가지는 LIS의 길이로 정의해서 풀었는데, 이 둘 중에서 유의미한 차이가 있을까 고민해봤습니다

싱겁게도 별 차이는 없을 것 같다는 결론이지만요... ㅎㅎㅎ 덕분에 머리도 좀 굴려보고 좋았습니다

풀이 잘 보았습니다!

Copy link
Contributor

Choose a reason for hiding this comment

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

다시 보니 dfs의 재귀호출이 쌓이므로 선재님의 top-down방식이 bottom-up으로 탐색하는 알고리즘보다는 공간 사용량이 조금 더 클 수도 있을 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

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

의도한 바는 아니지만 이 문제를 접근할 때 dfs를 사용해야겠다 생각하고 접근했지만 안타깝게도 time limited가 나오더라구요.
손으로 트리그리면서 중복된 부분이 발견되어 memoization을 사용하면 time limited에 걸리지않을까 시도해봤어요.
그러더니 운좋게? 통과하더라구요...ㅎㅎ

Copy link
Contributor Author

Choose a reason for hiding this comment

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

공간사용량은 최종depths는 동일하지만 case와 방식에 따라 공간사용량이 다르다고 생각해요.

case가 10, 7, 3, 6, 8, 9, 11 인경우
top down시 10, 7, 3에 대한 연산이 이루어지고
bottom up으로 진행시 11에서만 연산이 되어 모든 경우의 수가 발생되어 top down보다 공간을 더 사용한다고 생각해요.

@obzva 님이 의도하신 공간 사용량이 제가 언급한 예시로 해결이 됐을까요? 아니라면 다른 의미였는지 궁금합니다!

Copy link
Contributor

@obzva obzva Sep 21, 2024

Choose a reason for hiding this comment

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

원래 제가 드리고 싶었던 말은 이거였는데,

  • 재귀를 이용한 top-down: 탐색 수는 bottom-up과 동일하나, 재귀 함수를 이용하기 때문에 재귀 호출 스택만큼의 추가적인 공간 사용
  • 반복문을 이용한 bottom-up: 탐색 수는 top-down과 동일

좀 더 생각해보니 선재님 말씀이 맞다는 걸 알게 되었습니다 감사합니다 :)

Copy link
Contributor

Choose a reason for hiding this comment

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

예를 들어 선재님이 들어주신 case에서 top-down의 경우에는,

// dfs(10)을 반환하기 전까지 깊이 2만큼의 콜스택 사용
dfs(10) 새로 계산
  ∟dfs(11) 계산

// dfs(7)을 반환하기 전까지 깊이 4만큼의 콜스택 사용
dfs(7) 계산
  ∟dfs(8) 계산
    ∟dfs(9) 계산
      ∟dfs(11) memo에서 get
      
// dfs(3)을 반환하기 전까지 깊이 3만큼의 콜스택 사용
dfs(3) 계산
  ∟dfs(6) 계산
    ∟dfs(8) memo
    
dfs(6) memo

dfs(8) memo

dfs(9) memo

dfs(11) memo

Copy link
Contributor

@obzva obzva Sep 21, 2024

Choose a reason for hiding this comment

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

bottom-up으로 가는 경우에는,

// 첫 원소는 LIS의 길이 1
nums  10, 7, 3, 6, 8, 9, 11
memo  1

// 이전 원소들 중에서 현재 원소보다 값이 더 작은 원소에 대해 탐색
// 7보다 작은 원소 없었음
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  
                  
// 3보다 작은 원소 없었음
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  1
 
 // 6보다 작은 원소: 3
 // 연산 1번
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  1  2
                        
 // 8보다 작은 원소: 3, 6
 // 연산 2번
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  1  2  3
                             
 // 9보다 작은 원소: 3, 6, 8
 // 연산 3번
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  1  2  3  4
                                  
 // 11보다 작은 원소: 3, 6, 8, 9
 // 연산 4번
nums  10, 7, 3, 6, 8, 9, 11
memo  1   1  1  2  3  4   5

Copy link
Contributor

Choose a reason for hiding this comment

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

어머 선재님 말씀대로네요

Copy link
Contributor Author

Choose a reason for hiding this comment

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

아하 bottom up 방식은 이렇게 접근할 수 도 있군요..!
저도 저런식으로 풀고싶었는데 식이 명확하게 잡히지 않아 재귀함수를 사용했는데 for문으로 가능한지 몰랐네요.
리뷰감사합니다 :)

Copy link
Contributor

Choose a reason for hiding this comment

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

두 분의 상당히 깊이있는 대화를 흥미진진하게 읽었습니다. "재귀 + 메모이제이션"과 "동적 계획법" 간의 미묘한 차이를 잘 보여주는 토론라고 생각합니다. @Sunjae95 님께서 짚어주신 데로 연산량은 숫자의 구성에 따라서 아주 케바케일 것 같습니다. @obzva 님께서 짚어주신 데로 "재귀 + 메모이제이션" 구현이 호출 스택도 메모리를 쓰고 호출 결과를 캐싱하는데도 메모리를 써서 상대적으로 공간 효율이 떨어진다는 것에 저도 동의합니다.

어찌됐든 빅오 표현법으로는 둘 다 결국 공간 복잡도가 O(n)이기 때문에, 순수하게 코딩 테스트 관점에서 바라봤을 때는 우열을 가리는 것이 의미가 없을 정도로 두 분 다 훌륭한 답안을 제출하셨고 건강한 토론을 하셨다고 생각합니다. 👏💯🎖️


for (let i = 0; i < nums.length; i++) {
answer = Math.max(answer, dfs(i));
}

return answer;
};
45 changes: 45 additions & 0 deletions spiral-matrix/sunjae95.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/**
* @description
* brainstorming:
* just implement question
*
* m: length of matrix
* n: length of matrix[i]
* time complexity: O(m * n)
* space complexity: O(m * n)
*/
var spiralOrder = function (matrix) {
let count = matrix.length * matrix[0].length;
let [r, c] = [1, 1];
const answer = [];
const MAX_R = matrix.length + 2;
const MAX_C = matrix[0].length + 2;
const visited = Array.from({ length: MAX_R }, (_, i) => {
return Array.from(
{ length: MAX_C },
(_, j) => i === 0 || i === MAX_R - 1 || j === 0 || j === MAX_C - 1
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분은 뭘까 했는데, 일종의 프레임을 미리 만들어주는 것 같네요?? 새로운 방식이라 신기합니다 ㅎㅎ

);
});

while (count--) {
const check = {
left: c >= 1 && visited[r][c - 1],
right: c < MAX_C - 1 && visited[r][c + 1],
top: r >= 1 && visited[r - 1][c],
bottom: r < MAX_R - 1 && visited[r + 1][c],
};
Comment on lines +25 to +30
Copy link
Contributor

Choose a reason for hiding this comment

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

이런 방식은 처음 봤습니다 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

LOOK UP TABLE이라는 방식이에요.
가독성이나 유지보수성으로 좋은 방식인거 같아요!

visited[r][c] = true;
answer.push(matrix[r - 1][c - 1]);

if (check.left && check.top && check.bottom) c++;
else if (check.left && check.top && check.right) r++;
else if (check.right && check.top && check.bottom) c--;
else if (check.left && check.right && check.bottom) r--;
else if (check.left && check.top) c++;
else if (check.top && check.right) r++;
else if (check.right && check.bottom) c--;
else if (check.bottom && check.left) r--;
}

return answer;
};
32 changes: 32 additions & 0 deletions valid-parentheses/sunjae95.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @description
* brainstorming:
* stack
*
* n: length of s
* time complexity: O(n)
* space complexity: O(n)
*/
var isValid = function (s) {
const stack = [];

for (let i = 0; i < s.length; i++) {
if (s[i] === "(" || s[i] === "{" || s[i] === "[") {
stack.push(s[i]);
continue;
}

const top = stack.length ? stack[stack.length - 1] : null;
if (top === null) return false;

if (
(top === "(" && s[i] === ")") ||
(top === "{" && s[i] === "}") ||
(top === "[" && s[i] === "]")
) {
stack.pop();
} else return false;
}

return !stack.length;
};