diff --git a/3sum/jiji-hoon96.ts b/3sum/jiji-hoon96.ts index 3c8316e1db..e69de29bb2 100644 --- a/3sum/jiji-hoon96.ts +++ b/3sum/jiji-hoon96.ts @@ -1,71 +0,0 @@ -/** - * @param {number[]} nums - * @return {number[][]} - * - * 풀이 1 - * - * 이렇게 푸니까 시간복잡도 O(n^3) / 공간복잡도 O(n) 라서 복잡한 예시에는 time limit 이 발생함 - * 개선해보자.. - * - * function threeSum(nums: number[]): number[][] { - * nums.sort((a, b) => a - b); - * let result = [] - * for (let i= 0; i JSON.stringify(item))), - * str => JSON.parse(str) - * ); - * } - * - * 풀이 2 - * - * 투포인터를 활용해보자. - * 아래처럼 문제를 풀게되면 시간복잡도 O(n^2) / 공간복잡도 O(1) 이다. - * 시공간 복잡도가 줄긴하지만 메모리 사용량과 큰 숫자를 다룰 때 성능이 매우 좋다! - */ - - -function threeSum(nums: number[]): number[][] { - let result : number[][] = [] - nums.sort((a, b) => a - b); - const n = nums.length; - - for(let first = 0; first 0) break; - - //중복된 수는 건너뜀 - if(first > 0 && nums[first]===nums[first-1]) continue; - - let left = first + 1; - let right = n-1; - - while(left < right){ - const sum = nums[first] +nums[left] + nums[right]; - - if(sum < 0){ - left ++ - }else if(sum > 0){ - right --; - }else{ - result.push([nums[first],nums[left],nums[right]]); - // left, left+1 이 같을 때 중복된 수는 건너뜀 - while(left < right && nums[left] === nums[left+1]) left++; - // right, right+1 이 같을 때 중복된 수는 건너뜀 - while(left < right && nums[right] === nums[right-1]) right--; - left++; - right--; - } - } - } - return result; -} diff --git a/best-time-to-buy-and-sell-stock/jiji-hoon96.ts b/best-time-to-buy-and-sell-stock/jiji-hoon96.ts index 84c6244d5d..e69de29bb2 100644 --- a/best-time-to-buy-and-sell-stock/jiji-hoon96.ts +++ b/best-time-to-buy-and-sell-stock/jiji-hoon96.ts @@ -1,23 +0,0 @@ -function maxProfit(prices: number[]): number { - if (prices.length <= 1) return 0; - - let minPrice = prices[0]; - let maxProfit = 0; - - for (let i = 1; i < prices.length; i++) { - // 현재 가격이 최소가보다 낮으면 최소가 업데이트 - if (prices[i] < minPrice) { - minPrice = prices[i]; - } - // 현재 가격으로 팔았을 때의 이익 계산 - else { - const currentProfit = prices[i] - minPrice; - // 최대 이익 업데이트 - if (currentProfit > maxProfit) { - maxProfit = currentProfit; - } - } - } - - return maxProfit; -} diff --git a/climbing-stairs/jiji-hoon96.ts b/climbing-stairs/jiji-hoon96.ts index d4d1cb33e2..e69de29bb2 100644 --- a/climbing-stairs/jiji-hoon96.ts +++ b/climbing-stairs/jiji-hoon96.ts @@ -1,37 +0,0 @@ -/** - * @param n - * - * dp, 슬라이딩 윈도우 사용해서 풀 수 있다. - * - * 공간 복잡도를 줄이기 위해서 아래와같이 슬라이딩을 활용할 수 있다. - * - * function climbStairs(n: number): number { - * if (n <= 2) return n; - * - * let first = 1; // 1계단을 오르는 방법 수 - * let second = 2; // 2계단을 오르는 방법 수 - * - * for (let i = 3; i <= n; i++) { - * let current = first + second; - * first = second; - * second = current; - * } - * - * return second; - * } - */ - - -function climbStairs(n: number): number { - if(n <= 2) return n; - - let dp: number[] = new Array(n+1); - dp[1] = 1; - dp[2] = 2; - - for(let i=3;i<=n;i++){ - dp[i] = dp[i-1] +dp[i-2]; - } - - return dp[n] -}; diff --git a/combination-sum/jiji-hoon96.ts b/combination-sum/jiji-hoon96.ts index 710faec843..e69de29bb2 100644 --- a/combination-sum/jiji-hoon96.ts +++ b/combination-sum/jiji-hoon96.ts @@ -1,49 +0,0 @@ -/** - * - * @param candidates - * @param target - * - * backtracking 알고리즘으로 문제 해결 - * - */ - -function combinationSum(candidates: number[], target: number): number[][] { - const result : number[][] = []; - if(candidates.length === 0){ - return result ; - } - - candidates.sort((a,b)=> a-b); - - const validCandidates : number[] = candidates.filter(num => num <= target); - - if(validCandidates.length ===0) { - return result; - } - - const currentCombination : number[] = []; - - function backtrack (startIndex : number, remainingTarget : number) :void { - if(remainingTarget === 0){ - result.push([...currentCombination]); - return; - } - - for(let i=startIndex; i remainingTarget) { - break; - } - currentCombination.push(currentNum); - - backtrack(i,remainingTarget - currentNum) - - currentCombination.pop() - - } - } - - backtrack(0, target); - return result; - }; diff --git a/contains-duplicate/jiji-hoon96.ts b/contains-duplicate/jiji-hoon96.ts index af7a00da5f..de0be497c6 100644 --- a/contains-duplicate/jiji-hoon96.ts +++ b/contains-duplicate/jiji-hoon96.ts @@ -1,11 +1,9 @@ -/** - * 배열에 중복된 요소가 있는지 확인하는 함수 - * @param nums - 확인할 정수 배열 - * @returns 중복된 요소가 있으면 true, 모든 요소가 고유하면 false - * - * 기존에 for문을 사용해 filter,indexOf 방식을 사용했지만, 시간복잡도가 O(n^2)이라서 시간초과가 발생했다. - * Set을 사용하면 중복된 요소를 제거하고, size를 통해 중복된 요소가 있는지 확인할 수 있다. - */ function containsDuplicate(nums: number[]): boolean { - return new Set(nums).size !== nums.length -}; + let result = false; + if (nums.length !== new Set(nums).size) result = true; + + return result; +} + +containsDuplicate([1, 2, 3, 1]); //true +containsDuplicate([1, 1, 1, 3, 3, 4, 3, 2, 4, 2]); //true diff --git a/decode-ways/jiji-hoon96.ts b/decode-ways/jiji-hoon96.ts index 6ff14393a0..e69de29bb2 100644 --- a/decode-ways/jiji-hoon96.ts +++ b/decode-ways/jiji-hoon96.ts @@ -1,35 +0,0 @@ -function numDecodings(s: string): number { - // 빈 문자열이거나 0으로 시작하면 디코딩 불가 - if (!s || s[0] === '0') return 0; - - const n = s.length; - - // 문자열 길이가 1이면 바로 결과 반환 - if (n === 1) return 1; - - // 초기 상태 - let prev = 1; // dp[0] - let curr = s[0] === '0' ? 0 : 1; // dp[1] - - for (let i = 2; i <= n; i++) { - let temp = 0; - const oneDigit = parseInt(s[i - 1]); - const twoDigit = parseInt(s[i - 2] + s[i - 1]); - - // 한 자리 숫자로 디코딩 (1-9) - if (oneDigit >= 1) { - temp += curr; - } - - // 두 자리 숫자로 디코딩 (10-26) - if (twoDigit >= 10 && twoDigit <= 26) { - temp += prev; - } - - // 상태 업데이트 - prev = curr; - curr = temp; - } - - return curr; -} diff --git a/house-robber/jiji-hoon96.ts b/house-robber/jiji-hoon96.ts index 3bd03c4bc4..50556fd607 100644 --- a/house-robber/jiji-hoon96.ts +++ b/house-robber/jiji-hoon96.ts @@ -1,29 +1,17 @@ -/** - * - * @param {number[]} nums - * @param nums - * - * 풀이 - * dp 배열을 사용해 nums 배열의 길이만큼 초기화한다. - * dp[0]은 nums[0]으로 초기화하고, dp[1]은 nums[0]과 nums[1] 중 큰 값으로 초기화한다. - * dp[2]부터는 dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i])로 초기화한다. - * dp[i-1]은 i번째 집을 털지 않은 경우, dp[i-2] + nums[i]는 i번째 집을 털고 i-1번째 집을 털지 않은 경우이다. - * - */ - function rob(nums: number[]): number { - const n = nums.length - if(n===0) return 0 - if(n===1) return nums[0]; + let take = 0; + let skip = 0; + + for (const num of nums) { + const nextTake = skip + num; + const nextSkip = Math.max(take, skip); - let dp0 = nums[0] - let dp1 = Math.max(nums[0], nums[1]); + take = nextTake; + skip = nextSkip; + } - for(let i=2;i a - b); + let result = 0; + if (nums.length === 0) return result; - if (sortedNums.length === 1) return 1; + const no_duplicate = [...new Set(nums)].sort((a, b) => a - b); + let count = 1; - let currentCount = 1; - let maxCount = 1; - - for(let i = 0; i < sortedNums.length - 1; i++) { - const currentNum = sortedNums[i]; - const nextNum = sortedNums[i + 1]; - - if(currentNum + 1 === nextNum) { - currentCount++; - maxCount = Math.max(maxCount, currentCount); - } else { - currentCount = 1; - } + for (let i = 0; i < no_duplicate.length; i++) { + if (no_duplicate[i + 1] - no_duplicate[i] === 1) { + count++; + } else { + result = Math.max(result, count); + count = 1; } + } - return maxCount; + result = Math.max(result, count); + return result; } + +longestConsecutive([0, 3, 7, 2, 5, 8, 4, 6, 0, 1]); //9 +longestConsecutive([100, 4, 200, 1, 3, 2]); //4 +longestConsecutive([1, 0, 1, 2]); //3 +longestConsecutive([]); //0 +longestConsecutive([1, 2, 6, 7, 8]); //3 diff --git a/maximum-depth-of-binary-tree/jiji-hoon96.ts b/maximum-depth-of-binary-tree/jiji-hoon96.ts index 343a1f5c44..e69de29bb2 100644 --- a/maximum-depth-of-binary-tree/jiji-hoon96.ts +++ b/maximum-depth-of-binary-tree/jiji-hoon96.ts @@ -1,29 +0,0 @@ -/** - * Definition for a binary tree node. - * class TreeNode { - * val: number - * left: TreeNode | null - * right: TreeNode | null - * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { - * this.val = (val===undefined ? 0 : val) - * this.left = (left===undefined ? null : left) - * this.right = (right===undefined ? null : right) - * } - * } - */ - -function maxDepth(root: TreeNode | null): number { - // 기본 케이스: 노드가 없는 경우 깊이는 0 - if (root === null) { - return 0; - } - - // 왼쪽 서브트리의 최대 깊이 - const leftDepth = maxDepth(root.left); - - // 오른쪽 서브트리의 최대 깊이 - const rightDepth = maxDepth(root.right); - - // 현재 노드의 깊이는 왼쪽과 오른쪽 서브트리 중 더 깊은 것에 1을 더한 값 - return Math.max(leftDepth, rightDepth) + 1; -} diff --git a/maximum-subarray/jiji-hoon96.ts b/maximum-subarray/jiji-hoon96.ts index 282274cad6..e69de29bb2 100644 --- a/maximum-subarray/jiji-hoon96.ts +++ b/maximum-subarray/jiji-hoon96.ts @@ -1,20 +0,0 @@ -function maxSubArray(nums: number[]): number { - // 배열이 비어 있는 경우 체크 (제약조건에 의해 발생하지 않지만, 견고한 코드를 위해) - if (nums.length === 0) return 0; - - // 현재 부분 배열의 합과 전체 최대 부분 배열 합 초기화 - let currentSum = nums[0]; - let maxSum = nums[0]; - - // 두 번째 요소부터 순회 - for (let i = 1; i < nums.length; i++) { - // 현재 위치에서의 최대 부분 배열 합 계산 - // "이전까지의 합 + 현재 요소" vs "현재 요소부터 새로 시작" - currentSum = Math.max(nums[i], currentSum + nums[i]); - - // 전체 최대값 업데이트 - maxSum = Math.max(maxSum, currentSum); - } - - return maxSum; -} diff --git a/merge-two-sorted-lists/jiji-hoon96.ts b/merge-two-sorted-lists/jiji-hoon96.ts index c0b375f9b8..e69de29bb2 100644 --- a/merge-two-sorted-lists/jiji-hoon96.ts +++ b/merge-two-sorted-lists/jiji-hoon96.ts @@ -1,35 +0,0 @@ -/** - * Definition for singly-linked list. - * class ListNode { - * val: number - * next: ListNode | null - * constructor(val?: number, next?: ListNode | null) { - * this.val = (val===undefined ? 0 : val) - * this.next = (next===undefined ? null : next) - * } - * } - */ - -function mergeTwoLists(list1: ListNode | null, list2: ListNode | null): ListNode | null { - // 더미 헤드 노드를 생성하여 결과 리스트의 시작점으로 사용 - const dummy = new ListNode(0); - let current = dummy; - - // 두 리스트를 순회하면서 더 작은 값을 가진 노드를 결과 리스트에 연결 - while (list1 !== null && list2 !== null) { - if (list1.val <= list2.val) { - current.next = list1; - list1 = list1.next; - } else { - current.next = list2; - list2 = list2.next; - } - current = current.next; - } - - // 남은 노드들을 결과 리스트에 연결 - current.next = list1 === null ? list2 : list1; - - // 더미 노드의 다음 노드가 실제 병합된 리스트의 헤드 - return dummy.next; -} diff --git a/number-of-1-bits/jiji-hoon96.ts b/number-of-1-bits/jiji-hoon96.ts index ad2d34fd42..e69de29bb2 100644 --- a/number-of-1-bits/jiji-hoon96.ts +++ b/number-of-1-bits/jiji-hoon96.ts @@ -1,31 +0,0 @@ -/** - * - * @param n - * - * 풀이 1 - * - * function hammingWeight(n: number): number { - * let parse = (n).toString(2); - * let count = 0; - * for (const item of parse){ - * if(+item ===1) count ++ - * } - * return count - * }; - * - * 숫자를 문자열로 변환할때 오버헤드 발생 - * 비트 연산을 사용해야할듯! - * - * 검색해보니 Brian Kernighan 알고리즘을 사용하면 더 효율적이라고 한다 - */ - -function hammingWeight(n: number): number { - let count = 0; - - while (n !== 0) { - n &= (n - 1); - count++; - } - - return count; -} diff --git a/product-of-array-except-self/jiji-hoon96.ts b/product-of-array-except-self/jiji-hoon96.ts index 147befc305..e69de29bb2 100644 --- a/product-of-array-except-self/jiji-hoon96.ts +++ b/product-of-array-except-self/jiji-hoon96.ts @@ -1,44 +0,0 @@ -/** - * - * 풀이 1 - * - * 아래와 같이 문제를 푸니, 성능적으로 문제가 발생했다. - * 시간복잡도는 0(n2) / 공간 복잡도는 0(n) - * - * function productExceptSelf(nums: number[]): number[] { - * if(nums.every(num => num === 0)) return nums; 시간 복잡도 0(n) - * if(nums.length > 2 && nums.filter(num=> num ===0).length > 1) return new Array(nums.length).fill(0) 시간 복잡도 0(n) 공간 복잡도 0(n) - * let result = [] 공간 복잡도 0(n) - * for(let i =0;i index !== i); 시간 복잡도 0(n) 공간 복잡도 0(n) - * for(let item of a){ 시간 복잡도 0(n) - * multi *= item - * } - * result.push(multi) - * } - * return result - * }; - * - * 풀이 2 는 누적합 알고리즘을 사용해서 왼쪽 방향에서 시작해 오른쪽 방향으로 곱하는 방식으로 문제 해결 - * - */ - -function productExceptSelf(nums: number[]): number[] { - const n = nums.length; - const result = new Array(n).fill(1); - - let leftProduct = 1; - for (let i = 0; i < n; i++) { - result[i] *= leftProduct; - leftProduct *= nums[i]; - } - - let rightProduct = 1; - for (let i = n - 1; i >= 0; i--) { - result[i] *= rightProduct; - rightProduct *= nums[i]; - } - - return result; -} diff --git a/top-k-frequent-elements/jiji-hoon96.ts b/top-k-frequent-elements/jiji-hoon96.ts index a8c030bcfb..060f89fc76 100644 --- a/top-k-frequent-elements/jiji-hoon96.ts +++ b/top-k-frequent-elements/jiji-hoon96.ts @@ -1,24 +1,16 @@ -/** - * nums 배열에서 가장 많이 등장하는 k개의 요소를 반환하는 문제 - * @param {number[]} nums - * @param {number} k - * @return {number[]} - * - * 풀이 - * countObject 객체를 생성해 nums 배열의 요소를 key로, 등장 횟수를 value로 저장한다. - * 문제는 해결 했지만, O(n log n)의 시간복잡도로 개선할 여지가 있다. - * 개선하기 위해서 Heap 을 사용해볼 수 있다고하는데, Heap을 사용하는 방법을 알아야겠다. - * Heap 을 사용하면 O(n log k)의 시간복잡도로 개선할 수 있다고 한다.. - */ - function topKFrequent(nums: number[], k: number): number[] { - const countObject: { [key: number]: number } = {}; - - for(const num of nums){ - countObject[num] = (countObject[num] || 0) + 1; - } + const count = {} as Record; + for (const num of nums) { + count[num] = (count[num] || 0) + 1; + } - const sortObject = Object.entries(countObject).sort((a,b) => b[1] - a[1]); + return Object.entries(count) + .sort((a, b) => b[1] - a[1]) + .slice(0, k) + .map(([key]) => Number(key)); +} - return sortObject.slice(0, k).map(([key]) => Number(key)); -}; +topKFrequent([1, 1, 1, 2, 2, 3], 2); // [1,2] +topKFrequent([1], 1); // [1] +topKFrequent([1, 2, 1, 2, 1, 2, 3, 1, 3, 2], 2); // [1,2] +topKFrequent([4, 1, -1, 2, -1, 2, 3], 2); // [2,-1] diff --git a/two-sum/jiji-hoon96.ts b/two-sum/jiji-hoon96.ts index 014875bca5..dfbe24e97a 100644 --- a/two-sum/jiji-hoon96.ts +++ b/two-sum/jiji-hoon96.ts @@ -1,27 +1,14 @@ -/** - * 두 수의 합이 특정 값(target)이 되게 하는 나머지 하나의 수를 찾는 문제 => 보수를 찾는 문제 - * @param nums - 정수 배열 - * @param target - 두 수의 합 - * @returns 두 수의 인덱스 - * - * 풀이 1 - * 이중 for 문을 사용해 nums의 요소를 더한 값이 target과 같은지 확인한다. - * 이렇게 해결했더니 O(n^2)의 시간복잡도로 시간초과가 발생할 수 있다고 판단. - * - * 풀이 2 - * Map을 사용해 for 문을 한 번만 사용하도록 수정해보았다. - */ - function twoSum(nums: number[], target: number): number[] { - const newMap = new Map(); - - for(let i=0; i = {}; - - // 여기서는 늘려주고 O(n) - for(let char of s){ - charCount[char] = (charCount[char] || 0) + 1; - } - - // 여기는 존재하면 없애주자 O(n) - for(let char of t){ - if(!charCount[char]) return false; - charCount[char]--; - } - - return true -}; diff --git a/valid-palindrome/jiji-hoon96.ts b/valid-palindrome/jiji-hoon96.ts index 9dab82428b..e69de29bb2 100644 --- a/valid-palindrome/jiji-hoon96.ts +++ b/valid-palindrome/jiji-hoon96.ts @@ -1,34 +0,0 @@ -/** - * - * @param s - * - * 풀이 1 - * - * 특수문자 정규 표현식이 복잡하고, 분할과 합치는 과정이 중복된다 - * - * function isPalindrome(s: string): boolean { - * const reg= /[\{\}\[\]\/?.,;:|\)*~`!^\-_+<>@\#$%&\\\=\(\'\"]/gi; - * let palindrome= s.replace(reg,'').toLowerCase().split(''); - * - * return palindrome.join('').replace(/ /g,"")===palindrome.reverse().join('').replace(/ /g,"") - * }; - * - * 그래서 생각한 풀이 2는 s consists only of printable ASCII characters. 을 보고 숫자와 알파벳을 제외하고 나머지는 제거하고 - * 투포인트 방법으로 변경해서 문제 해결 - */ - -function isPalindrome(s: string): boolean { - const cleanStr = s.toLowerCase().replace(/[^a-z0-9]/g, ''); - - let left = 0; - let right = cleanStr.length-1; - - while(left < right){ - if(cleanStr[left] !== cleanStr[right]){ - return false; - } - left++; - right--; - } - return true -}; diff --git a/validate-binary-search-tree/jiji-hoon96.ts b/validate-binary-search-tree/jiji-hoon96.ts index 04edebee6b..e69de29bb2 100644 --- a/validate-binary-search-tree/jiji-hoon96.ts +++ b/validate-binary-search-tree/jiji-hoon96.ts @@ -1,58 +0,0 @@ -/** - * Definition for a binary tree node. - * class TreeNode { - * val: number - * left: TreeNode | null - * right: TreeNode | null - * constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) { - * this.val = (val===undefined ? 0 : val) - * this.left = (left===undefined ? null : left) - * this.right = (right===undefined ? null : right) - * } - * } - * - * 문제가 너무 이해가 안감.. 처음보는 개념 너무 어려움 - * - * 우선 이진트리에 대해서 공부해보기 위해서 https://www.youtube.com/watch?v=i57ZGhOVPcI 을 살펴보았음 - * - * 풀이 1 - * - * 재귀적 깊이 우선 탐색(DFS) 방식으로 해결 (메모리 효율성 미세하게 좋음) => 이 방법이 이진트리 개념에 조금 더 직관적임 - * - * function isValidBST(root: TreeNode | null): boolean { - * function validate(node: TreeNode | null, min: number | null, max: number | null): boolean { - * if (node === null) return true; - * - * if ((min !== null && node.val <= min) || (max !== null && node.val >= max)) { - * return false; - * } - * - * return validate(node.left, min, node.val) && validate(node.right, node.val, max); - * } - * - * return validate(root, null, null); - * } - * - * 풀이 2 - * - * 중위 순회 방법을 활용해보기 => 코드는 이 방법이 더 쉽고 이해하기 좋음 - */ - -function isValidBST(root: TreeNode | null): boolean { - let prev: number | null = null; - - function inorder(node: TreeNode | null): boolean { - if (node === null) return true; - - if (!inorder(node.left)) return false; - - if (prev !== null && node.val <= prev) { - return false; - } - prev = node.val; - - return inorder(node.right); - } - - return inorder(root); -}