From d00ba34689e35e68bc0edaf4e9965cd931bb187f Mon Sep 17 00:00:00 2001 From: seungriyou Date: Sun, 15 Jun 2025 12:13:54 +0900 Subject: [PATCH 1/6] solve(w12): 100. Same Tree --- same-tree/seungriyou.py | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 same-tree/seungriyou.py diff --git a/same-tree/seungriyou.py b/same-tree/seungriyou.py new file mode 100644 index 000000000..ff098a8c6 --- /dev/null +++ b/same-tree/seungriyou.py @@ -0,0 +1,34 @@ +# https://leetcode.com/problems/same-tree/ + +from typing import Optional + +# Definition for a binary tree node. +class TreeNode: + def __init__(self, val=0, left=None, right=None): + self.val = val + self.left = left + self.right = right + +class Solution: + def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool: + """ + [Complexity] + - TC: O(n) + - SC: O(height) (call stack) + + [Approach] + 재귀적으로 두 tree를 타고 내려가며 확인할 수 있다. + 각 단계에서 두 tree가 다르다고 판단할 수 있는 조건은 + (1) 한 쪽 node만 None이거나 + (2) 두 node의 값이 다른 + 경우이다. + """ + # base condition + if not p and not q: + return True + + # not same + if not p or not q or p.val != q.val: + return False + + return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right) From 2b442c93814176952db0c975102222158c83c6dd Mon Sep 17 00:00:00 2001 From: seungriyou Date: Sun, 15 Jun 2025 12:14:34 +0900 Subject: [PATCH 2/6] solve(w12): 323. Number of Connected Components in an Undirected Graph --- .../seungriyou.py | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 number-of-connected-components-in-an-undirected-graph/seungriyou.py diff --git a/number-of-connected-components-in-an-undirected-graph/seungriyou.py b/number-of-connected-components-in-an-undirected-graph/seungriyou.py new file mode 100644 index 000000000..71b32c323 --- /dev/null +++ b/number-of-connected-components-in-an-undirected-graph/seungriyou.py @@ -0,0 +1,108 @@ +# https://leetcode.com/problems/number-of-connected-components-in-an-undirected-graph/ + +from typing import List + +class Solution: + def countComponents_bfs(self, n: int, edges: List[List[int]]) -> int: + """ + [Complexity] + - TC: O(v + e) (모든 edge & node 한 번씩 탐색) + - SC: O(v + e) (graph) + + [Approach] + BFS로 visited를 기록해가며 connected component를 센다. + """ + from collections import deque + + graph = [[] for _ in range(n)] + for a, b in edges: + graph[a].append(b) + graph[b].append(a) + + visited, res = set(), 0 + + def bfs(start): + q = deque([start]) + visited.add(start) + + while q: + pos = q.popleft() + for npos in graph[pos]: + if npos not in visited: + q.append(npos) + visited.add(npos) + + return + + for i in range(n): + if i not in visited: + bfs(i) + res += 1 + + return res + + def countComponents_dfs(self, n: int, edges: List[List[int]]) -> int: + """ + [Complexity] + - TC: O(v + e) (모든 edge & node 한 번씩 탐색) + - SC: O(v + e) (graph) + + [Approach] + DFS로 visited를 기록해가며 connected component를 센다. + """ + graph = [[] for _ in range(n)] + for a, b in edges: + graph[a].append(b) + graph[b].append(a) + + visited, res = set(), 0 + + def dfs(pos): + # 이전에 visited 포함 여부 확인하므로 base condition 생략 가능 + + visited.add(pos) + + # recur + for npos in graph[pos]: + if npos not in visited: + dfs(npos) + + return + + for i in range(n): + if i not in visited: + dfs(i) + res += 1 + + return res + + def countComponents(self, n: int, edges: List[List[int]]) -> int: + """ + [Complexity] + - TC: O(v + e * α(v)) (모든 edge & node 한 번씩 탐색) + - SC: O(v) (parent, set(...)) + + [Approach] + edges를 iterate 하며 union-find 수행 후, parent의 종류의 개수를 세면 된다. + parent의 종류의 개수를 셀 때는 다시 find_parent(x)로 찾아야 한다! + """ + + def find_parent(x): + if x != parent[x]: + parent[x] = find_parent(parent[x]) + return parent[x] + + def union_parent(x, y): + px, py = find_parent(x), find_parent(y) + + if px < py: + parent[py] = px + else: + parent[px] = py + + parent = [i for i in range(n)] + + for x, y in edges: + union_parent(x, y) + + return len(set(find_parent(i) for i in range(n))) From 343ec33e12d075ad7b43d33d3d34f6eb4c8213dc Mon Sep 17 00:00:00 2001 From: seungriyou Date: Sun, 15 Jun 2025 12:14:50 +0900 Subject: [PATCH 3/6] solve(w12): 435. Non-overlapping Intervals --- non-overlapping-intervals/seungriyou.py | 57 +++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 non-overlapping-intervals/seungriyou.py diff --git a/non-overlapping-intervals/seungriyou.py b/non-overlapping-intervals/seungriyou.py new file mode 100644 index 000000000..20ad7aff2 --- /dev/null +++ b/non-overlapping-intervals/seungriyou.py @@ -0,0 +1,57 @@ +# https://leetcode.com/problems/non-overlapping-intervals/ + +from typing import List + +class Solution: + def eraseOverlapIntervals1(self, intervals: List[List[int]]) -> int: + """ + [Complexity] + - TC: O(nlogn) (sort) + - SC: O(1) (tim sort -> 최악의 경우 O(n)까지 가능) + + [Approach] + intervals를 start 기준으로 오름차순 정렬 후, greedy 하게 항상 작은 end를 가지는 interval을 선택한다. + """ + # sort asc by start + intervals.sort(key=lambda x: x[0]) + + # initialize prev_e + prev_e = intervals[0][0] - 1 + to_be_removed = 0 + + for s, e in intervals: + # (1) overlapping : greedy하게 prev_e와 e 중 더 작은 값으로 prev_e를 업데이트하고, 제거할 interval 수 증가 + if prev_e > s: + prev_e = min(prev_e, e) + to_be_removed += 1 + # (2) non-overlapping : prev_e만 업데이트 + else: + prev_e = e + + return to_be_removed + + def eraseOverlapIntervals(self, intervals: List[List[int]]) -> int: + """ + [Complexity] + - TC: O(nlogn) (sort) + - SC: O(1) (tim sort -> 최악의 경우 O(n)까지 가능) + + [Approach] + 이전 풀이의 (1) overlapping 단계에서 prev_e와 e 중 min을 고르는 로직을 제거하려면, intervals를 end 기준으로 오름차순 정렬하면 된다. + """ + # sort asc by end + intervals.sort(key=lambda x: x[1]) + + # initialize prev_e + prev_e = intervals[0][0] - 1 + to_be_removed = 0 + + for s, e in intervals: + # (1) overlapping : greedy하게 prev_e와 e 중 더 작은 값으로 선택하면 되므로 prev_e를 그대로 두고, 제거할 interval 수 증가 + if prev_e > s: + to_be_removed += 1 + # (2) non-overlapping : prev_e만 업데이트 + else: + prev_e = e + + return to_be_removed From 62f8abf6ea058031a09cc82de476539eb293145c Mon Sep 17 00:00:00 2001 From: seungriyou Date: Sun, 15 Jun 2025 13:41:35 +0900 Subject: [PATCH 4/6] solve(w12): 19. Remove Nth Node From End of List --- .../seungriyou.py | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 remove-nth-node-from-end-of-list/seungriyou.py diff --git a/remove-nth-node-from-end-of-list/seungriyou.py b/remove-nth-node-from-end-of-list/seungriyou.py new file mode 100644 index 000000000..0c3bbc97a --- /dev/null +++ b/remove-nth-node-from-end-of-list/seungriyou.py @@ -0,0 +1,102 @@ +# https://leetcode.com/problems/remove-nth-node-from-end-of-list/ + +from typing import Optional + +# Definition for singly-linked list. +class ListNode: + def __init__(self, val=0, next=None): + self.val = val + self.next = next + +class Solution: + def removeNthFromEnd_recur(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + """ + [Complexity] + - TC: O(len) + - SC: O(len) (call stack) + + [Approach] + 재귀적으로 linked list를 확인하며 끝에서부터의 순서를 확인 후, 다음 node의 순서가 n과 같을 때 다음 node를 건너뛰도록 한다. + head node를 제거해야 하는 경우(= 끝에서부터 n번째인 node가 head인 경우)에는, 재귀 함수 내에서 node 제거가 불가능하므로 head.next를 반환한다. + """ + + def check_order_from_end(curr): + # base condition + if not curr.next: + return 1 + + # recur + next_order_from_end = check_order_from_end(curr.next) + # nth from end인 node를 건너뛰기 + if next_order_from_end == n: + curr.next = curr.next.next + + return next_order_from_end + 1 + + # head node를 제거해야 하는 경우라면, check_order_from_end() 내에서 node 제거가 불가능하므로 head.next를 반환해야 함 + if check_order_from_end(head) == n: + return head.next + + return head + + def removeNthFromEnd_recur2(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + """ + [Complexity] + - TC: O(len) + - SC: O(len) (call stack) + + [Approach] + 이전 풀이에서 head node를 제거해야 하는 경우를 따로 처리하지 않으려면, 주어진 head를 가리키는 dummy node(= prev)를 추가하고 + dummy node의 next node를 반환하면 된다. + """ + + def check_order_from_end(curr): + # base condition + if not curr.next: + return 1 + + # recur + next_order_from_end = check_order_from_end(curr.next) + # nth from end인 node를 건너뛰기 + if next_order_from_end == n: + curr.next = curr.next.next + + return next_order_from_end + 1 + + prev = ListNode(next=head) + check_order_from_end(prev) + + return prev.next + + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + """ + [Complexity] + - TC: O(len) + - SC: O(1) + + [Approach] + slow, fast의 two pointer를 이용해 반복문으로 풀 수 있다. + 1. fast를 n 번 전진 + 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환 + 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임 + 4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함 + """ + slow = fast = head + + # 1. fast를 n 번 전진 + for _ in range(n): + fast = fast.next + + # 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환 + if not fast: + return head.next + + # 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, + # fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임 + while fast.next: + slow, fast = slow.next, fast.next + + # 4. 뒤에서부터 n + 1번째인 node가 n - 1번째 node를 가리키도록 함 + slow.next = slow.next.next + + return head From 7402dd6cbcdbe2d2e5d364eb4b12c8590c1a56ff Mon Sep 17 00:00:00 2001 From: seungriyou Date: Wed, 18 Jun 2025 22:00:01 +0900 Subject: [PATCH 5/6] solve(w12): 297. Serialize and Deserialize Binary Tree --- .../seungriyou.py | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 serialize-and-deserialize-binary-tree/seungriyou.py diff --git a/serialize-and-deserialize-binary-tree/seungriyou.py b/serialize-and-deserialize-binary-tree/seungriyou.py new file mode 100644 index 000000000..4ff5c51d0 --- /dev/null +++ b/serialize-and-deserialize-binary-tree/seungriyou.py @@ -0,0 +1,64 @@ +# https://leetcode.com/problems/serialize-and-deserialize-binary-tree/ + +# Definition for a binary tree node. +class TreeNode(object): + def __init__(self, x): + self.val = x + self.left = None + self.right = None + +class Codec: + + def serialize(self, root): + """Encodes a tree to a single string. + + :type root: TreeNode + :rtype: str + """ + res = [] + + def preorder_ser(node): + # base condition + if not node: + res.append("None") + return + + # traversal + res.append(str(node.val)) + preorder_ser(node.left) + preorder_ser(node.right) + + preorder_ser(root) + + return ",".join(res) + + def deserialize(self, data): + """Decodes your encoded data to tree. + + :type data: str + :rtype: TreeNode + """ + from collections import deque + + preorder_vals = deque(data.split(",")) + + def preorder_deser(): + val = preorder_vals.popleft() + + # base condition + if val == "None": + return None + + # traversal + node = TreeNode(int(val)) + node.left = preorder_deser() + node.right = preorder_deser() + + return node + + return preorder_deser() + +# Your Codec object will be instantiated and called as such: +# ser = Codec() +# deser = Codec() +# ans = deser.deserialize(ser.serialize(root)) From 7bcbedbfdb83e1799f0378f6e742ec38b5c797f2 Mon Sep 17 00:00:00 2001 From: seungriyou Date: Wed, 18 Jun 2025 22:14:07 +0900 Subject: [PATCH 6/6] solve(w12): 19. Remove Nth Node From End of List (modified) --- .../seungriyou.py | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/remove-nth-node-from-end-of-list/seungriyou.py b/remove-nth-node-from-end-of-list/seungriyou.py index 0c3bbc97a..1007d23c8 100644 --- a/remove-nth-node-from-end-of-list/seungriyou.py +++ b/remove-nth-node-from-end-of-list/seungriyou.py @@ -68,6 +68,37 @@ def check_order_from_end(curr): return prev.next + def removeNthFromEnd_length(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: + """ + [Complexity] + - TC: O(n) + - SC: O(1) + + [Approach] + linked list의 전체 길이를 구하고, head에서부터 (길이 - n - 1) 번 전진하여 node를 건너뛰면 된다. + (2 pass) + """ + # linked list의 length 구하기 + length = 0 + curr = head + while curr: + length += 1 + curr = curr.next + + # length == n라면, head를 제거 + if length == n: + return head.next + + # length - n - 1 번 이동 + curr = head + for _ in range(length - n - 1): + curr = curr.next + + # node 제거 + curr.next = curr.next.next + + return head + def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: """ [Complexity] @@ -75,7 +106,7 @@ def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNod - SC: O(1) [Approach] - slow, fast의 two pointer를 이용해 반복문으로 풀 수 있다. + slow, fast의 two pointer를 이용해 반복문으로 풀 수 있다. (1 pass) 1. fast를 n 번 전진 2. fast가 끝에 도달한 경우, 첫 번째 node를 제거해야하므로 head.next 반환 3. 현재 fast의 위치에서 slow와 fast를 함께 전진하면, fast가 끝에 도달할 때 slow는 뒤에서부터 n + 1번째 node임