-
-
Notifications
You must be signed in to change notification settings - Fork 195
[ysle0] Week1 #638
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
[ysle0] Week1 #638
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,55 @@ | ||
package contains_duplicate | ||
|
||
import "sort" | ||
|
||
/* | ||
1. 문제 | ||
|
||
주어진 int 배열 nums에 숫자가 중복되는 경우가 한 번이라도 있으면 true, 그렇지 않으면 false 를 리턴 | ||
|
||
2. 풀이 | ||
|
||
고유값만 저장하는 set(go 에서는 map)의 성질을 활용하여 | ||
nums를 순회하며 set에 값이 있는지 없는지 체크하여 | ||
숫자가 중복되는 경우를 체크 | ||
|
||
3. 분석 | ||
- 시간 복잡도: O(N) | ||
nums 탐색: O(N) | ||
배열 nums의 모든 원소를 단 한 번 순회 | ||
map 삽입, 탐색: O(1) | ||
map의 내부 구현은 해시 테이블. | ||
O(N)보다 작아 무시됨 | ||
- 공간 복잡도: O(N) | ||
최악의 경우라도 사용공간은 nums 의 크기만큼 + nums의 모든 원소를 포함한 map | ||
*/ | ||
func containsDuplicate(nums []int) bool { | ||
seen := map[int]int{} | ||
|
||
for _, n := range nums { | ||
if _, ok := seen[n]; ok { | ||
return true | ||
} | ||
|
||
seen[n] = 1 | ||
} | ||
|
||
return false | ||
} | ||
|
||
func containsDuplicate_SortedApproach(nums []int) bool { | ||
// early exit for small slices | ||
if len(nums) < 2 { | ||
return false | ||
} | ||
|
||
// sort in ascending order and check adjacent elements | ||
sort.Ints(nums) | ||
for i := 1; i < len(nums); i++ { | ||
if nums[i] == nums[i-1] { | ||
return true | ||
} | ||
} | ||
|
||
return false | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
package longest_consecutive_sequence | ||
|
||
import "slices" | ||
|
||
/* | ||
1. 문제 | ||
주어진 int 배열 nums에서 찾을 수 있는 가장 긴 연속된 원소의 길이 구하기 | ||
|
||
2. 풀이 | ||
모든 수의 중복을 제거하고, 오름차순으로 정렬하여 연속된 원소의 부분을 찾기 위해서 | ||
배열을 순회하여 인덱스 고정~전진하며 다음 원소가 연속된 원소인지 체크를 반복 | ||
|
||
3. 분석 | ||
|
||
- 시간 복잡도: O(N logN) | ||
배열 정렬 O(N logN) | ||
중복된 원소를 제거해주는 slices.Compact(nums): O(N) | ||
2중 포문은 for 문 순회 index 를 같이 쓰므로 O(N) | ||
|
||
- 공간 복잡도: O(N) | ||
*/ | ||
func longestConsecutive(nums []int) int { | ||
if len(nums) == 0 { | ||
return 0 | ||
} | ||
|
||
if len(nums) == 1 { | ||
return 1 | ||
} | ||
|
||
slices.Sort(nums) | ||
nums = slices.Compact(nums) | ||
// 중복을 제거하고 나서도 1개면 최장연속수는 1 | ||
if len(nums) == 1 { | ||
return 1 | ||
} | ||
|
||
cons := map[int]int{} | ||
cursor := 0 | ||
for cursor < len(nums)-1 { | ||
cons[cursor] = 1 | ||
wasConsecutive := false | ||
|
||
// cursor 는 고정하고, innerCursor 를 돌림 | ||
innerCursor := cursor | ||
for innerCursor+1 < len(nums) && | ||
nums[innerCursor]+1 == nums[innerCursor+1] { | ||
|
||
cons[cursor]++ | ||
innerCursor++ | ||
wasConsecutive = true | ||
} | ||
|
||
if wasConsecutive { | ||
cursor = innerCursor | ||
} | ||
cursor++ | ||
} | ||
|
||
//tmp := make([]int, 0, len(cons)) | ||
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. 불필요하다면 제거하는게 좋지 않을까요? 🧹 |
||
tmp := make([]int, 0, len(cons)) | ||
for _, v := range cons { | ||
tmp = append(tmp, v) | ||
} | ||
|
||
slices.SortFunc( | ||
tmp, | ||
func(a, b int) int { | ||
return b - a | ||
}) | ||
return tmp[0] | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
package top_k_frequent_elements | ||
|
||
//package main | ||
|
||
import ( | ||
"container/heap" | ||
"slices" | ||
) | ||
|
||
/* | ||
1. 문제 | ||
nums 에서 가장 많이 나온 숫자들 k 개를 반환. | ||
|
||
2. 풀이 | ||
map 에 빈도를 기록하여 내림차순 정렬한 후 k개 뽑기 | ||
|
||
3. 분석 | ||
|
||
- 시간 복잡도: O(N + M logM) --> O(N logN) | ||
빈도맵핑을 위한 nums 순회: O(N) | ||
오름차순정렬: O(M logM) | ||
|
||
- 공간 복잡도: O(N) | ||
주어진 배열 nums: O(N) | ||
빈도맵핑용 map: O(N) | ||
*/ | ||
type Kvp struct { | ||
k int | ||
v int | ||
} | ||
|
||
func topKFrequent(nums []int, k int) []int { | ||
freq := map[int]int{} | ||
|
||
// 빈도를 기록 | ||
for _, n := range nums { | ||
if _, ok := freq[n]; !ok { | ||
freq[n] = 1 | ||
} else { | ||
freq[n]++ | ||
} | ||
} | ||
|
||
// map->array | ||
tmp := make([]Kvp, 0, len(freq)) | ||
for key, v := range freq { | ||
tmp = append(tmp, Kvp{key, v}) | ||
} | ||
|
||
// 내림차순 정렬 (time O(M logM) | ||
slices.SortFunc(tmp, func(a, b Kvp) int { return b.v - a.v }) | ||
|
||
// []int 로 변환 | ||
res := make([]int, 0, len(tmp)) | ||
for _, kvp := range tmp { | ||
res = append(res, kvp.k) | ||
} | ||
|
||
// k 개 뽑기 | ||
return res[:k] | ||
} | ||
|
||
func topKElements_HeapBasedApproach(nums []int, k int) []int { | ||
freq := map[int]int{} | ||
for _, n := range nums { | ||
freq[n]++ | ||
} | ||
|
||
h := &IntHeap{} | ||
heap.Init(h) | ||
|
||
for k, v := range freq { | ||
heap.Push(h, Kvp{k, v}) | ||
if h.Len() > k { | ||
heap.Pop(h) | ||
} | ||
} | ||
|
||
res := make([]int, k) | ||
for i := k - 1; i >= 0; i-- { | ||
res[i] = heap.Pop(h).(Kvp).k | ||
} | ||
|
||
return res | ||
} | ||
|
||
type IntHeap []Kvp | ||
|
||
func (h *IntHeap) Len() int { return len(*h) } | ||
func (h *IntHeap) Less(i, j int) bool { return (*h)[i].v < (*h)[j].v } | ||
func (h *IntHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } | ||
func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(Kvp)) } | ||
func (h *IntHeap) Pop() interface{} { | ||
old := *h | ||
n := len(old) | ||
x := old[n-1] | ||
*h = old[0 : n-1] | ||
return x | ||
} | ||
|
||
func topKFrequentElements_BucketSort(nums []int, k int) []int { | ||
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. 👍 |
||
freq := map[int]int{} | ||
for _, n := range nums { | ||
freq[n]++ | ||
} | ||
|
||
buc := make([][]int, len(nums)+1) | ||
for k, v := range freq { | ||
buc[v] = append(buc[v], k) | ||
} | ||
|
||
res := []int{} | ||
for i := len(buc) - 1; i >= 0 && len(res) < k; i-- { | ||
res = append(res, buc[i]...) | ||
} | ||
|
||
return res[: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. 마지막 슬라이싱은 불필요하지 않을까요? 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. 앗 그렇네요 |
||
} | ||
|
||
// | ||
//func main() { | ||
// r1 := topKFrequent([]int{1, 1, 1, 2, 2, 3}, 2) | ||
// fmt.Println(r1) | ||
// | ||
// r2 := topKFrequent([]int{1}, 1) | ||
// fmt.Println(r2) | ||
//} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
package valid_palindrome | ||
|
||
import ( | ||
"regexp" | ||
"strings" | ||
) | ||
|
||
/* | ||
1. 문제 | ||
회문. 주어진 문자열 s 를 모두 소문자로 바꾸고, alphanumeric 이 아닌 문자를 제외할 때, | ||
앞으로 읽으나 뒤로 읽으나 같은 문자열인지 체크 (회문) | ||
|
||
2. 풀이 | ||
- 주어진 문자열 s 에서 alphanumeric character 만 남기고 제거. | ||
- 모두 소문자로 변환 | ||
- 앞, 뒤로 인덱스 위치를 기록하는 cursor 를 정의 | ||
커서 둘을 앞, 뒤로 전진하며 같지않은 문자가 나오면 false 를 반환, 그렇지 않고 회문이면 true 를 반환. | ||
|
||
3. 분석 | ||
- 시간 복잡도: O(N) | ||
regex.ReplaceAllString(): O(n) | ||
모든 문자열을 돌며 regex 검사 후 대체 | ||
strings.ToLower(str): O(n) | ||
모든 문자열을 돌며 소문자로 변환 | ||
palindrome 문자열 체크 loop | ||
앞 커서 < 뒤 커서 의 조건으로 O(n/2) ---> O(n) | ||
- 공간 복잡도: O(1) | ||
ysle0 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
새로운 저장공간은 없으며 주어진 문자열 s 하나뿐 | ||
*/ | ||
var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`) | ||
|
||
func isPalindrome(s string) bool { | ||
s = nonAlphanumericRegex.ReplaceAllString(s, "") | ||
s = strings.ToLower(s) | ||
// 앞, 뒤 커서 | ||
front, rear := 0, len(s)-1 | ||
|
||
for front < rear { | ||
frontCh := s[front] | ||
readCh := s[rear] | ||
|
||
// 선택한 두 문자가 다르면 실패! | ||
if frontCh != readCh { | ||
return false | ||
} | ||
|
||
front++ | ||
rear-- | ||
} | ||
|
||
return true | ||
} | ||
|
||
/* | ||
1. 개선점 | ||
- regex 오버헤드 제거 | ||
*/ | ||
func isPalindrome_Optimized(s string) bool { | ||
front, rear := 0, len(s)-1 | ||
|
||
for front < rear { | ||
for front < rear && !isAlphanumeric(s[front]) { | ||
front++ | ||
} | ||
|
||
for front < rear && !isAlphanumeric(s[rear]) { | ||
rear-- | ||
} | ||
|
||
if toLower(s[front]) != toLower(s[rear]) { | ||
return false | ||
} | ||
|
||
front++ | ||
rear-- | ||
} | ||
|
||
return true | ||
} | ||
|
||
func isAlphanumeric(ch byte) bool { | ||
return (ch >= 'a' && ch <= 'z') || | ||
(ch >= 'A' && ch <= 'Z') || | ||
(ch >= '0' && ch <= '9') | ||
} | ||
|
||
func toLower(ch byte) byte { | ||
if ch >= 'A' && ch <= 'Z' { | ||
return ch + 32 | ||
} | ||
return ch | ||
} |
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.
정렬 후 중복을 확인하는 부분에 대해서도 복잡도 분석을 해주시면 좋을 것 같아요
각각의 장단점을 확실하게 명시할 수 있지 않을까요?