Skip to content

Commit f4a74af

Browse files
authored
Merge branch 'DaleStudy:main' into main
2 parents df9a816 + bace583 commit f4a74af

File tree

15 files changed

+473
-0
lines changed

15 files changed

+473
-0
lines changed

contains-duplicate/JANGSEYEONG.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
시간복잡도: O(n) - new Set(nums)에서 배열 요소 순회하며 Set 생성 O(n) + 길이 비교 O(1)
3+
- Set 자료구조는 중복된 값을 자동으로 제거
4+
*/
5+
6+
/**
7+
* @param {number[]} nums
8+
* @return {boolean}
9+
*/
10+
var containsDuplicate = function (nums) {
11+
// Set으로 만들었을 때, 기존 배열과 사이즈가 다르면 중복이 제거된거임
12+
const numsSet = new Set(nums);
13+
return nums.length !== numsSet.size;
14+
};

contains-duplicate/Yn3-3xh.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
[문제풀이]
3+
time: O(N), space: O(N)
4+
- 같은 수가 하나라도 있으면 true
5+
- 모든 수가 다르면 false
6+
7+
[회고]
8+
ArrayList vs Set
9+
ArrayList: O(N^2), 매번 리스트를 처음부터 검색해야 하며, n번 반복
10+
Set : O(N) , 내부적으로 해시 테이블을 사용하여 중복 확인을 O(1)에 수행
11+
따라서 중복 검사에서 Set 더 효율적
12+
13+
set.add()의 return 자료형은 boolean 이다.
14+
이후에는 if(!set.add()) 처럼 사용해도 좋을 듯.
15+
*/
16+
class Solution {
17+
public boolean containsDuplicate(int[] nums) {
18+
Set<Integer> set = new HashSet<>();
19+
for (int num : nums) {
20+
if (set.contains(num)) {
21+
return true;
22+
}
23+
set.add(num);
24+
}
25+
return false;
26+
}
27+
}

contains-duplicate/yayyz.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
class Solution:
2+
def containsDuplicate(self, nums: List[int]) -> bool:
3+
4+
return len(nums) != len(set(nums))

house-robber/JANGSEYEONG.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/**
2+
* 시간복잡도: O(n) - 배열을 한 번만 순회
3+
* 공간복잡도: O(n) - 길이가 n+1인 DP 배열 사용
4+
*
5+
* @param {number[]} nums
6+
* @return {number}
7+
*/
8+
var rob = function (nums) {
9+
// arr[i] = i번째 집부터 마지막 집까지 고려했을 때 훔칠 수 있는 최대 금액
10+
const arr = new Array(nums.length + 1);
11+
12+
// 존재하지 않는 집(n+1번째)은 0, 점화식을 위한 계산용 공간임
13+
arr[nums.length] = 0;
14+
15+
// 마지막 집만 고려하면 그 집의 금액이 최대값 (이후 집들은 없으니까 0으로 계산한 결과)
16+
arr[nums.length - 1] = nums[nums.length - 1];
17+
18+
// 뒤에서부터 계산
19+
for (let i = nums.length - 2; i >= 0; i--) {
20+
// i번째 집에서의 결정:
21+
// 1. i번째 집을 털고 (i+2)번째 집부터 훔치는 경우: nums[i] + arr[i+2]
22+
// 2. i번째 집을 털지 않고 (i+1)번째 집부터 훔치는 경우: arr[i+1]
23+
// 두 가지 중 최대값을 선택
24+
arr[i] = Math.max(nums[i] + arr[i + 2], arr[i + 1]);
25+
}
26+
27+
// arr[0]은 0번째 집부터 고려했을 때 훔칠 수 있는 최대 금액
28+
return arr[0];
29+
};
30+
31+
/* 풀이 설명:
32+
* 문제의 핵심: 인접한 집은 연속해서 털 수 없을 때 최대로 털 수 있는 금액 찾기
33+
*
34+
* 접근 방식: 다이나믹 프로그래밍
35+
*
36+
* 1. 상태 정의:
37+
* - f(n) = n번째 집부터 마지막 집까지 고려했을 때 털 수 있는 최대 금액
38+
*
39+
* 2. 점화식 도출:
40+
* - 각 집마다 두 가지 선택이 있음:
41+
* 1) 현재 집을 털기: 현재 집의 돈 + 두 집 이후부터의 최대 금액
42+
* 2) 현재 집을 털지 않기: 다음 집부터의 최대 금액
43+
* - 따라서 점화식: f(n) = max(nums[n] + f(n+2), f(n+1))
44+
*
45+
* 3. 베이스 케이스:
46+
* - 존재하지 않는 집: f(n+1) = 0
47+
* - 마지막 집: f(n) = nums[n]
48+
*
49+
* 4. 계산 방향:
50+
* - 뒤에서부터 앞으로 계산 (Bottom-up)
51+
* - 마지막 집부터 시작해서 첫 번째 집까지 각 위치에서의 최대 금액 계산
52+
*
53+
* 예시 [1,2,3,1]:
54+
* - f(4) = 0 (존재하지 않는 집)
55+
* - f(3) = 1 (마지막 집)
56+
* - f(2) = max(3 + f(4), f(3)) = max(3 + 0, 1) = 3
57+
* - f(1) = max(2 + f(3), f(2)) = max(2 + 1, 3) = 3
58+
* - f(0) = max(1 + f(2), f(1)) = max(1 + 3, 3) = 4
59+
* - 결과: 4 (최적의 선택은 0번째와 2번째 집 털기)
60+
*/

