Skip to content

Commit

Permalink
Merge pull request #608 from obzva/main
Browse files Browse the repository at this point in the history
  • Loading branch information
obzva authored Nov 23, 2024
2 parents c2a2065 + cf06fdb commit ac2a2e9
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 0 deletions.
105 changes: 105 additions & 0 deletions alien-dictionary/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
/*
풀이
- 두 단어λ₯Ό μ•ŒνŒŒλ²³ ν•˜λ‚˜μ”© μ°¨λ‘€λŒ€λ‘œ λΉ„κ΅ν–ˆμ„ λ•Œ, 첫번째 차이가 λ°œμƒν•˜λŠ” μ§€μ μ—μ„œ alien dictionary의 orderλ₯Ό 찾을 수 μžˆμŠ΅λ‹ˆλ‹€
- 첫번째 단어뢀터 λ°”λ‘œ κ·Έ λ‹€μŒ 단어와 두 단어씩 μ§μ§€μ–΄μ„œ λΉ„κ΅ν•˜λ©΄ μ•žμ—μ„œ λ§ν•œ 일련의 orderλ₯Ό μ°Ύμ•„λ‚Ό 수 μžˆμŠ΅λ‹ˆλ‹€
μ•ŒνŒŒλ²³ xκ°€ μ•ŒνŒŒλ²³ y보닀 alien dictionary orderμ—μ„œ μ•žμ„œλŠ” 관계, 즉 x->y인 관계λ₯Ό μ°Ύμ•„μ„œ x: {y1, y2, y3, ...}인 μ§‘ν•©μ˜ map을 λ§Œλ“€κ² μŠ΅λ‹ˆλ‹€
그리고 이λ₯Ό nextLetters라고 λͺ…λͺ…ν•˜μ˜€μŠ΅λ‹ˆλ‹€
- λ§Œμ•½ νŠΉμ • μ•ŒνŒŒλ²³ x에 λŒ€ν•΄, z->x인 μ•ŒνŒŒλ²³ zκ°€ μ—†λ‹€λ©΄ xλŠ” μš°λ¦¬κ°€ μ •λ‹΅μœΌλ‘œ μ œμΆœν•  result string의 μ–΄λŠ μœ„μΉ˜μ—λ“  자유둭게 λΌμ›Œλ„£μ„ 수 μžˆμŠ΅λ‹ˆλ‹€
(* If there are multiple solutions, return any of them.)
μš°λ¦¬λŠ” 이런 μ•ŒνŒŒλ²³ xλ₯Ό μ°Ύμ•„λ‚Ό λ•Œλ§ˆλ‹€ λ°”λ‘œλ°”λ‘œ result string res에 μΆ”κ°€ν•˜κ² μŠ΅λ‹ˆλ‹€
- z->x인 μ•ŒνŒŒλ²³ zκ°€ ν˜„μž¬ μžˆλŠ”μ§€ μ—†λŠ”μ§€μ— λŒ€ν•œ μƒνƒœκ΄€λ¦¬λ₯Ό ν•˜κΈ° μœ„ν•΄μ„œ prevLetterCountsλΌλŠ” map을 λ§Œλ“€κ² μŠ΅λ‹ˆλ‹€
prevLetterCounts[x]: z->x인 z의 개수
- nextLetters, prevLetterCountsλ₯Ό 잘 μƒμ„±ν•œ ν›„μ—λŠ” prevLetterCountκ°€ 0인 μ•ŒνŒŒλ²³λΆ€ν„° queue에 λ“±λ‘μ‹œν‚¨ ν›„ BFSλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€
BFSλ₯Ό μ‹€ν–‰ν•˜λ©° prevLetterCountκ°€ 0인 μ•ŒνŒŒλ²³μ΄ μƒˆλ‘œ 발견될 경우 queue에 λ“±λ‘μ‹œν‚΅λ‹ˆλ‹€
- μ—£μ§€μΌ€μ΄μŠ€κ°€ 두 경우 λ°œμƒν•˜λŠ”λ°,
μ²«λ²ˆμ§ΈλŠ” nextLettersλ₯Ό μƒμ„±ν•˜λŠ” λ°˜λ³΅λ¬Έμ—μ„œ λ°œκ²¬λ©λ‹ˆλ‹€
λ‘λ²ˆμ§Έ 단어가 첫번째 λ‹¨μ–΄μ˜ prefix인 κ²½μš°λŠ” μ• μ΄ˆλΆ€ν„° 잘λͺ»λœ dictionary order인 κ²½μš°μž…λ‹ˆλ‹€
μœ„ κ²½μš°λŠ” λ‹¨μˆœ μ•ŒνŒŒλ²³ λΉ„κ΅λ‘œλŠ” λ°œκ²¬ν•˜κΈ° μ–΄λ €μš°λ―€λ‘œ flagλ₯Ό μ‚¬μš©ν•˜μ˜€μŠ΅λ‹ˆλ‹€
λ‘λ²ˆμ§ΈλŠ” result string의 길이가 input으둜 주어진 단어듀에 λ“±μž₯ν•œ μ•ŒνŒŒλ²³μ˜ κ°œμˆ˜λ³΄λ‹€ 적은 κ²½μš°μž…λ‹ˆλ‹€
이 κ²½μš°λŠ” nextLetters에 μˆœν™˜μ΄ λ°œμƒν•œ κ²½μš°μ΄λ―€λ‘œ dictionary orderκ°€ 잘λͺ»λ˜μ—ˆλ‹€κ³  νŒλ‹¨ν•  수 μžˆμŠ΅λ‹ˆλ‹€
Big O
- N: 주어진 λ°°μ—΄ words의 길이
- S(W): λ°°μ—΄ words에 μ†ν•œ λͺ¨λ“  string의 길이의 총합
- Time complexity: O(N + S(W))
- prevLetterCounts와 nextLetters 생성 -> O(N)
- nextLetters에 λ“€μ–΄κ°ˆ μ•ŒνŒŒλ²³ 전후관계 μ°ΎκΈ° -> O(S(W))
- μ•ŒνŒŒλ²³ μ†Œλ¬Έμžμ˜ μˆ˜λŠ” μ œν•œλ˜μ–΄ 있기 λ•Œλ¬Έμ— BFS의 μ‹œκ°„ λ³΅μž‘λ„ μƒν•œμ„ μ€ μ •ν•΄μ Έ μžˆμŠ΅λ‹ˆλ‹€ -> O(26 * 26) = O(1)
- Space complexity: O(1)
- μ•ŒνŒŒλ²³ μ†Œλ¬Έμžμ˜ μˆ˜λŠ” μ œν•œλ˜μ–΄ 있기 λ•Œλ¬Έμ— 곡간 λ³΅μž‘λ„μ˜ μƒν•œμ„ μ€ μ •ν•΄μ Έ μžˆμŠ΅λ‹ˆλ‹€
prevLetterCounts -> O(26) = O(1)
nextLetters -> O(26 * 26) = O(1)
queue -> O(26) = O(1)
*/

