Skip to content

Commit c3ee69e

Browse files
authored
Merge pull request #548 from obzva/main
[Flynn] week 11
2 parents e6c6b28 + 5000053 commit c3ee69e

File tree

5 files changed

+319
-0
lines changed

5 files changed

+319
-0
lines changed

binary-tree-maximum-path-sum/flynn.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
풀이
3+
- post order traversal dfs를 활용하여 풀이할 수 있습니다
4+
Big O
5+
- N: node의 개수
6+
- H: Tree의 높이
7+
- Time compleixty: O(N)
8+
- 모든 node를 최대 한 번 탐색합니다
9+
- Space complexity: O(H)
10+
- 재귀 호출 스택의 깊이는 H에 비례하여 증가합니다
11+
*/
12+
13+
/**
14+
* Definition for a binary tree node.
15+
* type TreeNode struct {
16+
* Val int
17+
* Left *TreeNode
18+
* Right *TreeNode
19+
* }
20+
*/
21+
func maxPathSum(root *TreeNode) int {
22+
res := root.Val
23+
24+
var maxSubtreeSum func(*TreeNode) int
25+
maxSubtreeSum = func(node *TreeNode) int {
26+
// base case
27+
if node == nil {
28+
return 0
29+
}
30+
// left subtree로 내려갔을 때 구할 수 있는 maximum path sum
31+
// left subtree로 path를 내려갔을 때, left subtree가 sum에 기여하는 값이 0보다 작을 경우
32+
// left subtree로는 path를 내려가지 않는 것이 좋음
33+
// 따라서 left < 0 인 경우엔 left = 0
34+
left := maxSubtreeSum(node.Left)
35+
if left < 0 {
36+
left = 0
37+
}
38+
// right subtree도 left subtree와 동일함
39+
right := maxSubtreeSum(node.Right)
40+
if right < 0 {
41+
right = 0
42+
}
43+
// 현재 탐색하고 있는 node의 조상 node를 path에 포함하지 않고도
44+
// maxPathSum이 구해지는 경우가 있음
45+
if res < left+right+node.Val {
46+
res = left + right + node.Val
47+
}
48+
// 현재까지 계산한 subtree path sum을 부모 node에게 전달해야 함
49+
// 현재 node의 부모와 이어지는 path여야 하므로, node.Val + max(left, right)를 반환하면 됨
50+
subTreeSum := node.Val
51+
if left > right {
52+
subTreeSum += left
53+
} else {
54+
subTreeSum += right
55+
}
56+
return subTreeSum
57+
}
58+
59+
maxSubtreeSum(root)
60+
return res
61+
}

graph-valid-tree/flynn.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
풀이
3+
- valid tree인지 판별하는 문제입니다
4+
주어진 input이 valid tree이려면,
5+
1. cycle이 없어야 합니다 (cycle이 있는 경우: [[0, 1], [1, 2], [2, 0]])
6+
2. 모든 node가 연결되어 있어야 합니다 (모든 node가 연결되지 않은 경우: [[0, 1], [2, 3]])
7+
- dfs 방식의 함수를 재귀 호출하여 풀이할 수 있습니다
8+
Big O
9+
- N: n
10+
- E: 주어진 배열 edges의 크기
11+
- Time complexity: O(N)
12+
- 모든 node를 최대 1번씩 탐색합니다
13+
- Space complexity: O(E + N)
14+
- visited의 크기는 N에 비례하여 증가합니다 -> O(N)
15+
- adj의 크기는 E에 비례하여 증가합니다 -> O(E)
16+
- dfs의 재귀호출 스택 깊이는 최악의 경우 N까지 커질 수 있습니다 -> O(N)
17+
*/
18+
19+
func validTree(n int, edges [][]int) bool {
20+
// valid tree는 n-1개의 edge를 가질 수 밖에 없습validTree
21+
// 아래 판별식을 이용하면 유효하지 않은 input에 대해 상당한 연산을 줄일 수 있습니다
22+
if len(edges) != n-1 {
23+
return false
24+
}
25+
// 주어진 2차원 배열 edges를 이용해 adjacency list를 생성합니다
26+
adj := make([][]int, n)
27+
for _, edge := range edges {
28+
adj[edge[0]] = append(adj[edge[0]], edge[1])
29+
adj[edge[1]] = append(adj[edge[1]], edge[0])
30+
}
31+
// cycle이 있는지 여부를 판단하기 위해 visited라는 map을 생성합니다 (Go에서는 map으로 set 기능을 대신함)
32+
visited := make(map[int]bool)
33+
34+
var dfs func(int, int) bool
35+
dfs = func(node int, parent int) bool {
36+
// cycle 발견시 false return
37+
if _, ok := visited[node]; ok {
38+
return false
39+
}
40+
visited[node] = true
41+
for _, next := range adj[node] {
42+
if next == parent {
43+
continue
44+
}
45+
if !dfs(next, node) {
46+
return false
47+
}
48+
}
49+
return true
50+
}
51+
// cycle 여부를 판단합니다
52+
if !dfs(0, -1) {
53+
return false
54+
}
55+
// node가 모두 연결되어 있는지 여부를 판단합니다
56+
return len(visited) == n
57+
}

