diff --git a/3sum/grapefruitgreentealoe.js b/3sum/grapefruitgreentealoe.js new file mode 100644 index 000000000..51fc0cb04 --- /dev/null +++ b/3sum/grapefruitgreentealoe.js @@ -0,0 +1,102 @@ +//1.dfs로 풀기 : 시간초과 남 + + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function(nums) { + /* + arr: 현재 고정 배열을 뜻함. dfs가 돌때마다 변경됨. + start: 끝난 index. arr와 마찬가지로 이것은 for문안에서 변경될 예정. + dep: dep은 현재 dfs의 dep+1을 해주면 됨. + */ + const numsLength = nums.length + if(numsLength ==3 && nums.reduce((a,b)=>a+b,0) == 0) return [nums] + + const ret = [] + const seen = new Set(); + nums.sort((a, b) => a - b); + + function dfs(arr,start,dep){ + if(dep == 3) { + if(arr.length !==3) return + const arrTotal = arr.reduce((a,b)=>a+b,0); + if(arrTotal == 0){ + const string = [...arr].sort((a,b)=>a-b).join(',') + if(!seen.has(string)){ + seen.add(string) + ret.push(arr) + } + } + return + } + //만약 start가 0이 되면, i를 감소시켜야하고, 새로운 배열을 추가해야한다. 기존의 배열에 넣어주는게 아니다. + + //끝점을 증가시키면서 진행해야함. 현재 고정 arr에 끝점 그 앞의 idx부터 진행해서 0까지 감소시키면서 dfs를 돌리면 된다. + //idx가 i-1부터이므로, i는 최소 1이어야 가능하다. + for(let idx = start; idx a - b): 배열 정렬에 O(NlogN) + +N개 숫자 중 3개 선택하는 조합 수 N(N−1)(N−2)/6 +정확히 알 수는 없지만, + +N^3 이상으로 보임. + +공간복잡도 : O(N^3) => seen 때문. + +*/ + +//2. 투포인터로 풀기 + +/* +우선 내가 문제에 대한 이해가 틀렸다. 숫자의 순서가 다르다고 하더라도, 같은 숫자의 조합을 가지고 있다면 안된다. +값이 원하는 값보다 작으면 오른쪽 값을 옮기고, 크면 왼쪽 값을 옮기는 투포인터 전략을 취해보았다. + + */ + +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function(nums) { + const triplets = [] + nums.sort((a,b)=>a-b) + for(let i =0;i 0 && nums[i] === nums[i - 1]) continue; + let low = i+1 + ,high = nums.length -1 + + while(low < high){ + const three_sum = nums[i] + nums[low] + nums[high] + if(three_sum<0){ + low +=1 + } + else if(three_sum >0){ + high -=1 + } + else{ + triplets.push([nums[i],nums[low],nums[high]]) + while (low < high && nums[low] === nums[low + 1]) low++; + while (low < high && nums[high] === nums[high - 1]) high--; + low = low+1 + high = high -1 + } + } + } + return triplets +}; diff --git a/climbing-stairs/grapefruitgreentealoe.js b/climbing-stairs/grapefruitgreentealoe.js new file mode 100644 index 000000000..3fd639f7a --- /dev/null +++ b/climbing-stairs/grapefruitgreentealoe.js @@ -0,0 +1,42 @@ +//특정 지점까지 얼마나 많은 개수의 길이 존재할까 +//갈 수 있는 방법은 1 혹은 2칸씩 오를 수 있다. + +/* 1. dp로 풀기. +점화식은 이렇다. +dp[n] = d[n-1] + d[n-2] +*/ + +var climbStairs = function(n) { + if (n === 1) return 1; + if (n === 2) return 2; + + const dp = Array(n + 1).fill(0); + dp[1] = 1; + dp[2] = 2; + + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + + return dp[n]; +}; + +//시간복잡도: O(n) +//공간복잡도: O(n) + +//2. 메모리 아끼기 +//dp배열 없이 변수로만 작성하기 +var climbStairs = function(n) { + if (n === 1) return 1; + let a = 1, b = 2; + for (let i = 3; i <= n; i++) { + let temp = a + b; + a = b; + b = temp; + } + return b; +}; + + +//시간복잡도: O(n) +//공간복잡도: O(1) diff --git a/product-of-array-except-self/grapefruitgreentealoe.js b/product-of-array-except-self/grapefruitgreentealoe.js new file mode 100644 index 000000000..487314a2a --- /dev/null +++ b/product-of-array-except-self/grapefruitgreentealoe.js @@ -0,0 +1,51 @@ +var productExceptSelf = function(nums) { + const n = nums.length; + const left = new Array(n).fill(1); + const right = new Array(n).fill(1); + const answer = new Array(n).fill(1); + + // 오른쪽 누적곱 + for (let i = n - 2; i >= 0; i--) { + right[i] = right[i + 1] * nums[i + 1]; + } + + // 왼쪽 누적곱 + for (let i = 1; i < n; i++) { + left[i] = left[i - 1] * nums[i - 1]; + } + + // 최종 곱셈 + for (let i = 0; i < n; i++) { + answer[i] = left[i] * right[i]; + } + + return answer; +}; +}; +//시간복잡도 O(n) +//공간복잡도 O(n) +// 어려웠던 점: 한번 풀었던 문제지만, 인덱스 범위를 설정하는 점에서 어려움을 겪엇다. + + +//여기서 공간복잡도를 O(1)로 최적화 할 수 있다. ( 리턴 배열 제외) +/** + * 문제 내에서 Follow up: Can you solve the problem in O(1) extra space complexity? (The output array does not count as extra space for space complexity analysis.) + */ +var productExceptSelf = function(nums) { + const n = nums.length; + const answer = new Array(n).fill(1); + + // 왼쪽 곱 + for (let i = 1; i < n; i++) { + answer[i] = answer[i - 1] * nums[i - 1]; + } + + // 오른쪽 곱 누적하면서 answer에 곱해줌 + let right = 1; + for (let i = n - 1; i >= 0; i--) { + answer[i] *= right; + right *= nums[i]; + } + + return answer; +}; diff --git a/valid-anagram/grapefruitgreentealoe.js b/valid-anagram/grapefruitgreentealoe.js new file mode 100644 index 000000000..4a50fc06e --- /dev/null +++ b/valid-anagram/grapefruitgreentealoe.js @@ -0,0 +1,21 @@ + +var isAnagram = function(s, t) { + if (s.length !== t.length) return false; + + const map = new Map(); + + for (let ch of s) { + map.set(ch, (map.get(ch) || 0) + 1); + } + + for (let ch of t) { + if (!map.has(ch)) return false; + map.set(ch, map.get(ch) - 1); + if (map.get(ch) < 0) return false; + } + + return true; +}; + +//시간복잡도 : O(n) +//공간복잡도 : O(1) (영문자니까 들어갈 수 있는 수가 한정되어있으므로. ) diff --git a/validate-binary-search-tree/grapefruitgreentealoe.js b/validate-binary-search-tree/grapefruitgreentealoe.js new file mode 100644 index 000000000..36efad703 --- /dev/null +++ b/validate-binary-search-tree/grapefruitgreentealoe.js @@ -0,0 +1,74 @@ +/* +유효한 BST는 이렇게 정의가 된다. +1. 노드의 좌측 서브 트리에는 노드의 키보다 작은 키를 가진 노드만 있다. +2. 노드의 우측 서브 트리에는 노드의 키보다 큰 키를 가진 노드만 있다. +3. 좌 우측 서브 트리도 모두 이진 탐색 트리여야 한다. + +트리의 순회는 재귀로 순회가 가능하다. +또한 BST 의 특성에 맞게, 하위값, 상한값의 개념이 들어간다. +만약 좌측으로 순회하게 될때는 +하위값은 부모노드의 하한값. +상한값은 부모노드의 값이 된다. + +우측 서브트리로 내려갈 때는, +하위값은 부모노드의 값, +상한값은 부모노드의 상한값이 된다. +*/ + +/** + * Definition for a binary tree node. + * function TreeNode(val, left, right) { + * this.val = (val===undefined ? 0 : val) + * this.left = (left===undefined ? null : left) + * this.right = (right===undefined ? null : right) + * } + */ +/** + * @param {TreeNode} root + * @return {boolean} + */ +var isValidBST = function(root) { + function dfs(node,low,high){ + if(!node) return true; // leaf + if( + (low !==null && node.val <= low)|| + (high !== null && node.val >= high)){ + return false + } + return dfs(node.left,left,node.val) && dfs(node.right,node.val,high) + + } + dfs(root,null,null) +}; + + + +/* +2. 두번째로는 중위순회로 해결할 수 있다. +중위순회 : 왼쪽 → 현재 노드 → 오른쪽 순서로 방문하는 방식 + +이진탐색 트리는 오름차순으로 모든 노드를 방문할 수 있다. +좌측트리를 먼저 순회하고, 부모노드를 방문하고, 그 다음 우측 트리를 순회하기 때문이다. + +따라서, 중위순회가 오름차순으로 진행되지 않는다면 이것은 유효한 이진 트리가 아니다. +*/ +var isValidBST = function(root) { + let prev = null; + + function inorder(node) { + if (!node) return true; //leaf + + if (!inorder(node.left)) return false; + + //좌측 순회 + if (prev !== null && node.val <= prev) return false; + prev = node.val; + + return inorder(node.right); + } + + return inorder(root); +}; +// 둘다 시간복잡도 O(N)이다. +// 공간복잡도 O(h) (재귀 스택만큼) +