import "strings"

func alienOrder(words []string) string {
n := len(words)
// prevLetterCounts[x] = count of letters y that are in relation of y->x
prevLetterCounts := make(map[string]int)
// nextLetters[x] = set of letters y that are in relation of x->y
nextLetters := make(map[string]map[string]bool)
for _, word := range words {
for _, c := range word {
if _, ok := prevLetterCounts[string(c)]; !ok {
prevLetterCounts[string(c)] = 0
nextLetters[string(c)] = make(map[string]bool)
}
}
}

for i := 0; i < n-1; i++ {
currWord := words[i]
nextWord := words[i+1]
// flag for edge case below
diff := false
for j := 0; j < len(currWord) && j < len(nextWord); j++ {
if currWord[j] != nextWord[j] {
diff = true
if _, ok := nextLetters[string(currWord[j])][string(nextWord[j])]; !ok {
prevLetterCounts[string(nextWord[j])]++
nextLetters[string(currWord[j])][string(nextWord[j])] = true
}
break
}
}
// tricky edge case!!!
// if nextWord is prefix of currWord, then the provided dictionary order is invalid
if !diff && len(currWord) > len(nextWord) {
return ""
}
}
// BFS
queue := make([]string, 0, len(prevLetterCounts))
for letter := range prevLetterCounts {
// we can arrange letters whose prevLetterCount is zero as we wish
if prevLetterCounts[letter] == 0 {
queue = append(queue, letter)
}
}
// in Go, using strings.Builder is the most efficient way to build strings
var sb strings.Builder
for len(queue) > 0 {
// pop the letter from the queue and append it to the result string
popped := queue[0]
queue = queue[1:]
sb.WriteString(popped)

for nextLetter := range nextLetters[popped] {
prevLetterCounts[nextLetter]--
// if prevLetterCount for nextLetter becomes zero, we can add it to the queue
// append to the result string (order) in the next iteration
if prevLetterCounts[nextLetter] == 0 {
queue = append(queue, nextLetter)
}
}
}
// res is result string
res := sb.String()
// this case means that there was a cycle
if len(res) != len(prevLetterCounts) {
return ""
}
// else return res
return res
}
54 changes: 54 additions & 0 deletions longest-palindromic-substring/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/*
풀이
- μŠ¬λΌμ΄λ”© μœˆλ„μš° 기법을 μ΄μš©ν•˜λ©΄ 풀이할 수 μžˆμŠ΅λ‹ˆλ‹€
Big O
- N: 주어진 λ¬Έμžμ—΄ s의 길이
- Time complexity: O(N^2)
- window ν•¨μˆ˜κ°€ O(N)의 μ‹œκ°„λ³΅μž‘λ„λ₯Ό κ°€μ§€λ―€λ‘œ
각 λ°˜λ³΅λ¬Έμ€ O(N * N) = O(N^2)의 μ‹œκ°„λ³΅μž‘λ„λ₯Ό 가진닀고 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€
- Space complexity: O(1)
- λ³„λ„μ˜ 좔가적인 곡간 λ³΅μž‘λ„λ₯Ό κ³ λ €ν•˜μ§€ μ•Šμ•„λ„ λ©λ‹ˆλ‹€
*/

