diff --git a/coin-change/dahyeong-yun.java b/coin-change/dahyeong-yun.java new file mode 100644 index 0000000000..239a2035af --- /dev/null +++ b/coin-change/dahyeong-yun.java @@ -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= coin) { + methods[j] = Math.min(methods[j], methods[j - coin] + 1); + } + } + } + + return methods[amount] == amount + 1 ? -1 : methods[amount]; + } +} diff --git a/find-minimum-in-rotated-sorted-array/dahyeong-yun.java b/find-minimum-in-rotated-sorted-array/dahyeong-yun.java new file mode 100644 index 0000000000..d6ffc55dd4 --- /dev/null +++ b/find-minimum-in-rotated-sorted-array/dahyeong-yun.java @@ -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; + + while(left < right) { + int mid = (left + right) / 2; + if(nums[mid] < nums[right]) { // mid 우측은 살펴볼 필요가 없음 + right = mid; // mid가 최소값일 가능성 있음 + } else { + // nums[mid] > nums[right] // mid 좌측은 살펴볼 필요가 없음 + left = mid + 1 ; // mid가 최소 값일 가능성 없음 + } + } + return nums[left]; + } +} diff --git a/maximum-depth-of-binary-tree/dahyeong-yun.java b/maximum-depth-of-binary-tree/dahyeong-yun.java new file mode 100644 index 0000000000..d1c911d6fc --- /dev/null +++ b/maximum-depth-of-binary-tree/dahyeong-yun.java @@ -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)); + } +} diff --git a/merge-two-sorted-lists/dahyeong-yun.java b/merge-two-sorted-lists/dahyeong-yun.java new file mode 100644 index 0000000000..c202f77699 --- /dev/null +++ b/merge-two-sorted-lists/dahyeong-yun.java @@ -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) { + + ListNode merged = new ListNode(); + 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; + } + return merged.next; + } +} diff --git a/word-search/dahyeong-yun.java b/word-search/dahyeong-yun.java new file mode 100644 index 0000000000..75c1e7f27c --- /dev/null +++ b/word-search/dahyeong-yun.java @@ -0,0 +1,81 @@ +/** + * [풀이 개요] + * - 시간복잡도 : O(N * M * 3^L) + * (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); + + 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 + ) { + // 기저 조건: 단어를 모두 완성한 경우 + if (cursor >= len) { + return true; + } + + // 현재 좌표 방문 처리 + visited[curRow][curCol] = true; + + 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; + } +}