Skip to content
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
31 changes: 31 additions & 0 deletions coin-change/dahyeong-yun.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🏷️ 알고리즘 패턴 분석

  • 패턴: Dynamic Programming, Greedy
  • 설명: 금액별 최소 코인 수를 구하는 Q로, 각 코인으로 상태를 업데이트하는 DP(2차원 아님) 패턴으로 풀이합니다. 코인 목록을 순회하며 금액 상태를 최소로 갱신하는 방식이 DP의 전형적인 예이며, 부분 문제 해를 합쳐 최종 해를 구합니다. 또한 문제의 특성상 최적해를 구하는 탐욕적 요소가 보일 수 있지만 실제 풀이에서 DP로 정확한 해를 계산합니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(N * amount) O(n * amount)
Space O(amount) O(amount)

피드백: 배낭형 DP로 각 금액별 최소 동전 수를 갱신하는 전형적인 풀이이며, 외부 루프가 코인 수, 내부 루프가 금액을 순회합니다.

개선 제안: 현재 구현이 적절해 보입니다.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

coins을 미리 소트해두면 좀 더 최적화 해볼 수 있을 거 같습니다.

}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

이 부분 j 시작점을 coin으로 바꾸시면 훨씬 보기 좋을것 같아요! 반복 횟수도 줄어들구요

for(int j=coin; j <= amount; j++) {
    methods[j] = Math.min(methods[j], methods[j - coin] + 1);
}

이렇게요!
작동 되는건 확인했어요

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

그렇네요. 어차피 그 부분을 체크를 안해도 되니까 분기를 만들 필요가 없네요 👍🏼

Comment on lines +20 to +27

@parkhojeong parkhojeong Jul 17, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i, j, coin, amount 등이 섞여 있어 이해하는데 약간 어려웠던 거 같습니다.

1부터 amount 까지 가는 루프만 인덱스를 필요로 하니 이 떄만 i를 사용해서 루프를 돌리고 코인은 변수 값을 바로 쓰는 형태로 써보셔도 좋을 거 같습니다. 포문 순서는 바꾸셔도 될 거 같은데 점화식 형태로 업데이트하니 바깥 포문은 1~amount까지 돌리는게 생각의 흐름과 맞을 거 같습니다.

Suggested change
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);
}
}
}
for(int i=1; i <= amount; i++){
for(int coin: coins){
if(i >= coin){
methods[i] = Math.min(methods[i], methods[i - coin] + 1);
}
}
}


return methods[amount] == amount + 1 ? -1 : methods[amount];
}
}
32 changes: 32 additions & 0 deletions find-minimum-in-rotated-sorted-array/dahyeong-yun.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🏷️ 알고리즘 패턴 분석

  • 패턴: Binary Search
  • 설명: 배열이 회전된 형태에서 최소값을 이분 탐색으로 찾는 로직으로, 좌우를 절반으로 좁혀가며 조건에 따라 탐색 범위를 줄이는 패턴입니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(log n) O(log n)
Space O(1) O(1)

피드백: 중간 값과 우측 끝 비교를 통해 최소값의 반으로 범위를 축소합니다.

개선 제안: 현재 구현이 적절해 보입니다.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

더 최적화 한다면 if(nums[left] < nums[mid] && nums[mid] < nums[right]) 다음과 같은 로직을 넣어보셔도 좋을 거 같습니다.

right = mid; // mid가 최소값일 가능성 있음
} else {
// nums[mid] > nums[right] // mid 좌측은 살펴볼 필요가 없음
Comment on lines +25 to +26

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nums[mid] > nums[right] 조건은 주석으로 쓰기 보단 코드로 나타내는게 더 정확할 거 같습니다. 실제로는 else 문이라 nums[mid] >= nums[right] 에 대한 로직이라 정확히 매치는 안되는 거 같습니다.

left = mid + 1 ; // mid가 최소 값일 가능성 없음
}
}
return nums[left];
}
}
17 changes: 17 additions & 0 deletions maximum-depth-of-binary-tree/dahyeong-yun.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🏷️ 알고리즘 패턴 분석

  • 패턴: Binary Search, Depth-First Search, Divide and Conquer
  • 설명: 루트부터 자식 노드를 재귀적으로 탐색하며 깊이를 1씩 증가시키고, 좌우 자식의 최대 깊이를 합쳐 최댓값을 구하는 전형적인 DFS/재귀를 활용한 Divide and Conquer 패턴으로 최대 깊이를 구합니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(n) O(n)
Space O(h) O(h)

피드백: 모든 노드를 방문하며 트리의 깊이에 비례하는 호출 스택을 사용합니다.

개선 제안: 현재 구현이 적절해 보입니다.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

깔끔하네요!

}
37 changes: 37 additions & 0 deletions merge-two-sorted-lists/dahyeong-yun.java

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🏷️ 알고리즘 패턴 분석

  • 패턴: Two Pointers, Merge / Two Pointer Technique
  • 설명: 두 정렬 리스트를 한 순서대로 합치는 과정에서 두 포인터(list1, list2)를 이동시키며 값을 비교해 새로운 연결 리스트를 만들어 간다. 모든 원소를 한 번씩 방문하므로 시간은 O(n+m), 추가 공간은 상수입니다.

📊 시간/공간 복잡도 분석

유저 분석 실제 분석 결과
Time O(n+m) O(n + m)
Space O(1) O(1)

피드백: 두 리스트를 순차 비교하여 새로운 노드를 생성하지 않고 노드를 연결합니다.

개선 제안: 현재 구현이 적절해 보입니다.

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) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dahyeong-yun 깔끔하게 풀어주셔서 더 코멘트 달 부분이 없네요. 저도 거의 같은 방식으로 풀었는데 재귀를 활용한 방법도 있더라고요? 문제는 이게 공간복잡도가 더 높아져서 구현해보지는 않았습니다 ㅎㅎ 뭐라도 말씀드려야 할 것 같아서 주저리 써보았습니다.


ListNode merged = new ListNode();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dahyeong-yun 3항 연산자로 쓰는것도 깔끔해 보입니다. (취향 차이이긴 하지만)

return merged.next;
}
}
81 changes: 81 additions & 0 deletions word-search/dahyeong-yun.java
Comment thread
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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dahyeong-yun 저는 이 문제 시간복잡도 분석에 시간이 좀 걸렸었는데, 혹시 어떻게 분석을 접근하셨는지 한번 풀어서 설명해주실 수 있나요?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

저도 쉽지 않았던 것 같아요. 정리된 생각으로 말씀드리면,

  • find를 하기 전에 바깥 쪽에 이중 루프가 있으므로 일단 n * m(즉, 가로 길이 * 세로 길이) 만큼은 순회를 한다고 봤고요.
  • find를 하면 각 글자에서 3방향으로 다시 뻗어 나가니까, 재귀가 호출 될 때마다 3씩 곱한만큼 콜스택이 쌓일거라 3^x 형태가 된다고 생각했어요. 이 때 x가 무엇일까 생각하면, 재귀는 단어의 길이까지 찾고 멈추니까 단어의 길이가 되겠네요.

(분석이 맞는지는 잘 모르겠네요)

* (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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@dahyeong-yun 저도 처음에 이렇게 visited 썼다가, 나중에 보니 해당 보드에 문자를 #와 같은걸로 마킹하고 다시 함수 끝에서 돌려놓으면 굳이 visited가 필요없더라고요! 요 방법도 적용해보셔도 좋을 것 같아요.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

그거 엄청 좋은 방식이네요!
visited로 임의 공간 사용 안해도 되니까 좋네요


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;
}
}
Loading