Skip to content

Commit 9eaa936

Browse files
authored
Merge pull request #1470 from KwonNayeon/main
[KwonNayeon] Week 7 solutions
2 parents e6de11e + d737da0 commit 9eaa936

File tree

6 files changed

+162
-33
lines changed

6 files changed

+162
-33
lines changed

longest-substring-without-repeating-characters/KwonNayeon.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,34 @@
1515
3. 중복 문자를 만나면 윈도우의 시작점(current_start)을 중복 문자 다음 위치로 이동
1616
4. 매 단계에서 현재 윈도우의 길이를 계산하고 최대 길이 갱신
1717
5. 최종적으로 가장 긴 중복 없는 부분 문자열의 길이 반환
18+
19+
노트:
20+
- 처음에 어려웠던 부분: 딕셔너리를 사용한다고 해서, 지금까지 캐릭터가 등장한 횟수를 저장해야 한다고 생각함
21+
- 실제로는 각 문자의 마지막 등장 위치(인덱스)를 저장하는 용도임
1822
"""
1923

24+
# e.g., s = "abca"
2025
class Solution:
2126
def lengthOfLongestSubstring(self, s: str) -> int:
22-
seen = {}
27+
seen = {} # 각 문자가 마지막으로 등장한 인덱스를 저장하는 딕셔너리
2328
current_start = 0
2429
max_length = 0
2530

2631
for i in range(len(s)):
2732
char = s[i]
2833

34+
# 현재 문자가 이미 등장했고, 그 위치가 현재 고려 중인 부분 문자열 내에 있는 경우
2935
if char in seen and seen[char] >= current_start:
36+
37+
# 중복 발생, 윈도우 시작점을 이전 등장 위치 다음으로 이동
3038
current_start = seen[char] + 1
3139

3240
seen[char] = i
3341

42+
# 현재 부분 문자열의 길이 계산 (현재 인덱스 - 시작 인덱스 + 1)
3443
current_length = i - current_start + 1
44+
45+
# 최대 길이 업데이트
3546
max_length = max(current_length, max_length)
3647

3748
return max_length

number-of-islands/KwonNayeon.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@
1313
- 최악의 경우(모든 셀이 '1'일 때) m * n 만큼의 재귀 호출 스택 사용
1414
1515
풀이 방법:
16-
1. 2중 for문으로 그리드의 모든 셀을 순회
17-
2. '1'을 발견하면 DFS로 연결된 모든 땅을 방문하고 '0'으로 표시
18-
3. '1'을 발견할 때마다 islands 카운트를 1씩 증가
16+
1. numIslands 메서드: 그리드를 순회하며 '1'을 찾고 섬의 개수를 세는 역할
17+
2. visit_island 메서드: 하나의 섬을 완전히 탐색하고 방문한 땅을 '0'으로 바꾸는 역할
18+
3. 두 함수가 작동하는 방식:
19+
- numIslands는 섬의 시작점('1')을 찾아 카운트를 증가시킴
20+
- visit_island는 찾은 섬을 완전히 탐색하고 방문 표시('0'으로 변경)
1921
"""
2022

2123
class Solution:
@@ -29,14 +31,17 @@ def numIslands(self, grid: List[List[str]]) -> int:
2931
return islands
3032

3133
def visit_island(self, grid, i, j):
34+
# base case: 위치가 범위를 벗어나거나 '1'이 아니면 함수를 종료
3235
if (i < 0 or i >= len(grid) or
3336
j < 0 or j >= len(grid[0]) or
3437
grid[i][j] != '1'):
3538
return
3639

40+
# 방문한 땅을 '0'으로 표시하여 다시 방문하지 않도록 함
3741
grid[i][j] = '0'
3842

39-
self.visit_island(grid, i+1, j) # 위
40-
self.visit_island(grid, i-1, j) # 아래
43+
# 네 방향(아래, 위, 오른쪽, 왼쪽)으로 DFS 재귀 호출
44+
self.visit_island(grid, i+1, j) # 아래
45+
self.visit_island(grid, i-1, j) # 위
4146
self.visit_island(grid, i, j+1) # 오른쪽
4247
self.visit_island(grid, i, j-1) # 왼쪽

reverse-bits/KwonNayeon.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
"""
22
Constraints:
3-
- The input must be a binary string of length 32
4-
3+
- The input must be a binary string of length 32
4+
5+
<Solution 1>
6+
57
Time Complexity: O(1)
6-
- 항상 고정된 32비트 문자열에 대해 연산하므로 상수 시간
8+
- 항상 고정된 32비트 문자열에 대해 연산하므로 상수 시간
79
810
Space Complexity: O(1)
9-
- 32비트 고정 크기의 문자열 연산만 사용하므로 상수 공간
11+
- 32비트 고정 크기의 문자열 연산만 사용하므로 상수 공간
1012
1113
풀이 방법:
12-
1. format(n, '032b')를 사용해 입력받은 정수를 32비트 이진수 문자열로 변환함
13-
2. 문자열 슬라이싱 [::-1]으로 비트를 뒤집음
14-
3. int(reversed_binary, 2)로 뒤집은 이진수 문자열을 다시 정수로 변환함
14+
1. format(n, '032b')를 사용해 입력받은 정수를 32비트 이진수 문자열로 변환함
15+
2. 문자열 슬라이싱 [::-1]으로 비트를 뒤집음
16+
3. int(reversed_binary, 2)로 뒤집은 이진수 문자열을 다시 정수로 변환함
1517
"""
1618