func longestPalindrome(s string) string {
n := len(s)
maxStart := 0
maxEnd := 0
// for odd lengths
for i := 0; i < n; i++ {
window(&s, &maxStart, &maxEnd, i, false)
}
// for even lengths
for i := 0; i < n-1; i++ {
window(&s, &maxStart, &maxEnd, i, true)
}

return s[maxStart : maxEnd+1]
}

/*
helper function for searching palindromic substring
from the pivotal index `i`
*/
func window(s *string, maxStart *int, maxEnd *int, i int, isEven bool) {
n := len(*s)
start := i
end := i
if isEven {
end++
}
for 0 <= start && end < n {
if (*s)[start] != (*s)[end] {
break
}

// if new palindromic substring is longer than the previously found one,
// update the start and end index
if *maxEnd-*maxStart < end-start {
*maxStart = start
*maxEnd = end
}
start--
end++
}
}
39 changes: 39 additions & 0 deletions rotate-image/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
풀이
- matrixλ₯Ό 4μ‚¬λΆ„λ©΄μœΌλ‘œ λ‚˜λˆ•λ‹ˆλ‹€
1μ‚¬λΆ„λ©΄μ˜ λͺ¨λ“  μ’Œν‘œμ— λŒ€ν•΄ μ•„λž˜μ™€ 같은 연산을 μˆ˜ν–‰ν•©λ‹ˆλ‹€
- 1μ‚¬λΆ„λ©΄μ˜ μ’Œν‘œ a1에 λŒ€ν•΄ a2, a3, a4λ₯Ό μ•„λž˜μ²˜λŸΌ μ •μ˜ν•©λ‹ˆλ‹€
a2: a1을 90도 νšŒμ „μ‹œμΌ°μ„ λ•Œμ˜ μ’Œν‘œ (2사뢄면에 μœ„μΉ˜ν•¨)
a3: a2λ₯Ό 90도 νšŒμ „μ‹œμΌ°μ„ λ•Œμ˜ μ’Œν‘œ (3사뢄면에 μœ„μΉ˜ν•¨)
a4: a3을 90도 νšŒμ „μ‹œμΌ°μ„ λ•Œμ˜ μ’Œν‘œ (4사뢄면에 μœ„μΉ˜ν•¨)
a1 -> a2, a2 -> a3, a3 -> a4, a4 -> a1으둜 값을 λ³€κ²½μ‹œν‚΅λ‹ˆλ‹€
Big O
- N: 맀트릭슀의 크기
- Time complexity: O(N^2)
- Space complexity: O(1)
*/

