-
Notifications
You must be signed in to change notification settings - Fork 126
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Vidam, Invidam] Week5 Solutions In Go #109
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Intuition | ||
예전에 풀어봤던 문제였어서 접근법을 알고있었다. | ||
|
||
# Approach | ||
1. 정렬을 하여 배열의 대소관계를 일정하게 한다. | ||
2. i,j,k를 설정해야 하는데, i < j < k이도록 한다. | ||
3. i는 맨 앞에서부터 순회한다. | ||
4. j는 i의 뒤부터 순회한다. | ||
5. k는 맨 뒤에서부터 순회한다. | ||
6. 세 인덱스가 가리키는 값들의 합을 비교하여 j와 k를 수정한다. | ||
|
||
# Complexity | ||
- Time complexity: $$O(n^2)$$ | ||
- 입력 배열의 길이 n에 대하여, `i`, `j와 k`를 순회한다. | ||
|
||
- Space complexity: $$O(n)$$ | ||
- 입력으로 들어온 배열의 길이 n에 대하여, 생성하는 결과 배열의 길이 역시 이와 동일하다. | ||
# Code | ||
|
||
```go | ||
func update(i int, j int, k int, sum int, nums []int) (int, int) { | ||
if sum <= 0 { | ||
j++ | ||
for j < len(nums) && j >= 1 && nums[j] == nums[j-1] { | ||
j++ | ||
} | ||
} else { | ||
k-- | ||
for k >= 0 && k+1 < len(nums) && nums[k] == nums[k+1] { | ||
k-- | ||
} | ||
} | ||
|
||
return j, k | ||
} | ||
|
||
func threeSum(nums []int) [][]int { | ||
var triplets [][]int | ||
|
||
sort.Ints(nums) | ||
for i := 0; i < len(nums); i++ { | ||
j, k := i+1, len(nums)-1 | ||
|
||
if i != 0 && nums[i-1] == nums[i] { | ||
continue | ||
} | ||
|
||
for j < k { | ||
sum := nums[i] + nums[j] + nums[k] | ||
if sum == 0 { | ||
triplets = append(triplets, []int{nums[i], nums[j], nums[k]}) | ||
} | ||
j, k = update(i, j, k, sum, nums) | ||
} | ||
} | ||
return triplets | ||
} | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
# Intuition | ||
구분자를 넣어 오류를 방지한다. | ||
# Approach | ||
<!-- Describe your approach to solving the problem. --> | ||
1. UTF-8에 벗어나는 구분자 `ㄱ`을 넣어 구분했다. | ||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
- 문자열의 길이 n에 대하여, 이를 순회하는 비용이 발생한다. | ||
|
||
- Space complexity: $$O(n)$$ | ||
- 문자열의 길이 n에 대하여, `encoded`를 만드는 공간이 발생한다. | ||
|
||
# Code | ||
```go | ||
const DIVIDE_CHAR_V1 = 'ㄱ' | ||
func encodeV1(strs []string) string { | ||
ret := strings.Join(strs, string(DIVIDE_CHAR_V1)) | ||
return ret | ||
} | ||
|
||
func decodeV1(encoded string) []string { | ||
sep := string(DIVIDE_CHAR_V1) | ||
return strings.Split(encoded, sep) | ||
} | ||
``` | ||
- - - | ||
# Intuition | ||
솔루션에서 네트워크 통신을 위한다는 목적을 듣고 수정해보았다. (유니코드를 넣는 게 의도는 아닌 듯했다.) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 의도를 잘 파악하셨네요 👏 |
||
# Approach | ||
1. 구분자 (`-`)와 이전 문자의 길이를 함께 저장한다. | ||
2. 구분자가 나온다면, 문자열의 길이를 추출하여 문자열을 디코딩한다. | ||
3. 위 과정을 배열을 순회하며 반복한다. | ||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
- 문자열의 길이 n에 대하여, 이를 순회하는 비용이 발생한다. | ||
|
||
- Space complexity: $$O(n)$$ | ||
- 문자열의 길이 n에 대하여, `encoded`를 만드는 공간이 발생한다. | ||
|
||
# Code | ||
```go | ||
const DIVIDE_CHAR = '-' | ||
func encode(strs []string) string { | ||
ret := "" | ||
for _, str := range strs { | ||
ret += str | ||
ret += fmt.Sprintf("%c%04d", DIVIDE_CHAR, len(str)) | ||
// a-1bcd-3 | ||
} | ||
return ret | ||
} | ||
|
||
func decode(encoded string) []string { | ||
ret := make([]string, 0) | ||
for i := 0; i < len(encoded); { | ||
if encoded[i] == DIVIDE_CHAR { | ||
lenStr := encoded[i+1 : i+5] | ||
len, _ := strconv.Atoi(lenStr) | ||
|
||
decodeStr := encoded[i-len : i] | ||
ret = append(ret, decodeStr) | ||
i += 5 | ||
} else { | ||
i += 1 | ||
} | ||
} | ||
|
||
return ret | ||
} | ||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# Intuition | ||
자기 자신 이후에 몇 개가 연속되었는지를 `dp`로 저장한다. | ||
# Approach | ||
1. 등장 여부를 `appear`에 기록한다. | ||
2. 자기 자신 이후 연속된 원소의 개수를 반환하는 함수를 만들고 매 배열마다 이를 호출한다. | ||
- `dp`로써, 자기 자신(`from`)에 따른 결과들을 유지한다. | ||
- 자기 자신이 등장했는지 여부에 따라 `0` 혹은 `다음 연속 개수 + 1`을 반환한다. | ||
3. 호출 결과 중 최대를 찾아 반환한다. | ||
# Complexity | ||
- Time complexity: $$O(nlog(n))$$ | ||
|
||
- Space complexity: $$O(n+m)$$ | ||
# Code | ||
## Original | ||
```go | ||
func longestConsecutiveFrom(num int, appear map[int]bool, cache map[int]int) (ret int) { | ||
if val, ok := cache[num]; ok { | ||
return val | ||
} | ||
|
||
if _, ok := appear[num]; ok { | ||
ret = longestConsecutiveFrom(num+1, appear, cache) + 1 | ||
} else { | ||
ret = 0 | ||
} | ||
cache[num] = ret | ||
return ret | ||
} | ||
|
||
func longestConsecutive(nums []int) int { | ||
appear := make(map[int]bool, len(nums)/2) | ||
cache := make(map[int]int, len(nums)/2) | ||
for _, num := range nums { | ||
appear[num] = true | ||
} | ||
|
||
var max int | ||
|
||
for _, num := range nums { | ||
ret := longestConsecutiveFrom(num, appear, cache) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 재귀로 푸시는 분은 많이 못 봤는데 신선하군요! |
||
if ret > max { | ||
max = ret | ||
} | ||
} | ||
return max | ||
} | ||
``` | ||
|
||
## Bottom-up | ||
```go | ||
func longestConsecutive(nums []int) int { | ||
appear := make(map[int]bool, len(nums)/2) | ||
for _, num := range nums { | ||
appear[num] = true | ||
} | ||
|
||
var max int | ||
|
||
for _, num := range nums { | ||
if appear[num-1] { | ||
continue | ||
} | ||
len := 1 | ||
for appear[num+len] { | ||
len++ | ||
} | ||
|
||
if len > max { | ||
max = len | ||
} | ||
} | ||
return max | ||
} | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
# Intuition | ||
0의 등장 횟수에 따라 경우의 수가 달라진다. 이를 이용한다. | ||
# Approach | ||
(사이트에서 Wrong Case를 제공하였기에 다양한 경우의 수를 대응할 수 있었다, 좋은 풀이는 아니라고 생각한다.) | ||
- 모든 수의 곱을 계산한 후, 배열을 순회하며 자기 자신을 나눈 값을 저장한다. | ||
- 0이 등장한 경우, 0이 아닌 배열의 원소들은 무조건 0을 저장해야 한다. | ||
- `golang`의 경우 `int`의 기본값이 0이라 아무것도 안해주면 된다. | ||
- 0이 2회 이상 등장한 경우, 모든 원소는 0이 된다. | ||
- `golang`의 경우 길이만 선언된 `int` 배열을 반환하면 된다. | ||
# Complexity | ||
- Time complexity: $$O(n)$$ | ||
- 배열의 길이 n에 대하여, 순회하는 비용 n이 발생한다. | ||
|
||
- Space complexity: $$O(n)$$ | ||
- 배열의 길이 n에 대하여, 결과를 저장할 배열의 크기 n이 소모된다. | ||
# Code | ||
```go | ||
func productExceptSelf(nums []int) []int { | ||
answer := make([]int, len(nums)) | ||
product := 1 | ||
zeroCnt := 0 | ||
for _, num := range nums { | ||
if num == 0 { | ||
zeroCnt++ | ||
continue | ||
} | ||
product *= num | ||
} | ||
|
||
if zeroCnt >= 2 { | ||
product = 0 | ||
} | ||
|
||
for i, num := range nums { | ||
if num == 0 { | ||
answer[i] = product | ||
} else if zeroCnt == 0 { | ||
answer[i] = product / num | ||
} | ||
} | ||
return answer | ||
} | ||
``` | ||
# 여담 | ||
부분곱문제로 해결하기 적합하다는 생각이 들었으나, `0`처리 때문에 시도해보진 않았다. | ||
솔루션들을 보며 좌, 우에 대해 2번을 사용하는 방법이 있다는 걸 알게되었다. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Intuition | ||
빈도수 계산, 정렬을 활용하면 가능하다고 생각했다. (깔끔하진 않아 해결될지는 의문이었다.) | ||
# Approach | ||
1. 빈도 수를 저장한다. | ||
2. 원소들을 빈도수(내림차순)대로 정렬한다. | ||
3. 정렬된 원소 중 앞에서부터 k개를 반환한다. | ||
# Complexity | ||
- Time complexity: $$O(nlog(n))$$ | ||
- 배열의 길이 n에 대하여, 정렬비용으로 소모된다. | ||
|
||
- Space complexity: $$O(n+k)$$ | ||
- 배열의 길이 n과 원소의 범위 k에 대하여, 빈도수 저장을 위해 선언하는 비용 `k`와 정렬된 원소를 저장하는 비용 `n`이 소모된다. | ||
|
||
# Code | ||
```go | ||
type NumAndFreq struct { | ||
Num int | ||
Freq int | ||
} | ||
|
||
func topKFrequent(nums []int, k int) []int { | ||
freq := make(map[int]int, 20000) | ||
|
||
for _, num := range nums { | ||
freq[num]++ | ||
} | ||
|
||
numAndFreqs := make([]NumAndFreq, 0, len(freq)) | ||
for n, f := range freq { | ||
numAndFreqs = append(numAndFreqs, NumAndFreq{Num: n, Freq: f}) | ||
} | ||
|
||
sort.Slice(numAndFreqs, func(i, j int) bool { | ||
return numAndFreqs[i].Freq > numAndFreqs[j].Freq | ||
}) | ||
|
||
numsSortedByFreqDesc := make([]int, 0, len(nums)) | ||
for _, e := range numAndFreqs { | ||
numsSortedByFreqDesc = append(numsSortedByFreqDesc, e.Num) | ||
} | ||
|
||
return numsSortedByFreqDesc[0:k] | ||
} | ||
|
||
``` | ||
|
||
# 여담 | ||
- 타 솔루션들을 보며 정렬을 하지 않고 빈도수별로 원소들을 모아놨다가 배열을 만드는 방법도 확인했다. 직관적이진 않다고 느꼈다. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 공감합니다 :) |
||
- 문제 해결 도중 `map`, `filter`와 같은 함수형 프로그래밍을 활용하면 좋겠다는 생각이 들어 golang에서도 이를 제공하나 찾아봤다. [링크1](https://stackoverflow.com/questions/71624828/is-there-a-way-to-map-an-array-of-objects-in-go)과 [링크2](https://github.com/golang/go/issues/45955)를 통해 다양한 논의가 있었으나, 여러 이유로 도입하지 않음을 알게되었다. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
go 문법 재미있네요! 혹시 이렇게 update 함수를 따로 만들어서 풀이하신 특별한 이유가 있을까요?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
재미있으셨다니 감사합니다 ㅎㅎ..
사실 제가 이전에 C++로 작성한 코드를 슬쩍봤었는데 가시성이 너무 떨어지는 것 같아 분리했었어요