Skip to content

Commit

Permalink
Merge branch 'DaleStudy:main' into main
Browse files Browse the repository at this point in the history
  • Loading branch information
gitsunmin authored Nov 3, 2024
2 parents 1f50aa2 + 849ece4 commit 648f6b2
Show file tree
Hide file tree
Showing 14 changed files with 707 additions and 0 deletions.
57 changes: 57 additions & 0 deletions merge-intervals/EGON.py
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()
30 changes: 30 additions & 0 deletions merge-intervals/TonyKim9401.java
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()][]);
}
}
41 changes: 41 additions & 0 deletions merge-intervals/flynn.go
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 number-of-connected-components-in-an-undirected-graph/EGON.py
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()
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 number-of-connected-components-in-an-undirected-graph/flynn.go
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
}
91 changes: 91 additions & 0 deletions remove-nth-node-from-end-of-list/EGON.py
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()
Loading

0 comments on commit 648f6b2

Please sign in to comment.