Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit c60b5f5

Browse files
authoredDec 12, 2024
Merge branch 'DaleStudy:main' into main
2 parents 9523fc8 + 895165f commit c60b5f5

File tree

14 files changed

+622
-0
lines changed

14 files changed

+622
-0
lines changed
 
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package leetcode_study
2+
3+
/**
4+
* Set 자료 구조로 변경 후 원소의 개수를 비교해 문제 해결
5+
* 시간 복잡도 : O(n)
6+
* -> 모든 Array의 원소를 순회해야함.
7+
* 공간 복잡도 : O(n)
8+
* -> IntArray의 요소 개수에 비례하여 추가적인 공간이 필요함.
9+
*/
10+
fun containsDuplicate(nums: IntArray): Boolean {
11+
val changeSet = nums.toSet()
12+
return changeSet.size != nums.size
13+
}

‎contains-duplicate/heypaprika.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Big-O 예상 : O(n)
2+
class Solution:
3+
def containsDuplicate(self, nums: List[int]) -> bool:
4+
num_dict = {}
5+
for num in nums:
6+
if num in num_dict:
7+
return True
8+
else:
9+
num_dict[num] = 1
10+
return False
11+

‎contains-duplicate/ysle0.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package contains_duplicate
2+
3+
import "sort"
4+
5+
/*
6+
1. 문제
7+
8+
주어진 int 배열 nums에 숫자가 중복되는 경우가 한 번이라도 있으면 true, 그렇지 않으면 false 를 리턴
9+
10+
2. 풀이
11+
12+
고유값만 저장하는 set(go 에서는 map)의 성질을 활용하여
13+
nums를 순회하며 set에 값이 있는지 없는지 체크하여
14+
숫자가 중복되는 경우를 체크
15+
16+
3. 분석
17+
- 시간 복잡도: O(N)
18+
nums 탐색: O(N)
19+
배열 nums의 모든 원소를 단 한 번 순회
20+
map 삽입, 탐색: O(1)
21+
map의 내부 구현은 해시 테이블.
22+
O(N)보다 작아 무시됨
23+
- 공간 복잡도: O(N)
24+
최악의 경우라도 사용공간은 nums 의 크기만큼 + nums의 모든 원소를 포함한 map
25+
*/
26+
func containsDuplicate(nums []int) bool {
27+
seen := map[int]int{}
28+
29+
for _, n := range nums {
30+
if _, ok := seen[n]; ok {
31+
return true
32+
}
33+
34+
seen[n] = 1
35+
}
36+
37+
return false
38+
}
39+
40+
func containsDuplicate_SortedApproach(nums []int) bool {
41+
// early exit for small slices
42+
if len(nums) < 2 {
43+
return false
44+
}
45+
46+
// sort in ascending order and check adjacent elements
47+
sort.Ints(nums)
48+
for i := 1; i < len(nums); i++ {
49+
if nums[i] == nums[i-1] {
50+
return true
51+
}
52+
}
53+
54+
return false
55+
}

‎house-robber/EcoFriendlyAppleSu.kt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package leetcode_study
2+
3+
/**
4+
* DP를 사용한 문제 풀이.
5+
* DP를 사용하지 않고 모든 경우의 수를 계산하여 최대 값을 구하려면 100!에 해당하는 연산이 필요하며, 이는 시간 초과를 초래합니다.
6+
* 시간 복잡도 : O(n)
7+
* -> 주어진 숫자 배열 만큼 반복 진행
8+
* 공간 복잡도 : O(n)
9+
* -> 숫자 배열만큼의 가중치를 담을 배열 필요
10+
*/
11+
fun rob(nums: IntArray): Int {
12+
val dp = IntArray(nums.size)
13+
14+
if (nums.size == 1) {
15+
return nums[0]
16+
}
17+
18+
if (nums.size == 2) {
19+
return max(nums[0], nums[1])
20+
}
21+
22+
dp[0] = nums[0]
23+
dp[1] = nums[1]
24+
dp[2] = nums[2] + dp[0]
25+
26+
for (i in 3 until nums.size) {
27+
dp[i] = max(dp[i-3], dp[i-2]) + nums[i]
28+
}
29+
return dp.max()
30+
}

