-
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 pull request #81 from Invidam/week03-invidam
[Vidam] Week 3 Solution | Go
- Loading branch information
Showing
5 changed files
with
379 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,83 @@ | ||
# Intuition | ||
<!-- Describe your first thoughts on how to solve this problem. --> | ||
This problem is a typical dynamic programming (DP) problem. (keyword: `Fibonacci`) | ||
|
||
DP has two methods: tabulation and memoization. I'll introduce both methods. | ||
# Approach (tabulation) | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Create an array to store the results. | ||
2. Initiate default values for the base cases `0` and `1`. | ||
3. While iterating through the array, fill in the values using this formula $$f(n) = f(n-1) + f(n-2)$$ | ||
# Complexity | ||
## Complexity (V1) | ||
- Time complexity: $$O(n)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O(n)$$ | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
# Complexity (V2) | ||
- Time complexity: $$O(n)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O(1)$$ | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
|
||
(n is value of `n`) | ||
# Code | ||
```go | ||
func climbStairsV1(n int) int { | ||
climbData := make([]int, n+2, n+2) | ||
climbData[0], climbData[1] = 1, 1 | ||
for s := 0; s < n; s++ { | ||
climbData[s+2] = climbData[s] + climbData[s+1] | ||
} | ||
|
||
return climbData[n] | ||
} | ||
|
||
func climbStairsV2(n int) int { | ||
prev, curr := 1, 1 | ||
for s := 1; s < n; s++ { | ||
prev, curr = curr, prev+curr | ||
} | ||
|
||
return curr | ||
} | ||
``` | ||
|
||
> As you can see in `V2`, it can be optimized. Remove the array and maintain only the `prev` and `curr` values. In Golang, `Multiple Assignment` prodives syntatic sugar. | ||
- - - | ||
|
||
# Approach (memoization) | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Create an hash map (or array) to **cache** the results. | ||
2. Initialize default values to indicate unvisited nodes. (`-1`). | ||
3. Call the recursion using this formula $$f(n) = f(n-1) + f(n-2)$$. | ||
4. If there are cached results, return it. | ||
5. Pay attension to the **base case**, and always update the cache. | ||
|
||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O(n)$$ | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
(n is value of `n`) | ||
# Code | ||
```go | ||
var cache map[int]int = map[int]int{} | ||
func climbStairs(n int) int { | ||
if n == 0 || n == 1 { | ||
return 1 | ||
} else if n < 0 { | ||
return 0 | ||
} else if val, ok := cache[n]; ok { | ||
return val | ||
} | ||
|
||
ret := climbStairs(n-1) + climbStairs(n-2) | ||
cache[n] = ret | ||
return ret | ||
} | ||
``` |
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,75 @@ | ||
# Intuition | ||
Recursuib is a natural method for iterating trees. | ||
|
||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Child function can calculate the depth of its subtrees automatically. | ||
2. Parent function only select the maximum of the two depths and return +1. (i.e. `+1` means parent's depth.) | ||
|
||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity | ||
- $$O(logN)$$ (best case for balanced tree) | ||
- $$O(N)$$ (worst case for skewed tree) | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
(N: size of node.) | ||
# Code | ||
``` | ||
func maxDepth(root *TreeNode) int { | ||
if root == nil { | ||
return 0 | ||
} | ||
return max(maxDepth(root.Left), maxDepth(root.Right)) + 1 | ||
} | ||
``` | ||
- - - | ||
# Intuition | ||
Implement Queue can be troublesome, but it is effective to problem that require tracking depths or levels. | ||
<!-- Describe your first thoughts on how to solve this problem. --> | ||
|
||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Maintain Element belonging to the same level in the queue. | ||
2. While Iterating through the queue, remove the current level and save the next level. | ||
- In GoLang, `range for loop` capture only first once. So We can maintain current level's easily. | ||
3. increase depth while iterationg through all elements until the end. | ||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity | ||
- $$O(logN)$$ (best case for balanced tree) | ||
- $$O(N)$$ (worst case for skewed tree) | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
(N: size of node.) | ||
|
||
# Code | ||
```go | ||
func maxDepth(root *TreeNode) int { | ||
if root == nil { | ||
return 0 | ||
} | ||
depth := 0 | ||
currLevel := []*TreeNode{root} | ||
|
||
for len(currLevel) != 0 { | ||
depth++ | ||
for _, curr := range currLevel { | ||
if curr.Left != nil { | ||
currLevel = append(currLevel, curr.Left) | ||
} | ||
if curr.Right != nil { | ||
currLevel = append(currLevel, curr.Right) | ||
} | ||
currLevel = currLevel[1:] | ||
} | ||
} | ||
|
||
return depth | ||
} | ||
``` | ||
|
||
# What I learned | ||
- [Slice](https://velog.io/@invidam/GoLang-Slice) |
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 @@ | ||
# Intuition | ||
It is a simple `greedy` problem. | ||
간단한 그리디 분류의 문제다. | ||
# Approach | ||
1. To find earliest interval, sort intervals by start time in ascending order. | ||
2. After sorting, while iterating through the array, check for conflict: the current interval's start time shoud be smaller than the next interval's end time. | ||
# Complexity | ||
- Time complexity: $$O(nlog(n))$$ | ||
- Space complexity: $$O(n)$$ | ||
|
||
(n is length of `intervals`) | ||
# Code | ||
```go | ||
func CanAttendMeetings(intervals []*Interval) bool { | ||
sort.Slice(intervals, func(i, j int) bool { | ||
return intervals[i].Start < intervals[j].Start | ||
}) | ||
|
||
curr := &Interval{-1, -1} | ||
for _, next := range intervals { | ||
if curr.End > next.Start { | ||
return false | ||
} | ||
curr = next | ||
} | ||
|
||
return true | ||
} | ||
``` | ||
|
||
# What I learned | ||
GoLang Sort | ||
- mechanism: [Pattern-defeating Quicksort](https://www.youtube.com/watch?v=jz-PBiWwNjc) | ||
- usage: https://hackthedeveloper.com/how-to-sort-in-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,82 @@ | ||
# Intuition (DFS, Recursion) | ||
<!-- Describe your first thoughts on how to solve this problem. --> | ||
Recursion is natural method to iterate trees. (Particularly, multiple trees!) | ||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Child nodes(i.e. Left and Right) are compared eqaulity with their subtrees. | ||
2. Parent nodes check their own values (`Val`) and their children's comparisions. | ||
|
||
(Tip: Comparing the values of nodes before recursion is more efficient. due to **short circuit**, which stops further evaluation(`isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right)`) when the outcome is already determined by comparing `p.Val == q.Val`) | ||
# Complexity | ||
- Time complexity: $$O(n+m)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O(h_n + h_m)$$ | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
|
||
(n and m are number of nodes in trees p and q. $$h_n$$ and $$h_m$$ are their heights.) | ||
# Code | ||
```go | ||
func isSameTree(p *TreeNode, q *TreeNode) bool { | ||
if p == nil || q == nil { | ||
return p == nil && q == nil | ||
} | ||
|
||
return p.Val == q.Val && isSameTree(p.Left, q.Left) && isSameTree(p.Right, q.Right) | ||
} | ||
``` | ||
- - - | ||
# BFS | ||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Like a typical BFS solution, Create Queue and iterate through the tree. However, in this case, mulitple queues are required. | ||
2. While Iterating, Check equality two nodes in p and q. | ||
# Complexity | ||
- Time complexity: $$O(n+m)$$ | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O(n + m)$$ | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
|
||
(n and m are number of nodes in trees p and q.) | ||
# Code | ||
```go | ||
func updateQueue(node *TreeNode, queue []*TreeNode) []*TreeNode { | ||
queue = append(queue, node.Left) | ||
queue = append(queue, node.Right) | ||
|
||
return queue | ||
} | ||
|
||
func isSameTree(p *TreeNode, q *TreeNode) bool { | ||
if p == nil || q == nil { | ||
return p == nil && q == nil | ||
} | ||
pQueue := []*TreeNode{p} | ||
qQueue := []*TreeNode{q} | ||
|
||
for len(pQueue) != 0 { | ||
pCurr := pQueue[0] | ||
qCurr := qQueue[0] | ||
|
||
pQueue = pQueue[1:] | ||
qQueue = qQueue[1:] | ||
|
||
if pCurr == nil && qCurr == nil { | ||
continue | ||
} | ||
|
||
if (pCurr == nil || qCurr == nil) || (pCurr.Val != qCurr.Val) { | ||
return false | ||
} | ||
pQueue = updateQueue(pCurr, pQueue) | ||
qQueue = updateQueue(qCurr, qQueue) | ||
} | ||
|
||
return true | ||
} | ||
``` | ||
|
||
# What I learned | ||
- Short circuit In Go. | ||
- Function couldn't update original value (like `updateQueue()'s queue`) |
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,105 @@ | ||
# Intuition (DFS & BFS) | ||
<!-- Describe your first thoughts on how to solve this problem. --> | ||
We need two main function: one to check the equality of nodes and the subtrees, and another to iterate through the main tree. | ||
|
||
We will use both DFS and BFS methods to solve this problem. | ||
|
||
Reference. [Same Tree](ttps://leetcode.com/problems/same-tree/solutions/5159658/go-simple-solution) | ||
|
||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. Create a function that, while iterating using DFS(Recursion) or BFS(Queue),checks if the two trees are equal. | ||
2. First, check if the two trees are equal. If not, iterate through the children of main tree. (`root.Left`, `root.Right`) | ||
# Complexity (DFS) | ||
- Time complexity: $$O(n * m)$$ | ||
(This complexity is $$O(n * m)$$. because the `isSubtree()` iterates throuth all modes while **simultaneously** calling the `isEqualtree()` for each node. | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O({h_n} + {h_m})$$ | ||
(This complexity is determined. because the maximun depth of the call stack, which doesn't exceed the sum of heights of both trees.) | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
|
||
# Code | ||
``` | ||
func isEqualTree(root *TreeNode, subRoot *TreeNode) bool { | ||
if (root == nil) || (subRoot == nil) { | ||
return root == subRoot | ||
} | ||
return (root.Val == subRoot.Val) && isEqualTree(root.Left, subRoot.Left) && isEqualTree(root.Right, subRoot.Right) | ||
} | ||
func isSubtree(root *TreeNode, subRoot *TreeNode) bool { | ||
if root == nil { | ||
//assert subRoot != nil | ||
return false | ||
} | ||
return isEqualTree(root, subRoot) || isSubtree(root.Left, subRoot) || isSubtree(root.Right, subRoot) | ||
} | ||
``` | ||
- - - | ||
# Complexity (BFS) | ||
- Time complexity: $$O(n * m)$$ | ||
(This complexity is $$O(n * m)$$. because the `isSubtree()` iterates throuth all modes while **simultaneously** calling the `isEqualtree()` for each node. | ||
<!-- Add your time complexity here, e.g. $$O(n)$$ --> | ||
|
||
- Space complexity: $$O({h_n} + {h_m})$$ | ||
(This complexity is determined. because the maximun sizes of the queues (`q`, `q1`, `q2`), which doesn't exceed the sum of sizes of both trees.) | ||
<!-- Add your space complexity here, e.g. $$O(n)$$ --> | ||
# Code | ||
```go | ||
func isEqualTree(root *TreeNode, subRoot *TreeNode) bool { | ||
q1 := []*TreeNode{root} | ||
q2 := []*TreeNode{subRoot} | ||
|
||
for len(q1) != 0 { | ||
f1 := q1[0] | ||
f2 := q2[0] | ||
|
||
q1 = q1[1:] | ||
q2 = q2[1:] | ||
|
||
if (f1 == nil) && (f2 == nil) { | ||
continue | ||
} | ||
if (f1 == nil) || (f2 == nil) || (f1.Val != f2.Val) { | ||
return false | ||
} | ||
|
||
q1 = append(q1, f1.Left) | ||
q1 = append(q1, f1.Right) | ||
|
||
q2 = append(q2, f2.Left) | ||
q2 = append(q2, f2.Right) | ||
} | ||
|
||
return true | ||
} | ||
|
||
func isSubtree(root *TreeNode, subRoot *TreeNode) bool { | ||
if root == nil { | ||
//assert subRoot != nil | ||
return false | ||
} | ||
|
||
q := []*TreeNode{root} | ||
|
||
for len(q) != 0 { | ||
node := q[0] | ||
q = q[1:] | ||
|
||
if node == nil { | ||
continue | ||
} | ||
if isEqualTree(node, subRoot) { | ||
return true | ||
} | ||
|
||
q = append(q, node.Left) | ||
q = append(q, node.Right) | ||
} | ||
|
||
return false | ||
} | ||
``` |