Skip to content

Commit 1c8e5e4

Browse files
committed
Merge remote-tracking branch 'origin/main'
# Conflicts: # longest-common-subsequence/jinvicky.java
2 parents 7c6c91c + eb1a643 commit 1c8e5e4

File tree

9 files changed

+424
-7
lines changed

9 files changed

+424
-7
lines changed

container-with-most-water/jinvicky.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public int maxArea(int[] height) {
1111

1212
while (start < end) {
1313
int y = Math.min(height[start], height[end]); // y축은 더 작은 값으로 설정
14-
int x = Math.abs(start - end);
14+
int x = Math.abs(start - end); // end - start도 가능
1515
int calculatedArea = x * y;
1616
area = Math.max(area, calculatedArea);
1717

linked-list-cycle/jinvicky.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import java.util.HashSet;
2+
import java.util.Set;
3+
4+
public class Solution {
5+
/**
6+
* 문제에서 말하는 pos는 설명을 돕는 사이클 발생 위치이지 코드 상에서 사용하지 않는다.
7+
*/
8+
public boolean hasCycle(ListNode head) {
9+
Set<Integer> set = new HashSet<>();
10+
while(head != null) {
11+
// set에서 이미 존재하는 숫자가 있으면 바로 return true;
12+
// set.add() 메서드는 추가 성공 시 true, 이미 존재하는 숫자면 추가하지 못하고 false를 반환한다.
13+
if(!set.add(head.val)) return true;
14+
head = head.next;
15+
}
16+
return false;
17+
}
18+
}

longest-common-subsequence/jinvicky.java

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,20 @@ class Solution {
22
public int longestCommonSubsequence(String text1, String text2) {
33
int m = text1.length();
44
int n = text2.length();
5-
int[][] dp = new int[m + 1][n + 1];
65

7-
for (int i = 1; i <= m; i++) {
8-
for (int j = 1; j <= n; j++) {
9-
if (text1.charAt(i - 1) == text2.charAt(j - 1)) {
10-
dp[i][j] = dp[i - 1][j - 1] + 1; // 문자가 같으면 대각선 값 + 1
6+
int[][] dp = new int[m+1][n+1];
7+
8+
for (int i = 0; i < m; i++) {
9+
for (int j = 0; j < n; j++) {
10+
// DP 계산을 +1로 했어야 했는데 이전 케이스를 재사용하는 -1로만 접근해서 범위 에러가 많이 났었다.
11+
if (text1.charAt(i) == text2.charAt(j)) {
12+
dp[i + 1][j + 1] = dp[i][j] + 1;
1113
} else {
12-
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); // 위 or 왼쪽 중 큰 값
14+
dp[i + 1][j + 1] = Math.max(dp[i][j + 1], dp[i + 1][j]);
1315
}
1416
}
1517
}
18+
1619
return dp[m][n];
1720
}
1821
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import java.util.HashMap;
2+
import java.util.Map;
3+
4+
class Solution {
5+
/**
6+
* 초기에 중복체크를 위해 HashSet으로 풀었다가 예외 케이스가 발생해서 이전 문자의 인덱스 위치를 알아야 함을 깨닫고
7+
* HashMap으로 문자:인덱스 쌍을 저장했다.
8+
* 자료구조와 반복문의 흐름은 맞았으나 중복 발견 시 left를 Math.max()로 2가지 케이스를 모두 비교하지 않아서 문제 발생
9+
* 기존 left와 해당 문자의 마지막 인덱스 중 더 큰값을 선택한다.
10+
*/
11+
public int lengthOfLongestSubstring(String s) {
12+
if (s == null || s.isEmpty()) return 0;
13+
14+
Map<Character, Integer> last = new HashMap<>();
15+
int left = 0, maxLen = 0;
16+
17+
for (int right = 0; right < s.length(); right++) {
18+
char ch = s.charAt(right);
19+
20+
// VIP:: left를 오른쪽으로 밀거나(left+=) left를 유지한다.
21+
// 왜 left를 가능한 한 오른쪽으로 업데이트할까? -> 그래야 중복 제거된 온전한 슬라이딩 윈도우를 유지할 수 있기 때문이다.
22+
// abba의 경우 두번째 b, 두번째 a가 이에 해당한다.
23+
// last(b) = max(0, 1+1=2) -> 2
24+
// last(a) = max(2, 0+1=1) -> 2
25+
if (last.containsKey(ch)) {
26+
left = Math.max(left, last.get(ch) + 1);
27+
}
28+
maxLen = Math.max(maxLen, right - left + 1);
29+
last.put(ch, right); // 방금 문자의 '마지막 위치' 갱신
30+
}
31+
return maxLen;
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
class Solution {
2+
public int maxProduct(int[] nums) {
3+
if (nums.length == 1)
4+
return nums[0];
5+
if (nums.length == 2) {
6+
return Math.max(nums[0], Math.max(nums[0] * nums[1], nums[1]));
7+
}
8+
9+
int len = nums.length;
10+
int[] max = new int[len];
11+
int[] min = new int[len];
12+
int overall = 0;
13+
14+
max[0] = min[0] = overall = nums[0];
15+
16+
for (int i = 1; i < len; i++) {
17+
// 후보 3을 준비
18+
int justNum = nums[i];
19+
// 계속 더한 값
20+
int keep = justNum * max[i-1];
21+
// 이전 최소에 음수 곱해서 리버스
22+
int reverse = justNum * min[i-1];
23+
24+
// max와 min 배열을 업데이트
25+
max[i] = Math.max(justNum, Math.max(keep, reverse));
26+
min[i] = Math.min(justNum, Math.min(keep, reverse));
27+
28+
// overall을 업데이트, 누적 비교로 최대 전역 유지
29+
overall = Math.max(overall, max[i]);
30+
}
31+
return overall;
32+
}
33+
}

number-of-islands/jinvicky.java

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
public class jinvicky {
2+
}
3+
4+
/**
5+
* 일관성을 위해 모든 변수들을 메서드 내 lv로 선언해서 푸는 것으로 한다.
6+
* dfs를 설계할 때 특히 반환 타입에 약한데 void인가 int,booelan 등 기타 타입인가를 확실히 하기 위해 별도 예제로 풀었다.
7+
* 또한 의도적으로 기존 grid를 1 -> 0으로 바꾸어 섬을 없애지 않고 공간복잡도가 증가하더라도 학습을 위해 방문 배열을 별도로 선언해 방문 여부를 체크했다.
8+
*/
9+
class Solution {
10+
public int numIslands1(char[][] grid) {
11+
if (grid == null || grid.length == 0) return 0;
12+
int rows = grid.length, cols = grid[0].length;
13+
boolean[][] visited = new boolean[rows][cols];
14+
15+
int count = 0;
16+
for (int r = 0; r < rows; r++) {
17+
for (int c = 0; c < cols; c++) {
18+
if (grid[r][c] == '1' && !visited[r][c]) {
19+
int size = dfsByInt(grid, visited, r, c, rows, cols);
20+
if (size > 0) count++; // 크기가 양수면 섬 1개
21+
}
22+
}
23+
}
24+
return count;
25+
}
26+
27+
// dfs는 해당 섬의 넓이를 반환
28+
private int dfsByInt(char[][] grid, boolean[][] visited, int r, int c, int rows, int cols) {
29+
if (r < 0 || c < 0 || r >= rows || c >= cols) return 0; // 범위를 벗어났다.
30+
if (grid[r][c] == '0' || visited[r][c]) return 0; // 문제의 조건에 맞지 않거나, 이미 방문했다.
31+
32+
visited[r][c] = true; // 방문 처리
33+
int area = 1;
34+
35+
area += dfsByInt(grid, visited, r + 1, c, rows, cols);
36+
area += dfsByInt(grid, visited, r - 1, c, rows, cols);
37+
area += dfsByInt(grid, visited, r, c + 1, rows, cols);
38+
area += dfsByInt(grid, visited, r, c - 1, rows, cols);
39+
40+
return area;
41+
}
42+
43+
public int numIslands(char[][] grid) {
44+
if (grid == null || grid.length == 0) return 0;
45+
int rows = grid.length, cols = grid[0].length;
46+
47+
boolean[][] visited = new boolean[rows][cols];
48+
int count = 0;
49+
50+
for (int r = 0; r < rows; r++) {
51+
for (int c = 0; c < cols; c++) {
52+
if (grid[r][c] == '1' && !visited[r][c]) {
53+
dfs(grid, visited, r, c, rows, cols);
54+
count++; // 섬 하나 탐색 끝나면 +1
55+
}
56+
}
57+
}
58+
return count;
59+
}
60+
61+
private void dfs(char[][] grid, boolean[][] visited, int r, int c, int rows, int cols) {
62+
if (r < 0 || c < 0 || r >= rows || c >= cols) return; // 그리드 범위를 벗어나면 종료
63+
if (grid[r][c] == '0' || visited[r][c]) return; // 물이거나 이미 방문한 섬이라면 종료
64+
65+
visited[r][c] = true; // 방문 처리
66+
67+
// 상하좌우 DFS
68+
dfs(grid, visited, r + 1, c, rows, cols);
69+
dfs(grid, visited, r - 1, c, rows, cols);
70+
dfs(grid, visited, r, c + 1, rows, cols);
71+
dfs(grid, visited, r, c - 1, rows, cols);
72+
}
73+
74+
/**
75+
* void 반환의 dfs면 return;
76+
* int 반환의 dfs면 return 0;
77+
* bool 반환의 dfs면 return false;
78+
*
79+
* 1. 리턴값 없이 넘겨받은 상태만 갱신
80+
* void dfs(Node node) {
81+
* if (범위 밖 || 조건 불만족 || 이미 방문) return;
82+
*
83+
* 방문 처리(node);
84+
*
85+
* for (이웃 nei : node) {
86+
* dfs(nei);
87+
* }
88+
* }
89+
*
90+
* 2. 최대/최소 값을 정수로 반환
91+
* int dfs(Node node) {
92+
* if (범위 밖 || 조건 불만족) return 0;
93+
*
94+
* 방문 처리(node);
95+
*
96+
* int result = 1; // 자기 자신 포함
97+
* for (이웃 nei : node) {
98+
* result += dfs(nei);
99+
* }
100+
* return result;
101+
* }
102+
* 3. 조건 만족 여부
103+
* boolean dfs(Node node) {
104+
* if (목표 도달) return true;
105+
* if (범위 밖 || 조건 불만족) return false;
106+
*
107+
* 방문 처리(node);
108+
*
109+
* for (이웃 nei : node) {
110+
* if (dfs(nei)) return true;
111+
* }
112+
* return false;
113+
* }
114+
*
115+
* 4. 메모이제이션/DP 결합
116+
* int dfs(Node node, Map<Node,Integer> memo) {
117+
* if (memo.containsKey(node)) return memo.get(node);
118+
* if (기저 조건) return 1;
119+
*
120+
* int best = 0;
121+
* for (이웃 nei : node) {
122+
* best = Math.max(best, 1 + dfs(nei, memo));
123+
* }
124+
* memo.put(node, best);
125+
* return best;
126+
* }
127+
*/
128+
}

reverse-linked-list/jinvicky.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import java.util.Stack;
2+
3+
class Solution {
4+
/**
5+
* 리버스와 동일한 후입선출 구조를 생각했고 그래서 자료구조로 스택을 결정했다.
6+
* 사이클 문제를 염두해 두고 조건을 설계했지만 사이클을 끊는 위치가 틀려서 오래 걸렸다.
7+
*/
8+
public ListNode reverseList(ListNode head) {
9+
if (head == null) return null;
10+
11+
Stack<ListNode> stack = new Stack<>();
12+
while (head != null) {
13+
stack.push(head);
14+
head = head.next;
15+
}
16+
17+
ListNode newHead = stack.pop(); // 정답 반환용
18+
ListNode current = newHead; // 포인터를 갱신하면서 계산하기용 (소위 더미 포인터)
19+
20+
while (!stack.isEmpty()) {
21+
current.next = stack.pop();
22+
current = current.next; // ← 수정: 자기 자신 가리키는 대신 앞으로 이동
23+
}
24+
current.next = null; // 마지막 tail 정리
25+
return newHead;
26+
}
27+
28+
/**
29+
* 포인터 반복문을 새로 배웠을 때 fast/slow와 같은 투 포인터 알고리즘과 헷갈렸지만 둘은 전혀 다르다.
30+
* 포인터 반복문
31+
* * 링크 방향 뒤집기
32+
* * 3개 포인터 (prev, cur, next)
33+
* * 리스트 자체 구조 변경
34+
* <p>
35+
* 투 포인터
36+
* * 중간, 사이클, 교차점 등 탐색
37+
* * 2개 포인터 (slow, fast)
38+
* * 리스트 구조 그대로, 위치 정보만 얻음
39+
* <p>
40+
* https://bcp0109.tistory.com/142
41+
*/
42+
public ListNode reverseListByPointer(ListNode head) {
43+
ListNode prev = null;
44+
ListNode cur = head;
45+
while (cur != null) {
46+
ListNode next = cur.next; // next라는 temp 변수를 사용해서 prev와 cur.next 값을 바꾼다.
47+
cur.next = prev;
48+
prev = cur;
49+
cur = next;
50+
// 대각선으로 / / / / 으로 변수명 암기하기
51+
}
52+
return prev;
53+
}
54+
55+
public ListNode reverseListByRecursion(ListNode head) {
56+
// head.next가 null인지도 확인하는 로직이 필요합니다. (nullPointerException 방지)
57+
if (head == null || head.next == null) return null; // 재귀의 끝, 이제 기존 연산을 취합한다.
58+
ListNode newHead = reverseListByRecursion(head.next);
59+
head.next.next = head;
60+
head.next = null;
61+
return newHead;
62+
}
63+
}

0 commit comments

Comments
 (0)