diff --git a/3sum/youngduck.js b/3sum/youngduck.js new file mode 100644 index 000000000..36cf6e04d --- /dev/null +++ b/3sum/youngduck.js @@ -0,0 +1,49 @@ +/** + * @param {number[]} nums + * @return {number[][]} + */ +var threeSum = function(nums) { + + // 최악의 경우 3중 for문이므로 투포인터 기법을써서 최적화 해도 for문 하나는 필요함 + + // 결과 배열 + const result = []; + + // 투포인터 기법 사용을 위한 정렬 + nums.sort((a,b)=>a-b); + + for(let i = 0; i < nums.length - 2; i++){ + // 첫 번째 요소의 중복 건너뛰기 + if(i > 0 && nums[i] === nums[i-1]) continue; + + let left = i + 1; + let right = nums.length - 1; + + while(left < right){ + const sum = nums[i] + nums[left] + nums[right]; + + if(sum < 0){ + left++; + } + else if (sum > 0){ + right--; + } + else{ + // 합이 0인 경우 결과에 추가 + result.push([nums[i], nums[left], nums[right]]); + + // 두 번째, 세 번째 요소의 중복 건너뛰기 + while(left < right && nums[left] === nums[left + 1]) left++; + while(left < right && nums[right] === nums[right - 1]) right--; + + left++; + right--; + } + } + } + + return result; +}; +// 시간복잡도: O(n^2) +// 공간복잡도: O(1) + diff --git a/climbing-stairs/youngduck.js b/climbing-stairs/youngduck.js new file mode 100644 index 000000000..abc9fd0b9 --- /dev/null +++ b/climbing-stairs/youngduck.js @@ -0,0 +1,42 @@ +/** + * @param {number} n + * @return {number} + */ +var climbStairs = function (n) { + // 문제를 보자마자 든 생각: dfs처럼 하나씩,두개씩 가지뻗어가면서? -> 점화식,피보나치처럼 최적화된 형태겠는데? 여기까지는 생각. 근데 2칸전, 1칸전의 덧셈이라는 결론까지 내지는 못함 + + // 그래서 default값을 n=1,2,3까지 세팅해놓고 생각했었다. 다른사람의 점화식 풀이 참고 후 역순으로 출발해서 2칸전, 1칸전에 대한 상황에 집중하는 방식 이해 완료. 구현은 내가 직접. + if (n === 1) { + return 1; + } + + if (n === 2) { + return 2; + } + + return climbStairs(n - 1) + climbStairs(n - 2); +}; + +// 시간복잡도: O(2^n) +// 공간복잡도: O(n) + +/** + * @param {number} n + * @return {number} + */ +var climbStairsOptimized = function (n) { + const dp = Array(n).fill(0); + dp[1] = 1; + dp[2] = 2; + + if (n >= 3) { + for (let i = 3; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + } + + return dp[n]; +}; + +// 시간복잡도: O(n) +// 공간복잡도: O(n) !! dp라는 배열대신 변수 두개로 O(1)로 줄일 수 있음 diff --git a/product-of-array-except-self/youngduck.js b/product-of-array-except-self/youngduck.js new file mode 100644 index 000000000..971f9c631 --- /dev/null +++ b/product-of-array-except-self/youngduck.js @@ -0,0 +1,28 @@ +/** + * @param {number[]} nums + * @return {number[]} + */ +var productExceptSelf = function (nums) { + const numsLength = nums.length; + + const result = new Array(numsLength).fill(1); + + // 일반적으로 for문 하나로 자기자신 제외하면서 곱셈을하면서 처리할경우 O(n^2). + // for문 두개로 나눠서 처리할경우 O(n). 누적곱 개념을 활용해줘야함 + // for문 하나로 누적곱 개념을 활용해서 처리할경우 O(n). + + let left = 1; + let right = 1; + + for (let i = 0; i < numsLength; i++) { + result[i] *= left; + left *= nums[i]; + + result[numsLength - i - 1] *= right; + right *= nums[numsLength - i - 1]; + } + + // 시간복잡도: O(n), 공간복잡도: O(1) + + return result; +}; diff --git a/valid-anagram/youngduck.js b/valid-anagram/youngduck.js new file mode 100644 index 000000000..faa3857bf --- /dev/null +++ b/valid-anagram/youngduck.js @@ -0,0 +1,59 @@ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ +var isAnagram = function (s, t) { + const mapS = new Map(); + const mapT = new Map(); + + [...s].map((item) => { + if (mapS.has(item)) { + const itemCount = mapS.get(item); + mapS.set(item, itemCount + 1); + } else { + mapS.set(item, 1); + } + }); + + [...t].map((item) => { + if (mapT.has(item)) { + const itemCount = mapT.get(item); + mapT.set(item, itemCount + 1); + } else { + mapT.set(item, 1); + } + }); + + // NOTE - t가 s의 anagram이라는 뜻을 갯수가 같지않아도 된다고 이해했으나 anagram정의는 s구성원을 모자람,남김없이 t를만들 수 있는 상태 + if (mapS.size !== mapT.size) { + return false; + } + + for (const [key, value] of mapS) { + if (mapT.get(key) !== value) { + return false; + } + } + + return true; +}; + +// 시간복잡도: O(n) +// - 문자열 s와 t를 각각 한 번씩 순회: O(n) + O(n) = O(2n) = O(n) +// - Map 비교를 위한 순회: O(k), 여기서 k는 고유 문자 개수 +// - 따라서 전체 시간복잡도는 O(n) +// 공간복잡도: O(1) +// - 두 개의 Map 객체 생성: mapS와 mapT +// - 각 Map은 최대 k개의 고유 문자를 저장 (k는 고유 문자 개수) +// - 소문자 영문자만 사용하므로 k ≤ 26 (a-z) +// - 따라서 전체 공간복잡도는 O(1) (상수 시간) + +// follow up: 유니코드 테스트 케이스. 큰 의미는 없음 +// console.log(isAnagram("😀😀", "😀😀😀")); // false +// console.log(isAnagram("한글글", "글한글")); // true +// console.log(isAnagram("café", "éfac")); // true +// console.log(isAnagram("Hello世界", "世界Hello")); // true +// console.log(isAnagram("안녕 하세요", "하세요 안녕")); // true +// console.log(isAnagram("Café", "éfac")); // false +// console.log(isAnagram("Café", "Éfac")); // false