Skip to content

Commit 4c9c346

Browse files
committed
solve: rest week7 problems
1 parent 22edc6a commit 4c9c346

File tree

3 files changed

+167
-68
lines changed

3 files changed

+167
-68
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
# Intuition
2+
<!-- Describe your first thoughts on how to solve this problem. -->
3+
트리를 순회하는 방법들 중, 레벨 순서대로 순회하는 방식을 알고있어 이를 이용했다.
4+
# Approach
5+
1. BFS를 진행한다.
6+
2. 진행하기 전, 해당 레벨에 몇개의 원소가 있는지(`currLen`)을 저장한다.
7+
3. 저장된 원소만큼 순회했다면, 레벨을 다 둘러본 것이므로 부가작업(`levels = append(levels, level)`)을 한다.
8+
9+
# Complexity
10+
- Time complexity: $O(n)$
11+
- 트리의 원소를 n개라고 했을 때, 모든 원소를 순회하는 비용 `O(n)`이 소모된다.
12+
- Space complexity: $O(n)$
13+
- 트리의 원소를 n개라고 했을 때, 모든 원소들을 저장하는 배열이 `O(n)`을 소모한다.
14+
15+
# Code
16+
```go
17+
func levelOrder(root *TreeNode) [][]int {
18+
levels := make([][]int, 0)
19+
20+
if root == nil {
21+
return levels
22+
}
23+
24+
q := []*TreeNode{root}
25+
26+
for len(q) > 0 {
27+
level := make([]int, 0)
28+
currLen := len(q)
29+
for i := 0; i < currLen; i++ {
30+
front := q[0]
31+
level = append(level, front.Val)
32+
q = q[1:]
33+
34+
if front.Left != nil {
35+
q = append(q, front.Left)
36+
}
37+
if front.Right != nil {
38+
q = append(q, front.Right)
39+
}
40+
}
41+
levels = append(levels, level)
42+
}
43+
44+
return levels
45+
}
46+
47+
```
48+
49+
```go
50+
func levelOrder(root *TreeNode) [][]int {
51+
levels := make([][]int, 0)
52+
53+
if root == nil {
54+
return levels
55+
}
56+
57+
q := []*TreeNode{root}
58+
var nextQ []*TreeNode
59+
60+
for len(q) > 0 {
61+
level := make([]int, 0)
62+
for _, front := range q {
63+
level = append(level, front.Val)
64+
65+
if front.Left != nil {
66+
nextQ = append(nextQ, front.Left)
67+
}
68+
if front.Right != nil {
69+
nextQ = append(nextQ, front.Right)
70+
}
71+
}
72+
q, nextQ = nextQ, q[:0]
73+
74+
levels = append(levels, level)
75+
}
76+
77+
return levels
78+
}
79+
80+
```
81+
- 첫 번째 코드는 `q[1:]`을 이용해서 큐의 `pop()`을 구현한다. 하지만 GoLang에서는 `pop()`을 한다고해서, 참조가 해제되지 않아 메모리를 계속 잡아먹는다.
82+
- `pop()`을 하더라도 `q`가 순회하는 모든 원소들을 참조하고 있다.
83+
- 두 번째 코드는 `q, nextQ = nextQ, q[:0]`을 이용해서 `q``nextQ`의 메모리 영역을 교체할 뿐, 부가적인 메모리 공간을 필요로 하지 않아 더욱 효율적이다.

remove-nth-node-from-end-of-list/invidam.go.md

