Skip to content

[grapefruitgreentealoe] WEEK 03 solutions #1789

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

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions combination-sum/grapefruitgreentealoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/**
* @param {number[]} candidates
* @param {number} target
* @return {number[][]}
*/

/**
* candidates를 여러번 사용해도 된다. 하지만 사용한 종류와 개수가 모두 같으면 안된다.
*
*/
var combinationSum = function(candidates, target) {
const candidatesLength = candidates.length
// 시작 시점에서 array를 만들어서 넘겨줘야한다.
// candidates 중에 넣으면 target이 되면 답에 그 배열을 넣고 탈출한다.
// 만약 더했는데 target보다 크면 그냥 리턴한다.
// target보다 작은것에 대해서 넣는다.
const ret = []
function dp(total,newNum,currentArr){
if(total + newNum > target){
return
}
if(total + newNum == target && ){
ret.push([...currentArr,newNum]);
return
}

//두 가지 어느것에도 해당하지 않으면 재귀를 또 다시 돈다.
for(let i = 0; i<candidatesLength; i++){
dp(total+newNum , candidates[i],[...currentArr,candidates[i]])
}
}
dp(0,0,[])
return ret
};

//여기서 문제가 생겼다.
// 중복이 생긴다는 것이다. 어떻게 중복을 제거하면 좋을까?
/**
* 1. Set을 이용한다.
* 2. 순서를 둔다. idx를 dfs에 넘겨주어서, 순서대로 작동하게 한다.
*/

var combinationSum = function(candidates, target) {
const candidatesLength = candidates.length
const ret = []
function dp(idx,total,currentArr){
if(total > target){
return
}
if(total == target){
ret.push(currentArr);
return
}

//idx를 넘겨받아서, 그 이후의 것에만(자기 자신 포함) 돌게 된다. 그러면 절반정도만 돈다고 볼 수 있다.
for(let i = idx; i < candidatesLength; i++){
dp(i,total+ candidates[i],[...currentArr,candidates[i]])
}
}
dp(0,0,[])
return ret
};



/**
시간복잡도: O(N^(target/min)) : N개 후보 중 선택하는 경우의 수가 target/min 깊이만큼 반복
공간복잡도: O(target/min) : 재귀 스택의 최대 깊이가 target/min


*/


// 2. dp를 활용한 문제 해결방법
var combinationSum = function(candidates, target) {
const dp = Array.from({length:target+1},()=>[])
dp[0] = [[]];
for(let candidate of candidates){
//여기서 candidate에 대해서, 이후 타겟까지 더할 수있는 숫자에 대한 조합에, candidate를 각각 넣어준다.
for(let i = candidate; i <= target; i++){
for(let comb of dp[i-candidate]){ //각 조합에 넣어줌
dp[i].push([...comb,candidate])
}
}
}
return dp[target]
};
/**
시간복잡도: O(N × target × M)
N개 후보를 순회하면서, 각 후보마다 target까지의 값들에 대해 기존 조합들을 복사해서 새 조합을 생성하는데, 기존의 조합의 수 M을 곱해야함
공간복잡도: O(target × M)
dp 배열의 각 인덱스에 해당 값을 만드는 모든 조합들을 저장. 조합개수에 따라서 커짐
*/
28 changes: 28 additions & 0 deletions decode-ways/grapefruitgreentealoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
var numDecodings = function(s) {
if (s[0] === '0') return 0;

const n = s.length;
const dp = new Array(n + 1).fill(0);
dp[0] = 1; // 빈 문자열. 계단의 첫 시작부분에 1을 넣어주는 것 처럼
dp[1] = 1; // 첫 글자가 0이 아님은 위에서 확인
//위 두 값이 기반이 되어 각 자리에 대한

for (let i = 2; i <= n; i++) {
const one = Number(s.slice(i - 1, i)); // 한 글자
const two = Number(s.slice(i - 2, i)); // 두 글자

if (one >= 1 && one <= 9) {
dp[i] += dp[i - 1];
}
if (two >= 10 && two <= 26) { //두자리수 일 때는 dp[i-2]도 더해준다.
dp[i] += dp[i - 2];
}
// 각 자리의 값(dp[i])은, 1글자 디코딩 시 바로 전 자리(dp[i-1])의 값과, 2글자 디코딩 시 두 자리 전(dp[i-2])의 값을 더해서 만든다. dp[i]는 도착지의 개념같은 것

}

return dp[n];
};