func rotate(matrix [][]int) {
n := len(matrix)
// μ‚¬λΆ„λ©΄μ˜ 크기, qr, qc: μ‚¬λΆ„λ©΄μ˜ ν–‰, μ—΄ 크기
qr := n / 2
qc := (n + 1) / 2

for r := 0; r < qr; r++ {
for c := 0; c < qc; c++ {
r1 := r
c1 := c

r2 := c
c2 := n - 1 - r

r3 := n - 1 - r
c3 := n - 1 - c

r4 := n - 1 - c
c4 := r

matrix[r1][c1], matrix[r2][c2], matrix[r3][c3], matrix[r4][c4] = matrix[r4][c4], matrix[r1][c1], matrix[r2][c2], matrix[r3][c3]
}
}
}
50 changes: 50 additions & 0 deletions subtree-of-another-tree/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
풀이
- 두 νŠΈλ¦¬κ°€ λ™μΌν•œμ§€ κ²€μ‚¬ν•˜λŠ” dfs ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜μ—¬ 풀이할 수 μžˆμŠ΅λ‹ˆλ‹€
Big O
- M: root 트리의 λ…Έλ“œ 개수
- N: subRoot 트리의 λ…Έλ“œ 개수
- Time complexity: O(MN)
- μ΅œμ•…μ˜ 경우 root 트리의 λͺ¨λ“  λ…Έλ“œμ— λŒ€ν•΄ isSameTree ν•¨μˆ˜λ₯Ό μ‹€ν–‰ (O(M))
μ΅œμ•…μ˜ 경우 isSameTree ν•¨μˆ˜λŠ” O(N)의 μ‹œκ°„λ³΅μž‘λ„λ₯Ό 가짐
- Space complexity: O(M+N)
- isSubTree의 μž¬κ·€ν˜ΈμΆœμŠ€νƒ κΉŠμ΄λŠ” μ΅œλŒ€ O(M)의 κ³΅κ°„λ³΅μž‘λ„λ₯Ό 가짐
- isSameTree의 μž¬κ·€ν˜ΈμΆœμŠ€νƒ κΉŠμ΄λŠ” μ΅œλŒ€ O(N)의 κ³΅κ°„λ³΅μž‘λ„λ₯Ό 가짐
*/

/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func isSubtree(root *TreeNode, subRoot *TreeNode) bool {
// handling nil(null) inputs
if root == nil {
return false
}
// return true if root and subroot are same
if isSameTree(root, subRoot) {
return true
}
// else, check root.left and root.right
return isSubtree(root.Left, subRoot) || isSubtree(root.Right, subRoot)
}

/*
dfs helper function checking whether two treenodes are same or not
*/
func isSameTree(a *TreeNode, b *TreeNode) bool {
// handling nil(null) cases
if a == nil || b == nil {
return a == b
}
// falsy cases
if a.Val != b.Val || !isSameTree(a.Left, b.Left) || !isSameTree(a.Right, b.Right) {
return false
}
// else, return true
return true
}
46 changes: 46 additions & 0 deletions validate-binary-search-tree/flynn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
풀이
- BST의 속성을 μ΄ν•΄ν•œ ν›„, ν•΄λ‹Ή 속성을 κ²€μ‚¬ν•˜λŠ” dfs ν•¨μˆ˜λ₯Ό μ΄μš©ν•˜λ©΄ 풀이할 수 μžˆμŠ΅λ‹ˆλ‹€
Big O
- N: root 트리의 λ…Έλ“œ 개수
- Time complexity: O(N)
- λͺ¨λ“  λ…Έλ“œμ— λŒ€ν•΄ μ΅œλŒ€ 1번의 탐색이 ν•„μš”ν•©λ‹ˆλ‹€
- Space complexity: O(logN) (O(N) at worst)
- check ν•¨μˆ˜μ˜ μž¬κ·€ν˜ΈμΆœ μŠ€νƒ κΉŠμ΄λŠ” 트리의 높이에 λΉ„λ‘€ν•˜μ—¬ μ¦κ°€ν•˜λ―€λ‘œ 일반적으둜 O(logN)의 κ³΅κ°„λ³΅μž‘λ„λ₯Ό 가진닀고 λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€
ν•˜μ§€λ§Œ νŠΈλ¦¬κ°€ μ‹¬ν•˜κ²Œ 치우친 경우 O(N)κΉŒμ§€ 컀질 수 μžˆμŠ΅λ‹ˆλ‹€
*/

/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/

const (
MIN = -(2_147_483_648 + 1)
MAX = 2_147_483_647 + 1
)

func isValidBST(root *TreeNode) bool {
return check(root.Left, MIN, root.Val) && check(root.Right, root.Val, MAX)
}

/*
helper dfs function
*/

func check(node *TreeNode, min int, max int) bool {
// base case
if node == nil {
return true
}
// node.val should be in the boundary (min, max)
if !(min < node.Val && node.Val < max) {
return false
}
// check for children nodes
return check(node.Left, min, node.Val) && check(node.Right, node.Val, max)
}

0 comments on commit ac2a2e9

Please sign in to comment.