diff --git a/construct-binary-tree-from-preorder-and-inorder-traversal/naringst.js b/construct-binary-tree-from-preorder-and-inorder-traversal/naringst.js new file mode 100644 index 000000000..d7cd58255 --- /dev/null +++ b/construct-binary-tree-from-preorder-and-inorder-traversal/naringst.js @@ -0,0 +1,32 @@ +/** + * [Idea] + * preorder는 val -> left -> right + * inorder는 left -> val -> right + * 따라서 preorder의 val을 기준으로 inorder 배열을 왼, 오로 나누면 val의 왼쪽 트리, 오른쪽 트리 구조가 된다. + * + * node.val = preorder[i] + * const leftAndRight = inorder.split(preorder[0]) + * node.left = leftAndRight[0] + * node.right = leftAndRight[1] + * + * 구현이 안되어 코드 참고 ... + * 직접 배열을 나누지 않고, val의 인덱스를 찾아 그 값을 기준으로 slice된 배열을 재귀적으로 buildTree에 넣는다. + * + */ + +var buildTree = function (preorder, inorder) { + if (preorder.length === 0 || inorder.length === 0) { + return null; + } + const root = new TreeNode(preorder[0]); + const rootIndex = inorder.indexOf(root.val); + root.left = buildTree( + preorder.slice(1, rootIndex + 1), + inorder.slice(0, rootIndex) + ); + root.right = buildTree( + preorder.slice(rootIndex + 1, preorder.length), + inorder.slice(rootIndex + 1, inorder.length) + ); + return root; +}; diff --git a/counting-bits/naringst.js b/counting-bits/naringst.js new file mode 100644 index 000000000..fbd8b247b --- /dev/null +++ b/counting-bits/naringst.js @@ -0,0 +1,61 @@ +/** + * @param {number} n + * @return {number[]} + */ + +/** + * Runtime: 82ms, Memory: 57.03MB + * + * Time complexity: O(logN < N+1 ? N+1 : logN 단,보통의 경우 N+1) + * Space complexity: O(N+1) + * + * Note: necessary to think of an alternative approach + * **/ + +function decimalToBinary(decimal) { + let binaryBitCount = 0; + + while (decimal > 1) { + binaryBitCount += decimal % 2 === 1 ? 1 : 0; + decimal = Math.floor(decimal / 2); + } + binaryBitCount += decimal === 1 ? 1 : 0; + + return binaryBitCount; +} +var countBits = function (n) { + const answer = []; + + for (let i = 0; i < n + 1; i++) { + answer.push(decimalToBinary(i)); + } + + return answer; +}; + +/** + * 인상 깊었던 풀이 + * + * Runtime : 60ms + * + * 비트 연산의 속성과 dp를 활용해 푼 풀이 + * + * [간단설명] + * 4일때 100이고, 5일때 101, 6일때 110이다. + * 이때 4를 2진수로 표현한 100이 가진 1의 개수를 활용해 5,6의 1의 개수를 찾는 것이다. + * 100에서 1을 더한 101이나, 110은 100의 1의 개수인 1개에서 1을 더한 2개가 된다. + * result[5 & 4] => 비트 연산을 통해 100과 101의 비트 앤드 연산을 해서 100이 되고, 이는 101의 가장 오른쪽 1을 제거한 값이 된다. + * result[6 & 5] => 비트 연산을 통해 110과 101의 비트 앤드 연산을 해서 100이 되고, 이는 110의 가장 오른쪽 1을 제거한 값이 된다. + * 이진수는 1씩 더하기 때문에 나보다 하나 큰 수와 앤드 연산을 하면 작은 수가 0으로 끝나면 큰 수는 1로 끝나고, + * 작은 수가 1로 끝나면 큰 수는 0으로 끝나기 대문에 이런 속성을 갖는다. + * + * + */ + +var countBits = function (n) { + let result = new Array(n + 1).fill(0); + for (let i = 1; i <= n; i++) { + result[i] = result[i & (i - 1)] + 1; + } + return result; +}; diff --git a/decode-ways/naringst.js b/decode-ways/naringst.js new file mode 100644 index 000000000..3f8a55f71 --- /dev/null +++ b/decode-ways/naringst.js @@ -0,0 +1,65 @@ +/** + * 답안 참고 풀이 + * 💡 왜 못풀었을까? + * 한 자리, 두 자리 경우 중 가능한 경우에 대해 조건 분기를 제대로 못했음. + */ + +var numDecodings = function (s) { + const dp = Array(s.length + 1).fill(0); + + // 예외 처리 + if (s[0] === "0") return 0; + + dp[0] = 1; + dp[1] = 1; + + for (let i = 2; i <= s.length; i++) { + // 1자리 값 , 2자리 값 파싱 + const oneDigit = +s.slice(i - 1, i); + const twoDigits = +s.slice(i - 2, i); + // 각 값이 가능한지 판단: 여기를 제대로 식을 세우지 못했음 + if (oneDigit > 0) dp[i] = dp[i - 1]; + if (twoDigits >= 10 && twoDigits <= 26) dp[i] += dp[i - 2]; + } + return dp[s.length]; +}; + +/** + * 처음 떠올렸던 방식인 dp로는 풀기 어려울 것 같아 풀이를 다시 생각해 봄. + * 문자를 만드는 걸 숫자를 칸막이로 나누는 개념으로 생각. ex) 2/5/2/4, 2/52/4 + * 그러면 숫자와 숫자 사이 칸막이를 넣을지, 말지의 문제로 귀결 + * 2의 (s.length-1)제곱의 경우의 수 발생 + * 그 중 안되는 경우를 제외하면 답이 됨. + * */ + +/** + * @param {string} s + * @return {number} + */ + +/** + * NOTE 첫 풀이 -> 잘못된 풀이 + * dp를 사용해서 한자리씩, 그리고 다음 자릿수와 함께 두 자리 씩 확인해 경우의 수를 추가하면 된다고 생각했는데, + * 2101과 같은 경우에 동작하지 않음. + * + * */ + +var numDecodings = function (s) { + let dp = Array(s.length).fill(0); + + // 시작 숫자가 0이면 불가능 -> 예외 처리 + if (s[0] !== "0") { + dp[0] = 1; + } else { + return 0; + } + + for (let i = 0; i < s.length; i++) { + if (i !== s.length - 1 && Number(s[i] + s[i + 1]) <= 26 && s[i] !== "0") { + dp[i + 1] = dp[i] + 1; + } else if (s[i + 1] === "0" || Number(s[i] + s[i + 1]) > 26) { + dp[i + 1] = dp[i]; + } + } + return dp[s.length - 1]; +}; diff --git a/valid-anagram/naringst.js b/valid-anagram/naringst.js new file mode 100644 index 000000000..215c5db0f --- /dev/null +++ b/valid-anagram/naringst.js @@ -0,0 +1,74 @@ +/** + * @param {string} s + * @param {string} t + * @return {boolean} + */ + +/** + * Runtime: 68ms, Memory: 54.49MB + * n = s.length > t.length ? s.length : t.length + * Time complexity: O(n) + * Space complexity: O(n) + * + * **/ + +function arrayToDict(arr) { + const dict = {}; + for (let element of arr) { + if (dict[element]) { + dict[element] += 1; + } else { + dict[element] = 1; + } + } + return dict; +} + +function isSameDict(dict1, dict2) { + if (Object.keys(dict1).length !== Object.keys(dict2).length) { + return false; + } + + for (const elem in dict1) { + if (dict1[elem] !== dict2[elem]) { + return false; + } + } + return true; +} + +var isAnagram = function (s, t) { + const sArr = [...s]; + const tArr = [...t]; + + const sDict = arrayToDict(sArr); + const tDict = arrayToDict(tArr); + + return isSameDict(sDict, tDict); +}; + +/** + * 같은 로직인데 53ms 걸린 다른 풀이 + */ + +var isAnagram = function (s, t) { + if (s.length !== t.length) { + return false; + } + + const countA = {}; + const countB = {}; + + for (let i = 0; i < s.length; i++) { + countA[s[i]] = 1 + (countA[s[i]] || 0); + countB[t[i]] = 1 + (countB[t[i]] || 0); + } + + for (const key in countA) { + if (countA[key] !== countB[key]) { + return false; + } + } + + return true; +};