diff --git a/longest-substring-without-repeating-characters/KwonNayeon.py b/longest-substring-without-repeating-characters/KwonNayeon.py new file mode 100644 index 000000000..e01b46072 --- /dev/null +++ b/longest-substring-without-repeating-characters/KwonNayeon.py @@ -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 diff --git a/number-of-islands/KwonNayeon.py b/number-of-islands/KwonNayeon.py new file mode 100644 index 000000000..d532e7700 --- /dev/null +++ b/number-of-islands/KwonNayeon.py @@ -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) # 왼쪽 diff --git a/reverse-linked-list/KwonNayeon.py b/reverse-linked-list/KwonNayeon.py new file mode 100644 index 000000000..2c3b31ed2 --- /dev/null +++ b/reverse-linked-list/KwonNayeon.py @@ -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 diff --git a/set-matrix-zeroes/KwonNayeon.py b/set-matrix-zeroes/KwonNayeon.py new file mode 100644 index 000000000..552168bf0 --- /dev/null +++ b/set-matrix-zeroes/KwonNayeon.py @@ -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) +- 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 diff --git a/unique-paths/KwonNayeon.py b/unique-paths/KwonNayeon.py new file mode 100644 index 000000000..6a31df3ef --- /dev/null +++ b/unique-paths/KwonNayeon.py @@ -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)