-
-
Notifications
You must be signed in to change notification settings - Fork 335
[liza0525] WEEK 09 Solutions #2581
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
Changes from all commits
2bef400
1e269c1
03be2d0
b3aa26b
da022f1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| # 7기 풀이 | ||
| class Solution: | ||
| # 1. 첫번째 풀이: 이 풀이는 한 칸 씩 헤드를 옮기는 것과 두 칸 씩 헤드를 옮기는 것을 비교하여 | ||
| # 헤드가 동일해지면 해당 리스트는 순환한다는 아이디어에서 착안 | ||
| # 시간 복잡도: O(n) | ||
| # - 리스트의 길이(n) 만큼의 시간이 최대 | ||
| # 공간 복잡도: O(1) | ||
| # - slow, fast 변수만 사용 | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| slow = head | ||
| fast = head | ||
|
|
||
| while slow and fast: | ||
| slow = slow.next | ||
|
|
||
| if not fast.next: | ||
| # fast의 다음 노드가 없다면 | ||
| # 순환이 되지 않다는 것을 의미하므로 loop 탈출 | ||
| break | ||
| fast = fast.next.next | ||
|
|
||
| if slow == fast: | ||
| # 같아지는 순간이 순환한다는 것을 의미하므로 True로 early return | ||
| return True | ||
|
|
||
| # loop 탈출 조건에 의해 순환하지 않음을 확인하여 False return | ||
| return False | ||
|
|
||
| # 2. 두번째 풀이: 이미 방문한 노드에 다시 방문하는 경우에 순환한다고 판단 | ||
| # 시간 복잡도: O(n) | ||
| # - 리스트의 길이(n) 만큼의 시간이 최대 | ||
| # 공간 복잡도: O(n) | ||
| # - list의 길이(n)이 최대 공간 복잡도(checked가 늘어남) | ||
| def hasCycle(self, head: Optional[ListNode]) -> bool: | ||
| checked = set() # 노드 방문 여부를 저장 | ||
| curr = head | ||
|
|
||
| while curr: # curr가 None이 되기 전까지(== 끝에 도달할 때까지) | ||
| if curr in checked: | ||
| # 이미 방문한 노드라면 순환한다는 의미이므로 True로 early return | ||
| return True | ||
|
|
||
| # 방문한 노드는 checked에 추가 | ||
| checked.add(curr) | ||
| curr = curr.next # 다음 node 탐색 | ||
|
|
||
| # loop 탈출 조건에 의해 순환하지 않음을 확인하여 False return | ||
| return False |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 한 번의 리스트 순회 내에서 현재 최대, 최소 곱을 갱신하며 최댓값을 찾기 때문에 시간 복잡도는 O(n)입니다. 변수만 사용하므로 공간 복잡도는 O(1)입니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n) | ||
| # - nums의 길이(n)만큼의 시간 복잡도 | ||
| # 공간 복잡도: O(1) | ||
| # - 몇 개의 변수만 사용 | ||
| class Solution: | ||
| # 문제에서는 이어지는 subarray 내에 음수가 짝수 번 만큼 있다면 최대가 만들어지기도 하므로 | ||
| # max_val과 min_val을 가지고 음수의 곱도 대응할 수 있도록 한다. | ||
| def maxProduct(self, nums: List[int]) -> int: | ||
| max_val, min_val = nums[0], nums[0] | ||
| res = nums[0] | ||
|
|
||
| for num in nums[1:]: | ||
| prev_max_val = max_val # 이전 max_val은 업데이트 되기 전에 잠시 저장 | ||
|
|
||
| # 현재 loop에서 num과 max_val(이전까지의 최대곱) * num과 min_val(이전까지의 최소곱) * num 중 | ||
| # 가장 큰 수를 max_val로 업데이트 | ||
| # 이때 num이 저장된다는 의미는 최대/최소곱을 해당 숫자에서 다시 시작하여 곱한다는 의미 | ||
| max_val = max(num, max_val * num, min_val * num) | ||
|
|
||
| # min_val도 max_val과 동일한 원리로 저장, 이때는 미리 저장한 prev_max_val을 이용 | ||
| min_val = min(num, prev_max_val * num, min_val * num) | ||
|
Comment on lines
+14
to
+22
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 음수 곱 때문에 |
||
|
|
||
| # 현재까지의 결과와 이번 loop에서의 max_val을 비교하여 더 큰 수를 res로 업데이트 | ||
| res = max(res, max_val) | ||
|
|
||
| return res | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 윈도우를 오른쪽으로 확장하고, 조건 충족 시 왼쪽을 이동하며 최적의 윈도우를 찾기 때문에 시간 복잡도는 문자열 길이 n에 비례하는 O(n)입니다. 알파벳 개수는 제한적이므로 공간은 상수에 가깝습니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| from collections import defaultdict | ||
|
|
||
|
|
||
| # 7기 풀이 | ||
| # 시간 복잡도: O(n) | ||
| # - s의 길이(n)만큼의 시간복잡도 | ||
| # 공간 복잡도: O(1) | ||
| # - char_dict에는 최대 26개의 알파벳만큼 저장 + 몇 개의 변수 | ||
| class Solution: | ||
| # 윈도우가 늘어나는 조건(right가 올라가는 조건): t의 알파벳 전체가 윈도우에 모두 포함될 때까지 | ||
| # 윈도우가 유지되는 조건(left가 올라가는 조건): t의 알파벳 전체가 윈도우에 포함이 된 후, 한 개라도 누락될 때까지 | ||
| def minWindow(self, s: str, t: str) -> str: | ||
| char_dict = defaultdict(int) | ||
|
|
||
| # t의 구성 알파벳 정보 저장 | ||
| for tt in t: | ||
| # 알파벳 당 몇 개가 있는지 char_dict에 저장 | ||
| char_dict[tt] += 1 | ||
| # t에 있는 알파벳 종류의 개수 | ||
| need_alpha = len(char_dict) | ||
|
|
||
| left = 0 | ||
| res = "" | ||
|
|
||
| for right in range(len(s)): | ||
| if s[right] in char_dict: | ||
| # s[right]가 char_dict에 있다면 윈도우에 해당 알파벳이 있다는 의미로 | ||
| # char_dict에서 하나 차감 | ||
| char_dict[s[right]] -= 1 | ||
| if char_dict[s[right]] == 0: | ||
| # 해당 알파벳의 개수가 0이 되면 필요한 알파벳 중 하나를 다 찾았으므로 | ||
| # need_alpha를 하나 차감 | ||
| need_alpha -= 1 | ||
|
|
||
| while need_alpha == 0: | ||
| # need_alpha가 0이라는 의미는 모든 글자를 다 찾았다는 의미 | ||
| # 윈도우를 유지하며 left를 올린다 | ||
| if not res or right - left + 1 < len(res): | ||
| # 현재의 res 글자보다 left / right 간 사이가 작을 때 res 업데이트 | ||
| res = s[left:right + 1] | ||
| if s[left] in char_dict: | ||
| # s[left]가 char_dict에 있다는 것은 left를 옮길 때 해당 알파벳은 누락이 되기 때문에 | ||
| # 다음 윈도우 구간에서는 다시 필요하므로 char_dict 내 해당 알파벳 개수를 다시 증가 | ||
| # need_alpha도 하나 증가 시켜줘야 한다. | ||
| if char_dict[s[left]] == 0: | ||
| need_alpha += 1 | ||
| char_dict[s[left]] += 1 | ||
|
|
||
| # left 이동 | ||
| left += 1 | ||
|
|
||
| return res |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 각 지점에서 DFS를 수행하며 방문 가능한 지점을 집합에 저장하므로 시간과 공간 모두 맵 크기 mn에 비례하는 O(mn)입니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(m * n) | ||
| # - 최대 재귀 스택 | ||
| # 공간 복잡도: O(m * n) | ||
| # - 인덱스 모두가 대서양과 태평양에 도달할 때 최대 공간 복잡도 | ||
| class Solution: | ||
| # 문제 접근 방향 | ||
| # 각 지점에서 시작하는 것이 아닌, 태평양과 대서양에 맞닿아 있는 인덱스에서 시작해서 | ||
| # 최대 높이에 도달할 때까지 DFS로 접근 후, 두 대양에서 모두 도달하는 인덱스를 찾는다. | ||
| def pacificAtlantic(self, heights: List[List[int]]) -> List[List[int]]: | ||
| directions = [(0, -1), (0, 1), (-1, 0), (1, 0)] | ||
|
|
||
| pacific_dest = set() # 태평양에서 시작할 때 갈 수 있는 곳 | ||
| atlantic_dest = set() # 대서양에서 시작했을 때 갈 수 있는 곳 | ||
|
|
||
| def dfs(i, j, dest): | ||
| dest.add((i, j)) | ||
| for dir_i, dir_j in directions: | ||
| next_i, next_j = i + dir_i, j + dir_j | ||
|
|
||
| if ( | ||
| 0 <= next_i < len(heights) | ||
| and 0 <= next_j < len(heights[0]) | ||
| and (next_i, next_j) not in dest | ||
| and heights[next_i][next_j] >= heights[i][j] # 다음 인덱스가 같거나 높은 경우만 탐색 | ||
| ): | ||
| dfs(next_i, next_j, dest) | ||
|
|
||
| for j in range(len(heights[0])): | ||
| dfs(0, j, pacific_dest) # 태평양에서 시작(맨 윗줄) | ||
| dfs(len(heights) - 1, j, atlantic_dest) # 대서양에서 시작(맨 아랫줄) | ||
|
|
||
| for i in range(len(heights)): | ||
| dfs(i, 0, pacific_dest) # 태평양에서 시작(맨 왼쪽 줄) | ||
| dfs(i, len(heights[0]) - 1, atlantic_dest) # 대서양에서 시작(맨 오른쪽 줄) | ||
|
|
||
| return list(pacific_dest.intersection(atlantic_dest)) # 겹치는 도착지만 리턴 |
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
피드백: 반복문은 최대 32번 수행하며, 변수만 사용하므로 시간 복잡도는 상수 O(1)입니다. 공간도 상수입니다. 개선 제안: 현재 구현이 적절해 보입니다. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # 7기 풀이 | ||
| # 시간 복잡도: O(1) | ||
| # - 숫자가 32비트이기 때문에 최대 32번의 루프 | ||
| # 공간 복잡도: O(1) | ||
| # - 몇 개의 변수만 | ||
| class Solution: | ||
| # 해당 문제는 bit 연산을 이용해서 덧셈을 구현 | ||
| def getSum(self, a: int, b: int) -> int: | ||
| # 문제 조건에 두 숫자는 모두 32bit 숫자임을 명시되었으며 | ||
| # 이 mask를 사용해야 음수 대응도 가능해진다. | ||
| mask = 0xFFFFFFFF | ||
|
|
||
| while b & mask: | ||
| carry = (a & b) << 1 # 올림 표현 | ||
| a = a ^ b # XOR 연산, 비트의 덧셈은 XOR과 동일하기 때문 | ||
| b = carry # 올림한 것을 다음 연산에 사용한다 | ||
|
|
||
| # b가 0이 아니라면 무한 비트로 올라가고 있음을 의미하며 | ||
| # 음수 대응을 위해 a와 mask를 and 연산하여 return | ||
| return a if b == 0 else a & mask |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🏷️ 알고리즘 패턴 분석
📊 시간/공간 복잡도 분석
풀이 1:
Solution.hasCycle (fast/slow pointers)— Time: ✅ O(n) → O(n) / Space: ✅ O(1) → O(1)피드백: 포인터 두 개를 이용하여 리스트를 한 번 순회하며 순환 여부를 판단하므로 시간 복잡도는 리스트 길이 n에 비례하는 O(n)입니다. 추가 공간은 상수만 사용하므로 O(1)입니다.
개선 제안: 현재 구현이 적절해 보입니다.
풀이 2:
Solution.hasCycle (set 방문 기록)— Time: ✅ O(n) → O(n) / Space: ✅ O(n) → O(n)피드백: 각 노드를 한 번씩 방문하며 방문한 노드를 집합에 저장하므로 시간은 리스트 길이 n에 비례하는 O(n)입니다. 공간은 방문 노드 수만큼 필요하여 O(n)입니다.
개선 제안: 현재 구현이 적절해 보입니다.