|
| 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