Skip to content

Commit

Permalink
Merge pull request #496 from lymchgmk/feat/week7
Browse files Browse the repository at this point in the history
  • Loading branch information
EgonD3V authored Sep 27, 2024
2 parents 027de11 + 348e0d4 commit ae84911
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 0 deletions.
80 changes: 80 additions & 0 deletions longest-substring-without-repeating-characters/EGON.py
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()
172 changes: 172 additions & 0 deletions number-of-islands/EGON.py
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()
88 changes: 88 additions & 0 deletions reverse-linked-list/EGON.py
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()
Loading

0 comments on commit ae84911

Please sign in to comment.