Skip to content

Commit ac2a2e9

Browse files
authored
Merge pull request #608 from obzva/main
2 parents c2a2065 + cf06fdb commit ac2a2e9

File tree

5 files changed

+294
-0
lines changed

5 files changed

+294
-0
lines changed

alien-dictionary/flynn.go

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
풀이
3+
- 두 단어를 알파벳 하나씩 차례대로 비교했을 때, 첫번째 차이가 발생하는 지점에서 alien dictionary의 order를 찾을 수 있습니다
4+
- 첫번째 단어부터 바로 그 다음 단어와 두 단어씩 짝지어서 비교하면 앞에서 말한 일련의 order를 찾아낼 수 있습니다
5+
알파벳 x가 알파벳 y보다 alien dictionary order에서 앞서는 관계, 즉 x->y인 관계를 찾아서 x: {y1, y2, y3, ...}인 집합의 map을 만들겠습니다
6+
그리고 이를 nextLetters라고 명명하였습니다
7+
- 만약 특정 알파벳 x에 대해, z->x인 알파벳 z가 없다면 x는 우리가 정답으로 제출할 result string의 어느 위치에든 자유롭게 끼워넣을 수 있습니다
8+
(* If there are multiple solutions, return any of them.)
9+
우리는 이런 알파벳 x를 찾아낼 때마다 바로바로 result string res에 추가하겠습니다
10+
- z->x인 알파벳 z가 현재 있는지 없는지에 대한 상태관리를 하기 위해서 prevLetterCounts라는 map을 만들겠습니다
11+
prevLetterCounts[x]: z->x인 z의 개수
12+
- nextLetters, prevLetterCounts를 잘 생성한 후에는 prevLetterCount가 0인 알파벳부터 queue에 등록시킨 후 BFS를 실행합니다
13+
BFS를 실행하며 prevLetterCount가 0인 알파벳이 새로 발견될 경우 queue에 등록시킵니다
14+
- 엣지케이스가 두 경우 발생하는데,
15+
첫번째는 nextLetters를 생성하는 반복문에서 발견됩니다
16+
두번째 단어가 첫번째 단어의 prefix인 경우는 애초부터 잘못된 dictionary order인 경우입니다
17+
위 경우는 단순 알파벳 비교로는 발견하기 어려우므로 flag를 사용하였습니다
18+
두번째는 result string의 길이가 input으로 주어진 단어들에 등장한 알파벳의 개수보다 적은 경우입니다
19+
이 경우는 nextLetters에 순환이 발생한 경우이므로 dictionary order가 잘못되었다고 판단할 수 있습니다
20+
Big O
21+
- N: 주어진 배열 words의 길이
22+
- S(W): 배열 words에 속한 모든 string의 길이의 총합
23+
- Time complexity: O(N + S(W))
24+
- prevLetterCounts와 nextLetters 생성 -> O(N)
25+
- nextLetters에 들어갈 알파벳 전후관계 찾기 -> O(S(W))
26+
- 알파벳 소문자의 수는 제한되어 있기 때문에 BFS의 시간 복잡도 상한선은 정해져 있습니다 -> O(26 * 26) = O(1)
27+
- Space complexity: O(1)
28+
- 알파벳 소문자의 수는 제한되어 있기 때문에 공간 복잡도의 상한선은 정해져 있습니다
29+
prevLetterCounts -> O(26) = O(1)
30+
nextLetters -> O(26 * 26) = O(1)
31+
queue -> O(26) = O(1)
32+
*/
33+
34+
import "strings"
35+
36+
func alienOrder(words []string) string {
37+
n := len(words)
38+
// prevLetterCounts[x] = count of letters y that are in relation of y->x
39+
prevLetterCounts := make(map[string]int)
40+
// nextLetters[x] = set of letters y that are in relation of x->y
41+
nextLetters := make(map[string]map[string]bool)
42+
for _, word := range words {
43+
for _, c := range word {
44+
if _, ok := prevLetterCounts[string(c)]; !ok {
45+
prevLetterCounts[string(c)] = 0
46+
nextLetters[string(c)] = make(map[string]bool)
47+
}
48+
}
49+
}
50+
51+
for i := 0; i < n-1; i++ {
52+
currWord := words[i]
53+
nextWord := words[i+1]
54+
// flag for edge case below
55+
diff := false
56+
for j := 0; j < len(currWord) && j < len(nextWord); j++ {
57+
if currWord[j] != nextWord[j] {
58+
diff = true
59+
if _, ok := nextLetters[string(currWord[j])][string(nextWord[j])]; !ok {
60+
prevLetterCounts[string(nextWord[j])]++
61+
nextLetters[string(currWord[j])][string(nextWord[j])] = true
62+
}
63+
break
64+
}
65+
}
66+
// tricky edge case!!!
67+
// if nextWord is prefix of currWord, then the provided dictionary order is invalid
68+
if !diff && len(currWord) > len(nextWord) {
69+
return ""
70+
}
71+
}
72+
// BFS
73+
queue := make([]string, 0, len(prevLetterCounts))
74+
for letter := range prevLetterCounts {
75+
// we can arrange letters whose prevLetterCount is zero as we wish
76+
if prevLetterCounts[letter] == 0 {
77+
queue = append(queue, letter)
78+
}
79+
}
80+
// in Go, using strings.Builder is the most efficient way to build strings
81+
var sb strings.Builder
82+
for len(queue) > 0 {
83+
// pop the letter from the queue and append it to the result string
84+
popped := queue[0]
85+
queue = queue[1:]
86+
sb.WriteString(popped)
87+
88+
for nextLetter := range nextLetters[popped] {
89+
prevLetterCounts[nextLetter]--
90+
// if prevLetterCount for nextLetter becomes zero, we can add it to the queue
91+
// append to the result string (order) in the next iteration
92+
if prevLetterCounts[nextLetter] == 0 {
93+
queue = append(queue, nextLetter)
94+
}
95+
}
96+
}
97+
// res is result string
98+
res := sb.String()
99+
// this case means that there was a cycle
100+
if len(res) != len(prevLetterCounts) {
101+
return ""
102+
}
103+
// else return res
104+
return res
105+
}
+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
/*
2+
풀이
3+
- 슬라이딩 윈도우 기법을 이용하면 풀이할 수 있습니다
4+
Big O
5+
- N: 주어진 문자열 s의 길이
6+
- Time complexity: O(N^2)
7+
- window 함수가 O(N)의 시간복잡도를 가지므로
8+
각 반복문은 O(N * N) = O(N^2)의 시간복잡도를 가진다고 볼 수 있습니다
9+
- Space complexity: O(1)
10+
- 별도의 추가적인 공간 복잡도를 고려하지 않아도 됩니다
11+
*/
12+
13+
func longestPalindrome(s string) string {
14+
n := len(s)
15+
maxStart := 0
16+
maxEnd := 0
17+
// for odd lengths
18+
for i := 0; i < n; i++ {
19+
window(&s, &maxStart, &maxEnd, i, false)
20+
}
21+
// for even lengths
22+
for i := 0; i < n-1; i++ {
23+
window(&s, &maxStart, &maxEnd, i, true)
24+
}
25+
26+
return s[maxStart : maxEnd+1]
27+
}
28+
29+
/*
30+
helper function for searching palindromic substring
31+
from the pivotal index `i`
32+
*/
33+
func window(s *string, maxStart *int, maxEnd *int, i int, isEven bool) {
34+
n := len(*s)
35+
start := i
36+
end := i
37+
if isEven {
38+
end++
39+
}
40+
for 0 <= start && end < n {
41+
if (*s)[start] != (*s)[end] {
42+
break
43+
}
44+
45+
// if new palindromic substring is longer than the previously found one,
46+
// update the start and end index
47+
if *maxEnd-*maxStart < end-start {
48+
*maxStart = start
49+
*maxEnd = end
50+
}
51+
start--
52+
end++
53+
}
54+
}

