-
Notifications
You must be signed in to change notification settings - Fork 120
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
[EGON] Week7 Solutions #496
Changes from all commits
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,80 @@ | ||
from unittest import TestCase, main | ||
|
||
|
||
class Solution: | ||
def lengthOfLongestSubstring(self, s: str) -> int: | ||
return self.solve_sliding_window(s) | ||
|
||
""" | ||
Runtime: 313 ms (Beats 8.97%) | ||
Time Complexity: | ||
- left가 0에서 len(s)까지 조회, right가 left + 1 부터 len(s)까지 조회하므로 O(n * (n + 1) / 2) | ||
- left가 조회할 때마다, 2항 max 연산하므로 * 2 | ||
> O((n * (n + 1) / 2) * 2) ~= O(n ^ 2) | ||
|
||
Memory: 16.51 (Beats 81.63%) | ||
Space Complexity: O(n) | ||
> checker가 최대 s의 길이만큼 커질 수 있으므로 O(n), upper bound | ||
""" | ||
def solve_two_pointer(self, s: str) -> int: | ||
if not s: | ||
return 0 | ||
|
||
max_length = 1 | ||
for left in range(len(s)): | ||
if len(s) - left + 1 < max_length: | ||
return max_length | ||
|
||
right = left + 1 | ||
checker = set(s[left]) | ||
while right < len(s): | ||
if s[right] in checker: | ||
break | ||
|
||
checker.add(s[right]) | ||
right += 1 | ||
|
||
max_length = max(max_length, len(checker)) | ||
|
||
return max_length | ||
|
||
""" | ||
Runtime: 58 ms (Beats 46.47%) | ||
Time Complexity: | ||
- 중복 검사는 set을 사용하므로 O(1) | ||
- right가 len(s)까지 조회하므로 O(n) | ||
- right가 조회한 뒤 2항 max 연산하는데 O(2) | ||
- left가 최대 right까지 조회하고 right < len(s) 이므로 O(n), upper bound | ||
> O(n) * O(2) + O(n) ~= O(n) | ||
|
||
Memory: 16.60 (Beats 41.73%) | ||
Space Complexity: O(n) | ||
> checker가 최대 s의 길이만큼 커질 수 있으므로 O(n), upper bound | ||
""" | ||
def solve_sliding_window(self, s: str) -> int: | ||
max_length = 0 | ||
left = right = 0 | ||
checker = set() | ||
while left < len(s) and right < len(s): | ||
while right < len(s) and s[right] not in checker: | ||
checker.add(s[right]) | ||
right += 1 | ||
|
||
max_length = max(max_length, len(checker)) | ||
|
||
while left < len(s) and (right < len(s) and s[right] in checker): | ||
checker.remove(s[left]) | ||
left += 1 | ||
|
||
return max_length | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
s = "pwwkew" | ||
output = 3 | ||
self.assertEqual(Solution.lengthOfLongestSubstring(Solution(), s), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
from collections import deque | ||
from typing import Generic, List, Optional, TypeVar | ||
from unittest import TestCase, main | ||
|
||
|
||
T = TypeVar('T') | ||
|
||
|
||
class Node(Generic[T]): | ||
def __init__(self, value: T): | ||
self.value = value | ||
self.prev = None | ||
self.post = None | ||
|
||
|
||
class Deque(Generic[T]): | ||
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. Deque을 직접 구현까지 하시고 무료하셨나 봅니다 ㅋ 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. 기회있을 때마다 자료구조 구현하기... |
||
def __init__(self): | ||
self.head: Optional[Node[T]] = None | ||
self.tail: Optional[Node[T]] = None | ||
self.size = 0 | ||
|
||
def is_empty(self): | ||
return self.size == 0 | ||
|
||
def appendright(self, value: T): | ||
node = Node(value) | ||
if self.is_empty(): | ||
self.head = self.tail = node | ||
else: | ||
node.prev = self.tail | ||
if self.tail: | ||
self.tail.post = node | ||
self.tail = node | ||
self.size += 1 | ||
|
||
def appendleft(self, value: T): | ||
node = Node(value) | ||
if self.is_empty(): | ||
self.head = self.tail = node | ||
else: | ||
node.post = self.head | ||
if self.head: | ||
self.head.prev = node | ||
self.head = node | ||
self.size += 1 | ||
|
||
def popright(self) -> T: | ||
if self.is_empty(): | ||
raise IndexError("Deque is empty!") | ||
|
||
value = self.tail.value | ||
self.tail = self.tail.prev | ||
if self.tail: | ||
self.tail.post = None | ||
else: | ||
self.head = None | ||
self.size -= 1 | ||
return value | ||
|
||
def popleft(self) -> T: | ||
if self.is_empty(): | ||
raise IndexError("Deque is empty!") | ||
|
||
value = self.head.value | ||
self.head = self.head.post | ||
if self.head: | ||
self.head.prev = None | ||
else: | ||
self.tail = None | ||
self.size -= 1 | ||
return value | ||
|
||
|
||
class Solution: | ||
def numIslands(self, grid: List[List[str]]) -> int: | ||
return self.solve_bfs_custom(grid) | ||
|
||
""" | ||
Runtime: 243 ms (Beats 60.00%) | ||
Time Complexity: O(MAX_R * MAX_C) | ||
- 2차원 배열 grid를 조회하며 deq에 enqueue하는데 O(MAX_R * MAX_C) | ||
- deq의 원소 하나 당 DIRS 크기만큼 탐색에 O(4), 원소에 해당하는 grid의 값이 "0"인 경우 탐색하지 않으므로 upper bound | ||
- deque의 appendleft, popleft에 O(1) | ||
> O(MAX_R * MAX_C) * O(4) ~= O(MAX_R * MAX_C) | ||
|
||
Memory: 18.82 (Beats 83.56%) | ||
Space Complexity: O(MAX_R * MAX_C) | ||
- visited 역할을 grid의 원소의 값을 변경하여 사용하였으므로 무시 | ||
- deque의 최대 크기는 MAX_R * MAX_C 이므로 O(MAX_R * MAX_C), upper bound | ||
> O(MAX_R * MAX_C) | ||
""" | ||
def solve_bfs(self, grid: List[List[str]]) -> int: | ||
|
||
def is_island(r: int, c: int) -> bool: | ||
nonlocal grid | ||
|
||
if grid[r][c] != "1": | ||
return False | ||
|
||
deq = deque([(r, c)]) | ||
deq.appendleft((r, c)) | ||
while deq: | ||
curr_r, curr_c = deq.popleft() | ||
grid[r][c] = "0" | ||
for dir_r, dir_c in DIRS: | ||
post_r, post_c = curr_r + dir_r, curr_c + dir_c | ||
if 0 <= post_r < MAX_R and 0 <= post_c < MAX_C and grid[post_r][post_c] == "1": | ||
grid[post_r][post_c] = "0" | ||
deq.appendleft((post_r, post_c)) | ||
|
||
return True | ||
|
||
MAX_R, MAX_C = len(grid), len(grid[0]) | ||
DIRS = ((-1, 0), (1, 0), (0, -1), (0, 1)) | ||
count = 0 | ||
for r in range(MAX_R): | ||
for c in range(MAX_C): | ||
count += 1 if is_island(r, c) else 0 | ||
return count | ||
|
||
""" | ||
Runtime: 283 ms (Beats 23.05%) | ||
Time Complexity: O(MAX_R * MAX_C) | ||
> solve_bfs와 동일 | ||
Memory: 19.98 (Beats 44.38%) | ||
Space Complexity: O(MAX_R * MAX_C) | ||
> solve_bfs와 동일 | ||
""" | ||
def solve_bfs_custom(self, grid: List[List[str]]) -> int: | ||
|
||
def is_island(r: int, c: int) -> bool: | ||
nonlocal grid | ||
|
||
if grid[r][c] != "1": | ||
return False | ||
|
||
deq = Deque() | ||
deq.appendleft((r, c)) | ||
while not deq.is_empty(): | ||
curr_r, curr_c = deq.popleft() | ||
grid[r][c] = "0" | ||
for dir_r, dir_c in DIRS: | ||
post_r, post_c = curr_r + dir_r, curr_c + dir_c | ||
if 0 <= post_r < MAX_R and 0 <= post_c < MAX_C and grid[post_r][post_c] == "1": | ||
grid[post_r][post_c] = "0" | ||
deq.appendleft((post_r, post_c)) | ||
|
||
return True | ||
|
||
MAX_R, MAX_C = len(grid), len(grid[0]) | ||
DIRS = ((-1, 0), (1, 0), (0, -1), (0, 1)) | ||
count = 0 | ||
for r in range(MAX_R): | ||
for c in range(MAX_C): | ||
count += 1 if is_island(r, c) else 0 | ||
return count | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
grid = [ | ||
["1", "1", "0", "0", "0"], | ||
["1", "1", "0", "0", "0"], | ||
["0", "0", "1", "0", "0"], | ||
["0", "0", "0", "1", "1"] | ||
] | ||
output = 3 | ||
self.assertEqual(Solution.numIslands(Solution(), grid), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
from typing import List, Optional | ||
from unittest import TestCase, main | ||
|
||
|
||
# Definition for singly-linked list. | ||
class ListNode: | ||
def __init__(self, val=0, next=None): | ||
self.val = val | ||
self.next = next | ||
|
||
def description(self) -> List[int]: | ||
desc = [] | ||
node = self | ||
while node: | ||
desc.append(node.val) | ||
node = node.next | ||
|
||
return desc | ||
|
||
|
||
class Solution: | ||
def reverseList(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
return self.solve_recursively(head) | ||
|
||
""" | ||
Runtime: 41 ms (Beats 28.37%) | ||
Time Complexity: O(n) | ||
- Linked List의 모든 node를 순회하는데 O(n) | ||
- post 변수 할당, curr.next, prev와 curr의 참조 변경은 O(1) | ||
> O(n) * O(1) ~= O(n) | ||
|
||
Memory: 17.66 (Beats 85.13%) | ||
Space Complexity: O(1) | ||
- Linked List의 크기와 무관한 ListNode 타입의 prev, curr, post 변수를 할당하여 사용 | ||
> O(1) | ||
""" | ||
def solve_iteratively(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
prev, curr = None, head | ||
while curr: | ||
post = curr.next | ||
curr.next = prev | ||
prev, curr = curr, post | ||
|
||
return prev | ||
|
||
""" | ||
Runtime: 38 ms (Beats 50.06%) | ||
Time Complexity: O(n) | ||
- Linked List의 모든 node 만큼 재귀를 호출하므로 O(n) | ||
- post 변수 할당, curr.next의 참조 변경은 O(1) | ||
> O(n) * O(1) ~= O(n) | ||
|
||
Memory: 17.78 (Beats 27.88%) | ||
Space Complexity: O(n) | ||
> Linked List의 모든 node 만큼 reverse 함수가 스택에 쌓이므로 O(n) | ||
""" | ||
def solve_recursively(self, head: Optional[ListNode]) -> Optional[ListNode]: | ||
def reverse(prev: Optional[ListNode], curr: Optional[ListNode]) -> Optional[ListNode]: | ||
if not curr: | ||
return prev | ||
|
||
post = curr.next | ||
curr.next = prev | ||
return reverse(curr, post) | ||
|
||
return reverse(None, head) | ||
Comment on lines
+58
to
+66
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. 두 개의 인자를 받는 재귀 구현 배워갑니다 🙇 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. Dale님 덕분에 즐겁게 문제 풀고 있습니다. 늘 감사드립니다. |
||
|
||
|
||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
node1 = ListNode(val=1) | ||
node2 = ListNode(val=2) | ||
node3 = ListNode(val=3) | ||
node4 = ListNode(val=4) | ||
node5 = ListNode(val=5) | ||
node1.next = node2 | ||
node2.next = node3 | ||
node3.next = node4 | ||
node4.next = node5 | ||
|
||
output = [5, 4, 3, 2, 1] | ||
self.assertEqual(Solution.reverseList(Solution(), node1).description(), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
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.
오, while 문 안에 두 개의 while 문이 대칭을 이루니 뭔가 코드가 아름답게 느껴지네요! 😆
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.
pseudo code스러운 파이썬의 장점 덕분인 것 같습니다. 감사합니다.