house-robber/Yn3-3xh.java

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
[문제풀이]
3+
(X) 주어진 nums 배열에서 index 홀수의 합과 짝수의 합을 비교해보자.
4+
class Solution {
5+
public int rob(int[] nums) {
6+
if (nums.length == 1) {
7+
return nums[0];
8+
}
9+
10+
for (int i = 1; i <= nums.length; i++) {
11+
int beforeStepIndex = i - 2;
12+
if (beforeStepIndex >= 1) {
13+
nums[i - 1] += nums[beforeStepIndex - 1];
14+
}
15+
}
16+
return Math.max(nums[nums.length - 1], nums[nums.length - 2]);
17+
}
18+
}
19+
>> 바로 옆이 아니어도 된다.
20+
21+
(O) 현재 num과 이전 num을 비교하여, 큰 값을 기준으로 더한다.
22+
time: O(N), space: O(1)
23+
class Solution {
24+
public int rob(int[] nums) {
25+
int prevNum = 0;
26+
int sum = 0;
27+
for (int num : nums) {
28+
int temp = Math.max(sum, prevNum + num);
29+
prevNum = sum;
30+
sum = temp;
31+
}
32+
return sum;
33+
}
34+
}
35+
>> 공간복잡도 최적화 솔루션으로, dp 보다 직관적이지 않아, 메모리 사용이 제한되거나 입력 크기가 매우 클 때 사용하는 것이 좋을 듯
36+
37+
time: O(N), space: O(N)
38+
class Solution {
39+
public int rob(int[] nums) {
40+
if (nums.length == 1) {
41+
return nums[0];
42+
}
43+
44+
int[] dp = new int[nums.length];
45+
dp[0] = nums[0];
46+
dp[1] = Math.max(nums[0], nums[1]);
47+
48+
for (int i = 2; i < nums.length; i++) {
49+
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
50+
}
51+
return dp[nums.length - 1];
52+
}
53+
}
54+
>> 공간복잡도는 좀 더 높지만, 직관적이다.
55+
[회고]
56+
개발은 혼자하는 것이 아니기도 하고, 디버깅하기 쉽게 직관적인 DP로 푸는게 좋지 않을까?..?
57+
*/
58+
class Solution {
59+
public int rob(int[] nums) {
60+
if (nums.length == 1) {
61+
return nums[0];
62+
}
63+
64+
int[] dp = new int[nums.length];
65+
dp[0] = nums[0];
66+
dp[1] = Math.max(nums[0], nums[1]);
67+
68+
for (int i = 2; i < nums.length; i++) {
69+
dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]);
70+
}
71+
return dp[nums.length - 1];
72+
}
73+
}