rotate-image/flynn.go

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
풀이
3+
- matrix를 4사분면으로 나눕니다
4+
1사분면의 모든 좌표에 대해 아래와 같은 연산을 수행합니다
5+
- 1사분면의 좌표 a1에 대해 a2, a3, a4를 아래처럼 정의합니다
6+
a2: a1을 90도 회전시켰을 때의 좌표 (2사분면에 위치함)
7+
a3: a2를 90도 회전시켰을 때의 좌표 (3사분면에 위치함)
8+
a4: a3을 90도 회전시켰을 때의 좌표 (4사분면에 위치함)
9+
a1 -> a2, a2 -> a3, a3 -> a4, a4 -> a1으로 값을 변경시킵니다
10+
Big O
11+
- N: 매트릭스의 크기
12+
- Time complexity: O(N^2)
13+
- Space complexity: O(1)
14+
*/
15+
16+
func rotate(matrix [][]int) {
17+
n := len(matrix)
18+
// 사분면의 크기, qr, qc: 사분면의 행, 열 크기
19+
qr := n / 2
20+
qc := (n + 1) / 2
21+
22+
for r := 0; r < qr; r++ {
23+
for c := 0; c < qc; c++ {
24+
r1 := r
25+
c1 := c
26+
27+
r2 := c
28+
c2 := n - 1 - r
29+
30+
r3 := n - 1 - r
31+
c3 := n - 1 - c
32+
33+
r4 := n - 1 - c
34+
c4 := r
35+
36+
matrix[r1][c1], matrix[r2][c2], matrix[r3][c3], matrix[r4][c4] = matrix[r4][c4], matrix[r1][c1], matrix[r2][c2], matrix[r3][c3]
37+
}
38+
}
39+
}