1719
class Solution:
@@ -22,3 +24,12 @@ def reverseBits(self, n: int) -> int:
2224
reversed_binary = binary[::-1]
2325

2426
return int(reversed_binary, 2)
27+
"""
28+
<Solution 2>
29+
30+
Time Complexity:
31+
32+
Space Complexity:
33+
34+
풀이 방법:
35+
"""

reverse-linked-list/KwonNayeon.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
- The number of nodes in the list is the range [0, 5000]
44
- -5000 <= Node.val <= 5000
55
6+
<Solution 1>
7+
68
Time Complexity: O(n)
79
- n은 linked list의 노드 수
8-
- 리스트를 한 번 순회하면서 각 노드를 한 번씩만 방문하기 때문
10+
- 리스트를 한 번 순회하면서 각 노드를 한 번씩만 방문
911
1012
Space Complexity: O(1)
1113
- 추가 공간으로 prev, curr, temp 세 개의 포인터만 사용
12-
- 입력 크기와 관계없이 일정한 추가 공간만 사용
14+
- 입력 크기와 관계없이 일정한 추가 공간만 사용함
1315
1416
풀이 방법:
1517
1. 세 개의 포인터를 사용하여 리스트를 순회하면서 뒤집기
@@ -45,3 +47,49 @@ def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
4547
curr = temp
4648

4749
return prev
50+
51+
"""
52+
<Solution 2>
53+
54+
Time Complexity: O(n)
55+
- 스택에 모든 노드를 넣을 때/뺄 때 각 O(n) 시간을 소모함
56+
57+
Space Complexity: O(n)
58+
- 스택에 모든 노드를 넣었다가 빼야 하므로
59+
60+
풀이방법:
61+
- 스택의 LIFO 특성을 활용
62+
63+
노트:
64+
- 풀이 1보다 공간 복잡도가 올라가지만, 더 이해하기 쉬운 풀이
65+
"""
66+
67+
class Solution:
68+
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
69+
# 모든 노드를 순서대로 저장할 빈 리스트 생성
70+
nodes = []
71+
72+
# 현재 노드를 헤드로 초기화
73+
node = head
74+
75+
# 링크드 리스트의 모든 노드를 순회하며 리스트에 저장
76+
while node:
77+
nodes.append(node)
78+
node = node.next
79+
80+
# 새 링크드 리스트의 시작점이 될 더미 노드
81+
dummy = ListNode(-1)
82+
83+
# 새 리스트를 만들기 위한 포인터 초기화
84+
node = dummy
85+
86+
# nodes 리스트에서 역순으로 노드를 꺼내서 새 리스트 구성
87+
while nodes:
88+
node.next = nodes.pop() # 리스트의 마지막 노드를 꺼내서 연결
89+
node = node.next # 노드 이동
90+
91+
# 마지막 노드의 next를 None으로 설정하여 리스트 종료
92+
node.next = None
93+
94+
# 더미 노드의 next = 뒤집힌 리스트의 헤드
95+
return dummy.next

set-matrix-zeroes/KwonNayeon.py

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,20 @@
55
- 1 <= m, n <= 200
66
- -2^31 <= matrix[i][j] <= 2^31 - 1
77
8-
Time Complexity: O(m*n)
9-
- m은 행, n은 열을 의미
10-
- 0 찾기: O(m*n)
11-
- 행과 열 변환: O(m*n)
8+
<Solution 1>
129
13-
Space Complexity: O(m*n)
14-
- zeros 배열이 최대 m*n 크기까지 저장 가능
10+
Time Complexity: O(m * n)
11+
- m은 행, n은 열을 의미함
12+
- 0 찾기: O(m * n)
13+
- 행과 열 변환: O(m * n)
14+
15+
Space Complexity: O(m * n)
16+
- zeros 배열이 최대 m * n 크기까지 저장 가능
1517
1618
풀이 방법:
17-
1. 0 위치 저장
19+
1. 0 위치를 탐색/저장
1820
2. 저장된 0의 행과 열을 모두 0으로 변환
19-
3. 주의점: 행렬 값 탐색과 변경을 동시에 수행하면 원래 어떤 값이 0이었는지 구분하기 어려워짐
2021
"""
21-
2222
class Solution:
2323
def setZeroes(self, matrix: List[List[int]]) -> None:
2424
"""
@@ -32,17 +32,21 @@ def setZeroes(self, matrix: List[List[int]]) -> None:
3232
zeros.append((r, c))
3333

