From a4ed5d3fc2c1c5a8d7510e806926c2d432ed9982 Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 10:10:51 +0900 Subject: [PATCH 1/6] add solution: Reverse Bits --- reverse-bits/flynn.go | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 reverse-bits/flynn.go diff --git a/reverse-bits/flynn.go b/reverse-bits/flynn.go new file mode 100644 index 000000000..fae53fb43 --- /dev/null +++ b/reverse-bits/flynn.go @@ -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 +} \ No newline at end of file From d0fafa41de1dd77d4305c683986154ba9458b0f1 Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 10:13:06 +0900 Subject: [PATCH 2/6] fix lint --- reverse-bits/flynn.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/reverse-bits/flynn.go b/reverse-bits/flynn.go index fae53fb43..aea5c0df0 100644 --- a/reverse-bits/flynn.go +++ b/reverse-bits/flynn.go @@ -10,20 +10,20 @@ Big O */ func reverseBits(num uint32) uint32 { - var res uint32 = 0 - for i := 0; i < 32; i++ { + 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 + // res = res * 2 + 1 + // } else { + // res *= 2 + // } + // num /= 2 // using bitwise operators res = (res << 1) | (num & 1) num >>= 1 - } + } - return res -} \ No newline at end of file + return res +} From 517b59d6c5aaa80e96b6734341f0aec8ada825b9 Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 10:59:15 +0900 Subject: [PATCH 3/6] add solution: binary tree level order traversal --- binary-tree-level-order-traversal/flynn.go | 52 ++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 binary-tree-level-order-traversal/flynn.go diff --git a/binary-tree-level-order-traversal/flynn.go b/binary-tree-level-order-traversal/flynn.go new file mode 100644 index 000000000..3e6d35fcb --- /dev/null +++ b/binary-tree-level-order-traversal/flynn.go @@ -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 +} From 30322dc0ae0e07057c3525aef43eaec2d5b82c39 Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 11:38:53 +0900 Subject: [PATCH 4/6] add solution: house robber ii --- house-robber-ii/flynn.go | 51 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 house-robber-ii/flynn.go diff --git a/house-robber-ii/flynn.go b/house-robber-ii/flynn.go new file mode 100644 index 000000000..0ed3847b4 --- /dev/null +++ b/house-robber-ii/flynn.go @@ -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 + } +} From 6e9197f1537d820be584214df73d455dace0d67e Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 12:13:27 +0900 Subject: [PATCH 5/6] add solution: meeting rooms ii --- meeting-rooms-ii/flynn.go | 46 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 meeting-rooms-ii/flynn.go diff --git a/meeting-rooms-ii/flynn.go b/meeting-rooms-ii/flynn.go new file mode 100644 index 000000000..13dcf9f8b --- /dev/null +++ b/meeting-rooms-ii/flynn.go @@ -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 +} From 369dc9ad925819b01109ea9f1dbd96530954265a Mon Sep 17 00:00:00 2001 From: obzva Date: Tue, 12 Nov 2024 17:00:08 +0900 Subject: [PATCH 6/6] add solution: word search ii --- word-search-ii/flynn.go | 99 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 word-search-ii/flynn.go diff --git a/word-search-ii/flynn.go b/word-search-ii/flynn.go new file mode 100644 index 000000000..7acb5a346 --- /dev/null +++ b/word-search-ii/flynn.go @@ -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') +}