insert-interval/flynn.go

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*
2+
풀이
3+
- newInterval의 start와 end에 대해 각각 이진탐색을 진행하여 insert할 index를 찾아낼 수 있습니다
4+
- index의 앞뒤 interval과 newInterval을 비교하여 merge 여부를 판별하면 문제에서 원하는 배열을 만들 수 있습니다
5+
Big O
6+
- N: 주어진 배열 intervals의 길이
7+
- Time complexity: O(N)
8+
- 이진 탐색 -> O(logN)
9+
- append(res, ...) -> O(N)
10+
- O(N + logN) = O(N)
11+
- Space complexity: O(N)
12+
- 반환하는 배열 res의 공간 복잡도를 고려하면 O(N)
13+
*/
14+
15+
func insert(intervals [][]int, newInterval []int) [][]int {
16+
n := len(intervals)
17+
// base case
18+
if n == 0 {
19+
return append(intervals, newInterval)
20+
}
21+
// 이진탐색 함수
22+
// isStart: newInterval의 start를 탐색할 땐 true, end를 탐색할 땐 false
23+
// target보다 큰 값 중에서 가장 작은 index를 반환함
24+
var binarySearch func(int, bool) int
25+
binarySearch = func(target int, isStart bool) int {
26+
lo := 0
27+
hi := len(intervals)
28+
for lo < hi {
29+
mid := lo + (hi-lo)/2
30+
if isStart {
31+
if intervals[mid][0] < target {
32+
lo = mid + 1
33+
} else {
34+
hi = mid
35+
}
36+
} else {
37+
if intervals[mid][1] < target {
38+
lo = mid + 1
39+
} else {
40+
hi = mid
41+
}
42+
}
43+
}
44+
return lo
45+
}
46+
47+
start := binarySearch(newInterval[0], true)
48+
// newInterval의 시작 지점이 intervals[start-1]의 끝 지점보다 작거나 같으면 merge해야 함
49+
mergeStart := start > 0 && newInterval[0] <= intervals[start-1][1]
50+
end := binarySearch(newInterval[1], false) - 1
51+
// newInterval의 끝 지점이 intervals[end+1]의 시작 지점보다 크거나 같으면 merge해야 함
52+
mergeEnd := end+1 < n && newInterval[1] >= intervals[end+1][0]
53+
54+
// -Go에서의 최적화를 위한 코드입니다-
55+
resCapacity := n + 1
56+
if mergeStart {
57+
resCapacity--
58+
}
59+
if mergeEnd {
60+
resCapacity--
61+
}
62+
// -----------------------------
63+
res := make([][]int, 0, resCapacity)
64+
// newInterval이 들어갈 index보다 앞 부분의 값들을 res에 append
65+
if mergeStart {
66+
res = append(res, intervals[:start-1]...)
67+
} else {
68+
res = append(res, intervals[:start]...)
69+
}
70+
// newInterval을 res에 append
71+
// mergeStart, mergeEnd 여부에 따라 병합할지 그대로 넣을지 판단
72+
if mergeStart && mergeEnd {
73+
res = append(res, []int{intervals[start-1][0], intervals[end+1][1]})
74+
} else if mergeStart {
75+
res = append(res, []int{intervals[start-1][0], newInterval[1]})
76+
} else if mergeEnd {
77+
res = append(res, []int{newInterval[0], intervals[end+1][1]})
78+
} else {
79+
res = append(res, newInterval)
80+
}
81+
// newInterval이 들어갈 index보다 뒷 부분의 값들을 res에 append
82+
if mergeEnd {
83+
res = append(res, intervals[end+2:]...)
84+
} else {
85+
res = append(res, intervals[end+1:]...)
86+
}
87+
88+
return res
89+
}

