-
Notifications
You must be signed in to change notification settings - Fork 126
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
5 changed files
with
398 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,43 @@ | ||
"""TC: O(), SC: O() | ||
아이디어: | ||
- | ||
SC: | ||
- | ||
TC: | ||
- | ||
""" | ||
|
||
|
||
# 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 maxPathSum(self, root: Optional[TreeNode]) -> int: | ||
sol = [-1001] # 노드의 최소값보다 1 작은 값. 현 문제 세팅에서 -inf 역할을 함. | ||
|
||
def try_get_best_path(node): | ||
if node is None: | ||
# 노드가 비어있을때 경로 없음. 이때 이 노드로부터 얻을 수 있는 최대 경로 값을 | ||
# 0으로 칠 수 있다. | ||
return 0 | ||
|
||
# 왼쪽, 오른쪽 노드로부터 얻을 수 있는 최대 경로 값. | ||
l = max(try_get_best_path(node.left), 0) | ||
r = max(try_get_best_path(node.right), 0) | ||
|
||
# 현 노드를 다리 삼아서 양쪽 자식 노드의 경로를 이었을때 나올 수 있는 경로 값이 | ||
# 최대 경로일 수도 있다. 이 값을 현 솔루션과 비교해서 업데이트 해준다. | ||
sol[0] = max(node.val + l + r, sol[0]) | ||
|
||
# 현 노드의 부모 노드가 `이 노드를 통해 얻을 수 있는 최대 경로 값`으로 사용할 값을 리턴. | ||
return node.val + max(l, r) | ||
|
||
try_get_best_path(root) | ||
return sol[0] |
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,139 @@ | ||
""" | ||
아이디어: | ||
- 트리여야 하므로 엣지 개수가 n-1개여야 한다. | ||
- 엣지 개수가 n-1개이므로 만약 중간에 사이클이 있다면 트리가 연결이 안 된다. | ||
- 연결이 안 되었으니 트리는 아니고... 몇 조각으로 쪼개진 그래프가 된다. 여튼, valid tree가 | ||
아니게 된다. | ||
- spanning tree 만들 때를 생각해보자. 엣지 하나를 더할 때마다 노드가 하나씩 트리에 추가되어야 | ||
엣지 n-1개로 노드 n개를 겨우 만들 수 있는데, 중간에 새로운 노드를 추가 안하고 엄한 곳에 | ||
엣지를 써서 사이클을 만들거나 하면 모든 노드를 연결할 방법이 없다. | ||
- 위의 예시보다 좀 더 일반적으로는 union-find 알고리즘에서 설명하는 union 시행으로도 설명이 | ||
가능하다. union-find에서는 처음에 n개의 노드들의 parent가 자기 자신으로 세팅되어 있는데, | ||
즉, 모든 노드들이 n개의 그룹으로 나뉘어있는데, 여기서 union을 한 번 시행할 때마다 그룹이 1개 | ||
혹은 0개 줄어들 수 있다. 그런데 위 문제에서는 union을 엣지 개수 만큼, 즉, n-1회 시행할 수 있으므로, | ||
만약 union 시행에서 그룹의 개수가 줄어들지 않는 경우(즉, 엣지 연결을 통해 사이클이 생길 경우)가 | ||
한 번이라도 발생하면 union 시행 후 그룹의 개수가 2 이상이 되어 노드들이 서로 연결되지 않아 트리를 | ||
이루지 못한다. | ||
""" | ||
|
||
"""TC: O(), SC: O() | ||
n은 주어진 노드의 개수, e는 주어진 엣지의 개수. | ||
아이디어(이어서): | ||
- union-find 아이디어를 그대로 활용한다. | ||
- 나이브한 접근: | ||
- union을 통해서 엣지로 연결된 두 집합을 합친다. | ||
- find를 통해서 0번째 노드와 모든 노드들이 같은 집합에 속해있는지 확인한다. | ||
- 더 좋은 구현: | ||
- union 시행 중 같은 집합에 속한 두 노드를 합치려고 하는 것을 발견하면 False 리턴 | ||
SC: | ||
- | ||
TC: | ||
- | ||
""" | ||
|
||
|
||
class Solution: | ||
""" | ||
@param n: An integer | ||
@param edges: a list of undirected edges | ||
@return: true if it's a valid tree, or false | ||
""" | ||
|
||
def valid_tree(self, n, edges): | ||
# write your code here | ||
|
||
# union find | ||
parent = list(range(n)) | ||
|
||
def find(x): | ||
if x == parent[x]: | ||
return x | ||
|
||
parent[x] = find(parent[x]) | ||
return parent[x] | ||
|
||
def union(a, b): | ||
pa = find(a) | ||
pb = find(b) | ||
parent[pb] = pa | ||
|
||
# 원래는 값을 리턴하지 않아도 되지만, 같은 집합에 속한 노드를 | ||
# union하려는 상황을 판별하기 위해 값 리턴. | ||
return pa == pb | ||
|
||
if len(edges) != n - 1: | ||
# 트리에는 엣지가 `(노드 개수) - 1`개 만큼 있다. | ||
# 이 조건 만족 안하면 커팅. | ||
return False | ||
|
||
# 나이브한 구현: | ||
# - 모든 엣지로 union 시행 | ||
# - find로 모든 노드가 0번 노드와 같은 집합에 속해있는지 확인 | ||
|
||
# for e in edges: | ||
# union(*e) | ||
|
||
# return all(find(0) == find(i) for i in range(n)) | ||
|
||
# 더 좋은 구현: | ||
# - union 시행 중 같은 집합에 속한 두 노드를 합치려고 하는 것을 발견하면 False 리턴 | ||
for e in edges: | ||
if union(*e): | ||
return False | ||
|
||
return True | ||
|
||
|
||
"""TC: O(), SC: O() | ||
n은 주어진 노드의 개수, e는 주어진 엣지의 개수. | ||
아이디어(이어서): | ||
- union-find를 쓰면 union을 여러 번 시행해야 하는데 이 과정에서 시간을 많이 잡아먹는것 같다. | ||
- 트리를 잘 이뤘는지 확인하려면 한 노드에서 시작해서 dfs를 돌려서 모든 노드들에 도달 가능한지 | ||
체크하면 되는데, 이게 시간복잡도에 더 유리하지 않을까? | ||
SC: | ||
- | ||
TC: | ||
- | ||
""" | ||
|
||
|
||
class Solution: | ||
""" | ||
@param n: An integer | ||
@param edges: a list of undirected edges | ||
@return: true if it's a valid tree, or false | ||
""" | ||
|
||
def valid_tree(self, n, edges): | ||
# write your code here | ||
if len(edges) != n - 1: | ||
# 트리에는 엣지가 `(노드 개수) - 1`개 만큼 있다. | ||
# 이 조건 만족 안하면 커팅. | ||
return False | ||
|
||
adj_list = [[] for _ in range(n)] | ||
for a, b in edges: | ||
adj_list[a].append(b) | ||
adj_list[b].append(a) | ||
|
||
visited = [False for _ in range(n)] | ||
|
||
def dfs(node): | ||
visited[node] = True | ||
for adj in adj_list[node]: | ||
if not visited[adj]: | ||
dfs(adj) | ||
|
||
# 한 노드에서 출발해서 모든 노드가 visted 되어야 주어진 엣지들로 트리를 만들 수 있다. | ||
# 아무 노드에서나 출발해도 되는데 0번째 노드를 선택하자. | ||
dfs(0) | ||
|
||
return all(visited) |
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,61 @@ | ||
"""TC: O(n), SC: O(1) | ||
n은 intervals로 주어진 인터벌의 개수. | ||
아이디어: | ||
- 주어진 인터벌들을 앞에서부터 순회하면서 새 인터벌(newInterval)과 겹치는지 보고, | ||
- 겹치면 합친다. 합친 인터벌로 newInterval을 업데이트 한다. | ||
- 안 겹치면 newInterval과 현재 확인 중인 인터벌(curInterval) 중에 필요한 인터벌을 | ||
결과 리스트에 넣어주어야 한다. | ||
- 안 겹치면, | ||
- 이때, curInterval이 newInterval보다 앞에 있으면 이후 인터벌들 중 newInterval과 합쳐야 하는 | ||
인터벌이 존재할 수 있다. newInterval은 건드리지 않고 curInterval만 결과 리스트에 넣는다. | ||
- curInterval이 newInterval보다 뒤에 있으면 newInterval을 결과 리스트에 더해주고, 그 다음 | ||
curInterval도 결과 리스트에 더해주어야 한다. | ||
- 그런데 curInterval이 들어있는 리스트가 정렬되어 있으므로, 이후에 순회할 curInterval | ||
중에는 더 이상 newInterval과 겹칠 인터벌이 없다. newInterval은 이제 더 이상 쓰이지 | ||
않으므로 None으로 바꿔준다. | ||
SC: | ||
- newInterval 값만 업데이트 하면서 관리. O(1). | ||
TC: | ||
- intervals에 있는 아이템을 순회하면서 매번 체크하는 시행이 O(1). | ||
- 위의 시행을 intervals에 있는 아이템 수만큼 진행하므로 O(n). | ||
""" | ||
|
||
|
||
class Solution: | ||
def insert( | ||
self, intervals: List[List[int]], newInterval: List[int] | ||
) -> List[List[int]]: | ||
res = [] | ||
for curInterval in intervals: | ||
if newInterval: | ||
# 아직 newInterval이 None으로 변경되지 않았다. | ||
if curInterval[1] < newInterval[0]: | ||
# cur, new가 겹치지 않고, curInterval이 더 앞에 있음. | ||
res.append(curInterval) | ||
elif curInterval[0] > newInterval[1]: | ||
# cur, new가 겹치지 않고, newInterval이 더 앞에 있음. | ||
res.append(newInterval) | ||
res.append(curInterval) | ||
newInterval = None | ||
else: | ||
# 겹치는 부분 존재. newInterval을 확장한다. | ||
newInterval = [ | ||
min(curInterval[0], newInterval[0]), | ||
max(curInterval[1], newInterval[1]), | ||
] | ||
else: | ||
# 더 이상 newInterval과 연관된 작업을 하지 않는다. 순회 중인 | ||
# curInterval을 결과 리스트에 더하고 끝. | ||
res.append(curInterval) | ||
|
||
if newInterval: | ||
# intervals에 있는 마지막 아이템이 newInterval과 겹쳤을 경우 아직 | ||
# 결과 리스트에 newInterval이 더해지지 않고 앞선 순회가 종료되었을 | ||
# 수 있다. 이 경우 newInterval이 아직 None이 아니므로 리스트에 더해준다. | ||
res.append(newInterval) | ||
|
||
return res |
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,29 @@ | ||
"""TC: O(n), SC: O(h) | ||
h는 주어진 트리의 높이, n은 주어진 트리의 노드 개수. | ||
아이디어: | ||
특정 노드의 깊이는 `max(오른쪽 깊이, 왼쪽 깊이) + 1`이다. 이렇게 설명하자니 부모 노드의 깊이 값이 | ||
자식의 깊이 값보다 더 큰 것이 이상하긴 한데... 큰 맥락에서 무슨 말을 하고 싶은지는 이해가 가능하다고 | ||
본다. | ||
SC: | ||
- 호출 스택은 트리의 높이(...혹은 깊이)만큼 커진다. O(h). | ||
TC: | ||
- 모든 노드를 방문한다. O(n). | ||
""" | ||
|
||
|
||
# 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 maxDepth(self, root: Optional[TreeNode]) -> int: | ||
def get_depth(node: Optional[TreeNode]) -> int: | ||
return max(get_depth(node.left), get_depth(node.right)) + 1 if node else 0 | ||
|
||
return get_depth(root) |
Oops, something went wrong.