//시간 복잡도: O(n)
//공간 복잡도: O(n)
18 changes: 18 additions & 0 deletions maximum-subarray/grapefruitgreentealoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
/**
* @param {number[]} nums
* @return {number}
*/
var maxSubArray = function(nums) {
//카데인 알고리즘
let currentSum = nums[0]
let maxSum = nums[0]
for(let i = 1; i<nums.length;i++){
currentSum = Math.max(nums[i],currentSum+nums[i])
maxSum = Math.max(maxSum,currentSum)
}

return maxSum
};

//시간복잡도 : O(n)
//공간복잡도: O(1)
53 changes: 53 additions & 0 deletions number-of-1-bits/grapefruitgreentealoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@



//풀이1. 나머지와 몫을 이용한 개수 구하기

/**
* @param {number} n
* @return {number}
*/
var hammingWeight = function(n) {
let remain = n;
let count = 0;
while(remain){
if(remain == 2 || remain ==1 ){
count +=1;
break;
}
if(remain %2 == 1){
count +=1
}
remain = Math.floor(remain /2)
}
return count;
};
/*2진법을 구할때, 나머지가 1일때마다 count++ 해주고,
마지막에 2나 1이 남으면 count ++ 하고 break 해주면 된다.

입력 값을 반으로 나누기 때문에 시간 복잡도는 O(log n)
공간복잡도 O(1)
*/

//풀이 2. n의 이진수에서 가장 오른쪽 1비트를 하나씩 제거하여 1의 개수를 세는 방법

/**
* @param {number} n
* @return {number}
*/
var hammingWeight = function(n) {
//정수 n이 주어졌을때, 1의 개수?
let count = 0;
while (n !== 0) {
n = n & (n - 1);
count++;
}
return count
};

/*
시간복잡도: O(k) : 여기서 k는 n에 포함된 1의 개수
공간복잡도: O(1)
*/

//이 외에도 비트연산자, 비트마스킹 방법으로 시간복잡도를 O(1), O(logn)으로 풀수있다.
56 changes: 56 additions & 0 deletions valid-palindrome/grapefruitgreentealoe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/**
* @param {string} s
* @return {boolean}
*/

String.prototype.isAlphaNum = function() {
return /^[a-z0-9]$/i.test(this);
};
var isPalindrome = function(s) {
//chatAt의 범위의 string, number만 건진다
const ss = s.split('').filter((x)=>x.isAlphaNum()).map(x=>x.toLowerCase())
//그 다음엔 index를 양쪽에서 비교하기
for(let i=0;i<(ss.length/2);i++){
console.log(ss[i],ss[ss.length-1-i])
if(ss[i]!==ss[ss.length-1-i]) return false
}
return true
};

// 시간 복잡도 O(N)
// 공간 복잡도 O(N)

//2. 투포인터 방식으로, 필터링과 string 메서드를 그때마다 사용하는 방식으로 효율성 올리기
var isPalindrome = function(s) {
let left = 0;
let right = s.length - 1;
//각 포인터가 영숫자 문자를 만날 때까지 이동
while (left < right) {
while (left < right && !s[left].isAlphaNum()) {
left++;
}
while (left < right && !s[right].isAlphaNum()) {
right--;
}
if (left < right && s[left].toLowerCase() !== s[right].toLowerCase()) {
return false;
}
left++;
right--;
}

return true;
};

// 시간 복잡도 O(N)
// 공간 복잡도 O(1)

/*
더 효율적이게 푸는 방법
1. String.prototype 확장 지양
성능적인 측면에서 String.prototype 메서드 호출이 직접적인 헬퍼 함수 호출보다 아주 미세하게 오버헤드가 있을 수 있다 (실제로는 거의 무시할 수 있는 수준)

2. isAlphaNum 내부 로직 최적화 (Micro-optimization)
- 정규 표현식 엔진이 내부적으로 파싱되고 컴파일되는 오버헤드가 있기 때문에, 정규 표현식, 단순한 문자 범위 체크에는 문자 코드(char code) 비교가 더 빠를 수 있다.

*/