‎house-robber/heypaprika.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Big-O 예상 : O(n)
2+
class Solution:
3+
def rob(self, nums: List[int]) -> int:
4+
a = [0] * len(nums)
5+
6+
if len(nums) == 1:
7+
return nums[0]
8+
elif len(nums) == 2:
9+
return max(nums[0], nums[1])
10+
11+
a[0] = nums[0]
12+
a[1] = nums[1]
13+
a[2] = max(a[0] + nums[2], a[1])
14+
15+
for i in range(3, len(nums)):
16+
a[i] = max(a[i-3], a[i-2]) + nums[i]
17+
18+
return max(a)
19+
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package leetcode_study
2+
3+
/**
4+
* HashSet을 사용한 풀이.
5+
* 시간 복잡도 O(n)
6+
* -> Loop를 두 번 돌기 때문에 O(n^2)이라고 생각할 수 있으나 최악의 상황이여도 O(n + (n-1))로 O(n)이 됨.
7+
* -> 1 부터 10억까지의 연속된 수가 Set 자료구조에 담겨 있고 최악의 상황으로 1이 마지막 순번에 뽑힌다고 가정했을 때, (Set은 순서가 존재하지 않음)
8+
* -> 1 부터 Longest Count 하더라도 주어진 nums에서 n 번 set에서 10억 -1번을 순회하므로 O(n^2)이 아닌 O(n)이 됨.
9+
*/
10+
fun longestConsecutive(nums: IntArray): Int {
11+
val numSet: HashSet<Int> = nums.toHashSet()
12+
var longest = 0
13+
14+
for (num in nums) {
15+
// 현재 요소보다 크기가 1 작은 숫자를 갖고 있다면 현재 요소는 최소값이 아니므로 다음으로 넘어감
16+
if (numSet.contains(num -1)) {
17+
continue
18+
}
19+
// 현재 요소보다 1 작은 연속된 숫자가 없으므로 현재 원소를 1 카운트 한 length 할당
20+
var length = 1
21+
while (numSet.contains(num + length)) {
22+
length++
23+
}
24+
longest = max(longest, length)
25+
}
26+
return longest
27+
}
28+
29+
/**
30+
* Time Limit 발생.
31+
* 시간 복잡도 O(n^2)
32+
* -> nums 안에 존재하는 요소마다 중복을 포함한 Loop(while) 진행
33+
*/
34+
fun longestConsecutive01(nums: IntArray): Int {
35+
// 결과를 담을 리스트
36+
val resultList = mutableListOf<Int>()
37+
38+
// 1차 loop
39+
for (i in nums.indices) {
40+
var tempResult = 0
41+
var currentNum = nums[i]
42+
// 2차 loop
43+
while (true) {
44+
if (nums.contains(currentNum)) {
45+
tempResult += 1
46+
currentNum += 1
47+
} else {
48+
break
49+
}
50+
}
51+
resultList.add(tempResult)
52+
}
53+
if (resultList.isEmpty()) {
54+
return 0
55+
}
56+
return resultList.max()
57+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Big-O 예상 : O(nlog(n))
2+
class Solution:
3+
def longestConsecutive(self, nums: List[int]) -> int:
4+
nums = sorted(list(set(nums)))
5+
if len(nums) == 0:
6+
return 0
7+
elif len(nums) == 1:
8+
return 1
9+
cur_long = 1
10+
longest = 1
11+
for i, num in enumerate(nums):
12+
if i == 0:
13+
continue
14+
else:
15+
if nums[i-1] + 1 == nums[i]:
16+
cur_long += 1
17+
if longest < cur_long:
18+
longest = cur_long
19+
else:
20+
cur_long = 1
21+
return longest
22+

‎longest-consecutive-sequence/ysle0.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package longest_consecutive_sequence
2+
3+
import "slices"
4+
5+
/*
6+
1. 문제
7+
주어진 int 배열 nums에서 찾을 수 있는 가장 긴 연속된 원소의 길이 구하기
8+
9+
2. 풀이
10+
모든 수의 중복을 제거하고, 오름차순으로 정렬하여 연속된 원소의 부분을 찾기 위해서
11+
배열을 순회하여 인덱스 고정~전진하며 다음 원소가 연속된 원소인지 체크를 반복
12+
13+
3. 분석
14+
15+
- 시간 복잡도: O(N logN)
16+
배열 정렬 O(N logN)
17+
중복된 원소를 제거해주는 slices.Compact(nums): O(N)
18+
2중 포문은 for 문 순회 index 를 같이 쓰므로 O(N)
19+
20+
- 공간 복잡도: O(N)
21+
*/
22+
func longestConsecutive(nums []int) int {
23+
if len(nums) == 0 {
24+
return 0
25+
}
26+
27+
if len(nums) == 1 {
28+
return 1
29+
}
30+
31+
slices.Sort(nums)
32+
nums = slices.Compact(nums)
33+
// 중복을 제거하고 나서도 1개면 최장연속수는 1
34+
if len(nums) == 1 {
35+
return 1
36+
}
37+
38+
cons := map[int]int{}
39+
cursor := 0
40+
for cursor < len(nums)-1 {
41+
cons[cursor] = 1
42+
wasConsecutive := false
43+
44+
// cursor 는 고정하고, innerCursor 를 돌림
45+
innerCursor := cursor
46+
for innerCursor+1 < len(nums) &&
47+
nums[innerCursor]+1 == nums[innerCursor+1] {
48+
49+
cons[cursor]++
50+
innerCursor++
51+
wasConsecutive = true
52+
}
53+
54+
if wasConsecutive {
55+
cursor = innerCursor
56+
}
57+
cursor++
58+
}
59+
60+
//tmp := make([]int, 0, len(cons))
61+
tmp := make([]int, 0, len(cons))
62+
for _, v := range cons {
63+
tmp = append(tmp, v)
64+
}
65+
66+
slices.SortFunc(
67+
tmp,
68+
func(a, b int) int {
69+
return b - a
70+
})
71+
return tmp[0]
72+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
package leetcode_study
2+
3+
/**
4+
* 주어진 숫자들에서 빈도 수가 가장 큰 k 개의 숫자를 구하는 문제. map 자료구조를 사용해 해결
5+
* 시간 복잡도 : O(nlogn)
6+
* -> Int Array를 순회해 map에 담는 과정 O(n)
7+
* -> 채워진 Map 자료구조에서 value 기준 내림차순으로 정렬 과정 O(nlogn)
8+
* -> 정렬된 Map에서 K 만큼 값을 가져오는 과정 O(K). (k는 상수)
9+
* 각 단계의 시간 복잡도를 더하면 : O(n) + O(nlogn) + O(k) -> O(nlogn)
10+
* 공간 복잡도 : O(n)
11+
* -> Int Array에 존재하는 유니크한 요소 만큼 필요함.
12+
*/
13+
fun topKFrequent(nums: IntArray, k: Int): IntArray {
14+
val map = mutableMapOf<Int, Int>()
15+
16+
for (i in nums.indices) {
17+
if (map.containsKey(nums[i])) {
18+
val value = map[nums[i]]!!
19+
map[nums[i]] = value + 1
20+
} else {
21+
map.putIfAbsent(nums[i], 1)
22+
}
23+
}
24+
val sortedMap = map.toList().sortedByDescending { it.second }.toMap()
25+
return sortedMap.entries.take(k).map { it.key }.toIntArray()
26+
}
27+
28+
/**
29+
* 주어진 수의 빈도수를 기준으로 숫자를 할당하고 내림차순으로 순회해 k 개의 숫자를 얻게 되면 답을 도출하는 방법
30+
* 시간 복잡도 : O(n)
31+
* -> Int Array를 순회해 map에 담는 과정 O(n)
32+
* -> 빈도수 배열에 값을 채우는 과정 O(n)
33+
* -> 빈도수 배열을 내림차순으로 순회해 k 개를 만족하면 답을 도출하는 과정 O(n).
34+
* 이중 for loop 이지만 실제로는 빈도수가 유일한 숫자들만 고려되므로 k가 n보다 작거나 같은 경우에는 O(n)으로 가늠할 수 있음.
35+
* 각 단계의 시간 복잡도를 더하면 : O(n) + O(n) + O(n) -> O(n)
36+
* 공간 복잡도 : O(n)
37+
* -> Int Array에 존재하는 유니크한 요소 만큼 필요함.
38+
*/
39+
fun topKFrequent01(nums: IntArray, k: Int): IntArray {
40+
val map = mutableMapOf<Int, Int>()
41+
for(num in nums) {
42+
map[num] = map.getOrDefault(num, 0) + 1
43+
}
44+
45+
// count List 초기화
46+
// map의 value는 nums Size를 넘을 수 없음.
47+
val countList = Array(nums.size + 1) { mutableListOf<Int>() }
48+
for ((key, value) in map) {
49+
countList[value].add(key)
50+
}
51+
52+
val result = mutableListOf<Int>()
53+
for (i in countList.size - 1 downTo 0) {
54+
for (num in countList[i]) {
55+
result.add(num)
56+
if (result.size == k) {
57+
return result.toIntArray()
58+
}
59+
}
60+
}
61+
return result.toIntArray()
62+
}

‎top-k-frequent-elements/heypaprika.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Big-O 예상 : O(nlog(n))
2+
import heapq
3+
class Solution:
4+
def topKFrequent(self, nums: List[int], k: int) -> List[int]:
5+
6+
num_dict = {}
7+
for num in nums:
8+
num_dict[num] = num_dict.get(num, 0) + 1
9+
heap = []
10+
for k_, v in num_dict.items():
11+
heapq.heappush(heap, [-v, k_])
12+
ans = []
13+
for i in range(k):
14+
ans.append(heapq.heappop(heap)[1])
15+
return ans
16+

‎top-k-frequent-elements/ysle0.go

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package top_k_frequent_elements
2+
3+
//package main
4+
5+
import (
6+
"container/heap"
7+
"slices"
8+
)
9+
10+
/*
11+
1. 문제
12+
nums 에서 가장 많이 나온 숫자들 k 개를 반환.
13+
14+
2. 풀이
15+
map 에 빈도를 기록하여 내림차순 정렬한 후 k개 뽑기
16+
17+
3. 분석
18+
19+
- 시간 복잡도: O(N + M logM) --> O(N logN)
20+
빈도맵핑을 위한 nums 순회: O(N)
21+
오름차순정렬: O(M logM)
22+
23+
- 공간 복잡도: O(N)
24+
주어진 배열 nums: O(N)
25+
빈도맵핑용 map: O(N)
26+
*/
27+
type Kvp struct {
28+
k int
29+
v int
30+
}
31+
32+
func topKFrequent(nums []int, k int) []int {
33+
freq := map[int]int{}
34+
35+
// 빈도를 기록
36+
for _, n := range nums {
37+
if _, ok := freq[n]; !ok {
38+
freq[n] = 1
39+
} else {
40+
freq[n]++
41+
}
42+
}
43+
44+
// map->array
45+
tmp := make([]Kvp, 0, len(freq))
46+
for key, v := range freq {
47+
tmp = append(tmp, Kvp{key, v})
48+
}
49+
50+
// 내림차순 정렬 (time O(M logM)
51+
slices.SortFunc(tmp, func(a, b Kvp) int { return b.v - a.v })
52+
53+
// []int 로 변환
54+
res := make([]int, 0, len(tmp))
55+
for _, kvp := range tmp {
56+
res = append(res, kvp.k)
57+
}
58+
59+
// k 개 뽑기
60+
return res[:k]
61+
}
62+
63+
func topKElements_HeapBasedApproach(nums []int, k int) []int {
64+
freq := map[int]int{}
65+
for _, n := range nums {
66+
freq[n]++
67+
}
68+
69+
h := &IntHeap{}
70+
heap.Init(h)
71+
72+
for k, v := range freq {
73+
heap.Push(h, Kvp{k, v})
74+
if h.Len() > k {
75+
heap.Pop(h)
76+
}
77+
}
78+
79+
res := make([]int, k)
80+
for i := k - 1; i >= 0; i-- {
81+
res[i] = heap.Pop(h).(Kvp).k
82+
}
83+
84+
return res
85+
}
86+
87+
type IntHeap []Kvp
88+
89+
func (h *IntHeap) Len() int { return len(*h) }
90+
func (h *IntHeap) Less(i, j int) bool { return (*h)[i].v < (*h)[j].v }
91+
func (h *IntHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] }
92+
func (h *IntHeap) Push(x interface{}) { *h = append(*h, x.(Kvp)) }
93+
func (h *IntHeap) Pop() interface{} {
94+
old := *h
95+
n := len(old)
96+
x := old[n-1]
97+
*h = old[0 : n-1]
98+
return x
99+
}
100+
101+
func topKFrequentElements_BucketSort(nums []int, k int) []int {
102+
freq := map[int]int{}
103+
for _, n := range nums {
104+
freq[n]++
105+
}
106+
107+
buc := make([][]int, len(nums)+1)
108+
for k, v := range freq {
109+
buc[v] = append(buc[v], k)
110+
}
111+
112+
res := []int{}
113+
for i := len(buc) - 1; i >= 0 && len(res) < k; i-- {
114+
res = append(res, buc[i]...)
115+
}
116+
117+
return res[:k]
118+
}
119+
120+
//
121+
//func main() {
122+
// r1 := topKFrequent([]int{1, 1, 1, 2, 2, 3}, 2)
123+
// fmt.Println(r1)
124+
//
125+
// r2 := topKFrequent([]int{1}, 1)
126+
// fmt.Println(r2)
127+
//}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package leetcode_study
2+
3+
/**
4+
* 문자열의 대칭 판단
5+
* 시간 복잡도 : O(n)
6+
* -> 주어진(n) 크기의 문자열을 순회하며 비교함
7+
*/
8+
fun isPalindrome(s: String): Boolean {
9+
10+
// 주어진 문자열에서 모든 non-alphanumeric characters를 필터한 문자 배열 할당
11+
val splitGivenString = s.toCharArray()
12+
.filter { it.isLetterOrDigit() }
13+
.map { it.lowercaseChar() }
14+
.toCharArray()
15+
16+
// 필터된 문자열이 비어있다면 true 반환
17+
if (splitGivenString.isEmpty()) { return true }
18+
19+
// 대칭을 비교하기 위한 시작, 끝 변수
20+
var start = 0
21+
var end = splitGivenString.size - 1
22+
23+
// 반복적으로 수행하며 같다면 시작 +1, 끝 -1 을 진행해 대칭 판단
24+
while (start <= end) {
25+
if (splitGivenString[start] == splitGivenString[end]) {
26+
start += 1
27+
end -= 1
28+
continue
29+
}
30+
return false
31+
}
32+
return true
33+
}

‎valid-palindrome/heypaprika.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Big-O 예상 : O(n)
2+
class Solution:
3+
def isPalindrome(self, s: str) -> bool:
4+
s = "".join(s.lower().split(" "))
5+
new_s = ""
6+
for item in s:
7+
if (ord("a") <= ord(item) <= ord("z")) or (ord("0") <= ord(item) <= ord("9")):
8+
new_s += item
9+
output = True
10+
new_s_2 = new_s[::-1]
11+
return new_s_2 == new_s
12+
return output
13+

‎valid-palindrome/ysle0.go

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
package valid_palindrome
2+
3+
import (
4+
"regexp"
5+
"strings"
6+
)
7+
8+
/*
9+
1. 문제
10+
회문. 주어진 문자열 s 를 모두 소문자로 바꾸고, alphanumeric 이 아닌 문자를 제외할 때,
11+
앞으로 읽으나 뒤로 읽으나 같은 문자열인지 체크 (회문)
12+
13+
2. 풀이
14+
- 주어진 문자열 s 에서 alphanumeric character 만 남기고 제거.
15+
- 모두 소문자로 변환
16+
- 앞, 뒤로 인덱스 위치를 기록하는 cursor 를 정의
17+
커서 둘을 앞, 뒤로 전진하며 같지않은 문자가 나오면 false 를 반환, 그렇지 않고 회문이면 true 를 반환.
18+
19+
3. 분석
20+
- 시간 복잡도: O(N)
21+
regex.ReplaceAllString(): O(n)
22+
모든 문자열을 돌며 regex 검사 후 대체
23+
strings.ToLower(str): O(n)
24+
모든 문자열을 돌며 소문자로 변환
25+
palindrome 문자열 체크 loop
26+
앞 커서 < 뒤 커서 의 조건으로 O(n/2) ---> O(n)
27+
- 공간 복잡도: O(1)
28+
새로운 저장공간은 없으며 주어진 문자열 s 하나뿐
29+
*/
30+
var nonAlphanumericRegex = regexp.MustCompile(`[^a-zA-Z0-9]+`)
31+
32+
func isPalindrome(s string) bool {
33+
s = nonAlphanumericRegex.ReplaceAllString(s, "")
34+
s = strings.ToLower(s)
35+
// 앞, 뒤 커서
36+
front, rear := 0, len(s)-1
37+
38+
for front < rear {
39+
frontCh := s[front]
40+
readCh := s[rear]
41+
42+
// 선택한 두 문자가 다르면 실패!
43+
if frontCh != readCh {
44+
return false
45+
}
46+
47+
front++
48+
rear--
49+
}
50+
51+
return true
52+
}
53+
54+
/*
55+
1. 개선점
56+
- regex 오버헤드 제거
57+
*/
58+
func isPalindrome_Optimized(s string) bool {
59+
front, rear := 0, len(s)-1
60+
61+
for front < rear {
62+
for front < rear && !isAlphanumeric(s[front]) {
63+
front++
64+
}
65+
66+
for front < rear && !isAlphanumeric(s[rear]) {
67+
rear--
68+
}
69+
70+
if toLower(s[front]) != toLower(s[rear]) {
71+
return false
72+
}
73+
74+
front++
75+
rear--
76+
}
77+
78+
return true
79+
}
80+
81+
func isAlphanumeric(ch byte) bool {
82+
return (ch >= 'a' && ch <= 'z') ||
83+
(ch >= 'A' && ch <= 'Z') ||
84+
(ch >= '0' && ch <= '9')
85+
}
86+
87+
func toLower(ch byte) byte {
88+
if ch >= 'A' && ch <= 'Z' {
89+
return ch + 32
90+
}
91+
return ch
92+
}

0 commit comments

Comments
 (0)
Please sign in to comment.