Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Flynn] Week14 #591

Merged
merged 6 commits into from
Nov 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 52 additions & 0 deletions binary-tree-level-order-traversal/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
풀이
- queue 자료구조를 사용하여 풀이합니다
Big O
- N: 노드의 개수
- Time complexity: O(N)
- 모든 노드를 조회합니다
- Space complexity: O(N)
- 반환 결과값인 2차원 배열 levels -> O(N)
- queue -> O(3/4 * N) = O(N)
- 한 층이 가질 수 있는 최대 노드 개수는 약 N / 2
- queue의 크기가 가장 클 때는 두 층의 노드를 가지고 있으므로 N / 2 + (N / 2) / 2 개의 노드를 갖게 됨
- level -> O(N/2) = O(N)
- - 한 층이 가질 수 있는 최대 노드 개수는 약 N / 2
*/

/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func levelOrder(root *TreeNode) [][]int {
levels := make([][]int, 0)

if root == nil {
return levels
}

queue := make([]*TreeNode, 0)
queue = append(queue, root)
for len(queue) > 0 {
k := len(queue)
level := make([]int, k)
for i := 0; i < k; i++ {
node := queue[i]
level[i] = node.Val
if node.Left != nil {
queue = append(queue, node.Left)
}
if node.Right != nil {
queue = append(queue, node.Right)
}
}
queue = queue[k:]
levels = append(levels, level)
}

return levels
}
51 changes: 51 additions & 0 deletions house-robber-ii/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
풀이
- house robber 문제와 비슷합니다
이전 풀이 링크: https://github.com/DaleStudy/leetcode-study/pull/576/files#diff-a98dce0d933d299b3b8e2cc345b95a398c894391b7b86b4b85e3c0aea9d0757f
- 첫번째 집을 터는 경우와 안 터는 경우를 나누어 계산합니다
Big O
- N: 주어진 배열 nums의 길이
- Time complexity: O(N)
- Space complexity: O(1)
*/

import "slices"

func rob(nums []int) int {
n := len(nums)

if n < 4 {
return slices.Max(nums)
} else if n == 4 {
return max(nums[0]+nums[2], nums[1]+nums[3])
}

// rob nums[0] (can't rob nums[1] and nums[n-1])
robFirst := nums[0]
ppRobbed := nums[2] // initial value = nums[2] because we can't rob nums[1]
pRobbed := nums[3]
pUnrobbed := nums[2]
for i := 4; i < n-1; i++ { // i < n-1 because we can't rob nums[n-1]
ppRobbed, pRobbed, pUnrobbed = pRobbed, nums[i]+max(ppRobbed, pUnrobbed), max(pRobbed, pUnrobbed)
}
robFirst += max(pRobbed, pUnrobbed)

// skip nums[0]
ppRobbed = nums[1]
pRobbed = nums[2]
pUnrobbed = nums[1]
for i := 3; i < n; i++ {
ppRobbed, pRobbed, pUnrobbed = pRobbed, nums[i]+max(ppRobbed, pUnrobbed), max(pRobbed, pUnrobbed)
}
skipFirst := max(pRobbed, pUnrobbed)

return max(robFirst, skipFirst)
}

func max(a, b int) int {
if a > b {
return a
} else {
return b
}
}
46 changes: 46 additions & 0 deletions meeting-rooms-ii/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
풀이
- 회의의 시작시간과 종료시간을 각각 오름차순 정렬하여 풀이할 수 있습니다
Big O
- N: 주어진 배열 intervals의 길이
- Time complexity: O(NlogN)
- starts, ends 생성 -> O(N)
- slices.Sort() -> O(NlogN)
- 마지막 반복문 -> O(N)
- Space complexity: O(logN)
- Go의 slices.Sort()는 퀵소트를 이용하므로 재귀 호출 스택 깊이 O(logN)이 고려되어야 함
*/

import "slices"