house-robber/yayyz.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
class Solution:
2+
def rob(self, nums: List[int]) -> int:
3+
n = len(nums)
4+
5+
if n == 1:
6+
return nums[0]
7+
8+
dp = [0] * n
9+
dp[0] = nums[0]
10+
dp[1] = max(nums[0], nums[1])
11+
12+
# i 번째를 털지 않은 조건과 i번째를 털은 조건 중 max를 비교
13+
for i in range(2, n):
14+
dp[i] = max(dp[i-1], dp[i-2] + nums[i])
15+
16+
return dp[-1]
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/*
2+
시간복잡도: O(n) - 모든 요소에 대해 최대 한 번씩만 검사하기 때문
3+
- Set을 사용하여 O(1) 시간에 요소 존재 여부 확인 가능
4+
- 각 숫자는 연속 수열의 시작점인 경우에만 while 루프를 실행
5+
-> 모든 요소에 대해 while 루프의 총 반복 횟수는 전체 요소 수를 초과하지 않음
6+
*/
7+
/**
8+
* @param {number[]} nums
9+
* @return {number}
10+
*/
11+
12+
var longestConsecutive = function (nums) {
13+
const numSet = new Set(nums);
14+
let longest = 0;
15+
16+
for (let n of [...numSet]) {
17+
if (!numSet.has(n - 1)) {
18+
let length = 0;
19+
while (numSet.has(n + length)) {
20+
length += 1;
21+
}
22+
longest = Math.max(length, longest);
23+
}
24+
}
25+
return longest;
26+
};
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
[문제풀이]
3+
time: O(N), space: O(N)
4+
- 중복 제거
5+
- 연속된 횟수 max 구하기
6+
-- 연속된 숫자 그룹 중 첫번째부터 시작되도록
7+
8+
[회고]
9+
연속된 숫자 그룹 중 첫번째부터 시작되도록 하는 부분에서 막혔다..
10+
차분히 생각해보면 무리없이 풀 수 있지 않았을까..
11+
*/
12+
class Solution {
13+
public int longestConsecutive(int[] nums) {
14+
if (nums.length == 0) {
15+
return 0;
16+
}
17+
18+
Set<Integer> numsSet = new HashSet<>();
19+
for (int num : nums) {
20+
numsSet.add(num);
21+
}
22+
23+
int maxCount = 1;
24+
for (int num : numsSet) {
25+
if (!numsSet.contains(num - 1)) {
26+
int count = 1;
27+
int currentNum = num;
28+
while (numsSet.contains(currentNum + 1)) {
29+
count++;
30+
currentNum++;
31+
}
32+
maxCount = Math.max(maxCount, count);
33+
}
34+
}
35+
return maxCount;
36+
}
37+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class Solution:
2+
def longestConsecutive(self, nums: List[int]) -> int:
3+
if len(nums) == 0:
4+
return 0
5+
6+
nums = sorted(set(nums))
7+
8+
currLength = 1
9+
lastNum = nums[0]
10+
result = 1
11+
12+
for i in range(1, len(nums)):
13+
if nums[i] == lastNum + 1:
14+
currLength += 1
15+
else:
16+
currLength = 1
17+
18+
result = max(result, currLength)
19+
lastNum = nums[i]
20+
21+
return result
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
시간복잡도: O(n) - 배열을 두 번 순회하지만 여전히 선형
3+
배열에서 0의 개수에 따라 결과가 달라지는 점을 이용
4+
- 0이 두 개 이상: 모든 결과는 0
5+
- 0이 하나: 0 위치만 전체곱, 나머지는 0
6+
- 0이 없음: 전체곱 ÷ 현재 원소
7+
*/
8+
9+
/**
10+
* @param {number[]} nums
11+
* @return {number[]}
12+
*/
13+
var productExceptSelf = function (nums) {
14+
// 0이 있는 위치들 체크용
15+
const zeros = new Map();
16+
17+
// 전체 곱 구하기 - O(n)
18+
const allProductExceptZero = nums.reduce((acc, x, i) => {
19+
if (x === 0) zeros.set(i, true);
20+
return x !== 0 ? acc * x : acc;
21+
}, 1);
22+
23+
// 배열 전체 돌면서 조건에 맞게 return 시키기 - O(n)
24+
return nums.map((x, i) => {
25+
// 0이 2개 이상이라면 무조건 0
26+
if (zeros.size > 1) {
27+
return 0;
28+
}
29+
30+
// 0이 1개일 때
31+
if (zeros.size === 1) {
32+
// 그게 나일 때
33+
if (zeros.has(i)) return allProductExceptZero;
34+
35+
// 그게 다른애일 때
36+
return 0;
37+
}
38+
39+
// 0이 없을 때
40+
return allProductExceptZero / x;
41+
});
42+
};

0 commit comments

Comments
 (0)