Lines changed: 0 additions & 68 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Intuition (Array)
2+
BST는 정렬 관계를 만족하므로, 순서에 맞게 배열에 저장한 후 정렬 관계가 맞는지 비교했다.
3+
# Approach
4+
<!-- Describe your approach to solving the problem. -->
5+
1. 중위 순회(좌 --> 루트 --> 우)를 하며 배열을 채운다. (`fillNodes()`)
6+
2. 배열을 순회하며 정렬 관계가 벗어났는지 판별한다. (`if nodes[i] >= nodes[i+1]`)
7+
8+
# Complexity
9+
- Time complexity: $O(n)$
10+
- 트리의 원소를 n개라고 했을 때, 모든 원소를 순회하는 비용 `O(n)`이 소모된다.
11+
- Space complexity: $O(n)$
12+
- 트리의 원소를 n개라고 했을 때, 모든 원소들을 저장하는 배열이 `O(n)`을 소모한다.
13+
14+
# Code
15+
```go
16+
func fillNodes(root *TreeNode) []int {
17+
nodes := make([]int, 0)
18+
if root.Left != nil {
19+
nodes = append(nodes, fillNodes(root.Left)...)
20+
}
21+
nodes = append(nodes, root.Val)
22+
if root.Right != nil {
23+
nodes = append(nodes, fillNodes(root.Right)...)
24+
}
25+
return nodes
26+
}
27+
28+
func isValidBST(root *TreeNode) bool {
29+
nodes := fillNodes(root)
30+
for i := 0; i < len(nodes)-1; i++ {
31+
if nodes[i] >= nodes[i+1] {
32+
return false
33+
}
34+
}
35+
return true
36+
}
37+
38+
```
39+
# Intuition (Recursion)
40+
예제를 참고했을 때, BST의 범위(최소, 최대)를 벗어나지 않는지를 유지하면 판별할 수 있다고 생각했다.
41+
# Approach
42+
<!-- Describe your approach to solving the problem. -->
43+
1. 전위 순회(루트 --> 좌 --> 우)를 한다.
44+
2. 해당 노드에서 주어진 범위를 벗어나는지 판별한다. (`if !(min < root.Val && root.Val < max)`)
45+
3. 자식 노드들에 대해서도 BST가 만족하는지 재귀함수 호출을 통해 판별한다.
46+
- 가능한 범위는 해당 루트 노드를 기준으로 갱신한다.
47+
# Complexity
48+
- Time complexity: $O(n)$
49+
- 트리의 원소를 n개라고 했을 때, 모든 원소를 순회하는 비용 `O(n)`이 소모된다.
50+
- Space complexity: $O(n)$
51+
- 트리의 원소를 n개라고 했을 때, 트리의 높이만큼 콜 스택이 발생하여 복잡도가 소모된다.
52+
- 최악의 경우(`skewed tree`), 높이는 `n`개이므로 `O(n)`을 소모한다.
53+
- 최선의 경우(`perfect binary tree`), 높이는 `log(n)`개 이므로 `O(log(n))`을 소모한다.
54+
55+
# Code
56+
```go
57+
func isValidBSTFrom(root *TreeNode, min, max int) bool {
58+
if root == nil {
59+
return true
60+
}
61+
if !(min < root.Val && root.Val < max) {
62+
return false
63+
}
64+
return isValidBSTFrom(root.Left, min, root.Val) && isValidBSTFrom(root.Right, root.Val, max)
65+
}
66+
67+
func isValidBST(root *TreeNode) bool {
68+
return isValidBSTFrom(root, math.MinInt, math.MaxInt)
69+
}
70+
71+
```
72+
73+
# Learned
74+
- `math` 라이브러리의 `MinInt`, `MaxInt` 사용법
75+
- `MinInt``-1 << 31`로 계산한다.
76+
-`-`을 쉬프트하는지 궁금해서 찾아봤다.
77+
- 비트 맨뒤가 1인 수들은 `<< 31`을 하면 모두 `MinInt`를 만들 수 있지만, `1 << 31``MaxInt`와 대비되도록 `-1`을 선택한 것 같다. 다른 수들에 비해 좀 더 직관적이기도 하다.
78+
79+
- `...` operator (Three dots, Ellipsis)
80+
- unpacking을 하는 용도이다.
81+
- 사례
82+
- 함수 인자로 몇 개인지 알 수 없는 인자들이 들어올 때
83+
- 배열 초기화 시 길이를 몰라 컴파일러에게 맡기고 싶을 때
84+
- 참고: [링크](https://blog.advenoh.pe.kr/go/Go%EC%97%90%EC%84%9C-%EC%82%BC-%EB%8F%84%ED%8A%B8-dot-%EC%82%AC%EC%9A%A9%EB%B0%A9%EB%B2%95-Three-Dots-Usage/)

0 commit comments

Comments
 (0)