subtree-of-another-tree/flynn.go

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
풀이
3+
- 두 트리가 동일한지 검사하는 dfs 함수를 이용하여 풀이할 수 있습니다
4+
Big O
5+
- M: root 트리의 노드 개수
6+
- N: subRoot 트리의 노드 개수
7+
- Time complexity: O(MN)
8+
- 최악의 경우 root 트리의 모든 노드에 대해 isSameTree 함수를 실행 (O(M))
9+
최악의 경우 isSameTree 함수는 O(N)의 시간복잡도를 가짐
10+
- Space complexity: O(M+N)
11+
- isSubTree의 재귀호출스택 깊이는 최대 O(M)의 공간복잡도를 가짐
12+
- isSameTree의 재귀호출스택 깊이는 최대 O(N)의 공간복잡도를 가짐
13+
*/
14+
15+
/**
16+
* Definition for a binary tree node.
17+
* type TreeNode struct {
18+
* Val int
19+
* Left *TreeNode
20+
* Right *TreeNode
21+
* }
22+
*/
23+
func isSubtree(root *TreeNode, subRoot *TreeNode) bool {
24+
// handling nil(null) inputs
25+
if root == nil {
26+
return false
27+
}
28+
// return true if root and subroot are same
29+
if isSameTree(root, subRoot) {
30+
return true
31+
}
32+
// else, check root.left and root.right
33+
return isSubtree(root.Left, subRoot) || isSubtree(root.Right, subRoot)
34+
}
35+
36+
/*
37+
dfs helper function checking whether two treenodes are same or not
38+
*/
39+
func isSameTree(a *TreeNode, b *TreeNode) bool {
40+
// handling nil(null) cases
41+
if a == nil || b == nil {
42+
return a == b
43+
}
44+
// falsy cases
45+
if a.Val != b.Val || !isSameTree(a.Left, b.Left) || !isSameTree(a.Right, b.Right) {
46+
return false
47+
}
48+
// else, return true
49+
return true
50+
}

validate-binary-search-tree/flynn.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/*
2+
풀이
3+
- BST의 속성을 이해한 후, 해당 속성을 검사하는 dfs 함수를 이용하면 풀이할 수 있습니다
4+
Big O
5+
- N: root 트리의 노드 개수
6+
- Time complexity: O(N)
7+
- 모든 노드에 대해 최대 1번의 탐색이 필요합니다
8+
- Space complexity: O(logN) (O(N) at worst)
9+
- check 함수의 재귀호출 스택 깊이는 트리의 높이에 비례하여 증가하므로 일반적으로 O(logN)의 공간복잡도를 가진다고 볼 수 있습니다
10+
하지만 트리가 심하게 치우친 경우 O(N)까지 커질 수 있습니다
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+
22+
const (
23+
MIN = -(2_147_483_648 + 1)
24+
MAX = 2_147_483_647 + 1
25+
)
26+
27+
func isValidBST(root *TreeNode) bool {
28+
return check(root.Left, MIN, root.Val) && check(root.Right, root.Val, MAX)
29+
}
30+
31+
/*
32+
helper dfs function
33+
*/
34+
35+
func check(node *TreeNode, min int, max int) bool {
36+
// base case
37+
if node == nil {
38+
return true
39+
}
40+
// node.val should be in the boundary (min, max)
41+
if !(min < node.Val && node.Val < max) {
42+
return false
43+
}
44+
// check for children nodes
45+
return check(node.Left, min, node.Val) && check(node.Right, node.Val, max)
46+
}

0 commit comments

Comments
 (0)