-
Notifications
You must be signed in to change notification settings - Fork 120
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #496 from lymchgmk/feat/week7
- Loading branch information
Showing
5 changed files
with
443 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]): | ||
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
|
||
|
||
|
||
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() |
Oops, something went wrong.