3434
for r, c in zeros:
35+
# 행(row)을 0으로 채움
3536
for i in range(len(matrix[0])):
3637
matrix[r][i] = 0
38+
# 열(column)을 0으로 채움
3739
for i in range(len(matrix)):
3840
matrix[i][c] = 0
3941

4042
"""
41-
Time Complexity: O(m*n)
42-
- 행렬 순회: O(m*n)
43-
- 행과 열 변환: O(m*n)
43+
<Solution 2>
44+
45+
Time Complexity: O(m * n)
46+
- 행렬 순회: O(m * n)
47+
- 행과 열 변환: O(m * n)
4448
45-
Space Complexity: O(m+n)
49+
Space Complexity: O(m + n)
4650
- zero_rows: O(m)
4751
- zero_cols: O(n)
4852
@@ -51,7 +55,6 @@ def setZeroes(self, matrix: List[List[int]]) -> None:
5155
2. 행과 열 정보를 분리 저장하여 메모리를 효율적으로 사용
5256
3. 행과 열을 독립적으로 처리하여 불필요한 반복 연산 제거
5357
"""
54-
5558
class Solution:
5659
def setZeroes(self, matrix: List[List[int]]) -> None:
5760
"""
@@ -75,7 +78,9 @@ def setZeroes(self, matrix: List[List[int]]) -> None:
7578
matrix[i][c] = 0
7679

7780
"""
78-
Time Complexity: O(m*n)
81+
<Solution 3>
82+
83+
Time Complexity: O(m * n)
7984
8085
Space Complexity: O(1)
8186
- 추가적인 메모리를 사용하지 않고 첫 행과 열을 마커로 활용하여 해결

unique-paths/KwonNayeon.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
Constraints:
33
- 1 <= m, n <= 100
44
5+
<Solution 1: 조합 활용>
6+
57
Time Complexity: O(1)
68
- math.comb() 사용
79
@@ -13,11 +15,58 @@
1315
1. (m-1)번 아래로, (n-1)번 오른쪽으로 가야함 -> 총 (m+n-2)번 이동
1416
2. 결국 (m+n-2)번의 이동 중 (n-1)번의 오른쪽 이동을 선택하는 조합의 수
1517
3. Combination 공식 적용: (m+n-2)C(n-1)
16-
17-
Further Consideration:
18-
- DP로 풀어보기
18+
- C(m+n-2, n-1) = (m+n-2)! / ((n-1)! × (m-1)!)
19+
- 풀이가 간결함, 하지만 큰 수에서 오버플로우 가능성 있음
1920
"""
2021
class Solution:
2122
def uniquePaths(self, m: int, n: int) -> int:
2223
from math import comb
2324
return comb(m+n-2, n-1)
25+
26+
"""
27+
<Solution 2: DP 활용>
28+
29+
Time Complexity: O(m * n)
30+
- m은 row, n은 column의 길이
31+
- 메모이제이션 활용, 모든 좌표에서 재귀 함수의 호출이 딱 한번만 일어나기 때문
32+
33+
Space Complexity: O(m * n)
34+
- 함수의 호출 결과를 저장하는데 격자의 넓이에 비례하는 공간이 필요
35+
36+
풀이방법:
37+
- 구현이 복잡함, 하지만 큰 수에서도 안정적임
38+
- 재귀와 메모이제이션을 활용한 Top-down DP 접근법
39+
- 현재 위치에서 목적지까지의 경로 수 = 아래로 이동 + 오른쪽으로 이동
40+
41+
2x3 그리드 예시:
42+
각 위치에서 목적지까지 가는 경로 수:
43+
(0,0)=3 (0,1)=2 (0,2)=1
44+
(1,0)=1 (1,1)=1 (1,2)=1
45+
46+
위치 (0,0)에서 시작:
47+
dfs(0,0) = dfs(1,0) + dfs(0,1) = 1 + 2 = 3
48+
49+
경로 3개:
50+
1. 오른쪽, 오른쪽, 아래
51+
2. 오른쪽, 아래, 오른쪽
52+
3. 아래, 오른쪽, 오른쪽
53+
"""
54+
from functools import cache
55+
class Solution:
56+
def uniquePaths(self, m: int, n: int) -> int:
57+
@cache
58+
def dfs(row, col):
59+
if row == m - 1 and col == n - 1:
60+
return 1
61+
62+
total = 0
63+
64+
if row + 1 < m:
65+
total += dfs(row + 1, col)
66+
67+
if col + 1 < n:
68+
total += dfs(row, col + 1)
69+
70+
return total
71+
72+
return dfs(0, 0)

0 commit comments

Comments
 (0)