func minMeetingRooms(intervals [][]int) int {
n := len(intervals)

if n == 1 {
return 1
}

starts := make([]int, n) // 회의 시작시간들
ends := make([]int, n) // 회의 종료시간들
for i, interval := range intervals {
starts[i] = interval[0]
ends[i] = interval[1]
}
// 오름차순 정렬
slices.Sort(starts)
slices.Sort(ends)

rooms := 0
sPtr := 0
ePtr := 0
for sPtr < n {
if starts[sPtr] < ends[ePtr] { // 현재 사용가능한 회의실이 없음 (현재 진행중인 모든 회의가 starts[sPtr]보다 큼)
rooms++ // 새로운 회의실 추가함
} else {
ePtr++ // 기존 회의실 중에 남는 방이 있음, 새로운 회의실 추가하지 않음
}
sPtr++
}

return rooms
}
29 changes: 29 additions & 0 deletions reverse-bits/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
풀이
- 원본 uint32 num에 대하여 LSB부터(가장 오른쪽 bit) 탐색합니다
LSB % 2 == 1 -> uint32의 새로운 MSB에(가장 왼쪽 bit) 1 추가
else -> 0 추가
Big O
- Time complexity: O(1)
- input num에 상관 없이 32번의 반복을 고정적으로 실행합니다
- Space complexity: O(1)
*/

func reverseBits(num uint32) uint32 {
var res uint32 = 0
for i := 0; i < 32; i++ {
// using numerical operators
// if num % 2 == 1 {
// res = res * 2 + 1
// } else {
// res *= 2
// }
// num /= 2

// using bitwise operators
res = (res << 1) | (num & 1)
num >>= 1
}

return res
}
99 changes: 99 additions & 0 deletions word-search-ii/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/*
풀이
- Trie 자료구조와 2차원 배열에서의 DFS & Backtracking을 사용하여 풀이할 수 있습니다
Big O
- M, N: 주어진 2차원 배열 board의 행, 열 크기
- Max(W): 주어진 배열 words 중에서 가장 길이가 긴 단어의 길이
- Sum(W): 주어진 배열 words에 포함된 모든 단어의 길이의 총합
- Time complexity: O(Sum(W) + MN * Max(W))
- building trie: O(Sum(W))
- 각 단어의 모든 문자를 한 번씩 조회합니다
- 반복문: O(MN * Max(W))
- 행, 열에 대한 반복: O(MN)
- dfs: O(Max(W))
- 한 좌표에 대해 dfs 함수를 trie의 최대 깊이, 즉 O(Max(W))만큼 호출합니다
- Space complexity: O(Sum(W) + Max(W))
- building trie: O(Sum(W)) at worst
- dfs: O(Max(W))
- 재귀 호출 스택의 깊이를 고려해야 합니다
*/

func findWords(board [][]byte, words []string) []string {
// building trie
root := &trieNode{}
for _, w := range words {
root.add(w)
}

m := len(board)
n := len(board[0])
res := make([]string, 0, len(words)) // create result
for r := 0; r < m; r++ {
for c := 0; c < n; c++ {
if len(res) == len(words) { // early break if we found all the words
break
}
if root.children[idx(board[r][c])] != nil { // dfs if you found starting letter of any words
dfs(r, c, &board, root, &res)
}
}
}

return res
}

// ----- Implementation of TrieNode -----
type trieNode struct {
children [26]*trieNode
word string
}

func (t *trieNode) add(w string) {
for i, c := range w {
if t.children[c-'a'] == nil { // create new child node if there wasn't
t.children[c-'a'] = &trieNode{}
}
t = t.children[c-'a']
if i == len(w)-1 {
t.word = w
}
}
}

// ----- Dfs -----
func dfs(r, c int, board *[][]byte, parentNode *trieNode, res *[]string) {
currLetter := (*board)[r][c]
currNode := parentNode.children[idx(currLetter)]
// if current trie node represents some word, then append it to the result
if currNode.word != "" {
*res = append(*res, currNode.word)
currNode.word = "" // prevent duplicate words into the result
}
// mark current cell as visited
(*board)[r][c] = '#'
// search for the 4 directions
m := len(*board)
n := len((*board)[0])
dr := []int{0, 0, 1, -1}
dc := []int{1, -1, 0, 0}
for i := 0; i < 4; i++ {
nr := r + dr[i]
nc := c + dc[i]
if !(0 <= nr && nr < m) || !(0 <= nc && nc < n) { // skip the invalid coordinates
continue
}
if (*board)[nr][nc] == '#' { // skip the visited cell
continue
}
if currNode.children[idx((*board)[nr][nc])] != nil {
dfs(nr, nc, board, currNode, res)
}
}
// restore the cell
(*board)[r][c] = currLetter
}

// ----- Helper -----
func idx(b byte) int {
return int(b - 'a')
}