-
-
Notifications
You must be signed in to change notification settings - Fork 362
[dahyeong-yun] WEEK 04 Solutions #2740
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
Changes from all commits
aea7608
a411acc
87c00ae
1c8753b
c746ca4
6a9dd58
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,31 @@ | ||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * [풀이 개요] | ||||||||||||||||||||||||||||||||
| * - 시간복잡도 : O(N * amount) (N: 동전 종류의 개수, 최대 12 * 10,000 = 120,000번 연산) | ||||||||||||||||||||||||||||||||
| * - 공간복잡도 : O(amount) (DP 테이블 크기) | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| class Solution { | ||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||
| * [문제 풀이 아이디어] | ||||||||||||||||||||||||||||||||
| * - amount를 금액이라고 해석했을 때, 금액을 만들 수 있는 최소 코인의 갯수를 구하는 문제. | ||||||||||||||||||||||||||||||||
| * - 가짓 수를 의미하는 배열을 동해 동적 프로그래밍 방식으로 풀 수 있음. | ||||||||||||||||||||||||||||||||
| * - 바깥 루프에서는 코인을, 안쪽 루프에서는 수를 차례로 순회함. | ||||||||||||||||||||||||||||||||
| * - 이때 모든 수에 대해 각 코인으로 만들 수 있는 최저 수를 계산해서 넣고, 코인의 액면 값 만큼 적은 횟수에서 해당 코인을 더해서 만들 수 있는 수와 비교한 최솟값을 구함. | ||||||||||||||||||||||||||||||||
| * - 시간 복잡도는 코인의 수 * 금액의 수 이므로, 문제의 제약에 따라 12 * 10,000 = 120000 이 된다. | ||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||
| public int coinChange(int[] coins, int amount) { | ||||||||||||||||||||||||||||||||
| int[] methods = new int[amount + 1]; // 금액별 코인의 최소 갯수 | ||||||||||||||||||||||||||||||||
| Arrays.fill(methods, amount+1); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| methods[0] = 0; // 금액 0원을 만들기 위해서는 코인을 안쓰면 됨. | ||||||||||||||||||||||||||||||||
| for(int i=0; i<coins.length; i++) { | ||||||||||||||||||||||||||||||||
| int coin = coins[i]; | ||||||||||||||||||||||||||||||||
| for(int j=1; j <= amount; j++) { | ||||||||||||||||||||||||||||||||
| if(j >= coin) { | ||||||||||||||||||||||||||||||||
| methods[j] = Math.min(methods[j], methods[j - coin] + 1); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+23
to
+24
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. coins을 미리 소트해두면 좀 더 최적화 해볼 수 있을 거 같습니다. |
||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 이 부분 j 시작점을 coin으로 바꾸시면 훨씬 보기 좋을것 같아요! 반복 횟수도 줄어들구요 이렇게요!
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그렇네요. 어차피 그 부분을 체크를 안해도 되니까 분기를 만들 필요가 없네요 👍🏼
Comment on lines
+20
to
+27
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i, j, coin, amount 등이 섞여 있어 이해하는데 약간 어려웠던 거 같습니다. 1부터 amount 까지 가는 루프만 인덱스를 필요로 하니 이 떄만 i를 사용해서 루프를 돌리고 코인은 변수 값을 바로 쓰는 형태로 써보셔도 좋을 거 같습니다. 포문 순서는 바꾸셔도 될 거 같은데 점화식 형태로 업데이트하니 바깥 포문은 1~amount까지 돌리는게 생각의 흐름과 맞을 거 같습니다.
Suggested change
|
||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| return methods[amount] == amount + 1 ? -1 : methods[amount]; | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 중간 값과 우측 끝 비교를 통해 최소값의 반으로 범위를 축소합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| /** | ||
| * [풀이 개요] | ||
| * - 시간복잡도 : O(log n) | ||
| * - 공간복잡도 : O(1) | ||
| */ | ||
| class Solution { | ||
| /** | ||
| * [문제 풀이 아이디어] | ||
| * - 정렬된 배열이 시프트 된 형태로 주어짐. 그러니까 원래는 오름차순 정렬임 | ||
| * - 이때 최소 값을 찾아야 함 | ||
| * - 반씩 나누어 탐색하는 이분 탐색 형태로 시간복잡도는 O(log n) | ||
| * - 별도로 유의미한 공간 할당을 하지 않으므로 공간복잡도는 O(1) | ||
| */ | ||
| public int findMin(int[] nums) { | ||
| int len = nums.length - 1; | ||
| // 중간 지점은 전체 갯 수를 2로 나눈 수로 함. 배열의 크기는 1 이상 5000 이하 이므로 int 커버 됨. | ||
|
|
||
| int left = 0; | ||
| int right = len; | ||
|
Comment on lines
+15
to
+19
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: len은 한번만 사용이 되서 인라인해도 될 거 같습니다. |
||
|
|
||
| while(left < right) { | ||
| int mid = (left + right) / 2; | ||
| if(nums[mid] < nums[right]) { // mid 우측은 살펴볼 필요가 없음 | ||
|
Comment on lines
+21
to
+23
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 더 최적화 한다면 |
||
| right = mid; // mid가 최소값일 가능성 있음 | ||
| } else { | ||
| // nums[mid] > nums[right] // mid 좌측은 살펴볼 필요가 없음 | ||
|
Comment on lines
+25
to
+26
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| left = mid + 1 ; // mid가 최소 값일 가능성 없음 | ||
| } | ||
| } | ||
| return nums[left]; | ||
| } | ||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 모든 노드를 방문하며 트리의 깊이에 비례하는 호출 스택을 사용합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| /** | ||
| * [풀이 개요] | ||
| * - 시간복잡도 : O(n) : 트리의 모든 노드를 방문해야 함 | ||
| * - 공간복잡도 : O(h) : 재귀의 콜 스택은 트리의 높이(h) 만큼 쌓임 | ||
| */ | ||
| class Solution { | ||
| /** | ||
| * [문제 풀이 아이디어] | ||
| * - 이진 트리의 최대 레벨을 구하는 문제 | ||
| * - 루트만 존재해도 레벨은 1암. | ||
| * - 자식 노드를 재귀적으로 조회하면서 레벨을 1씩 늘리고, 최대값을 갱신하므로 편향 트리라도 최대값으로 갱신됨. | ||
| */ | ||
| public int maxDepth(TreeNode root) { | ||
| if(root == null) return 0; | ||
| return 1 + Math.max(maxDepth(root.left), maxDepth(root.right)); | ||
| } | ||
|
Comment on lines
+13
to
+16
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 깔끔하네요! |
||
| } | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 두 리스트를 순차 비교하여 새로운 노드를 생성하지 않고 노드를 연결합니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| /** | ||
| * [풀이 개요] | ||
| * - 시간복잡도 : O(n+m) | ||
| * - 공간복잡도 : O(1) | ||
| */ | ||
| class Solution { | ||
| /** | ||
| * [문제 풀이 아이디어] | ||
| * - 노드를 돌면서 더 작은 수를 다음으로 넣고, 커서를 다음으로 옮김 | ||
| * - ListNode 중 하나라도 비면 비지 않은 것을 죽 이어 붙임 | ||
| * - 이 경우 시간 복잡도는 두 노드의 길이의 합만큼 반복하게 되어 각 길이를 n, m 이라 했을 때 n+m이 됨 | ||
| * - 별도의 유의미한 공간 할당을 하지 않으므로 O(1)의 공간복잡도를 가짐. | ||
| */ | ||
| public ListNode mergeTwoLists(ListNode list1, ListNode list2) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dahyeong-yun 깔끔하게 풀어주셔서 더 코멘트 달 부분이 없네요. 저도 거의 같은 방식으로 풀었는데 재귀를 활용한 방법도 있더라고요? 문제는 이게 공간복잡도가 더 높아져서 구현해보지는 않았습니다 ㅎㅎ 뭐라도 말씀드려야 할 것 같아서 주저리 써보았습니다. |
||
|
|
||
| ListNode merged = new ListNode(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dahyeong-yun 보통 dummy로 많이 쓰는데 merged도 실제로 머지된 노드를 나타내는거라 괜찮아 보이네요! |
||
| ListNode cursor = merged; | ||
|
|
||
| while(list1 != null && list2 != null) { | ||
| if(list1.val <= list2.val) { | ||
| cursor.next = list1; | ||
| list1 = list1.next; | ||
| } else { | ||
| cursor.next = list2; | ||
| list2 = list2.next; | ||
| } | ||
| cursor = cursor.next; | ||
| } | ||
|
|
||
| if(list1 != null) { | ||
| cursor.next = list1; | ||
| } else { | ||
| cursor.next = list2; | ||
| } | ||
|
Comment on lines
+30
to
+34
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dahyeong-yun 3항 연산자로 쓰는것도 깔끔해 보입니다. (취향 차이이긴 하지만) |
||
| return merged.next; | ||
| } | ||
| } | ||
|
dahyeong-yun marked this conversation as resolved.
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,81 @@ | ||
| /** | ||
| * [풀이 개요] | ||
| * - 시간복잡도 : O(N * M * 3^L) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dahyeong-yun 저는 이 문제 시간복잡도 분석에 시간이 좀 걸렸었는데, 혹시 어떻게 분석을 접근하셨는지 한번 풀어서 설명해주실 수 있나요?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 저도 쉽지 않았던 것 같아요. 정리된 생각으로 말씀드리면,
(분석이 맞는지는 잘 모르겠네요) |
||
| * (N, M은 격자 크기, L은 단어의 길이. 각 칸에서 사방 탐색 시 이전 칸을 제외한 3방향으로 최대 L번 재귀 호출) | ||
| * - 공간복잡도 : O(N * M) | ||
| * (N * M 크기의 visited 배열 및 최대 L 깊이의 재귀 호출 스택 사용) | ||
| */ | ||
| class Solution { | ||
| // 세로(row), 가로(col) 이동을 위한 오프셋 | ||
| // dy -> dRow (상하 이동), dx -> dCol (좌우 이동) | ||
| int[] dRow = { 0, 1, 0, -1 }; | ||
| int[] dCol = { 1, 0, -1, 0 }; | ||
|
|
||
| public boolean exist(char[][] board, String word) { | ||
| int maxRow = board.length; // 세로 길이 (limitY) | ||
| int maxCol = board[0].length; // 가로 길이 (limitX) | ||
|
|
||
| // visited 배열도 board와 동일하게 [row][col] 크기로 선언 | ||
| boolean[][] visited = new boolean[maxRow][maxCol]; | ||
| char startChar = word.charAt(0); | ||
|
Comment on lines
+18
to
+20
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. visited 없이 board 에 마킹했다 되돌리는 식으로 사용하시면 보다 간결하게 작성이 되실 거 같습니다. |
||
|
|
||
| for (int r = 0; r < maxRow; r++) { | ||
| for (int c = 0; c < maxCol; c++) { | ||
| if (startChar == board[r][c]) { | ||
| // 첫 글자가 일치하면 백트래킹 시작 | ||
| if (find(1, word.length(), r, c, maxRow, maxCol, board, word, visited)) { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| return false; | ||
| } | ||
|
|
||
| public boolean find( | ||
| int cursor, | ||
| int len, | ||
| int curRow, | ||
| int curCol, | ||
| int maxRow, | ||
| int maxCol, | ||
| char[][] board, | ||
| String word, | ||
| boolean[][] visited | ||
| ) { | ||
|
Comment on lines
+36
to
+46
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 파라미터가 다소 많은 거 같아서 줄여보셔도 좋을 거 같아요. 첫번째 파라미터 cursor는 인덱스 값을 가져서 idx 와 같은 네이밍을 고려하셔도 좋을 거 같습니다. |
||
| // 기저 조건: 단어를 모두 완성한 경우 | ||
| if (cursor >= len) { | ||
| return true; | ||
| } | ||
|
|
||
| // 현재 좌표 방문 처리 | ||
| visited[curRow][curCol] = true; | ||
|
Comment on lines
+52
to
+53
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dahyeong-yun 저도 처음에 이렇게 visited 썼다가, 나중에 보니 해당 보드에 문자를 #와 같은걸로 마킹하고 다시 함수 끝에서 돌려놓으면 굳이 visited가 필요없더라고요! 요 방법도 적용해보셔도 좋을 것 같아요.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 그거 엄청 좋은 방식이네요! |
||
|
|
||
| for (int i = 0; i < 4; i++) { | ||
| int nextRow = curRow + dRow[i]; | ||
| int nextCol = curCol + dCol[i]; | ||
|
|
||
| // 1. 격자 경계선 체크 | ||
| if (nextRow < 0 || nextRow >= maxRow || nextCol < 0 || nextCol >= maxCol) { | ||
| continue; | ||
| } | ||
|
|
||
| // 2. 방문하지 않았고, 다음 목표 문자와 일치하는지 체크 | ||
| if (!visited[nextRow][nextCol]) { | ||
| char nextChar = board[nextRow][nextCol]; | ||
|
|
||
| if (word.charAt(cursor) == nextChar) { | ||
| if (find(cursor + 1, len, nextRow, nextCol, maxRow, maxCol, board, word, visited)) { | ||
| return true; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| // 탐색이 실패로 끝나면 현재 좌표 방문 해제 (Backtrack) | ||
| visited[curRow][curCol] = false; | ||
|
|
||
| return false; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 배낭형 DP로 각 금액별 최소 동전 수를 갱신하는 전형적인 풀이이며, 외부 루프가 코인 수, 내부 루프가 금액을 순회합니다.
개선 제안: 현재 구현이 적절해 보입니다.