diff --git a/combination-sum/togo26.js b/combination-sum/togo26.js new file mode 100644 index 000000000..9df46aa5f --- /dev/null +++ b/combination-sum/togo26.js @@ -0,0 +1,35 @@ +/** + * @param {number[]} candidates + * @param {number} target + * @return {number[][]} + */ + +// C = candidates, T = target, m = smallest candidate value +// TC: O(C^(T/m)) -> 후보와 타겟 깊이 간 지수적 증가 +// SC: O(T/m) -> 재귀 스택 깊이 (*출력 결과는 복잡도 계산 제외) +var combinationSum = function (candidates, target) { + const result = []; + + function go(candi, combi, acc, start) { + if (acc === target) { + result.push(combi); + return; + } + + for (let i = start; i < candi.length; i++) { + const newAcc = acc + candi[i]; + const newCombi = [...combi, candi[i]]; + if (newAcc > target) break; + go(candi, newCombi, newAcc, i); + } + } + + go( + [...candidates].sort((a, b) => a - b), // 값 순서 보장으로 새로운 누적 값에서 조기 종료 가능. 오름차순에 따라 newAcc가 target을 넘어가면 이후의 후보들은 계산할 필요 없음. + [], + 0, + 0, + ); + + return result; +}; diff --git a/decode-ways/togo26.js b/decode-ways/togo26.js new file mode 100644 index 000000000..c2a79a4aa --- /dev/null +++ b/decode-ways/togo26.js @@ -0,0 +1,76 @@ +/** + * @param {string} s + * @return {number} + */ + +// TLE +// var numDecodings = function(s) { +// const codeMap = {}; +// for (let i = 1; i <= 26; i++) { +// codeMap[i] = String.fromCharCode(64 + i); +// } + +// const memo = {}; +// let count = 0; +// function go(string, start) { +// if (start >= string.length) { +// count++; +// return; +// } + +// const firstDigit = string[start]; +// const secondDigit = string[start + 1]; +// if (firstDigit === "0") return; + +// if (codeMap[firstDigit]) { +// go(string, start + 1); +// } + +// const twoDigits = firstDigit + secondDigit; +// if (Number(twoDigits) <= 26 && codeMap[twoDigits]) { +// go(string, start + 2); +// } +// } + +// go(s, 0, ""); + +// return count; +// }; + +// TC: O(n) -> string 길이만큼 +// SC: O(n) -> string 길이만큼 스택 생성 +var numDecodings = function (s) { + const codeMap = {}; + for (let i = 1; i <= 26; i++) { + codeMap[i] = String.fromCharCode(64 + i); + } + + const memo = {}; + + function go(start) { + if (start >= s.length) return 1; + if (memo[start] !== undefined) return memo[start]; + + const firstDigit = s[start]; + const secondDigit = s[start + 1]; + + if (firstDigit === '0') return 0; + + let ways = 0; + + if (codeMap[firstDigit]) { + ways += go(start + 1); + } + + const twoDigits = firstDigit + secondDigit; + if (Number(twoDigits) <= 26 && codeMap[twoDigits]) { + ways += go(start + 2); + } + + memo[start] = ways; + + return ways; + } + + return go(0); +}; diff --git a/maximum-subarray/togo26.js b/maximum-subarray/togo26.js new file mode 100644 index 000000000..9d824c1bc --- /dev/null +++ b/maximum-subarray/togo26.js @@ -0,0 +1,53 @@ +/** + * @param {number[]} nums + * @return {number} + */ + +// TLE +// var maxSubArray = function(nums) { +// if (nums.length <= 1) return nums[0]; + +// const sum = (arr) => arr.reduce((acc, cur) => { +// acc += cur; +// return acc; +// }, 0); + +// let total = -Infinity; +// for (let i = 0; i < nums.length; i++) { +// for (let j = i; j < nums.length; j++) { +// const result = sum(nums.slice(i, j + 1)); +// total = Math.max(total, result); +// } +// } + +// return total; +// }; + +// TLE +// var maxSubArray = function(nums) { +// if (nums.length <= 1) return nums[0]; + +// let total = -Infinity; +// for (let i = 0; i < nums.length; i++) { +// let acc = 0; +// for (let j = i; j < nums.length; j++) { +// acc += nums[j]; +// total = Math.max(total, acc); +// } +// } + +// return total; +// }; + +// TC: O(n) / SC: O(1) +var maxSubArray = function (nums) { + let total = nums[0]; + let acc = nums[0]; + + for (let i = 1; i < nums.length; i++) { + acc = Math.max(nums[i], acc + nums[i]); // 이전 누적값 선택 vs 현재값 선택 (카데인 알고리즘) + total = Math.max(total, acc); // 최대 합산 값 갱신 + } + + return total; +}; diff --git a/number-of-1-bits/togo26.js b/number-of-1-bits/togo26.js new file mode 100644 index 000000000..cdefd18fb --- /dev/null +++ b/number-of-1-bits/togo26.js @@ -0,0 +1,64 @@ +/** + * @param {number} n + * @return {number} + */ + +// 31 비트 범위는 상수로 취급 + +// Log-based calculation +// TC: O(1) / SC: O(1) +// 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) +var hammingWeight = function (n) { + const BIT_RANGE = 31; + let bits = Array.from({ length: BIT_RANGE }).fill(0); + + let remainder = n; + while (remainder > 0) { + const exp = Math.min(Math.floor(Math.log2(remainder)), BIT_RANGE - 1); + bits[exp] = true; + const value = remainder <= 1 ? 1 : Math.pow(2, exp); + remainder -= value; + } + + return bits.filter(value => value).length; +}; + +// Log-based calculation without an array +// TC: O(1) / SC: O(1) +// 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(1) +var hammingWeight = function (n) { + const BIT_RANGE = 31; + let bitCount = 0; + + let remainder = n; + while (remainder > 0) { + const exp = Math.min(Math.floor(Math.log2(remainder)), BIT_RANGE - 1); + const value = remainder <= 1 ? 1 : Math.pow(2, exp); + remainder -= value; + bitCount++; + } + + return bitCount; +}; + +// Converting binary string with the JS feature +// TC: O(1) / SC: O(1) +// 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) +var hammingWeight = function (n) { + const binaryString = n.toString(2); + return [...binaryString].filter(v => v === '1').length; +}; + +// Bit manipulation +// TC: O(1) / SC: O(1) +// 비트 제약이 없을 경우 -> TC: O(logn) / SC: O(logn) +var hammingWeight = function (n) { + let count = 0; + + while (n > 0) { + n = n & (n - 1); + count++; + } + + return count; +}; diff --git a/valid-palindrome/togo26.js b/valid-palindrome/togo26.js new file mode 100644 index 000000000..993eff86b --- /dev/null +++ b/valid-palindrome/togo26.js @@ -0,0 +1,28 @@ +/** + * @param {string} s + * @return {boolean} + */ +// TC: O(n) / SC: O(n) +var isPalindrome = function (s) { + const processed = s.toLowerCase().replaceAll(/[^a-z0-9]+/g, ''); + const reversed = [...processed].reverse().join(''); + return processed === reversed; +}; + +// TC: O(n) / SC: O(1) +// With two pointers +var isPalindrome = function (s) { + const nonAlphanumeric = new RegExp(/[^a-zA-Z0-9]+/); + let left = 0; + let right = s.length - 1; + + while (left < right) { + while (left < right && nonAlphanumeric.test(s[left])) left++; // 단일 문자 테스트 O(1) 상수 취급 + while (left < right && nonAlphanumeric.test(s[right])) right--; + if (s[left].toLowerCase() !== s[right].toLowerCase()) return false; + left++; + right--; + } + + return true; +};