Skip to content

Commit 38e4c7b

Browse files
authored
[dahyeong-yun] WEEK 04 Solutions (#2740)
* 0021-merge-two-sorted-lists * 0079-word-search * 0153-find-minimum-in-rotated-sorted-array * 0322-coin-change * 0104-maximum-depth-of-binary-tree * add newline
1 parent 0ca4e18 commit 38e4c7b

5 files changed

Lines changed: 198 additions & 0 deletions

File tree

coin-change/dahyeong-yun.java

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* [풀이 개요]
3+
* - 시간복잡도 : O(N * amount) (N: 동전 종류의 개수, 최대 12 * 10,000 = 120,000번 연산)
4+
* - 공간복잡도 : O(amount) (DP 테이블 크기)
5+
*/
6+
class Solution {
7+
/**
8+
* [문제 풀이 아이디어]
9+
* - amount를 금액이라고 해석했을 때, 금액을 만들 수 있는 최소 코인의 갯수를 구하는 문제.
10+
* - 가짓 수를 의미하는 배열을 동해 동적 프로그래밍 방식으로 풀 수 있음.
11+
* - 바깥 루프에서는 코인을, 안쪽 루프에서는 수를 차례로 순회함.
12+
* - 이때 모든 수에 대해 각 코인으로 만들 수 있는 최저 수를 계산해서 넣고, 코인의 액면 값 만큼 적은 횟수에서 해당 코인을 더해서 만들 수 있는 수와 비교한 최솟값을 구함.
13+
* - 시간 복잡도는 코인의 수 * 금액의 수 이므로, 문제의 제약에 따라 12 * 10,000 = 120000 이 된다.
14+
*/
15+
public int coinChange(int[] coins, int amount) {
16+
int[] methods = new int[amount + 1]; // 금액별 코인의 최소 갯수
17+
Arrays.fill(methods, amount+1);
18+
19+
methods[0] = 0; // 금액 0원을 만들기 위해서는 코인을 안쓰면 됨.
20+
for(int i=0; i<coins.length; i++) {
21+
int coin = coins[i];
22+
for(int j=1; j <= amount; j++) {
23+
if(j >= coin) {
24+
methods[j] = Math.min(methods[j], methods[j - coin] + 1);
25+
}
26+
}
27+
}
28+
29+
return methods[amount] == amount + 1 ? -1 : methods[amount];
30+
}
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/**
2+
* [풀이 개요]
3+
* - 시간복잡도 : O(log n)
4+
* - 공간복잡도 : O(1)
5+
*/
6+
class Solution {
7+
/**
8+
* [문제 풀이 아이디어]
9+
* - 정렬된 배열이 시프트 된 형태로 주어짐. 그러니까 원래는 오름차순 정렬임
10+
* - 이때 최소 값을 찾아야 함
11+
* - 반씩 나누어 탐색하는 이분 탐색 형태로 시간복잡도는 O(log n)
12+
* - 별도로 유의미한 공간 할당을 하지 않으므로 공간복잡도는 O(1)
13+
*/
14+
public int findMin(int[] nums) {
15+
int len = nums.length - 1;
16+
// 중간 지점은 전체 갯 수를 2로 나눈 수로 함. 배열의 크기는 1 이상 5000 이하 이므로 int 커버 됨.
17+
18+
int left = 0;
19+
int right = len;
20+
21+
while(left < right) {
22+
int mid = (left + right) / 2;
23+
if(nums[mid] < nums[right]) { // mid 우측은 살펴볼 필요가 없음
24+
right = mid; // mid가 최소값일 가능성 있음
25+
} else {
26+
// nums[mid] > nums[right] // mid 좌측은 살펴볼 필요가 없음
27+
left = mid + 1 ; // mid가 최소 값일 가능성 없음
28+
}
29+
}
30+
return nums[left];
31+
}
32+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* [풀이 개요]
3+
* - 시간복잡도 : O(n) : 트리의 모든 노드를 방문해야 함
4+
* - 공간복잡도 : O(h) : 재귀의 콜 스택은 트리의 높이(h) 만큼 쌓임
5+
*/
6+
class Solution {
7+
/**
8+
* [문제 풀이 아이디어]
9+
* - 이진 트리의 최대 레벨을 구하는 문제
10+
* - 루트만 존재해도 레벨은 1암.
11+
* - 자식 노드를 재귀적으로 조회하면서 레벨을 1씩 늘리고, 최대값을 갱신하므로 편향 트리라도 최대값으로 갱신됨.
12+
*/
13+
public int maxDepth(TreeNode root) {
14+
if(root == null) return 0;
15+
return 1 + Math.max(maxDepth(root.left), maxDepth(root.right));
16+
}
17+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/**
2+
* [풀이 개요]
3+
* - 시간복잡도 : O(n+m)
4+
* - 공간복잡도 : O(1)
5+
*/
6+
class Solution {
7+
/**
8+
* [문제 풀이 아이디어]
9+
* - 노드를 돌면서 더 작은 수를 다음으로 넣고, 커서를 다음으로 옮김
10+
* - ListNode 중 하나라도 비면 비지 않은 것을 죽 이어 붙임
11+
* - 이 경우 시간 복잡도는 두 노드의 길이의 합만큼 반복하게 되어 각 길이를 n, m 이라 했을 때 n+m이 됨
12+
* - 별도의 유의미한 공간 할당을 하지 않으므로 O(1)의 공간복잡도를 가짐.
13+
*/
14+
public ListNode mergeTwoLists(ListNode list1, ListNode list2) {
15+
16+
ListNode merged = new ListNode();
17+
ListNode cursor = merged;
18+
19+
while(list1 != null && list2 != null) {
20+
if(list1.val <= list2.val) {
21+
cursor.next = list1;
22+
list1 = list1.next;
23+
} else {
24+
cursor.next = list2;
25+
list2 = list2.next;
26+
}
27+
cursor = cursor.next;
28+
}
29+
30+
if(list1 != null) {
31+
cursor.next = list1;
32+
} else {
33+
cursor.next = list2;
34+
}
35+
return merged.next;
36+
}
37+
}

word-search/dahyeong-yun.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/**
2+
* [풀이 개요]
3+
* - 시간복잡도 : O(N * M * 3^L)
4+
* (N, M은 격자 크기, L은 단어의 길이. 각 칸에서 사방 탐색 시 이전 칸을 제외한 3방향으로 최대 L번 재귀 호출)
5+
* - 공간복잡도 : O(N * M)
6+
* (N * M 크기의 visited 배열 및 최대 L 깊이의 재귀 호출 스택 사용)
7+
*/
8+
class Solution {
9+
// 세로(row), 가로(col) 이동을 위한 오프셋
10+
// dy -> dRow (상하 이동), dx -> dCol (좌우 이동)
11+
int[] dRow = { 0, 1, 0, -1 };
12+
int[] dCol = { 1, 0, -1, 0 };
13+
14+
public boolean exist(char[][] board, String word) {
15+
int maxRow = board.length; // 세로 길이 (limitY)
16+
int maxCol = board[0].length; // 가로 길이 (limitX)
17+
18+
// visited 배열도 board와 동일하게 [row][col] 크기로 선언
19+
boolean[][] visited = new boolean[maxRow][maxCol];
20+
char startChar = word.charAt(0);
21+
22+
for (int r = 0; r < maxRow; r++) {
23+
for (int c = 0; c < maxCol; c++) {
24+
if (startChar == board[r][c]) {
25+
// 첫 글자가 일치하면 백트래킹 시작
26+
if (find(1, word.length(), r, c, maxRow, maxCol, board, word, visited)) {
27+
return true;
28+
}
29+
}
30+
}
31+
}
32+
33+
return false;
34+
}
35+
36+
public boolean find(
37+
int cursor,
38+
int len,
39+
int curRow,
40+
int curCol,
41+
int maxRow,
42+
int maxCol,
43+
char[][] board,
44+
String word,
45+
boolean[][] visited
46+
) {
47+
// 기저 조건: 단어를 모두 완성한 경우
48+
if (cursor >= len) {
49+
return true;
50+
}
51+
52+
// 현재 좌표 방문 처리
53+
visited[curRow][curCol] = true;
54+
55+
for (int i = 0; i < 4; i++) {
56+
int nextRow = curRow + dRow[i];
57+
int nextCol = curCol + dCol[i];
58+
59+
// 1. 격자 경계선 체크
60+
if (nextRow < 0 || nextRow >= maxRow || nextCol < 0 || nextCol >= maxCol) {
61+
continue;
62+
}
63+
64+
// 2. 방문하지 않았고, 다음 목표 문자와 일치하는지 체크
65+
if (!visited[nextRow][nextCol]) {
66+
char nextChar = board[nextRow][nextCol];
67+
68+
if (word.charAt(cursor) == nextChar) {
69+
if (find(cursor + 1, len, nextRow, nextCol, maxRow, maxCol, board, word, visited)) {
70+
return true;
71+
}
72+
}
73+
}
74+
}
75+
76+
// 탐색이 실패로 끝나면 현재 좌표 방문 해제 (Backtrack)
77+
visited[curRow][curCol] = false;
78+
79+
return false;
80+
}
81+
}

0 commit comments

Comments
 (0)