Skip to content

[KwonNayeon] Week 7 #931

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 25, 2025
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
37 changes: 37 additions & 0 deletions longest-substring-without-repeating-characters/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
"""
Constraints:
- 0 <= s.length <= 5 * 10^4
- s consists of English letters, digits, symbols and spaces.

Time Complexity: O(n)
- 문자열을 한번만 순회

Space Complexity: O(n)
- 딕셔너리에 문자와 인덱스 저장

풀이 방법:
1. 슬라이딩 윈도우와 딕셔너리를 활용
2. seen 딕셔너리에 각 문자의 마지막 등장 위치를 저장
3. 중복 문자를 만나면 윈도우의 시작점(current_start)을 중복 문자 다음 위치로 이동
4. 매 단계에서 현재 윈도우의 길이를 계산하고 최대 길이 갱신
5. 최종적으로 가장 긴 중복 없는 부분 문자열의 길이 반환
"""

class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
seen = {}
current_start = 0
max_length = 0

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

if char in seen and seen[char] >= current_start:
current_start = seen[char] + 1

seen[char] = i

current_length = i - current_start + 1
max_length = max(current_length, max_length)

return max_length
42 changes: 42 additions & 0 deletions number-of-islands/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"""
Constraints:
- m == grid.length
- n == grid[i].length
- 1 <= m, n <= 300
- grid[i][j] is '0' or '1'.

Time Complexity: O(m * n)
- 모든 셀을 한 번씩 방문
- 여기서 m은 행, n은 열을 의미함

Space Complexity: O(m * n)
- 최악의 경우(모든 셀이 '1'일 때) m * n 만큼의 재귀 호출 스택 사용

풀이 방법:
1. 2중 for문으로 그리드의 모든 셀을 순회
2. '1'을 발견하면 DFS로 연결된 모든 땅을 방문하고 '0'으로 표시
3. '1'을 발견할 때마다 islands 카운트를 1씩 증가
"""

class Solution:
def numIslands(self, grid: List[List[str]]) -> int:
islands = 0
for i in range(len(grid)):
for j in range(len(grid[0])):
if grid[i][j] == '1':
self.visit_island(grid, i, j)
islands += 1
return islands

def visit_island(self, grid, i, j):
if (i < 0 or i >= len(grid) or
j < 0 or j >= len(grid[0]) or
grid[i][j] != '1'):
return

grid[i][j] = '0'

self.visit_island(grid, i+1, j) # 위
self.visit_island(grid, i-1, j) # 아래
self.visit_island(grid, i, j+1) # 오른쪽
self.visit_island(grid, i, j-1) # 왼쪽
47 changes: 47 additions & 0 deletions reverse-linked-list/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
"""
Constraints:
- The number of nodes in the list is the range [0, 5000]
- -5000 <= Node.val <= 5000

Time Complexity: O(n)
- n은 linked list의 노드 수
- 리스트를 한 번 순회하면서 각 노드를 한 번씩만 방문하기 때문

Space Complexity: O(1)
- 추가 공간으로 prev, curr, temp 세 개의 포인터만 사용
- 입력 크기와 관계없이 일정한 추가 공간만 사용

풀이 방법:
1. 세 개의 포인터를 사용하여 리스트를 순회하면서 뒤집기
- prev: 이전 노드를 가리키는 포인터
- curr: 현재 노드를 가리키는 포인터
- temp: 다음 노드를 임시 저장하는 포인터

2. 각 단계에서:
- 다음 노드 임시 저장 (temp)
- 현재 노드의 next를 이전 노드로 변경
- 포인터들을 한 칸씩 전진

3. 참고:
- 포인터들의 이동 순서가 중요
- prev가 새로운 head가 됨
"""

# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]:
prev = None
curr = head

while curr is not None:

temp = curr.next
curr.next = prev
prev = curr
curr = temp

return prev
116 changes: 116 additions & 0 deletions set-matrix-zeroes/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
"""
Constraints:
- m == matrix.length
- n == matrix[0].length
- 1 <= m, n <= 200
- -2^31 <= matrix[i][j] <= 2^31 - 1

Time Complexity: O(m*n)
- m은 행, n은 열을 의미
- 0 찾기: O(m*n)
- 행과 열 변환: O(m*n)

Space Complexity: O(m*n)
- zeros 배열이 최대 m*n 크기까지 저장 가능

풀이 방법:
1. 0 위치 저장
2. 저장된 0의 행과 열을 모두 0으로 변환
3. 주의점: 행렬 값 탐색과 변경을 동시에 수행하면 원래 어떤 값이 0이었는지 구분하기 어려워짐
"""

class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
zeros = []

for r in range(len(matrix)):
for c in range(len(matrix[0])):
if matrix[r][c] == 0:
zeros.append((r, c))

