diff --git a/3sum/ysle0.go b/3sum/ysle0.go new file mode 100644 index 000000000..0d90ce9b8 --- /dev/null +++ b/3sum/ysle0.go @@ -0,0 +1,72 @@ +// package _sum +package main + +import ( + "slices" + "strconv" +) + +/* + * 첫 번째 수 n1 + * 두 번째 수 n2 + * 세 번째 수 n3 일때, + * 2sum 문제와 동일하게 map에 모든 수를 맵핑하여 + * 두가지 수를 계산하여 얻을 수 있는 세가지 수가 map에 있는지를 확인하여 + * 중복되지 않도록 결과를 map에 저장 -> [][]int로 반환 + * 시간 복잡도: O(N^2) + * 공간 복잡도: O(N) + */ +func threeSum(nums []int) [][]int { + triplets := make(map[string][]int) + for i, n1 := range nums[:len(nums)-2] { + seen := make(map[int]int) + for _, n2 := range nums[i+1:] { + target := -n1 - n2 + if _, ok := seen[target]; ok { + item := []int{n1, n2, target} + slices.Sort(item) + key := strconv.Itoa(item[0]) + strconv.Itoa(item[1]) + strconv.Itoa(item[2]) + triplets[key] = item + } + seen[n2] = n2 + } + } + + ret := make([][]int, 0) + for _, t := range triplets { + ret = append(ret, t) + } + return ret +} + +/* + * 따로 맵핑하여 첫 번째 수를 고정 후, 2,3 번째 수를 순회하며 도는 첫 번째 방식과 동일하게 O(N^2) 비용은 유지 + * 되지만, 공간 복잡도 면에서 O(N) -> O(1)로 개선이 됨. + */ +func threeSum2(nums []int) [][]int { + slices.Sort(nums) + triplets := make(map[string][]int, 0) + + for i := 0; i < len(nums); i++ { + l, h := i+1, len(nums)-1 + for l < h { + sum := nums[l] + nums[h] + nums[i] + if sum < 0 { + l++ + } else if sum > 0 { + h-- + } else { + k := strconv.Itoa(nums[l]) + strconv.Itoa(nums[h]) + strconv.Itoa(nums[i]) + nums := []int{nums[i], nums[l], nums[h]} + triplets[k] = nums + l++ + h-- + } + } + } + ret := make([][]int, 0) + for _, t := range triplets { + ret = append(ret, t) + } + return ret +} diff --git a/climbing-stairs/ysle0.go b/climbing-stairs/ysle0.go new file mode 100644 index 000000000..c4a21c113 --- /dev/null +++ b/climbing-stairs/ysle0.go @@ -0,0 +1,25 @@ +package climbing_stairs + +/** + * 풀이: 이전 스텝을 계산하여 고정 한후 다음 조건을 확인하는 것에서 dp유형의 문제라는 생각이 들었지만 + * dp가 뭔지몰라 무서워 패턴은 못찾았습니다! (감정적) + * 그래서 힌트 보고 피보나치 수열인 것을 파악 후 재귀하지않는 방식으로 풀었습니다. + * 풀면서 피보나치도 dp의 방식으로 풀 수 있다는 걸 명확하게 알았네요. (memoization) + */ +func climbStairs(n int) int { + return fibo(n) +} + +func fibo(n int) int { + ret := []int{0, 1} + for i := 2; i <= n; i++ { + ret = append(ret, ret[i-1]+ret[i-2]) + } + ret = ret[len(ret)-2:] + + sum := 0 + for _, n := range ret { + sum += n + } + return sum +} diff --git a/contains-duplicate/ysle0.go b/contains-duplicate/ysle0.go index 2fd2018ed..70e868e98 100644 --- a/contains-duplicate/ysle0.go +++ b/contains-duplicate/ysle0.go @@ -3,15 +3,13 @@ package contains_duplicate import "sort" /* -1. 문제 + 1. 문제 + 주어진 int 배열 nums에 숫자가 중복되는 경우가 한 번이라도 있으면 true, 그렇지 않으면 false 를 리턴 - 주어진 int 배열 nums에 숫자가 중복되는 경우가 한 번이라도 있으면 true, 그렇지 않으면 false 를 리턴 - -2. 풀이 - - 고유값만 저장하는 set(go 에서는 map)의 성질을 활용하여 - nums를 순회하며 set에 값이 있는지 없는지 체크하여 - 숫자가 중복되는 경우를 체크 + 2. 풀이 + 고유값만 저장하는 set(go 에서는 map)의 성질을 활용하여 + nums를 순회하며 set에 값이 있는지 없는지 체크하여 + 숫자가 중복되는 경우를 체크 3. 분석 - 시간 복잡도: O(N) @@ -37,6 +35,12 @@ func containsDuplicate(nums []int) bool { return false } +/* +* 처음부터 오름차순으로 정렬하여 랜덤 접근의 필요성을 없앰. +* 복잡도 측면에서의 개선점은 없어보임. + - 시간 복잡도: O(N) + - 공간 복잡도: O(N) +*/ func containsDuplicate_SortedApproach(nums []int) bool { // early exit for small slices if len(nums) < 2 { diff --git a/longest-consecutive-sequence/ysle0.go b/longest-consecutive-sequence/ysle0.go index b4fcb500a..1c81ad669 100644 --- a/longest-consecutive-sequence/ysle0.go +++ b/longest-consecutive-sequence/ysle0.go @@ -57,7 +57,7 @@ func longestConsecutive(nums []int) int { cursor++ } - //tmp := make([]int, 0, len(cons)) + // map->array tmp := make([]int, 0, len(cons)) for _, v := range cons { tmp = append(tmp, v) @@ -68,5 +68,6 @@ func longestConsecutive(nums []int) int { func(a, b int) int { return b - a }) + return tmp[0] } diff --git a/top-k-frequent-elements/ysle0.go b/top-k-frequent-elements/ysle0.go index 34e34948c..692c42a99 100644 --- a/top-k-frequent-elements/ysle0.go +++ b/top-k-frequent-elements/ysle0.go @@ -114,14 +114,5 @@ func topKFrequentElements_BucketSort(nums []int, k int) []int { res = append(res, buc[i]...) } - return res[:k] + return res } - -// -//func main() { -// r1 := topKFrequent([]int{1, 1, 1, 2, 2, 3}, 2) -// fmt.Println(r1) -// -// r2 := topKFrequent([]int{1}, 1) -// fmt.Println(r2) -//} diff --git a/valid-anagram/ysle0.go b/valid-anagram/ysle0.go new file mode 100644 index 000000000..1973655f8 --- /dev/null +++ b/valid-anagram/ysle0.go @@ -0,0 +1,57 @@ +package valid_anagram + +/** + * 풀이: + * t가 s의 anagram인지 확인하는 문제! + * s를 hashmap에 맵핑하고, t의 rune r을 하나씩 s[r] 로 갯수를 체크하여 풀음. + * anagram이면 return true, otherwise false + * true 까지 조건 3개가 있음. + * + * TC: O(N) -> s나 t의 길이 만큼만 돌음 + * SC: O(N) -> M * N에서 최고차항 정리 + */ +func isAnagram(s string, t string) bool { + ht := make(map[rune]int, len(s)) + for _, r := range s { + ht[r]++ + } + + for _, r := range t { + cnt, ok := ht[r] + if !ok { // 1. t에 존재하지 않는 rune -> false + return false + } + + ht[r] -= 1 // t의 rune이 s에 존재하므로 갯수 1 차감 + if cnt-1 < 0 { // 2. s에 있는 rune보다 더 많이 가지고 있음 + return false + } + } + + for _, v := range ht { + if v > 0 { // 3. t를 순회하고 s에 남아있는게 있어도 false + return false + } + } + + return true +} + +func isAnagram_faster(s string, t string) bool { + if len(s) != len(t) { // 둘이 길이가 다르면 당연히 실패함 + return false + } + + ht := make(map[byte]int, len(s)) + for i, _ := range s { + ht[s[i]]++ + ht[t[i]]-- // 기존에 푼 방식에서 ok 체크 안해버림? .. + } + + for _, v := range ht { + if v != 0 { + return false + } + } + return true +} diff --git a/valid-palindrome/ysle0.go b/valid-palindrome/ysle0.go index 21770bbd0..4da2c22d1 100644 --- a/valid-palindrome/ysle0.go +++ b/valid-palindrome/ysle0.go @@ -24,7 +24,7 @@ import ( 모든 문자열을 돌며 소문자로 변환 palindrome 문자열 체크 loop 앞 커서 < 뒤 커서 의 조건으로 O(n/2) ---> O(n) - - 공간 복잡도: O(1) + - 공간 복잡도: O(n) 새로운 저장공간은 없으며 주어진 문자열 s 하나뿐 */ var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`) @@ -54,6 +54,7 @@ func isPalindrome(s string) bool { /* 1. 개선점 - regex 오버헤드 제거 + 공간 복잡도를 개선: O(N) -> O(1) */ func isPalindrome_Optimized(s string) bool { front, rear := 0, len(s)-1