-
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.
Merge branch 'DaleStudy:main' into main
- Loading branch information
Showing
14 changed files
with
707 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,57 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class Solution: | ||
def merge(self, intervals: List[List[int]]) -> List[List[int]]: | ||
return self.solve_stack(intervals) | ||
|
||
""" | ||
Runtime: 8 ms (Beats 49.77%) | ||
Time Complexity: | ||
- intervals의 길이를 n이라 하면, intervals를 시작 지점을 기준으로 정렬하는데 O(n log n) | ||
- intervals를 조회하면서 연산하는데, 내부 연산은 모두 O(1)이므로 O(n) | ||
> O(n log n) + O(n) ~= O(n log n) | ||
Memory: 19.88 MB (Beats: 99.31%) | ||
Space Complexity: O(n) | ||
- intervals는 내부 정렬만 했으므로 추가 메모리 사용 메모리 없음 | ||
- 겹치는 구간이 없는 최악의 경우 merged의 크기는 intervals의 크기와 같아지므로, O(n) upper bound | ||
""" | ||
|
||
def solve_stack(self, intervals: List[List[int]]) -> List[List[int]]: | ||
intervals.sort() | ||
|
||
merged = [] | ||
for interval in intervals: | ||
if not merged: | ||
merged.append(interval) | ||
continue | ||
|
||
new_start, new_end = interval | ||
_, last_end = merged[-1] | ||
if last_end < new_start: | ||
merged.append(interval) | ||
else: | ||
merged[-1][1] = max(last_end, new_end) | ||
|
||
return merged | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
|
||
def test_1(self): | ||
intervals = [[1, 3], [2, 6], [8, 10], [15, 18]] | ||
output = [[1, 6], [8, 10], [15, 18]] | ||
|
||
self.assertEqual(Solution().merge(intervals), output) | ||
|
||
def test_2(self): | ||
intervals = [[1, 4], [4, 5]] | ||
output = [[1, 5]] | ||
|
||
self.assertEqual(Solution().merge(intervals), 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,30 @@ | ||
// TC: O(n log n) | ||
// It takes n log n to sort array, visit all elements in O(n) time, total O(n log n) | ||
// SC: O(n) | ||
// Needs max O(n) space to save intervals | ||
class Solution { | ||
public int[][] merge(int[][] intervals) { | ||
int n = intervals.length; | ||
if (n == 1) return intervals; | ||
|
||
Arrays.sort(intervals, (a, b) -> Integer.compare(a[0], b[0])); | ||
List<int[]> output = new ArrayList<>(); | ||
output.add(intervals[0]); | ||
|
||
int[] currentInterval = intervals[0]; | ||
|
||
for (int[] interval : intervals) { | ||
int currentEnd = currentInterval[1]; | ||
int nextStart = interval[0]; | ||
int nextEnd = interval[1]; | ||
if (currentEnd >= nextStart) { | ||
currentInterval[1] = Math.max(currentEnd, nextEnd); | ||
} else { | ||
currentInterval = interval; | ||
output.add(currentInterval); | ||
} | ||
} | ||
|
||
return output.toArray(new int[output.size()][]); | ||
} | ||
} |
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,41 @@ | ||
/* | ||
Big O | ||
- N: 주어진 배열 intervals의 길이 | ||
- Time complexity: O(NlogN) | ||
- intervals를 start의 오름차순으로 정렬 -> O(NlogN) | ||
- 반복문 -> O(N) | ||
- O(NlogN + N) = O(NlogN) | ||
- Space complexity: O(N) | ||
- 정답 배열의 크기 -> O(N) | ||
*/ | ||
|
||
import "sort" | ||
|
||
func merge(intervals [][]int) [][]int { | ||
sort.Slice(intervals, func(i, j int) bool { | ||
return intervals[i][0] < intervals[j][0] | ||
}) | ||
res := make([][]int, 0) | ||
start := intervals[0][0] | ||
end := intervals[0][1] | ||
for i := 1; i < len(intervals); i++ { | ||
curr := intervals[i] | ||
if end >= curr[0] { | ||
end = max(end, curr[1]) | ||
} else { | ||
res = append(res, []int{start, end}) | ||
start = curr[0] | ||
end = curr[1] | ||
} | ||
} | ||
res = append(res, []int{start, end}) | ||
return res | ||
} | ||
|
||
func max(a, b int) int { | ||
if a > b { | ||
return a | ||
} else { | ||
return b | ||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
number-of-connected-components-in-an-undirected-graph/EGON.py
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,77 @@ | ||
from typing import List | ||
from unittest import TestCase, main | ||
|
||
|
||
class ListNode: | ||
def __init__(self, val=0, next=None): | ||
self.val = val | ||
self.next = next | ||
|
||
|
||
class Solution: | ||
def countComponents(self, n: int, edges: List[List[int]]) -> int: | ||
return self.solve_union_find(n, edges) | ||
|
||
""" | ||
LintCode 로그인이 안되어서 https://neetcode.io/problems/count-connected-components에서 실행시키고 통과만 확인했습니다. | ||
Runtime: ? ms (Beats ?%) | ||
Time Complexity: O(max(m, n)) | ||
- UnionFind의 parent 생성에 O(n) | ||
- edges 조회에 O(m) | ||
- Union-find 알고리즘의 union을 매 조회마다 사용하므로, * O(α(n)) (α는 아커만 함수의 역함수) | ||
- UnionFind의 각 노드의 부모를 찾기 위해, n번 find에 O(n * α(n)) | ||
> O(n) + O(m * α(n)) + O(n * α(n)) ~= O(max(m, n) * α(n)) ~= O(max(m, n)) (∵ α(n) ~= C) | ||
Memory: ? MB (Beats ?%) | ||
Space Complexity: O(n) | ||
- UnionFind의 parent와 rank가 크기가 n인 리스트이므로, O(n) + O(n) | ||
> O(n) + O(n) ~= O(n) | ||
""" | ||
|
||
def solve_union_find(self, n: int, edges: List[List[int]]) -> int: | ||
|
||
class UnionFind: | ||
def __init__(self, size: int): | ||
self.parent = [i for i in range(size)] | ||
self.rank = [1] * size | ||
|
||
def union(self, first: int, second: int): | ||
first_parent, second_parent = self.find(first), self.find(second) | ||
|
||
if self.rank[first_parent] > self.rank[second_parent]: | ||
self.parent[second_parent] = first_parent | ||
elif self.rank[first_parent] < self.rank[second_parent]: | ||
self.parent[first_parent] = second_parent | ||
else: | ||
self.parent[second_parent] = first_parent | ||
self.rank[first_parent] += 1 | ||
|
||
def find(self, node: int): | ||
if self.parent[node] != node: | ||
self.parent[node] = self.find(self.parent[node]) | ||
|
||
return self.parent[node] | ||
|
||
union_find = UnionFind(size=n) | ||
for first, second in edges: | ||
union_find.union(first, second) | ||
|
||
result = set() | ||
for i in range(n): | ||
result.add(union_find.find(i)) | ||
|
||
return len(result) | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
n = 5 | ||
edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]] | ||
output = False | ||
|
||
self.assertEqual(Solution().countComponents(n, edges), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
34 changes: 34 additions & 0 deletions
34
number-of-connected-components-in-an-undirected-graph/TonyKim9401.java
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,34 @@ | ||
// TC: O(n + m) | ||
// n = the number of nodes, m = the number of edges | ||
// SC: O(n + m) | ||
// n, m are the each size of the 2 demension array 'edges' | ||
public class Solution { | ||
public int countComponents(int n, int[][] edges) { | ||
List<List<Integer>> graph = new ArrayList<>(); | ||
|
||
for (int i = 0; i < n; i++) graph.add(new ArrayList<>()); | ||
|
||
for (int[] edge : edges) { | ||
graph.get(edge[0]).add(edge[1]); | ||
graph.get(edge[1]).add(edge[0]); | ||
} | ||
|
||
boolean[] visit = new boolean[n]; | ||
int count = 0; | ||
|
||
for (int i = 0; i < n; i++) { | ||
if (!visit[i]) { | ||
count += 1; | ||
dfs(i, graph, visit); | ||
} | ||
} | ||
return count; | ||
} | ||
|
||
private void dfs(int node, List<List<Integer>> graph, boolean[] visit) { | ||
visit[node] = true; | ||
for (int neighbor : graph.get(node)) { | ||
if (!visit[neighbor]) dfs(neighbor, graph, visit); | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
number-of-connected-components-in-an-undirected-graph/flynn.go
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,47 @@ | ||
/* | ||
풀이 | ||
- DFS와 hashmap(set)을 이용하여 풀이할 수 있습니다 | ||
- 이전에 풀이했던 course schedule 문제와 유사합니다 | ||
Big O | ||
- N: 노드 개수 | ||
- E: 간선의 개수 | ||
- Time complexity: O(N + E) | ||
- adj를 생성하는 반복문의 시간복잡도는 E에 비례하여 증가합니다 | ||
- 전체 노드를 최대 1번씩 조회하므로 두번째 반복문의 시간복잡도는 N에 비례하여 증가합니다 | ||
- Space complexity: O(N + E) | ||
- adjacency list의 크기는 E에 비례하여 증가합니다 | ||
- checked의 크기는 N에 비례하여 증가합니다 | ||
- check 함수의 재귀 호출 스택 깊이 또한 최악의 경우, N에 비례하여 증가합니다 | ||
*/ | ||
|
||
func countComponents(n int, edges [][]int) int { | ||
adj := make(map[int][]int) | ||
for _, edge := range edges { | ||
adj[edge[0]] = append(adj[edge[0]], edge[1]) | ||
adj[edge[1]] = append(adj[edge[1]], edge[0]) | ||
} | ||
// Go는 {int: bool} hashmap을 set처럼 사용함 | ||
checked := make(map[int]bool) // 모든 탐색이 끝난 노드를 기록함 | ||
// 각 node를 조회하는 함수 | ||
var check func(int) | ||
check = func(i int) { | ||
checked[i] = true | ||
for _, nxt := range adj[i] { | ||
if _, ok := checked[nxt]; ok { | ||
continue | ||
} | ||
check(nxt) | ||
} | ||
} | ||
|
||
res := 0 | ||
for i := 0; i < n; i++ { | ||
if _, ok := checked[i]; ok { | ||
continue | ||
} | ||
res++ | ||
check(i) | ||
} | ||
|
||
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,91 @@ | ||
from typing import 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 values(self) -> [int]: | ||
result = [] | ||
node = self | ||
while node: | ||
result.append(node.val) | ||
node = node.next | ||
|
||
return result | ||
|
||
|
||
class Solution: | ||
def removeNthFromEnd(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
return self.solve_two_pointer(head, n) | ||
""" | ||
Runtime: 0 ms (Beats 100.00%) | ||
Time Complexity: O(n) | ||
> list의 모든 node + dummy node를 탐색하므로 O(n + 1) ~= O(n) | ||
Memory: 16.62 MB (Beats 15.78%) | ||
Space Complexity: O(1) | ||
> 포인터만 사용하므로 O(1) | ||
""" | ||
def solve_two_pointer(self, head: Optional[ListNode], n: int) -> Optional[ListNode]: | ||
if head.next is None: | ||
return None | ||
|
||
dummy = ListNode(-1) | ||
dummy.next = head | ||
head = dummy | ||
|
||
slow = fast = head | ||
while n: | ||
if fast.next: | ||
fast = fast.next | ||
n -= 1 | ||
|
||
while fast is not None: | ||
if fast.next is None: | ||
slow.next = slow.next.next | ||
break | ||
else: | ||
slow = slow.next | ||
fast = fast.next | ||
|
||
return head.next | ||
|
||
|
||
class _LeetCodeTestCases(TestCase): | ||
def test_1(self): | ||
node_1 = ListNode(1) | ||
node_2 = ListNode(2) | ||
node_3 = ListNode(3) | ||
node_4 = ListNode(4) | ||
node_5 = ListNode(5) | ||
|
||
node_1.next = node_2 | ||
node_2.next = node_3 | ||
node_3.next = node_4 | ||
node_4.next = node_5 | ||
|
||
n = 2 | ||
output = [1, 2, 3, 5] | ||
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) | ||
|
||
def test_2(self): | ||
node_1 = ListNode(1) | ||
n = 1 | ||
output = [] | ||
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) | ||
|
||
def test_3(self): | ||
node_1 = ListNode(1) | ||
node_2 = ListNode(2) | ||
node_1.next = node_2 | ||
n = 2 | ||
output = [2] | ||
self.assertEqual(Solution().removeNthFromEnd(node_1, n).values(), output) | ||
|
||
|
||
if __name__ == '__main__': | ||
main() |
Oops, something went wrong.