for r, c in zeros:
for i in range(len(matrix[0])):
matrix[r][i] = 0
for i in range(len(matrix)):
matrix[i][c] = 0

"""
Time Complexity: O(m*n)
- 행렬 순회: O(m*n)
- 행과 열 변환: O(m*n)

Space Complexity: O(m+n)
Copy link
Contributor

Choose a reason for hiding this comment

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

공간복잡도를 개선하는 과정을 보여주시니 좋네요!
혹시 시간이 가능하시다면, 한번 O(1)까지 확보해 보시는것은 어떨까요?
이미 한단계 개선 스텝을 밟으셔서, 다음 스텝도 금방 찾으실수 있을 것 같아요!

약간의 힌트라면, 주어진 배열이 굳이 0 또는 1 말고 잠깐 다른 값을 가져도 문제가 될건 없겠죠?

- zero_rows: O(m)
- zero_cols: O(n)

풀이 방법:
1. set 자료구조를 활용하여 중복 제거
2. 행과 열 정보를 분리 저장하여 메모리를 효율적으로 사용
3. 행과 열을 독립적으로 처리하여 불필요한 반복 연산 제거
"""

class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
"""
Do not return anything, modify matrix in-place instead.
"""
zero_rows = set()
zero_cols = set()

for r in range(len(matrix)):
for c in range(len(matrix[0])):
if matrix[r][c] == 0:
zero_rows.add(r)
zero_cols.add(c)

for r in zero_rows:
for i in range(len(matrix[0])):
matrix[r][i] = 0

for c in zero_cols:
for i in range(len(matrix)):
matrix[i][c] = 0

"""
Time Complexity: O(m*n)

Space Complexity: O(1)
- 추가적인 메모리를 사용하지 않고 첫 행과 열을 마커로 활용하여 해결
- first_row_zero, first_col_zero 두 변수만 사용

풀이 방법:
1. 첫 행과 첫 열의 0 여부를 변수에 저장 (나중에 처리하기 위함)
2. 첫 행과 첫 열을 마커로 사용: 행렬의 0 위치를 첫 행/열에 표시
3. 표시된 0을 기준으로 나머지 행렬을 변경 (행/열 전체를 0으로 변경)
4. 저장해둔 변수로 첫 행/열 처리 (원래 0이었던 행/열 처리)
"""
class Solution:
def setZeroes(self, matrix: List[List[int]]) -> None:
# 첫 행/열의 0 여부 저장
first_row_zero = any(matrix[0][c] == 0 for c in range(len(matrix[0])))
first_col_zero = any(matrix[r][0] == 0 for r in range(len(matrix)))

# 0이 있는 위치의 첫 행/열에 표시
for r in range(1, len(matrix)):
for c in range(1, len(matrix[0])):
if matrix[r][c] == 0:
matrix[r][0] = 0 # 첫 열에 표시
matrix[0][c] = 0 # 첫 행에 표시

# 표시된 0을 기준으로 나머지 위치 변경
for r in range(1, len(matrix)):
for c in range(1, len(matrix[0])):
if matrix[r][0] == 0 or matrix[0][c] == 0:
matrix[r][c] = 0

# 첫 행/열 처리
if first_row_zero:
for i in range(len(matrix[0])):
matrix[0][i] = 0

if first_col_zero:
for i in range(len(matrix)):
matrix[i][0] = 0
23 changes: 23 additions & 0 deletions unique-paths/KwonNayeon.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Constraints:
- 1 <= m, n <= 100

Time Complexity: O(1)
- math.comb() 사용

Space Complexity: O(1)
- 추가 공간 필요없음

풀이 방법:
- 오른쪽 아래 코너로 가는 유니크한 방법의 갯수 찾기
1. (m-1)번 아래로, (n-1)번 오른쪽으로 가야함 -> 총 (m+n-2)번 이동
2. 결국 (m+n-2)번의 이동 중 (n-1)번의 오른쪽 이동을 선택하는 조합의 수
3. Combination 공식 적용: (m+n-2)C(n-1)

Further Consideration:
- DP로 풀어보기
"""
class Solution:
def uniquePaths(self, m: int, n: int) -> int:
from math import comb
return comb(m+n-2, n-1)
Copy link
Contributor

Choose a reason for hiding this comment

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

역시 파이썬..!

중요하지 않은 내용입니다.
혹시 내장 라이브러리를 사용하지 못하는 제한이 생긴다면 어떨까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@HC-kang 님 꼼꼼한 리뷰 감사합니다! 저도 내장 라이브러리를 활용한 방법 말고 다른 방법도 찾아봐야지... 생각만 하고 있었는데, 리뷰 달아주셨으니 다른 방법도 꼭 찾아봐야겠어요 😂👍

Loading