maximum-depth-of-binary-tree/flynn.go

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
풀이
3+
- DFS
4+
Big O
5+
- N: 노드의 개수
6+
- H: 트리의 높이
7+
- Time complexity: O(N)
8+
- 모든 노드를 탐색합니다
9+
- Space complexity: O(H)
10+
- 재귀호출 스택의 크기는 트리의 높이에 비례하여 증가합니다
11+
*/
12+
13+
/**
14+
* Definition for a binary tree node.
15+
* type TreeNode struct {
16+
* Val int
17+
* Left *TreeNode
18+
* Right *TreeNode
19+
* }
20+
*/
21+
func maxDepth(root *TreeNode) int {
22+
maxDepth := 0
23+
var dig func(*TreeNode, int)
24+
dig = func(node *TreeNode, depth int) {
25+
if node == nil {
26+
if maxDepth < depth {
27+
maxDepth = depth
28+
}
29+
return
30+
}
31+
dig(node.Left, depth+1)
32+
dig(node.Right, depth+1)
33+
}
34+
dig(root, 0)
35+
return maxDepth
36+
}
37+
38+
/*
39+
풀이
40+
- BFS
41+
Big O
42+
- N: 노드의 개수
43+
- Time complexity: O(N)
44+
- 모든 노드를 탐색합니다
45+
- Space complexity: O(N)
46+
- 노드 N개 짜리 트리에서 한 층의 폭은 N을 넘지 않습니다
47+
따라서 queue의 공간복잡도는 O(N)입니다
48+
*/
49+
50+
/**
51+
* Definition for a binary tree node.
52+
* type TreeNode struct {
53+
* Val int
54+
* Left *TreeNode
55+
* Right *TreeNode
56+
* }
57+
*/
58+
func maxDepth(root *TreeNode) int {
59+
level := 0
60+
queue := make([]*TreeNode, 0)
61+
if root != nil {
62+
queue = append(queue, root)
63+
level++
64+
}
65+
for len(queue) > 0 {
66+
currQSize := len(queue)
67+
for currQSize > 0 {
68+
node := queue[0]
69+
queue = queue[1:]
70+
currQSize--
71+
if node.Left != nil {
72+
queue = append(queue, node.Left)
73+
}
74+
if node.Right != nil {
75+
queue = append(queue, node.Right)
76+
}
77+
}
78+
if len(queue) > 0 {
79+
level++
80+
}
81+
}
82+
return level
83+
}

reorder-list/flynn.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/**
2+
* Definition for singly-linked list.
3+
* type ListNode struct {
4+
* Val int
5+
* Next *ListNode
6+
* }
7+
*/
8+
func reorderList(head *ListNode) {
9+
// find a middle node
10+
slow, fast := head, head
11+
for fast != nil && fast.Next != nil {
12+
slow = slow.Next
13+
fast = fast.Next.Next
14+
}
15+
// reverse the second part of the list
16+
var prev, curr *ListNode = nil, slow
17+
for curr != nil {
18+
tmp := curr.Next
19+
curr.Next = prev
20+
prev = curr
21+
curr = tmp
22+
}
23+
// merge two parts of the list
24+
for curr1, curr2 := head, prev; curr2.Next != nil; {
25+
tmp1, tmp2 := curr1.Next, curr2.Next
26+
curr1.Next, curr2.Next = curr2, curr1.Next
27+
curr1, curr2 = tmp1, tmp2
28+
}
29+
}

0 commit comments

Comments
 (0)